summaryrefslogtreecommitdiff
path: root/Utilities/cmcurl
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-10-30 15:39:57 -0700
committerAnas Nashif <anas.nashif@intel.com>2012-10-30 15:39:57 -0700
commit035c7fabc3b82cbc9a346c11abe2e9462b4c0379 (patch)
tree7e40f5a790eae329a8c5d3e59f046451767956ff /Utilities/cmcurl
downloadcmake-035c7fabc3b82cbc9a346c11abe2e9462b4c0379.tar.gz
cmake-035c7fabc3b82cbc9a346c11abe2e9462b4c0379.tar.bz2
cmake-035c7fabc3b82cbc9a346c11abe2e9462b4c0379.zip
Imported Upstream version 2.8.9upstream/2.8.9
Diffstat (limited to 'Utilities/cmcurl')
-rw-r--r--Utilities/cmcurl/CMake/CMakeConfigurableFile.in2
-rw-r--r--Utilities/cmcurl/CMake/CurlCheckCSourceCompiles.cmake75
-rw-r--r--Utilities/cmcurl/CMake/CurlCheckCSourceRuns.cmake83
-rw-r--r--Utilities/cmcurl/CMake/CurlTests.c526
-rw-r--r--Utilities/cmcurl/CMake/OtherTests.cmake242
-rw-r--r--Utilities/cmcurl/CMakeLists.txt722
-rw-r--r--Utilities/cmcurl/COPYING21
-rw-r--r--Utilities/cmcurl/Platforms/WindowsCache.cmake121
-rw-r--r--Utilities/cmcurl/Platforms/config-aix.h486
-rw-r--r--Utilities/cmcurl/Testing/CMakeLists.txt19
-rw-r--r--Utilities/cmcurl/Testing/curlgtk.c95
-rw-r--r--Utilities/cmcurl/Testing/curltest.c159
-rw-r--r--Utilities/cmcurl/Testing/ftpget.c84
-rw-r--r--Utilities/cmcurl/Testing/ftpgetresp.c64
-rw-r--r--Utilities/cmcurl/Testing/ftpupload.c93
-rw-r--r--Utilities/cmcurl/Testing/getinmemory.c83
-rw-r--r--Utilities/cmcurl/Testing/http-post.c35
-rw-r--r--Utilities/cmcurl/Testing/httpput.c100
-rw-r--r--Utilities/cmcurl/Testing/multithread.c70
-rw-r--r--Utilities/cmcurl/Testing/persistant.c53
-rw-r--r--Utilities/cmcurl/Testing/postit2.c92
-rw-r--r--Utilities/cmcurl/Testing/sepheaders.c78
-rw-r--r--Utilities/cmcurl/Testing/simple.c28
-rw-r--r--Utilities/cmcurl/Testing/simplessl.c118
-rw-r--r--Utilities/cmcurl/Testing/testconfig.h.in7
-rw-r--r--Utilities/cmcurl/Testing/win32sockets.c49
-rw-r--r--Utilities/cmcurl/amigaos.c74
-rw-r--r--Utilities/cmcurl/amigaos.h58
-rw-r--r--Utilities/cmcurl/arpa_telnet.h101
-rw-r--r--Utilities/cmcurl/base64.c366
-rw-r--r--Utilities/cmcurl/base64.h28
-rw-r--r--Utilities/cmcurl/ca-bundle.h1
-rw-r--r--Utilities/cmcurl/config.h.in726
-rw-r--r--Utilities/cmcurl/connect.c905
-rw-r--r--Utilities/cmcurl/connect.h46
-rw-r--r--Utilities/cmcurl/content_encoding.c424
-rw-r--r--Utilities/cmcurl/content_encoding.h41
-rw-r--r--Utilities/cmcurl/cookie.c1017
-rw-r--r--Utilities/cmcurl/cookie.h107
-rw-r--r--Utilities/cmcurl/curl/curl.h1644
-rw-r--r--Utilities/cmcurl/curl/curlver.h56
-rw-r--r--Utilities/cmcurl/curl/easy.h81
-rw-r--r--Utilities/cmcurl/curl/mprintf.h80
-rw-r--r--Utilities/cmcurl/curl/multi.h327
-rw-r--r--Utilities/cmcurl/curl/stdcheaders.h34
-rw-r--r--Utilities/cmcurl/curl/types.h1
-rw-r--r--Utilities/cmcurl/curlx.h107
-rw-r--r--Utilities/cmcurl/dict.c280
-rw-r--r--Utilities/cmcurl/dict.h30
-rw-r--r--Utilities/cmcurl/easy.c895
-rw-r--r--Utilities/cmcurl/easyif.h40
-rw-r--r--Utilities/cmcurl/escape.c181
-rw-r--r--Utilities/cmcurl/escape.h30
-rw-r--r--Utilities/cmcurl/file.c407
-rw-r--r--Utilities/cmcurl/file.h31
-rw-r--r--Utilities/cmcurl/formdata.c1694
-rw-r--r--Utilities/cmcurl/formdata.h97
-rw-r--r--Utilities/cmcurl/ftp.c3865
-rw-r--r--Utilities/cmcurl/ftp.h43
-rw-r--r--Utilities/cmcurl/getenv.c69
-rw-r--r--Utilities/cmcurl/getinfo.c234
-rw-r--r--Utilities/cmcurl/getinfo.h28
-rw-r--r--Utilities/cmcurl/gtls.c640
-rw-r--r--Utilities/cmcurl/gtls.h46
-rw-r--r--Utilities/cmcurl/hash.c315
-rw-r--r--Utilities/cmcurl/hash.h61
-rw-r--r--Utilities/cmcurl/hostares.c307
-rw-r--r--Utilities/cmcurl/hostasyn.c174
-rw-r--r--Utilities/cmcurl/hostip.c636
-rw-r--r--Utilities/cmcurl/hostip.h271
-rw-r--r--Utilities/cmcurl/hostip4.c389
-rw-r--r--Utilities/cmcurl/hostip6.c306
-rw-r--r--Utilities/cmcurl/hostsyn.c138
-rw-r--r--Utilities/cmcurl/hostthre.c840
-rw-r--r--Utilities/cmcurl/http.c2422
-rw-r--r--Utilities/cmcurl/http.h85
-rw-r--r--Utilities/cmcurl/http_chunks.c360
-rw-r--r--Utilities/cmcurl/http_chunks.h104
-rw-r--r--Utilities/cmcurl/http_digest.c504
-rw-r--r--Utilities/cmcurl/http_digest.h58
-rw-r--r--Utilities/cmcurl/http_negotiate.c327
-rw-r--r--Utilities/cmcurl/http_negotiate.h39
-rw-r--r--Utilities/cmcurl/http_ntlm.c1111
-rw-r--r--Utilities/cmcurl/http_ntlm.h146
-rw-r--r--Utilities/cmcurl/if2ip.c134
-rw-r--r--Utilities/cmcurl/if2ip.h67
-rw-r--r--Utilities/cmcurl/inet_ntoa_r.h44
-rw-r--r--Utilities/cmcurl/inet_ntop.c224
-rw-r--r--Utilities/cmcurl/inet_ntop.h37
-rw-r--r--Utilities/cmcurl/inet_pton.c241
-rw-r--r--Utilities/cmcurl/inet_pton.h42
-rw-r--r--Utilities/cmcurl/krb4.c425
-rw-r--r--Utilities/cmcurl/krb4.h70
-rw-r--r--Utilities/cmcurl/ldap.c702
-rw-r--r--Utilities/cmcurl/ldap.h29
-rw-r--r--Utilities/cmcurl/llist.c138
-rw-r--r--Utilities/cmcurl/llist.h60
-rw-r--r--Utilities/cmcurl/md5.c352
-rw-r--r--Utilities/cmcurl/md5.h29
-rw-r--r--Utilities/cmcurl/memdebug.c298
-rw-r--r--Utilities/cmcurl/memdebug.h125
-rw-r--r--Utilities/cmcurl/memory.h50
-rw-r--r--Utilities/cmcurl/mprintf.c1218
-rw-r--r--Utilities/cmcurl/multi.c1988
-rw-r--r--Utilities/cmcurl/multiif.h46
-rw-r--r--Utilities/cmcurl/netrc.c247
-rw-r--r--Utilities/cmcurl/netrc.h34
-rw-r--r--Utilities/cmcurl/nwlib.c300
-rw-r--r--Utilities/cmcurl/parsedate.c425
-rw-r--r--Utilities/cmcurl/parsedate.h28
-rw-r--r--Utilities/cmcurl/progress.c424
-rw-r--r--Utilities/cmcurl/progress.h70
-rw-r--r--Utilities/cmcurl/security.c493
-rw-r--r--Utilities/cmcurl/select.c315
-rw-r--r--Utilities/cmcurl/select.h60
-rw-r--r--Utilities/cmcurl/sendf.c663
-rw-r--r--Utilities/cmcurl/sendf.h72
-rw-r--r--Utilities/cmcurl/setup.h390
-rw-r--r--Utilities/cmcurl/setup_once.h153
-rw-r--r--Utilities/cmcurl/share.c219
-rw-r--r--Utilities/cmcurl/share.h56
-rw-r--r--Utilities/cmcurl/sockaddr.h38
-rw-r--r--Utilities/cmcurl/socks.c585
-rw-r--r--Utilities/cmcurl/socks.h41
-rw-r--r--Utilities/cmcurl/speedcheck.c75
-rw-r--r--Utilities/cmcurl/speedcheck.h34
-rw-r--r--Utilities/cmcurl/splay.c425
-rw-r--r--Utilities/cmcurl/splay.h54
-rw-r--r--Utilities/cmcurl/ssh.c979
-rw-r--r--Utilities/cmcurl/ssh.h49
-rw-r--r--Utilities/cmcurl/sslgen.c618
-rw-r--r--Utilities/cmcurl/sslgen.h84
-rw-r--r--Utilities/cmcurl/ssluse.c1945
-rw-r--r--Utilities/cmcurl/ssluse.h71
-rw-r--r--Utilities/cmcurl/strdup.c46
-rw-r--r--Utilities/cmcurl/strdup.h34
-rw-r--r--Utilities/cmcurl/strequal.c143
-rw-r--r--Utilities/cmcurl/strequal.h43
-rw-r--r--Utilities/cmcurl/strerror.c748
-rw-r--r--Utilities/cmcurl/strerror.h34
-rw-r--r--Utilities/cmcurl/strtok.c68
-rw-r--r--Utilities/cmcurl/strtok.h38
-rw-r--r--Utilities/cmcurl/strtoofft.c165
-rw-r--r--Utilities/cmcurl/strtoofft.h73
-rw-r--r--Utilities/cmcurl/telnet.c1403
-rw-r--r--Utilities/cmcurl/telnet.h30
-rw-r--r--Utilities/cmcurl/tftp.c816
-rw-r--r--Utilities/cmcurl/tftp.h31
-rw-r--r--Utilities/cmcurl/timeval.c116
-rw-r--r--Utilities/cmcurl/timeval.h76
-rw-r--r--Utilities/cmcurl/transfer.c2494
-rw-r--r--Utilities/cmcurl/transfer.h51
-rw-r--r--Utilities/cmcurl/url.c4252
-rw-r--r--Utilities/cmcurl/url.h85
-rw-r--r--Utilities/cmcurl/urldata.h1340
-rw-r--r--Utilities/cmcurl/version.c249
156 files changed, 54533 insertions, 0 deletions
diff --git a/Utilities/cmcurl/CMake/CMakeConfigurableFile.in b/Utilities/cmcurl/CMake/CMakeConfigurableFile.in
new file mode 100644
index 000000000..4cf74a12b
--- /dev/null
+++ b/Utilities/cmcurl/CMake/CMakeConfigurableFile.in
@@ -0,0 +1,2 @@
+@CMAKE_CONFIGURABLE_FILE_CONTENT@
+
diff --git a/Utilities/cmcurl/CMake/CurlCheckCSourceCompiles.cmake b/Utilities/cmcurl/CMake/CurlCheckCSourceCompiles.cmake
new file mode 100644
index 000000000..d025769aa
--- /dev/null
+++ b/Utilities/cmcurl/CMake/CurlCheckCSourceCompiles.cmake
@@ -0,0 +1,75 @@
+# - Check if the source code provided in the SOURCE argument compiles.
+# CURL_CHECK_C_SOURCE_COMPILES(SOURCE VAR)
+# - macro which checks if the source code compiles
+# SOURCE - source code to try to compile
+# VAR - variable to store whether the source code compiled
+#
+# The following variables may be set before calling this macro to
+# modify the way the check is run:
+#
+# CMAKE_REQUIRED_FLAGS = string of compile command line flags
+# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
+# CMAKE_REQUIRED_INCLUDES = list of include directories
+# CMAKE_REQUIRED_LIBRARIES = list of libraries to link
+
+MACRO(CURL_CHECK_C_SOURCE_COMPILES SOURCE VAR)
+ IF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN")
+ SET(message "${VAR}")
+ # If the number of arguments is greater than 2 (SOURCE VAR)
+ IF(${ARGC} GREATER 2)
+ # then add the third argument as a message
+ SET(message "${ARGV2} (${VAR})")
+ ENDIF(${ARGC} GREATER 2)
+ SET(MACRO_CHECK_FUNCTION_DEFINITIONS
+ "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+ IF(CMAKE_REQUIRED_LIBRARIES)
+ SET(CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES
+ "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}")
+ ELSE(CMAKE_REQUIRED_LIBRARIES)
+ SET(CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES)
+ ENDIF(CMAKE_REQUIRED_LIBRARIES)
+ IF(CMAKE_REQUIRED_INCLUDES)
+ SET(CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES
+ "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+ ELSE(CMAKE_REQUIRED_INCLUDES)
+ SET(CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES)
+ ENDIF(CMAKE_REQUIRED_INCLUDES)
+ SET(src "")
+ FOREACH(def ${EXTRA_DEFINES})
+ SET(src "${src}#define ${def} 1\n")
+ ENDFOREACH(def)
+ FOREACH(inc ${HEADER_INCLUDES})
+ SET(src "${src}#include <${inc}>\n")
+ ENDFOREACH(inc)
+
+ SET(src "${src}\nint main() { ${SOURCE} ; return 0; }")
+ SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${src}")
+ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/CMake/CMakeConfigurableFile.in
+ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c"
+ IMMEDIATE)
+ MESSAGE(STATUS "Performing Test ${message}")
+ TRY_COMPILE(${VAR}
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c
+ COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+ CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+ "${CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}"
+ "${CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}"
+ OUTPUT_VARIABLE OUTPUT)
+ IF(${VAR})
+ SET(${VAR} 1 CACHE INTERNAL "Test ${message}")
+ MESSAGE(STATUS "Performing Test ${message} - Success")
+ FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+ "Performing C SOURCE FILE Test ${message} succeded with the following output:\n"
+ "${OUTPUT}\n"
+ "Source file was:\n${src}\n")
+ ELSE(${VAR})
+ MESSAGE(STATUS "Performing Test ${message} - Failed")
+ SET(${VAR} "" CACHE INTERNAL "Test ${message}")
+ FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+ "Performing C SOURCE FILE Test ${message} failed with the following output:\n"
+ "${OUTPUT}\n"
+ "Source file was:\n${src}\n")
+ ENDIF(${VAR})
+ ENDIF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN")
+ENDMACRO(CURL_CHECK_C_SOURCE_COMPILES)
diff --git a/Utilities/cmcurl/CMake/CurlCheckCSourceRuns.cmake b/Utilities/cmcurl/CMake/CurlCheckCSourceRuns.cmake
new file mode 100644
index 000000000..19681bd85
--- /dev/null
+++ b/Utilities/cmcurl/CMake/CurlCheckCSourceRuns.cmake
@@ -0,0 +1,83 @@
+# - Check if the source code provided in the SOURCE argument compiles and runs.
+# CURL_CHECK_C_SOURCE_RUNS(SOURCE VAR)
+# - macro which checks if the source code runs
+# SOURCE - source code to try to compile
+# VAR - variable to store size if the type exists.
+#
+# The following variables may be set before calling this macro to
+# modify the way the check is run:
+#
+# CMAKE_REQUIRED_FLAGS = string of compile command line flags
+# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
+# CMAKE_REQUIRED_INCLUDES = list of include directories
+# CMAKE_REQUIRED_LIBRARIES = list of libraries to link
+
+MACRO(CURL_CHECK_C_SOURCE_RUNS SOURCE VAR)
+ IF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN")
+ SET(message "${VAR}")
+ # If the number of arguments is greater than 2 (SOURCE VAR)
+ IF(${ARGC} GREATER 2)
+ # then add the third argument as a message
+ SET(message "${ARGV2} (${VAR})")
+ ENDIF(${ARGC} GREATER 2)
+ SET(MACRO_CHECK_FUNCTION_DEFINITIONS
+ "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+ IF(CMAKE_REQUIRED_LIBRARIES)
+ SET(CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES
+ "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}")
+ ELSE(CMAKE_REQUIRED_LIBRARIES)
+ SET(CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES)
+ ENDIF(CMAKE_REQUIRED_LIBRARIES)
+ IF(CMAKE_REQUIRED_INCLUDES)
+ SET(CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES
+ "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+ ELSE(CMAKE_REQUIRED_INCLUDES)
+ SET(CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES)
+ ENDIF(CMAKE_REQUIRED_INCLUDES)
+ SET(src "")
+ FOREACH(def ${EXTRA_DEFINES})
+ SET(src "${src}#define ${def} 1\n")
+ ENDFOREACH(def)
+ FOREACH(inc ${HEADER_INCLUDES})
+ SET(src "${src}#include <${inc}>\n")
+ ENDFOREACH(inc)
+
+ SET(src "${src}\nint main() { ${SOURCE} ; return 0; }")
+ SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${src}")
+ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/CMake/CMakeConfigurableFile.in
+ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c"
+ IMMEDIATE)
+ MESSAGE(STATUS "Performing Test ${message}")
+ TRY_RUN(${VAR} ${VAR}_COMPILED
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c
+ COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+ CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+ "${CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}"
+ "${CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}"
+ OUTPUT_VARIABLE OUTPUT)
+ # if it did not compile make the return value fail code of 1
+ IF(NOT ${VAR}_COMPILED)
+ SET(${VAR} 1)
+ ENDIF(NOT ${VAR}_COMPILED)
+ # if the return value was 0 then it worked
+ SET(result_var ${${VAR}})
+ IF("${result_var}" EQUAL 0)
+ SET(${VAR} 1 CACHE INTERNAL "Test ${message}")
+ MESSAGE(STATUS "Performing Test ${message} - Success")
+ FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+ "Performing C SOURCE FILE Test ${message} succeded with the following output:\n"
+ "${OUTPUT}\n"
+ "Return value: ${${VAR}}\n"
+ "Source file was:\n${src}\n")
+ ELSE("${result_var}" EQUAL 0)
+ MESSAGE(STATUS "Performing Test ${message} - Failed")
+ SET(${VAR} "" CACHE INTERNAL "Test ${message}")
+ FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+ "Performing C SOURCE FILE Test ${message} failed with the following output:\n"
+ "${OUTPUT}\n"
+ "Return value: ${result_var}\n"
+ "Source file was:\n${src}\n")
+ ENDIF("${result_var}" EQUAL 0)
+ ENDIF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN")
+ENDMACRO(CURL_CHECK_C_SOURCE_RUNS)
diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c
new file mode 100644
index 000000000..d74a4f016
--- /dev/null
+++ b/Utilities/cmcurl/CMake/CurlTests.c
@@ -0,0 +1,526 @@
+#ifdef TIME_WITH_SYS_TIME
+/* Time with sys/time test */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+ ;
+ return 0;
+}
+
+#endif
+
+#ifdef HAVE_O_NONBLOCK
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int
+main ()
+{
+ /* try to compile O_NONBLOCK */
+
+#if defined(sun) || defined(__sun__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+# if defined(__SVR4) || defined(__srv4__)
+# define PLATFORM_SOLARIS
+# else
+# define PLATFORM_SUNOS4
+# endif
+#endif
+#if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX4)
+# define PLATFORM_AIX_V3
+#endif
+
+#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) || (defined(__BEOS__) && !defined(__HAIKU__))
+#error "O_NONBLOCK does not work on this platform"
+#endif
+ int socket;
+ int flags = fcntl(socket, F_SETFL, flags | O_NONBLOCK);
+ return 0;
+}
+#endif
+
+#ifdef HAVE_GETHOSTBYADDR_R_5
+#include <sys/types.h>
+#include <netdb.h>
+int
+main ()
+{
+
+char * address;
+int length;
+int type;
+struct hostent h;
+struct hostent_data hdata;
+int rc;
+#ifndef gethostbyaddr_r
+ (void)gethostbyaddr_r;
+#endif
+rc = gethostbyaddr_r(address, length, type, &h, &hdata);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYADDR_R_5_REENTRANT
+#define _REENTRANT
+#include <sys/types.h>
+#include <netdb.h>
+int
+main ()
+{
+
+char * address;
+int length;q
+int type;
+struct hostent h;
+struct hostent_data hdata;
+int rc;
+#ifndef gethostbyaddr_r
+ (void)gethostbyaddr_r;
+#endif
+rc = gethostbyaddr_r(address, length, type, &h, &hdata);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYADDR_R_7
+#include <sys/types.h>
+#include <netdb.h>
+int
+main ()
+{
+
+char * address;
+int length;
+int type;
+struct hostent h;
+char buffer[8192];
+int h_errnop;
+struct hostent * hp;
+
+#ifndef gethostbyaddr_r
+ (void)gethostbyaddr_r;
+#endif
+hp = gethostbyaddr_r(address, length, type, &h,
+ buffer, 8192, &h_errnop);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYADDR_R_7_REENTRANT
+#define _REENTRANT
+#include <sys/types.h>
+#include <netdb.h>
+int
+main ()
+{
+
+char * address;
+int length;
+int type;
+struct hostent h;
+char buffer[8192];
+int h_errnop;
+struct hostent * hp;
+
+#ifndef gethostbyaddr_r
+ (void)gethostbyaddr_r;
+#endif
+hp = gethostbyaddr_r(address, length, type, &h,
+ buffer, 8192, &h_errnop);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYADDR_R_8
+#include <sys/types.h>
+#include <netdb.h>
+int
+main ()
+{
+
+char * address;
+int length;
+int type;
+struct hostent h;
+char buffer[8192];
+int h_errnop;
+struct hostent * hp;
+int rc;
+
+#ifndef gethostbyaddr_r
+ (void)gethostbyaddr_r;
+#endif
+rc = gethostbyaddr_r(address, length, type, &h,
+ buffer, 8192, &hp, &h_errnop);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYADDR_R_8_REENTRANT
+#define _REENTRANT
+#include <sys/types.h>
+#include <netdb.h>
+int
+main ()
+{
+
+char * address;
+int length;
+int type;
+struct hostent h;
+char buffer[8192];
+int h_errnop;
+struct hostent * hp;
+int rc;
+
+#ifndef gethostbyaddr_r
+ (void)gethostbyaddr_r;
+#endif
+rc = gethostbyaddr_r(address, length, type, &h,
+ buffer, 8192, &hp, &h_errnop);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYNAME_R_3
+#include <string.h>
+#include <sys/types.h>
+#include <netdb.h>
+#undef NULL
+#define NULL (void *)0
+
+int
+main ()
+{
+
+struct hostent_data data;
+#ifndef gethostbyname_r
+ (void)gethostbyname_r;
+#endif
+gethostbyname_r(NULL, NULL, NULL);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYNAME_R_3_REENTRANT
+#define _REENTRANT
+#include <string.h>
+#include <sys/types.h>
+#include <netdb.h>
+#undef NULL
+#define NULL (void *)0
+
+int
+main ()
+{
+
+struct hostent_data data;
+#ifndef gethostbyname_r
+ (void)gethostbyname_r;
+#endif
+gethostbyname_r(NULL, NULL, NULL);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYNAME_R_5
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#undef NULL
+#define NULL (void *)0
+
+int
+main ()
+{
+#ifndef gethostbyname_r
+ (void)gethostbyname_r;
+#endif
+gethostbyname_r(NULL, NULL, NULL, 0, NULL);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYNAME_R_5_REENTRANT
+#define _REENTRANT
+#include <sys/types.h>
+#include <netdb.h>
+#undef NULL
+#define NULL (void *)0
+
+int
+main ()
+{
+
+#ifndef gethostbyname_r
+ (void)gethostbyname_r;
+#endif
+gethostbyname_r(NULL, NULL, NULL, 0, NULL);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYNAME_R_6
+#include <sys/types.h>
+#include <netdb.h>
+#undef NULL
+#define NULL (void *)0
+
+int
+main ()
+{
+
+#ifndef gethostbyname_r
+ (void)gethostbyname_r;
+#endif
+gethostbyname_r(NULL, NULL, NULL, 0, NULL, NULL);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_GETHOSTBYNAME_R_6_REENTRANT
+#define _REENTRANT
+#include <sys/types.h>
+#include <netdb.h>
+#undef NULL
+#define NULL (void *)0
+
+int
+main ()
+{
+
+#ifndef gethostbyname_r
+ (void)gethostbyname_r;
+#endif
+gethostbyname_r(NULL, NULL, NULL, 0, NULL, NULL);
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_SOCKLEN_T
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+int
+main ()
+{
+if ((socklen_t *) 0)
+ return 0;
+if (sizeof (socklen_t))
+ return 0;
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_IN_ADDR_T
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+int
+main ()
+{
+if ((in_addr_t *) 0)
+ return 0;
+if (sizeof (in_addr_t))
+ return 0;
+ ;
+ return 0;
+}
+#endif
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+int main() { return 0; }
+#endif
+#ifdef RETSIGTYPE_TEST
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+# undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int
+main ()
+{
+ return 0;
+}
+#endif
+#ifdef HAVE_INET_NTOA_R_DECL
+#include <arpa/inet.h>
+
+typedef void (*func_type)();
+
+int main()
+{
+#ifndef inet_ntoa_r
+ func_type func;
+ func = (func_type)inet_ntoa_r;
+#endif
+ return 0;
+}
+#endif
+#ifdef HAVE_INET_NTOA_R_DECL_REENTRANT
+#define _REENTRANT
+#include <arpa/inet.h>
+
+typedef void (*func_type)();
+
+int main()
+{
+#ifndef inet_ntoa_r
+ func_type func;
+ func = (func_type)&inet_ntoa_r;
+#endif
+ return 0;
+}
+#endif
+#ifdef HAVE_GETADDRINFO
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+int main(void) {
+ struct addrinfo hints, *ai;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+#ifndef getaddrinfo
+ (void)getaddrinfo;
+#endif
+ error = getaddrinfo("127.0.0.1", "8080", &hints, &ai);
+ if (error) {
+ return 1;
+ }
+ return 0;
+}
+#endif
+#ifdef HAVE_FILE_OFFSET_BITS
+#ifdef _FILE_OFFSET_BITS
+#undef _FILE_OFFSET_BITS
+#endif
+#define _FILE_OFFSET_BITS 64
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int main () { ; return 0; }
+#endif
+#ifdef HAVE_IOCTLSOCKET
+#include <windows.h>
+
+int
+main ()
+{
+
+/* ioctlsocket source code */
+ int socket;
+ unsigned long flags = ioctlsocket(socket, FIONBIO, &flags);
+
+ ;
+ return 0;
+}
+
+#endif
+#ifdef HAVE_IOCTLSOCKET_CASE
+#include <windows.h>
+
+int
+main ()
+{
+
+/* IoctlSocket source code */
+ int socket;
+ int flags = IoctlSocket(socket, FIONBIO, (long)1);
+
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_FIONBIO
+/* headers for FIONBIO test */
+#include <unistd.h>
+#include <stropts.h>
+
+int
+main ()
+{
+
+/* FIONBIO source test (old-style unix) */
+ int socket;
+ int flags = ioctl(socket, FIONBIO, &flags);
+
+ ;
+ return 0;
+}
+#endif
+#ifdef HAVE_SO_NONBLOCK
+
+/* headers for SO_NONBLOCK test (BeOS) */
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int main()
+{
+/* SO_NONBLOCK source code */
+ long b = 1;
+ int socket;
+ int flags = setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
+ return 0;
+}
+#endif
+#ifdef HAVE_GLIBC_STRERROR_R
+#include <string.h>
+#include <errno.h>
+int
+main () {
+ char buffer[1024]; /* big enough to play with */
+ char *string =
+ strerror_r(EACCES, buffer, sizeof(buffer));
+ /* this should've returned a string */
+ if(!string || !string[0])
+ return 99;
+ return 0;
+}
+#endif
+#ifdef HAVE_POSIX_STRERROR_R
+#include <string.h>
+#include <errno.h>
+int
+main () {
+ char buffer[1024]; /* big enough to play with */
+ int error =
+ strerror_r(EACCES, buffer, sizeof(buffer));
+ /* This should've returned zero, and written an error string in the
+ buffer.*/
+ if(!buffer[0] || error)
+ return 99;
+ return 0;
+}
+#endif
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake
new file mode 100644
index 000000000..7d2c66fa1
--- /dev/null
+++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -0,0 +1,242 @@
+INCLUDE(CurlCheckCSourceCompiles)
+SET(EXTRA_DEFINES "__unused1\n#undef inline\n#define __unused2")
+SET(HEADER_INCLUDES)
+SET(headers_hack)
+
+MACRO(add_header_include check header)
+ IF(${check})
+ SET(headers_hack
+ "${headers_hack}\n#include <${header}>")
+ #SET(HEADER_INCLUDES
+ # ${HEADER_INCLUDES}
+ # "${header}")
+ ENDIF(${check})
+ENDMACRO(add_header_include)
+
+SET(signature_call_conv)
+IF(HAVE_WINDOWS_H)
+ add_header_include(HAVE_WINDOWS_H "windows.h")
+ add_header_include(HAVE_WINSOCK2_H "winsock2.h")
+ add_header_include(HAVE_WINSOCK_H "winsock.h")
+ SET(EXTRA_DEFINES ${EXTRA_DEFINES}
+ "__unused7\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#define __unused3")
+ SET(signature_call_conv "PASCAL")
+ELSE(HAVE_WINDOWS_H)
+ add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
+ add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h")
+ENDIF(HAVE_WINDOWS_H)
+
+SET(EXTRA_DEFINES_BACKUP "${EXTRA_DEFINES}")
+SET(EXTRA_DEFINES "${EXTRA_DEFINES_BACKUP}\n${headers_hack}\n${extern_line}\n#define __unused5")
+CURL_CHECK_C_SOURCE_COMPILES("recv(0, 0, 0, 0)" curl_cv_recv)
+IF(curl_cv_recv)
+ # AC_CACHE_CHECK([types of arguments and return type for recv],
+ #[curl_cv_func_recv_args], [
+ #SET(curl_cv_func_recv_args "unknown")
+ #for recv_retv in 'int' 'ssize_t'; do
+ IF(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown")
+ FOREACH(recv_retv "int" "ssize_t" )
+ FOREACH(recv_arg1 "int" "ssize_t" "SOCKET")
+ FOREACH(recv_arg2 "void *" "char *")
+ FOREACH(recv_arg3 "size_t" "int" "socklen_t" "unsigned int")
+ FOREACH(recv_arg4 "int" "unsigned int")
+ IF(NOT curl_cv_func_recv_done)
+ SET(curl_cv_func_recv_test "UNKNOWN")
+ SET(extern_line "extern ${recv_retv} ${signature_call_conv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})\;")
+ SET(EXTRA_DEFINES "${EXTRA_DEFINES_BACKUP}\n${headers_hack}\n${extern_line}\n#define __unused5")
+ CURL_CHECK_C_SOURCE_COMPILES("
+ ${recv_arg1} s=0;
+ ${recv_arg2} buf=0;
+ ${recv_arg3} len=0;
+ ${recv_arg4} flags=0;
+ ${recv_retv} res = recv(s, buf, len, flags)"
+ curl_cv_func_recv_test
+ "${recv_retv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})")
+ IF(curl_cv_func_recv_test)
+ SET(curl_cv_func_recv_args
+ "${recv_arg1},${recv_arg2},${recv_arg3},${recv_arg4},${recv_retv}")
+ SET(RECV_TYPE_ARG1 "${recv_arg1}")
+ SET(RECV_TYPE_ARG2 "${recv_arg2}")
+ SET(RECV_TYPE_ARG3 "${recv_arg3}")
+ SET(RECV_TYPE_ARG4 "${recv_arg4}")
+ SET(RECV_TYPE_RETV "${recv_retv}")
+ SET(HAVE_RECV 1)
+ SET(curl_cv_func_recv_done 1)
+ ENDIF(curl_cv_func_recv_test)
+ ENDIF(NOT curl_cv_func_recv_done)
+ ENDFOREACH(recv_arg4)
+ ENDFOREACH(recv_arg3)
+ ENDFOREACH(recv_arg2)
+ ENDFOREACH(recv_arg1)
+ ENDFOREACH(recv_retv)
+ ELSE(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown")
+ STRING(REGEX REPLACE "^([^,]*),[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG1 "${curl_cv_func_recv_args}")
+ STRING(REGEX REPLACE "^[^,]*,([^,]*),[^,]*,[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG2 "${curl_cv_func_recv_args}")
+ STRING(REGEX REPLACE "^[^,]*,[^,]*,([^,]*),[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG3 "${curl_cv_func_recv_args}")
+ STRING(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$" "\\1" RECV_TYPE_ARG4 "${curl_cv_func_recv_args}")
+ STRING(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" RECV_TYPE_RETV "${curl_cv_func_recv_args}")
+ #MESSAGE("RECV_TYPE_ARG1 ${RECV_TYPE_ARG1}")
+ #MESSAGE("RECV_TYPE_ARG2 ${RECV_TYPE_ARG2}")
+ #MESSAGE("RECV_TYPE_ARG3 ${RECV_TYPE_ARG3}")
+ #MESSAGE("RECV_TYPE_ARG4 ${RECV_TYPE_ARG4}")
+ #MESSAGE("RECV_TYPE_RETV ${RECV_TYPE_RETV}")
+ ENDIF(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown")
+
+ IF("${curl_cv_func_recv_args}" STREQUAL "unknown")
+ MESSAGE(FATAL_ERROR "Cannot find proper types to use for recv args")
+ ENDIF("${curl_cv_func_recv_args}" STREQUAL "unknown")
+ELSE(curl_cv_recv)
+ MESSAGE(FATAL_ERROR "Unable to link function recv")
+ENDIF(curl_cv_recv)
+SET(curl_cv_func_recv_args "${curl_cv_func_recv_args}" CACHE INTERNAL "Arguments for recv")
+SET(HAVE_RECV 1)
+
+CURL_CHECK_C_SOURCE_COMPILES("send(0, 0, 0, 0)" curl_cv_send)
+IF(curl_cv_send)
+ # AC_CACHE_CHECK([types of arguments and return type for send],
+ #[curl_cv_func_send_args], [
+ #SET(curl_cv_func_send_args "unknown")
+ #for send_retv in 'int' 'ssize_t'; do
+ IF(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown")
+ FOREACH(send_retv "int" "ssize_t" )
+ FOREACH(send_arg1 "int" "ssize_t" "SOCKET")
+ FOREACH(send_arg2 "const void *" "void *" "char *" "const char *")
+ FOREACH(send_arg3 "size_t" "int" "socklen_t" "unsigned int")
+ FOREACH(send_arg4 "int" "unsigned int")
+ IF(NOT curl_cv_func_send_done)
+ SET(curl_cv_func_send_test "UNKNOWN")
+ SET(extern_line "extern ${send_retv} ${signature_call_conv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})\;")
+ SET(EXTRA_DEFINES "${EXTRA_DEFINES_BACKUP}\n${headers_hack}\n${extern_line}\n#define __unused5")
+ CURL_CHECK_C_SOURCE_COMPILES("
+ ${send_arg1} s=0;
+ ${send_arg2} buf=0;
+ ${send_arg3} len=0;
+ ${send_arg4} flags=0;
+ ${send_retv} res = send(s, buf, len, flags)"
+ curl_cv_func_send_test
+ "${send_retv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})")
+ IF(curl_cv_func_send_test)
+ #MESSAGE("Found arguments: ${curl_cv_func_send_test}")
+ STRING(REGEX REPLACE "(const) .*" "\\1" send_qual_arg2 "${send_arg2}")
+ STRING(REGEX REPLACE "const (.*)" "\\1" send_arg2 "${send_arg2}")
+ SET(curl_cv_func_send_args
+ "${send_arg1},${send_arg2},${send_arg3},${send_arg4},${send_retv},${send_qual_arg2}")
+ SET(SEND_TYPE_ARG1 "${send_arg1}")
+ SET(SEND_TYPE_ARG2 "${send_arg2}")
+ SET(SEND_TYPE_ARG3 "${send_arg3}")
+ SET(SEND_TYPE_ARG4 "${send_arg4}")
+ SET(SEND_TYPE_RETV "${send_retv}")
+ SET(HAVE_SEND 1)
+ SET(curl_cv_func_send_done 1)
+ ENDIF(curl_cv_func_send_test)
+ ENDIF(NOT curl_cv_func_send_done)
+ ENDFOREACH(send_arg4)
+ ENDFOREACH(send_arg3)
+ ENDFOREACH(send_arg2)
+ ENDFOREACH(send_arg1)
+ ENDFOREACH(send_retv)
+ ELSE(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown")
+ STRING(REGEX REPLACE "^([^,]*),[^,]*,[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG1 "${curl_cv_func_send_args}")
+ STRING(REGEX REPLACE "^[^,]*,([^,]*),[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG2 "${curl_cv_func_send_args}")
+ STRING(REGEX REPLACE "^[^,]*,[^,]*,([^,]*),[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG3 "${curl_cv_func_send_args}")
+ STRING(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,([^,]*),[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG4 "${curl_cv_func_send_args}")
+ STRING(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$" "\\1" SEND_TYPE_RETV "${curl_cv_func_send_args}")
+ STRING(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" SEND_QUAL_ARG2 "${curl_cv_func_send_args}")
+ #MESSAGE("SEND_TYPE_ARG1 ${SEND_TYPE_ARG1}")
+ #MESSAGE("SEND_TYPE_ARG2 ${SEND_TYPE_ARG2}")
+ #MESSAGE("SEND_TYPE_ARG3 ${SEND_TYPE_ARG3}")
+ #MESSAGE("SEND_TYPE_ARG4 ${SEND_TYPE_ARG4}")
+ #MESSAGE("SEND_TYPE_RETV ${SEND_TYPE_RETV}")
+ #MESSAGE("SEND_QUAL_ARG2 ${SEND_QUAL_ARG2}")
+ ENDIF(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown")
+
+ IF("${curl_cv_func_send_args}" STREQUAL "unknown")
+ MESSAGE(FATAL_ERROR "Cannot find proper types to use for send args")
+ ENDIF("${curl_cv_func_send_args}" STREQUAL "unknown")
+ SET(SEND_QUAL_ARG2 "const")
+ELSE(curl_cv_send)
+ MESSAGE(FATAL_ERROR "Unable to link function send")
+ENDIF(curl_cv_send)
+SET(curl_cv_func_send_args "${curl_cv_func_send_args}" CACHE INTERNAL "Arguments for send")
+SET(HAVE_SEND 1)
+
+SET(EXTRA_DEFINES "${EXTRA_DEFINES}\n${headers_hack}\n#define __unused5")
+CURL_CHECK_C_SOURCE_COMPILES("int flag = MSG_NOSIGNAL" HAVE_MSG_NOSIGNAL)
+
+SET(EXTRA_DEFINES "__unused1\n#undef inline\n#define __unused2")
+SET(HEADER_INCLUDES)
+SET(headers_hack)
+
+MACRO(add_header_include check header)
+ IF(${check})
+ SET(headers_hack
+ "${headers_hack}\n#include <${header}>")
+ #SET(HEADER_INCLUDES
+ # ${HEADER_INCLUDES}
+ # "${header}")
+ ENDIF(${check})
+ENDMACRO(add_header_include header)
+
+IF(HAVE_WINDOWS_H)
+ SET(EXTRA_DEFINES ${EXTRA_DEFINES}
+ "__unused7\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#define __unused3")
+ add_header_include(HAVE_WINDOWS_H "windows.h")
+ add_header_include(HAVE_WINSOCK2_H "winsock2.h")
+ add_header_include(HAVE_WINSOCK_H "winsock.h")
+ELSE(HAVE_WINDOWS_H)
+ add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
+ add_header_include(HAVE_SYS_TIME_H "sys/time.h")
+ add_header_include(TIME_WITH_SYS_TIME "time.h")
+ add_header_include(HAVE_TIME_H "time.h")
+ENDIF(HAVE_WINDOWS_H)
+SET(EXTRA_DEFINES "${EXTRA_DEFINES}\n${headers_hack}\n#define __unused5")
+CURL_CHECK_C_SOURCE_COMPILES("struct timeval ts;\nts.tv_sec = 0;\nts.tv_usec = 0" HAVE_STRUCT_TIMEVAL)
+
+
+SET(HAVE_SIG_ATOMIC_T 1)
+SET(EXTRA_DEFINES)
+SET(HEADER_INCLUDES)
+IF(HAVE_SIGNAL_H)
+ SET(HEADER_INCLUDES "signal.h")
+ SET(CMAKE_EXTRA_INCLUDE_FILES "signal.h")
+ENDIF(HAVE_SIGNAL_H)
+CHECK_TYPE_SIZE("sig_atomic_t" SIZEOF_SIG_ATOMIC_T)
+IF(HAVE_SIZEOF_SIG_ATOMIC_T)
+ CURL_CHECK_C_SOURCE_COMPILES("static volatile sig_atomic_t dummy = 0" HAVE_SIG_ATOMIC_T_NOT_VOLATILE)
+ IF(NOT HAVE_SIG_ATOMIC_T_NOT_VOLATILE)
+ SET(HAVE_SIG_ATOMIC_T_VOLATILE 1)
+ ENDIF(NOT HAVE_SIG_ATOMIC_T_NOT_VOLATILE)
+ENDIF(HAVE_SIZEOF_SIG_ATOMIC_T)
+
+SET(CHECK_TYPE_SIZE_PREINCLUDE
+ "#undef inline")
+
+IF(HAVE_WINDOWS_H)
+ SET(CHECK_TYPE_SIZE_PREINCLUDE "${CHECK_TYPE_SIZE_PREINCLUDE}
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+ #include <windows.h>")
+ IF(HAVE_WINSOCK2_H)
+ SET(CHECK_TYPE_SIZE_PREINCLUDE "${CHECK_TYPE_SIZE_PREINCLUDE}\n#include <winsock2.h>")
+ ENDIF(HAVE_WINSOCK2_H)
+ELSE(HAVE_WINDOWS_H)
+ IF(HAVE_SYS_SOCKET_H)
+ SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES}
+ "sys/socket.h")
+ ENDIF(HAVE_SYS_SOCKET_H)
+ IF(HAVE_NETINET_IN_H)
+ SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES}
+ "netinet/in.h")
+ ENDIF(HAVE_NETINET_IN_H)
+ IF(HAVE_ARPA_INET_H)
+ SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES}
+ "arpa/inet.h")
+ ENDIF(HAVE_ARPA_INET_H)
+ENDIF(HAVE_WINDOWS_H)
+
+CHECK_TYPE_SIZE("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE)
+IF(HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE)
+ SET(HAVE_STRUCT_SOCKADDR_STORAGE 1)
+ENDIF(HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE)
+
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
new file mode 100644
index 000000000..caa44f1d6
--- /dev/null
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -0,0 +1,722 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6.3 FATAL_ERROR)
+PROJECT(LIBCURL C)
+
+# Setup package meta-data
+SET(PACKAGE "curl")
+SET(VERSION "7.16.1")
+SET(PACKAGE_TARNAME "curl")
+SET(PACKAGE_BUGREPORT " ")
+SET(PACKAGE_NAME "curl")
+SET(PACKAGE_VERSION "-")
+SET(PACKAGE_STRING "curl-")
+SET(PACKAGE_BUGREPORT "a suitable curl mailing list => http://curl.haxx.se/mail/")
+SET(OPERATING_SYSTEM "${CMAKE_SYSTEM_NAME}")
+
+# We need ansi c-flags, especially on HP
+SET(CMAKE_C_FLAGS "${CMAKE_ANSI_CFLAGS} ${CMAKE_C_FLAGS}")
+SET(CMAKE_REQUIRED_FLAGS ${CMAKE_ANSI_CFLAGS})
+
+# Disable warnings on Borland to avoid changing 3rd party code.
+IF(BORLAND)
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w-")
+ENDIF(BORLAND)
+
+# If we are on AIX, do the _ALL_SOURCE magic
+IF(${CMAKE_SYSTEM_NAME} MATCHES AIX)
+ SET(_ALL_SOURCE 1)
+ENDIF(${CMAKE_SYSTEM_NAME} MATCHES AIX)
+
+# Include all the necessary files for macros
+SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
+INCLUDE (CheckFunctionExists)
+INCLUDE (CheckIncludeFile)
+INCLUDE (CheckIncludeFiles)
+INCLUDE (CheckLibraryExists)
+INCLUDE (CheckSymbolExists)
+INCLUDE (CheckTypeSize)
+
+SET(libCurl_SRCS
+ # amigaos.c - does not build on AmigaOS
+ base64.c
+ connect.c
+ content_encoding.c
+ cookie.c
+ dict.c
+ easy.c
+ escape.c
+ file.c
+ formdata.c
+ ftp.c
+ getenv.c
+ getinfo.c
+ gtls.c
+ hash.c
+ hostares.c
+ hostasyn.c
+ hostip4.c
+ hostip6.c
+ hostip.c
+ hostsyn.c
+ hostthre.c
+ http.c
+ http_chunks.c
+ http_digest.c
+ http_negotiate.c
+ http_ntlm.c
+ if2ip.c
+ inet_ntop.c
+ inet_pton.c
+ krb4.c
+ ldap.c
+ llist.c
+ md5.c
+# memdebug.c -not used
+ mprintf.c
+ multi.c
+ netrc.c
+ # nwlib.c - Not used
+ parsedate.c
+ progress.c
+ security.c
+ select.c
+ sendf.c
+ share.c
+ socks.c
+ speedcheck.c
+ splay.c
+ ssh.c
+ sslgen.c
+ ssluse.c
+ strdup.c
+ strequal.c
+ strerror.c
+ # strtok.c - specify later
+ # strtoofft.c - specify later
+ telnet.c
+ tftp.c
+ timeval.c
+ transfer.c
+ url.c
+ version.c
+ )
+
+SET(CURL_DISABLE_LDAP 1)
+IF(NOT CURL_DISABLE_LDAP)
+ SET(libCurl_SRCS
+ ${libCurl_SRCS}
+ ldap.c
+ )
+ENDIF(NOT CURL_DISABLE_LDAP)
+
+# if we have Kerberos 4, right now this is never on
+#OPTION(CURL_KRB4 "Use Kerberos 4" OFF)
+IF(CURL_KRB4)
+ SET(libCurl_SRCS ${libCurl_SRCS}
+ krb4.c
+ security.c
+ )
+ENDIF(CURL_KRB4)
+
+#OPTION(CURL_MALLOC_DEBUG "Debug mallocs in Curl" OFF)
+MARK_AS_ADVANCED(CURL_MALLOC_DEBUG)
+IF(CURL_MALLOC_DEBUG)
+ SET(libCurl_SRCS ${libCurl_SRCS}
+ memdebug.c
+ )
+ENDIF(CURL_MALLOC_DEBUG)
+
+# On windows preload settings
+IF(WIN32 AND NOT MINGW)
+ INCLUDE(${LIBCURL_SOURCE_DIR}/Platforms/WindowsCache.cmake)
+ENDIF()
+
+# This macro checks if the symbol exists in the library and if it
+# does, it appends library to the list.
+SET(CURL_LIBS "")
+MACRO(CHECK_LIBRARY_EXISTS_CONCAT LIBRARY SYMBOL VARIABLE)
+ CHECK_LIBRARY_EXISTS("${LIBRARY};${CURL_LIBS}" ${SYMBOL} ""
+ ${VARIABLE})
+ IF(${VARIABLE})
+ SET(CURL_LIBS ${CURL_LIBS} ${LIBRARY})
+ ENDIF(${VARIABLE})
+ENDMACRO(CHECK_LIBRARY_EXISTS_CONCAT)
+
+# Check for all needed libraries
+# use the cmake defined dl libs as dl is should not be used
+# on HPUX, but rather dld this avoids a warning
+SET(CURL_LIBS ${CURL_LIBS} ${CMAKE_DL_LIBS})
+#CHECK_LIBRARY_EXISTS_CONCAT("dl" dlopen HAVE_LIBDL)
+#CHECK_LIBRARY_EXISTS_CONCAT("ucb" gethostname HAVE_LIBUCB)
+CHECK_LIBRARY_EXISTS_CONCAT("socket" connect HAVE_LIBSOCKET)
+CHECK_LIBRARY_EXISTS("c" gethostbyname "" NOT_NEED_LIBNSL)
+
+# Yellowtab Zeta needs different libraries than BeOS 5.
+IF(BEOS)
+ SET(NOT_NEED_LIBNSL 1)
+ CHECK_LIBRARY_EXISTS_CONCAT("bind" gethostbyname HAVE_LIBBIND)
+ CHECK_LIBRARY_EXISTS_CONCAT("bnetapi" closesocket HAVE_LIBBNETAPI)
+ENDIF(BEOS)
+
+CHECK_LIBRARY_EXISTS_CONCAT("network" recv HAVE_LIBNETWORK)
+
+IF(NOT NOT_NEED_LIBNSL)
+ CHECK_LIBRARY_EXISTS_CONCAT("nsl" gethostbyname HAVE_LIBNSL)
+ENDIF(NOT NOT_NEED_LIBNSL)
+
+CHECK_LIBRARY_EXISTS_CONCAT("ws2_32" getch HAVE_LIBWS2_32)
+CHECK_LIBRARY_EXISTS_CONCAT("winmm" getch HAVE_LIBWINMM)
+IF(NOT CURL_SPECIAL_LIBZ)
+ CHECK_LIBRARY_EXISTS_CONCAT("z" inflateEnd HAVE_LIBZ)
+ENDIF(NOT CURL_SPECIAL_LIBZ)
+
+OPTION(CMAKE_USE_OPENSSL "Use OpenSSL code." OFF)
+MARK_AS_ADVANCED(CMAKE_USE_OPENSSL)
+IF(CMAKE_USE_OPENSSL)
+ SET(USE_SSLEAY TRUE)
+ SET(USE_OPENSSL TRUE)
+ IF(WIN32)
+ FIND_PATH(SSLINCLUDE openssl/crypto.h
+ PATHS c:/hoffman/Tools/openssl_w32vc6-0.9.8g/inc32)
+ INCLUDE_DIRECTORIES(${SSLINCLUDE})
+ FIND_LIBRARY(LIBEAY NAMES libeay32)
+ FIND_LIBRARY(SSLEAY NAMES ssleay32)
+ SET(CURL_LIBS ${CURL_LIBS} ${LIBEAY} ${SSLEAY} )
+ ELSE(WIN32)
+ CHECK_LIBRARY_EXISTS_CONCAT("crypto" CRYPTO_lock HAVE_LIBCRYPTO)
+ CHECK_LIBRARY_EXISTS_CONCAT("ssl" SSL_connect HAVE_LIBSSL)
+ ENDIF(WIN32)
+ SET(CURL_CA_BUNDLE "" CACHE FILEPATH "Path to SSL CA Certificate Bundle")
+ MARK_AS_ADVANCED(CURL_CA_BUNDLE)
+ IF(CURL_CA_BUNDLE)
+ ADD_DEFINITIONS(-DCURL_CA_BUNDLE="${CURL_CA_BUNDLE}")
+ ENDIF(CURL_CA_BUNDLE)
+ENDIF(CMAKE_USE_OPENSSL)
+
+# Check for idn
+CHECK_LIBRARY_EXISTS_CONCAT("idn" idna_to_ascii_lz HAVE_LIBIDN)
+
+# Check for symbol dlopen (same as HAVE_LIBDL)
+CHECK_LIBRARY_EXISTS("${CURL_LIBS}" dlopen "" HAVE_DLOPEN)
+
+# For other tests to use the same libraries
+SET(CMAKE_REQUIRED_LIBRARIES ${CURL_LIBS})
+
+IF(CURL_SPECIAL_LIBZ)
+ SET(CURL_LIBS ${CURL_LIBS} "${CURL_SPECIAL_LIBZ}")
+ INCLUDE_DIRECTORIES(${CURL_SPECIAL_LIBZ_INCLUDES})
+ SET(HAVE_LIBZ 0)
+ SET(HAVE_ZLIB_H 0)
+ENDIF(CURL_SPECIAL_LIBZ)
+
+# do we have process.h
+CHECK_INCLUDE_FILE("process.h" HAVE_PROCESS_H)
+
+# If we have features.h, then do the _BSD_SOURCE magic
+CHECK_INCLUDE_FILE("features.h" HAVE_FEATURES_H)
+IF(HAVE_FEATURES_H)
+ SET_SOURCE_FILES_PROPERTIES(
+ cookie.c
+ easy.c
+ formdata.c
+ getenv.c
+ hash.c
+ http.c
+ if2ip.c
+ mprintf.c
+ multi.c
+ sendf.c
+ telnet.c
+ transfer.c
+ url.c
+ COMPILE_FLAGS -D_BSD_SOURCE)
+ENDIF(HAVE_FEATURES_H)
+
+# Check if header file exists and add it to the list.
+MACRO(CHECK_INCLUDE_FILE_CONCAT FILE VARIABLE)
+ CHECK_INCLUDE_FILES("${CURL_INCLUDES};${FILE}" ${VARIABLE})
+ IF(${VARIABLE})
+ SET(CURL_INCLUDES ${CURL_INCLUDES} ${FILE})
+ ENDIF(${VARIABLE})
+ENDMACRO(CHECK_INCLUDE_FILE_CONCAT)
+
+# Check for header files
+IF(UNIX)
+ SET(HAVE_WINDOWS_H 0)
+ SET(HAVE_WINSOCK_H 0)
+ SET(HAVE_WS2TCPIP_H 0)
+ SET(HAVE_WINSOCK2_H 0)
+ENDIF(UNIX)
+IF(NOT UNIX)
+ CHECK_INCLUDE_FILE_CONCAT("ws2tcpip.h" HAVE_WS2TCPIP_H)
+ CHECK_INCLUDE_FILE_CONCAT("winsock2.h" HAVE_WINSOCK2_H)
+ENDIF(NOT UNIX)
+CHECK_INCLUDE_FILE_CONCAT("stdio.h" HAVE_STDIO_H)
+IF(NOT UNIX)
+ CHECK_INCLUDE_FILE_CONCAT("windows.h" HAVE_WINDOWS_H)
+ CHECK_INCLUDE_FILE_CONCAT("winsock.h" HAVE_WINSOCK_H)
+ENDIF(NOT UNIX)
+CHECK_INCLUDE_FILE_CONCAT("stddef.h" HAVE_STDDEF_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/types.h" HAVE_SYS_TYPES_H)
+CHECK_INCLUDE_FILE_CONCAT("inttypes.h" HAVE_INTTYPES_H)
+CHECK_INCLUDE_FILE_CONCAT("alloca.h" HAVE_ALLOCA_H)
+CHECK_INCLUDE_FILE_CONCAT("arpa/inet.h" HAVE_ARPA_INET_H)
+CHECK_INCLUDE_FILE_CONCAT("dlfcn.h" HAVE_DLFCN_H)
+CHECK_INCLUDE_FILE_CONCAT("fcntl.h" HAVE_FCNTL_H)
+CHECK_INCLUDE_FILE_CONCAT("malloc.h" HAVE_MALLOC_H)
+CHECK_INCLUDE_FILE_CONCAT("memory.h" HAVE_MEMORY_H)
+CHECK_INCLUDE_FILE_CONCAT("netdb.h" HAVE_NETDB_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/poll.h" HAVE_SYS_POLL_H)
+CHECK_INCLUDE_FILE_CONCAT("assert.h" HAVE_ASSERT_H)
+CHECK_INCLUDE_FILE_CONCAT("limits.h" HAVE_LIMITS_H)
+
+IF(CMAKE_USE_OPENSSL)
+ CHECK_INCLUDE_FILE_CONCAT("openssl/x509.h" HAVE_OPENSSL_X509_H)
+ CHECK_INCLUDE_FILE_CONCAT("openssl/engine.h" HAVE_OPENSSL_ENGINE_H)
+ CHECK_INCLUDE_FILE_CONCAT("openssl/rsa.h" HAVE_OPENSSL_RSA_H)
+ CHECK_INCLUDE_FILE_CONCAT("openssl/crypto.h" HAVE_OPENSSL_CRYPTO_H)
+ CHECK_INCLUDE_FILE_CONCAT("openssl/pem.h" HAVE_OPENSSL_PEM_H)
+ CHECK_INCLUDE_FILE_CONCAT("openssl/ssl.h" HAVE_OPENSSL_SSL_H)
+ CHECK_INCLUDE_FILE_CONCAT("openssl/err.h" HAVE_OPENSSL_ERR_H)
+ CHECK_INCLUDE_FILE_CONCAT("openssl/rand.h" HAVE_OPENSSL_RAND_H)
+ CHECK_INCLUDE_FILE_CONCAT("openssl/pkcs12.h" HAVE_OPENSSL_PKCS12_H)
+ENDIF(CMAKE_USE_OPENSSL)
+
+IF(NOT CURL_SPECIAL_LIBZ)
+ CHECK_INCLUDE_FILE_CONCAT("zlib.h" HAVE_ZLIB_H)
+ENDIF(NOT CURL_SPECIAL_LIBZ)
+CHECK_INCLUDE_FILE_CONCAT("sys/socket.h" HAVE_SYS_SOCKET_H)
+CHECK_INCLUDE_FILE_CONCAT("netinet/in.h" HAVE_NETINET_IN_H)
+CHECK_INCLUDE_FILE_CONCAT("net/if.h" HAVE_NET_IF_H)
+CHECK_INCLUDE_FILE_CONCAT("netinet/if_ether.h"
+ HAVE_NETINET_IF_ETHER_H)
+CHECK_INCLUDE_FILE_CONCAT("netinet/tcp.h"
+ HAVE_NETINET_TCP_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/select.h" HAVE_SYS_SELECT_H)
+CHECK_INCLUDE_FILE_CONCAT("utime.h" HAVE_UTIME_H)
+CHECK_INCLUDE_FILE_CONCAT("netinet/in.h" HAVE_NETINET_IN_H)
+CHECK_INCLUDE_FILE_CONCAT("pwd.h" HAVE_PWD_H)
+CHECK_INCLUDE_FILE_CONCAT("sgtty.h" HAVE_SGTTY_H)
+CHECK_INCLUDE_FILE_CONCAT("stdint.h" HAVE_STDINT_H)
+CHECK_INCLUDE_FILE_CONCAT("stdlib.h" HAVE_STDLIB_H)
+CHECK_INCLUDE_FILE_CONCAT("string.h" HAVE_STRING_H)
+CHECK_INCLUDE_FILE_CONCAT("strings.h" HAVE_STRINGS_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/param.h" HAVE_SYS_PARAM_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/stat.h" HAVE_SYS_STAT_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/time.h" HAVE_SYS_TIME_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/resource.h" HAVE_SYS_RESOURCE_H)
+CHECK_INCLUDE_FILE_CONCAT("termios.h" HAVE_TERMIOS_H)
+CHECK_INCLUDE_FILE_CONCAT("termio.h" HAVE_TERMIO_H)
+CHECK_INCLUDE_FILE_CONCAT("io.h" HAVE_IO_H)
+CHECK_INCLUDE_FILE_CONCAT("time.h" HAVE_TIME_H)
+CHECK_INCLUDE_FILE_CONCAT("unistd.h" HAVE_UNISTD_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/utime.h" HAVE_SYS_UTIME_H)
+CHECK_INCLUDE_FILE_CONCAT("sockio.h" HAVE_SOCKIO_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/sockio.h" HAVE_SYS_SOCKIO_H)
+CHECK_INCLUDE_FILE_CONCAT("x509.h" HAVE_X509_H)
+CHECK_INCLUDE_FILE_CONCAT("locale.h" HAVE_LOCALE_H)
+CHECK_INCLUDE_FILE_CONCAT("setjmp.h" HAVE_SETJMP_H)
+CHECK_INCLUDE_FILE_CONCAT("signal.h" HAVE_SIGNAL_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/ioctl.h" HAVE_SYS_IOCTL_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/utsname.h" HAVE_SYS_UTSNAME_H)
+CHECK_INCLUDE_FILE_CONCAT("idn-free.h" HAVE_IDN_FREE_H)
+CHECK_INCLUDE_FILE_CONCAT("idna.h" HAVE_IDNA_H)
+CHECK_INCLUDE_FILE_CONCAT("tld.h" HAVE_TLD_H)
+CHECK_INCLUDE_FILE_CONCAT("arpa/tftp.h" HAVE_ARPA_TFTP_H)
+CHECK_INCLUDE_FILE_CONCAT("errno.h" HAVE_ERRNO_H)
+CHECK_INCLUDE_FILE_CONCAT("libgen.h" HAVE_LIBGEN_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/filio.h" HAVE_SYS_FILIO_H)
+CHECK_TYPE_SIZE(size_t SIZEOF_SIZE_T)
+CHECK_TYPE_SIZE(ssize_t SIZEOF_SSIZE_T)
+CHECK_TYPE_SIZE("long long" SIZEOF_LONG_LONG)
+CHECK_TYPE_SIZE("long" SIZEOF_LONG)
+CHECK_TYPE_SIZE("__int64" SIZEOF___INT64)
+CHECK_TYPE_SIZE("time_t" SIZEOF_TIME_T)
+
+IF(HAVE_SIZEOF_LONG_LONG)
+ SET(HAVE_LONGLONG 1)
+ SET(HAVE_LL 1)
+ENDIF(HAVE_SIZEOF_LONG_LONG)
+
+FIND_FILE(RANDOM_FILE urandom /dev)
+MARK_AS_ADVANCED(RANDOM_FILE)
+
+#strtoll \
+#socket \
+#select \
+#strdup \
+#strstr \
+#strtok_r \
+#uname \
+#strcasecmp \
+#stricmp \
+#strcmpi \
+#gethostbyaddr \
+#gettimeofday \
+#inet_addr \
+#inet_ntoa \
+#inet_pton \
+#perror \
+#closesocket \
+#siginterrupt \
+#sigaction \
+#signal \
+#getpass_r \
+#strlcat \
+#getpwuid \
+#geteuid \
+#dlopen \
+#utime \
+#sigsetjmp \
+#basename \
+#setlocale \
+#ftruncate \
+#pipe \
+#poll \
+#getprotobyname \
+#getrlimit \
+#setrlimit \
+#fork
+
+# Check for some functions that are used
+CHECK_SYMBOL_EXISTS(basename "${CURL_INCLUDES}" HAVE_BASENAME)
+CHECK_SYMBOL_EXISTS(socket "${CURL_INCLUDES}" HAVE_SOCKET)
+CHECK_SYMBOL_EXISTS(poll "${CURL_INCLUDES}" HAVE_POLL)
+CHECK_SYMBOL_EXISTS(select "${CURL_INCLUDES}" HAVE_SELECT)
+CHECK_SYMBOL_EXISTS(strdup "${CURL_INCLUDES}" HAVE_STRDUP)
+CHECK_SYMBOL_EXISTS(strstr "${CURL_INCLUDES}" HAVE_STRSTR)
+CHECK_SYMBOL_EXISTS(strtok_r "${CURL_INCLUDES}" HAVE_STRTOK_R)
+CHECK_SYMBOL_EXISTS(strftime "${CURL_INCLUDES}" HAVE_STRFTIME)
+CHECK_SYMBOL_EXISTS(uname "${CURL_INCLUDES}" HAVE_UNAME)
+CHECK_SYMBOL_EXISTS(strcasecmp "${CURL_INCLUDES}" HAVE_STRCASECMP)
+CHECK_SYMBOL_EXISTS(stricmp "${CURL_INCLUDES}" HAVE_STRICMP)
+CHECK_SYMBOL_EXISTS(strcmpi "${CURL_INCLUDES}" HAVE_STRCMPI)
+CHECK_SYMBOL_EXISTS(strncmpi "${CURL_INCLUDES}" HAVE_STRNCMPI)
+CHECK_SYMBOL_EXISTS(basename "${CURL_INCLUDES}" HAVE_BASENAME)
+IF(NOT HAVE_STRNCMPI)
+ SET(HAVE_STRCMPI)
+ENDIF(NOT HAVE_STRNCMPI)
+CHECK_SYMBOL_EXISTS(gethostbyaddr "${CURL_INCLUDES}" HAVE_GETHOSTBYADDR)
+CHECK_SYMBOL_EXISTS(gettimeofday "${CURL_INCLUDES}" HAVE_GETTIMEOFDAY)
+CHECK_SYMBOL_EXISTS(inet_addr "${CURL_INCLUDES}" HAVE_INET_ADDR)
+# windows only has this for vista, but will link with it and say
+# that it has it at link time! So, force it off
+IF(WIN32)
+ SET(HAVE_INET_PTON 0 CACHE "" INTERNAL )
+ENDIF(WIN32)
+CHECK_SYMBOL_EXISTS(inet_pton "${CURL_INCLUDES}" HAVE_INET_PTON)
+CHECK_SYMBOL_EXISTS(inet_ntoa "${CURL_INCLUDES}" HAVE_INET_NTOA)
+CHECK_SYMBOL_EXISTS(inet_ntoa_r "${CURL_INCLUDES}" HAVE_INET_NTOA_R)
+CHECK_SYMBOL_EXISTS(tcsetattr "${CURL_INCLUDES}" HAVE_TCSETATTR)
+CHECK_SYMBOL_EXISTS(tcgetattr "${CURL_INCLUDES}" HAVE_TCGETATTR)
+CHECK_SYMBOL_EXISTS(perror "${CURL_INCLUDES}" HAVE_PERROR)
+CHECK_SYMBOL_EXISTS(closesocket "${CURL_INCLUDES}" HAVE_CLOSESOCKET)
+CHECK_SYMBOL_EXISTS(setvbuf "${CURL_INCLUDES}" HAVE_SETVBUF)
+CHECK_SYMBOL_EXISTS(sigsetjmp "${CURL_INCLUDES}" HAVE_SIGSETJMP)
+CHECK_SYMBOL_EXISTS(getpass_r "${CURL_INCLUDES}" HAVE_GETPASS_R)
+CHECK_SYMBOL_EXISTS(strlcat "${CURL_INCLUDES}" HAVE_STRLCAT)
+CHECK_SYMBOL_EXISTS(getpwuid "${CURL_INCLUDES}" HAVE_GETPWUID)
+CHECK_SYMBOL_EXISTS(geteuid "${CURL_INCLUDES}" HAVE_GETEUID)
+CHECK_SYMBOL_EXISTS(utime "${CURL_INCLUDES}" HAVE_UTIME)
+IF(CMAKE_USE_OPENSSL)
+ CHECK_SYMBOL_EXISTS(RAND_status "${CURL_INCLUDES}" HAVE_RAND_STATUS)
+ CHECK_SYMBOL_EXISTS(RAND_screen "${CURL_INCLUDES}" HAVE_RAND_SCREEN)
+ CHECK_SYMBOL_EXISTS(RAND_egd "${CURL_INCLUDES}" HAVE_RAND_EGD)
+ CHECK_SYMBOL_EXISTS(CRYPTO_cleanup_all_ex_data "${CURL_INCLUDES}"
+ HAVE_CRYPTO_CLEANUP_ALL_EX_DATA)
+ENDIF(CMAKE_USE_OPENSSL)
+CHECK_SYMBOL_EXISTS(gmtime_r "${CURL_INCLUDES}" HAVE_GMTIME_R)
+CHECK_SYMBOL_EXISTS(localtime_r "${CURL_INCLUDES}" HAVE_LOCALTIME_R)
+
+CHECK_SYMBOL_EXISTS(gethostbyname "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME)
+CHECK_SYMBOL_EXISTS(gethostbyname_r "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R)
+CHECK_SYMBOL_EXISTS(gethostbyaddr_r "${CURL_INCLUDES}" HAVE_GETHOSTBYADDR_R)
+
+CHECK_SYMBOL_EXISTS(signal "${CURL_INCLUDES}" HAVE_SIGNAL_FUNC)
+CHECK_SYMBOL_EXISTS(SIGALRM "${CURL_INCLUDES}" HAVE_SIGNAL_MACRO)
+IF(HAVE_SIGNAL_FUNC AND HAVE_SIGNAL_MACRO)
+ SET(HAVE_SIGNAL 1)
+ENDIF(HAVE_SIGNAL_FUNC AND HAVE_SIGNAL_MACRO)
+CHECK_SYMBOL_EXISTS(uname "${CURL_INCLUDES}" HAVE_UNAME)
+CHECK_SYMBOL_EXISTS(strtoll "${CURL_INCLUDES}" HAVE_STRTOLL)
+CHECK_SYMBOL_EXISTS(_strtoi64 "${CURL_INCLUDES}" HAVE__STRTOI64)
+CHECK_SYMBOL_EXISTS(strerror_r "${CURL_INCLUDES}" HAVE_STRERROR_R)
+CHECK_SYMBOL_EXISTS(siginterrupt "${CURL_INCLUDES}" HAVE_SIGINTERRUPT)
+CHECK_SYMBOL_EXISTS(perror "${CURL_INCLUDES}" HAVE_PERROR)
+CHECK_SYMBOL_EXISTS(fork "${CURL_INCLUDES}" HAVE_FORK)
+CHECK_SYMBOL_EXISTS(pipe "${CURL_INCLUDES}" HAVE_PIPE)
+CHECK_SYMBOL_EXISTS(ftruncate "${CURL_INCLUDES}" HAVE_FTRUNCATE)
+CHECK_SYMBOL_EXISTS(getprotobyname "${CURL_INCLUDES}" HAVE_GETPROTOBYNAME)
+CHECK_SYMBOL_EXISTS(getrlimit "${CURL_INCLUDES}" HAVE_GETRLIMIT)
+CHECK_SYMBOL_EXISTS(idn_free "${CURL_INCLUDES}" HAVE_IDN_FREE)
+CHECK_SYMBOL_EXISTS(idna_strerror "${CURL_INCLUDES}" HAVE_IDNA_STRERROR)
+CHECK_SYMBOL_EXISTS(tld_strerror "${CURL_INCLUDES}" HAVE_TLD_STRERROR)
+CHECK_SYMBOL_EXISTS(setlocale "${CURL_INCLUDES}" HAVE_SETLOCALE)
+CHECK_SYMBOL_EXISTS(setrlimit "${CURL_INCLUDES}" HAVE_SETRLIMIT)
+
+# only build compat strtok if we need to
+IF (NOT HAVE_STRTOK_R)
+ SET(libCurl_SRCS ${libCurl_SRCS}
+ strtok.c
+ )
+ENDIF (NOT HAVE_STRTOK_R)
+
+# only build compat strtoofft if we need to
+IF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64)
+ SET(libCurl_SRCS ${libCurl_SRCS}
+ strtoofft.c
+ )
+ENDIF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64)
+
+# sigaction and sigsetjmp are special. Use special mechanism for
+# detecting those, but only if previous attempt failed.
+IF(HAVE_SIGNAL_H)
+ CHECK_SYMBOL_EXISTS(sigaction "signal.h" HAVE_SIGACTION)
+ENDIF(HAVE_SIGNAL_H)
+
+IF(NOT HAVE_SIGSETJMP)
+ IF(HAVE_SETJMP_H)
+ CHECK_SYMBOL_EXISTS(sigsetjmp "setjmp.h" HAVE_MACRO_SIGSETJMP)
+ IF(HAVE_MACRO_SIGSETJMP)
+ SET(HAVE_SIGSETJMP 1)
+ ENDIF(HAVE_MACRO_SIGSETJMP)
+ ENDIF(HAVE_SETJMP_H)
+ENDIF(NOT HAVE_SIGSETJMP)
+
+# For other curl specific tests, use this macro.
+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")
+ FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+ "Performing Curl Test ${CURL_TEST} passed with the following output:\n"
+ "${OUTPUT}\n")
+ ELSE(${CURL_TEST})
+ MESSAGE(STATUS "Performing Curl Test ${CURL_TEST} - Failed")
+ SET(${CURL_TEST} "" CACHE INTERNAL "Curl test ${FUNCTION}")
+ FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+ "Performing Curl Test ${CURL_TEST} failed with the following output:\n"
+ "${OUTPUT}\n")
+ ENDIF(${CURL_TEST})
+ ENDIF("${CURL_TEST}" MATCHES "^${CURL_TEST}$")
+ENDMACRO(CURL_INTERNAL_TEST)
+
+# Do curl specific tests
+#OPTION(CURL_HAVE_DISABLED_NONBLOCKING "Disable non-blocking socket detection" OFF)
+SET(CURL_NONBLOCKING_TESTS)
+IF(NOT CURL_HAVE_DISABLED_NONBLOCKING)
+ SET(CURL_NONBLOCKING_TESTS
+ HAVE_FIONBIO
+ HAVE_IOCTLSOCKET
+ HAVE_IOCTLSOCKET_CASE
+ HAVE_O_NONBLOCK
+ HAVE_SO_NONBLOCK
+ )
+ENDIF(NOT CURL_HAVE_DISABLED_NONBLOCKING)
+FOREACH(CURL_TEST
+ ${CURL_NONBLOCKING_TESTS}
+ TIME_WITH_SYS_TIME
+ HAVE_O_NONBLOCKHAVE_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
+ HAVE_GETADDRINFO
+ HAVE_FILE_OFFSET_BITS
+ )
+ CURL_INTERNAL_TEST(${CURL_TEST})
+ENDFOREACH(CURL_TEST)
+IF(HAVE_FILE_OFFSET_BITS)
+ SET(_FILE_OFFSET_BITS 64)
+ENDIF(HAVE_FILE_OFFSET_BITS)
+
+# Check for 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
+ 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)
+
+# Some other minor tests
+
+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)
+
+# Fix libz / zlib.h
+
+IF(NOT CURL_SPECIAL_LIBZ)
+ IF(NOT HAVE_LIBZ)
+ SET(HAVE_ZLIB_H 0)
+ ENDIF(NOT HAVE_LIBZ)
+
+ IF(NOT HAVE_ZLIB_H)
+ SET(HAVE_LIBZ 0)
+ ENDIF(NOT HAVE_ZLIB_H)
+ENDIF(NOT CURL_SPECIAL_LIBZ)
+
+IF(_FILE_OFFSET_BITS)
+ SET(_FILE_OFFSET_BITS 64)
+ENDIF(_FILE_OFFSET_BITS)
+SET(CMAKE_REQUIRED_FLAGS "-D_FILE_OFFSET_BITS=64")
+SET(CMAKE_EXTRA_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/curl/curl.h")
+CHECK_TYPE_SIZE("curl_off_t" SIZEOF_CURL_OFF_T)
+SET(CMAKE_EXTRA_INCLUDE_FILES)
+SET(CMAKE_REQUIRED_FLAGS)
+
+
+# Check for nonblocking
+SET(HAVE_DISABLED_NONBLOCKING 1)
+IF(HAVE_FIONBIO OR
+ HAVE_IOCTLSOCKET OR
+ HAVE_IOCTLSOCKET_CASE OR
+ HAVE_O_NONBLOCK)
+ SET(HAVE_DISABLED_NONBLOCKING)
+ENDIF(HAVE_FIONBIO OR
+ HAVE_IOCTLSOCKET OR
+ HAVE_IOCTLSOCKET_CASE OR
+ HAVE_O_NONBLOCK)
+
+IF(RETSIGTYPE_TEST)
+ SET(RETSIGTYPE void)
+ELSE(RETSIGTYPE_TEST)
+ SET(RETSIGTYPE int)
+ENDIF(RETSIGTYPE_TEST)
+
+IF(CMAKE_COMPILER_IS_GNUCC AND APPLE)
+ INCLUDE(CheckCCompilerFlag)
+ CHECK_C_COMPILER_FLAG(-Wno-long-double HAVE_C_FLAG_Wno_long_double)
+ IF(HAVE_C_FLAG_Wno_long_double)
+ # The Mac version of GCC warns about use of long double. Disable it.
+ GET_SOURCE_FILE_PROPERTY(MPRINTF_COMPILE_FLAGS mprintf.c COMPILE_FLAGS)
+ IF(MPRINTF_COMPILE_FLAGS)
+ SET(MPRINTF_COMPILE_FLAGS "${MPRINTF_COMPILE_FLAGS} -Wno-long-double")
+ ELSE(MPRINTF_COMPILE_FLAGS)
+ SET(MPRINTF_COMPILE_FLAGS "-Wno-long-double")
+ ENDIF(MPRINTF_COMPILE_FLAGS)
+ SET_SOURCE_FILES_PROPERTIES(mprintf.c PROPERTIES
+ COMPILE_FLAGS ${MPRINTF_COMPILE_FLAGS})
+ ENDIF(HAVE_C_FLAG_Wno_long_double)
+ENDIF(CMAKE_COMPILER_IS_GNUCC AND APPLE)
+
+INCLUDE(CMake/OtherTests.cmake)
+
+# The rest of the build
+
+INCLUDE_DIRECTORIES(${LIBCURL_SOURCE_DIR})
+INCLUDE_DIRECTORIES(${LIBCURL_BINARY_DIR})
+OPTION(CMAKE_BUILD_CURL_SHARED "Should curl be built shared" TRUE)
+IF(CMAKE_BUILD_CURL_SHARED)
+ SET(LIBRARY_TYPE SHARED)
+ ADD_DEFINITIONS(-DHAVE_CONFIG_H)
+ELSE(CMAKE_BUILD_CURL_SHARED)
+ ADD_DEFINITIONS(-DHAVE_CONFIG_H
+ -DCURL_STATICLIB)
+ENDIF(CMAKE_BUILD_CURL_SHARED)
+SET(CURL_STATICLIB)
+
+# Support CheckTypeSize module from CMake 2.8.0 and lower.
+FOREACH(var
+ SIZEOF_CURL_OFF_T
+ SIZEOF_LONG
+ SIZEOF_LONG_LONG
+ SIZEOF___INT64
+ SIZEOF_SIZE_T
+ SIZEOF_SSIZE_T
+ SIZEOF_TIME_T
+ )
+ IF(NOT ${var}_CODE)
+ MESSAGE("creating ${var}_CODE")
+ IF(${var})
+ SET(${var}_CODE "#define ${var} ${${var}}")
+ ELSE()
+ SET(${var}_CODE "/* #undef ${var} */")
+ ENDIF()
+ ENDIF()
+ENDFOREACH()
+
+CONFIGURE_FILE(${LIBCURL_SOURCE_DIR}/config.h.in
+ ${LIBCURL_BINARY_DIR}/config.h)
+
+
+ADD_LIBRARY(cmcurl ${LIBRARY_TYPE} ${libCurl_SRCS})
+TARGET_LINK_LIBRARIES(cmcurl ${CURL_LIBS})
+IF(CMAKE_BUILD_CURL_SHARED)
+ SET_TARGET_PROPERTIES(cmcurl PROPERTIES DEFINE_SYMBOL BUILDING_LIBCURL
+ RUNTIME_OUTPUT_DIRECTORY ${CMake_BIN_DIR})
+ INSTALL_TARGETS(/bin cmcurl)
+ENDIF(CMAKE_BUILD_CURL_SHARED)
+
+OPTION(CURL_TESTING "Do libCurl testing" OFF)
+IF(CURL_TESTING)
+ SUBDIRS(Testing)
+ENDIF(CURL_TESTING)
+
+ADD_EXECUTABLE(LIBCURL Testing/curltest.c)
+TARGET_LINK_LIBRARIES(LIBCURL cmcurl ${CMAKE_DL_LIBS})
+
+IF(CMAKE_CURL_TEST_URL)
+ ADD_TEST(curl LIBCURL ${CMAKE_CURL_TEST_URL})
+ENDIF(CMAKE_CURL_TEST_URL)
+
+INSTALL(FILES COPYING DESTINATION ${CMake_DOC_DEST}/cmcurl)
diff --git a/Utilities/cmcurl/COPYING b/Utilities/cmcurl/COPYING
new file mode 100644
index 000000000..048cf5657
--- /dev/null
+++ b/Utilities/cmcurl/COPYING
@@ -0,0 +1,21 @@
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1996 - 2009, Daniel Stenberg, <daniel@haxx.se>.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright
+notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall not
+be used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization of the copyright holder.
diff --git a/Utilities/cmcurl/Platforms/WindowsCache.cmake b/Utilities/cmcurl/Platforms/WindowsCache.cmake
new file mode 100644
index 000000000..b4515ce04
--- /dev/null
+++ b/Utilities/cmcurl/Platforms/WindowsCache.cmake
@@ -0,0 +1,121 @@
+IF(NOT UNIX)
+ IF(WIN32)
+ SET(HAVE_LIBDL 0)
+ SET(HAVE_LIBUCB 0)
+ SET(HAVE_LIBSOCKET 0)
+ SET(NOT_NEED_LIBNSL 0)
+ SET(HAVE_LIBNSL 0)
+ SET(HAVE_LIBZ 0)
+ SET(HAVE_LIBCRYPTO 0)
+
+ SET(HAVE_DLOPEN 0)
+
+ SET(HAVE_ALLOCA_H 0)
+ SET(HAVE_ARPA_INET_H 0)
+ SET(HAVE_DLFCN_H 0)
+ SET(HAVE_FCNTL_H 1)
+ SET(HAVE_FEATURES_H 0)
+ SET(HAVE_INTTYPES_H 0)
+ SET(HAVE_IO_H 1)
+ SET(HAVE_MALLOC_H 1)
+ SET(HAVE_MEMORY_H 1)
+ SET(HAVE_NETDB_H 0)
+ SET(HAVE_NETINET_IF_ETHER_H 0)
+ SET(HAVE_NETINET_IN_H 0)
+ SET(HAVE_NET_IF_H 0)
+ SET(HAVE_PROCESS_H 1)
+ SET(HAVE_PWD_H 0)
+ SET(HAVE_SETJMP_H 1)
+ SET(HAVE_SGTTY_H 0)
+ SET(HAVE_SIGNAL_H 1)
+ SET(HAVE_SOCKIO_H 0)
+ SET(HAVE_STDINT_H 0)
+ SET(HAVE_STDLIB_H 1)
+ SET(HAVE_STRINGS_H 0)
+ SET(HAVE_STRING_H 1)
+ SET(HAVE_SYS_PARAM_H 0)
+ SET(HAVE_SYS_POLL_H 0)
+ SET(HAVE_SYS_SELECT_H 0)
+ SET(HAVE_SYS_SOCKET_H 0)
+ SET(HAVE_SYS_SOCKIO_H 0)
+ SET(HAVE_SYS_STAT_H 1)
+ SET(HAVE_SYS_TIME_H 0)
+ SET(HAVE_SYS_TYPES_H 1)
+ SET(HAVE_SYS_UTIME_H 1)
+ SET(HAVE_TERMIOS_H 0)
+ SET(HAVE_TERMIO_H 0)
+ SET(HAVE_TIME_H 1)
+ SET(HAVE_UNISTD_H 0)
+ SET(HAVE_UTIME_H 0)
+ SET(HAVE_X509_H 0)
+ SET(HAVE_ZLIB_H 0)
+
+ SET(HAVE_SIZEOF_LONG_DOUBLE 1)
+ SET(SIZEOF_LONG_DOUBLE 8)
+
+ SET(HAVE_SOCKET 1)
+ SET(HAVE_POLL 0)
+ SET(HAVE_SELECT 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 1)
+ SET(HAVE_GETHOSTBYADDR 1)
+ SET(HAVE_GETTIMEOFDAY 0)
+ SET(HAVE_INET_ADDR 1)
+ SET(HAVE_INET_NTOA 1)
+ SET(HAVE_INET_NTOA_R 0)
+ SET(HAVE_TCGETATTR 0)
+ SET(HAVE_TCSETATTR 0)
+ SET(HAVE_PERROR 1)
+ SET(HAVE_CLOSESOCKET 1)
+ SET(HAVE_SETVBUF 0)
+ SET(HAVE_SIGSETJMP 0)
+ SET(HAVE_GETPASS_R 0)
+ SET(HAVE_STRLCAT 0)
+ SET(HAVE_GETPWUID 0)
+ SET(HAVE_GETEUID 0)
+ SET(HAVE_UTIME 1)
+ SET(HAVE_RAND_EGD 0)
+ SET(HAVE_RAND_SCREEN 0)
+ SET(HAVE_RAND_STATUS 0)
+ SET(HAVE_GMTIME_R 0)
+ SET(HAVE_LOCALTIME_R 0)
+ SET(HAVE_GETHOSTBYADDR_R 0)
+ SET(HAVE_GETHOSTBYNAME_R 0)
+ SET(HAVE_SIGNAL_FUNC 1)
+ SET(HAVE_SIGNAL_MACRO 0)
+
+ SET(HAVE_GETHOSTBYADDR_R_5 0)
+ SET(HAVE_GETHOSTBYADDR_R_5_REENTRANT 0)
+ SET(HAVE_GETHOSTBYADDR_R_7 0)
+ SET(HAVE_GETHOSTBYADDR_R_7_REENTRANT 0)
+ SET(HAVE_GETHOSTBYADDR_R_8 0)
+ SET(HAVE_GETHOSTBYADDR_R_8_REENTRANT 0)
+ SET(HAVE_GETHOSTBYNAME_R_3 0)
+ SET(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
+ SET(HAVE_GETHOSTBYNAME_R_5 0)
+ SET(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
+ SET(HAVE_GETHOSTBYNAME_R_6 0)
+ SET(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
+
+ SET(TIME_WITH_SYS_TIME 0)
+ SET(HAVE_O_NONBLOCK 0)
+ SET(HAVE_IN_ADDR_T 0)
+ SET(HAVE_INET_NTOA_R_DECL 0)
+ SET(HAVE_INET_NTOA_R_DECL_REENTRANT 0)
+ SET(HAVE_GETADDRINFO 0)
+ SET(STDC_HEADERS 1)
+ SET(RETSIGTYPE_TEST 1)
+
+ SET(HAVE_SIGACTION 0)
+ SET(HAVE_MACRO_SIGSETJMP 0)
+ ELSE(WIN32)
+ MESSAGE("This file should be included on Windows platform only")
+ ENDIF(WIN32)
+ENDIF(NOT UNIX)
+
diff --git a/Utilities/cmcurl/Platforms/config-aix.h b/Utilities/cmcurl/Platforms/config-aix.h
new file mode 100644
index 000000000..86d10938f
--- /dev/null
+++ b/Utilities/cmcurl/Platforms/config-aix.h
@@ -0,0 +1,486 @@
+/* lib/config.h. Generated by configure. */
+/* lib/config.h.in. Generated from configure.in by autoheader. */
+/* Name of this package! */
+#define PACKAGE "curl"
+
+/* Version number of this archive. */
+#define VERSION "7.10.2"
+
+/* Define if you have the getpass function. */
+/* #undef HAVE_GETPASS */
+
+/* Define cpu-machine-OS */
+#define OS "powerpc-ibm-aix5.1.0.0"
+
+/* Define if you have the gethostbyaddr_r() function with 5 arguments */
+#define HAVE_GETHOSTBYADDR_R_5 1
+
+/* Define if you have the gethostbyaddr_r() function with 7 arguments */
+/* #undef HAVE_GETHOSTBYADDR_R_7 */
+
+/* Define if you have the gethostbyaddr_r() function with 8 arguments */
+/* #undef HAVE_GETHOSTBYADDR_R_8 */
+
+/* Define if you have the gethostbyname_r() function with 3 arguments */
+#define HAVE_GETHOSTBYNAME_R_3 1
+
+/* Define if you have the gethostbyname_r() function with 5 arguments */
+/* #undef HAVE_GETHOSTBYNAME_R_5 */
+
+/* Define if you have the gethostbyname_r() function with 6 arguments */
+/* #undef HAVE_GETHOSTBYNAME_R_6 */
+
+/* Define if you have the inet_ntoa_r function declared. */
+/* #undef HAVE_INET_NTOA_R_DECL */
+
+/* Define if you need the _REENTRANT define for some functions */
+/* #undef NEED_REENTRANT */
+
+/* Define if you have the Kerberos4 libraries (including -ldes) */
+/* #undef KRB4 */
+
+/* Define if you want to enable IPv6 support */
+#define ENABLE_IPV6 1
+
+/* Define this to 'int' if ssize_t is not an available typedefed type */
+/* #undef ssize_t */
+
+/* Define this to 'int' if socklen_t is not an available typedefed type */
+/* #undef socklen_t */
+
+/* Define this as a suitable file to read random data from */
+/* #undef RANDOM_FILE */
+
+/* Define this to your Entropy Gathering Daemon socket pathname */
+/* #undef EGD_SOCKET */
+
+/* Define if you have a working OpenSSL installation */
+/* #undef OPENSSL_ENABLED */
+
+/* Define the one correct non-blocking socket method below */
+/* #undef HAVE_FIONBIO */
+/* #undef HAVE_IOCTLSOCKET */
+/* #undef HAVE_IOCTLSOCKET_CASE */
+/* #undef HAVE_O_NONBLOCK */
+#define HAVE_DISABLED_NONBLOCKING 1
+
+/* Define this to 'int' if in_addr_t is not an available typedefed type */
+/* #undef in_addr_t */
+
+/* Define to disable DICT */
+/* #undef CURL_DISABLE_DICT */
+
+/* Define to disable FILE */
+/* #undef CURL_DISABLE_FILE */
+
+/* Define to disable FTP */
+/* #undef CURL_DISABLE_FTP */
+
+/* Define to disable GOPHER */
+/* #undef CURL_DISABLE_GOPHER */
+
+/* Define to disable HTTP */
+/* #undef CURL_DISABLE_HTTP */
+
+/* Define to disable LDAP */
+/* #undef CURL_DISABLE_LDAP */
+
+/* Define to disable TELNET */
+/* #undef CURL_DISABLE_TELNET */
+
+/* Define if you have zlib present */
+#define HAVE_LIBZ 1
+
+/* CA bundle full path name */
+#define CURL_CA_BUNDLE "/usr/local/share/curl/curl-ca-bundle.crt"
+
+/* to disable FILE */
+/* #undef CURL_DISABLE_FILE */
+
+/* to disable FTP */
+/* #undef CURL_DISABLE_FTP */
+
+/* to disable GOPHER */
+/* #undef CURL_DISABLE_GOPHER */
+
+/* to disable HTTP */
+/* #undef CURL_DISABLE_HTTP */
+
+/* to disable LDAP */
+/* #undef CURL_DISABLE_LDAP */
+
+/* to disable TELNET */
+/* #undef CURL_DISABLE_TELNET */
+
+/* Set to explicitly specify we don't want to use thread-safe functions */
+/* #undef DISABLED_THREADSAFE */
+
+/* your Entropy Gathering Daemon socket pathname */
+/* #undef EGD_SOCKET */
+
+/* Define if you want to enable IPv6 support */
+#define ENABLE_IPV6 1
+
+/* Define to 1 if you have the <alloca.h> header file. */
+#define HAVE_ALLOCA_H 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Define to 1 if you have the `closesocket' function. */
+/* #undef HAVE_CLOSESOCKET */
+
+/* Define to 1 if you have the <crypto.h> header file. */
+/* #undef HAVE_CRYPTO_H */
+
+/* Define to 1 if you have the <des.h> header file. */
+/* #undef HAVE_DES_H */
+
+/* to disable NON-BLOCKING connections */
+#define HAVE_DISABLED_NONBLOCKING 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `dlopen' function. */
+#define HAVE_DLOPEN 1
+
+/* Define to 1 if you have the <err.h> header file. */
+/* #undef HAVE_ERR_H */
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define if getaddrinfo exists and works */
+#define HAVE_GETADDRINFO 1
+
+/* Define to 1 if you have the `geteuid' function. */
+#define HAVE_GETEUID 1
+
+/* Define to 1 if you have the `gethostbyaddr' function. */
+#define HAVE_GETHOSTBYADDR 1
+
+/* Define to 1 if you have the `gethostbyaddr_r' function. */
+#define HAVE_GETHOSTBYADDR_R 1
+
+/* Define to 1 if you have the `gethostbyname_r' function. */
+#define HAVE_GETHOSTBYNAME_R 1
+
+/* Define to 1 if you have the `getpass_r' function. */
+/* #undef HAVE_GETPASS_R */
+
+/* Define to 1 if you have the `getpwuid' function. */
+#define HAVE_GETPWUID 1
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#define HAVE_GMTIME_R 1
+
+/* Define to 1 if you have the `inet_addr' function. */
+#define HAVE_INET_ADDR 1
+
+/* Define to 1 if you have the `inet_ntoa' function. */
+#define HAVE_INET_NTOA 1
+
+/* Define to 1 if you have the `inet_ntoa_r' function. */
+#define HAVE_INET_NTOA_R 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <io.h> header file. */
+/* #undef HAVE_IO_H */
+
+/* Define to 1 if you have the `krb_get_our_ip_for_realm' function. */
+/* #undef HAVE_KRB_GET_OUR_IP_FOR_REALM */
+
+/* Define to 1 if you have the <krb.h> header file. */
+/* #undef HAVE_KRB_H */
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+/* #undef HAVE_LIBCRYPTO */
+
+/* Define to 1 if you have the `dl' library (-ldl). */
+/* #undef HAVE_LIBDL */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+/* #undef HAVE_LIBRESOLV */
+
+/* Define to 1 if you have the `resolve' library (-lresolve). */
+/* #undef HAVE_LIBRESOLVE */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Define to 1 if you have the `ssl' library (-lssl). */
+/* #undef HAVE_LIBSSL */
+
+/* If zlib is available */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if you have the `localtime_r' function. */
+#define HAVE_LOCALTIME_R 1
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#define HAVE_MALLOC_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define to 1 if you have the <netinet/if_ether.h> header file. */
+#define HAVE_NETINET_IF_ETHER_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#define HAVE_NET_IF_H 1
+
+/* Define to 1 if you have the <openssl/crypto.h> header file. */
+/* #undef HAVE_OPENSSL_CRYPTO_H */
+
+/* Define to 1 if you have the <openssl/engine.h> header file. */
+/* #undef HAVE_OPENSSL_ENGINE_H */
+
+/* Define to 1 if you have the <openssl/err.h> header file. */
+/* #undef HAVE_OPENSSL_ERR_H */
+
+/* Define to 1 if you have the <openssl/pem.h> header file. */
+/* #undef HAVE_OPENSSL_PEM_H */
+
+/* Define to 1 if you have the <openssl/rsa.h> header file. */
+/* #undef HAVE_OPENSSL_RSA_H */
+
+/* Define to 1 if you have the <openssl/ssl.h> header file. */
+/* #undef HAVE_OPENSSL_SSL_H */
+
+/* Define to 1 if you have the <openssl/x509.h> header file. */
+/* #undef HAVE_OPENSSL_X509_H */
+
+/* Define to 1 if you have the <pem.h> header file. */
+/* #undef HAVE_PEM_H */
+
+/* Define to 1 if you have the `perror' function. */
+#define HAVE_PERROR 1
+
+/* Define to 1 if you have the `poll' function. */
+#define HAVE_POLL 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have the `RAND_egd' function. */
+/* #undef HAVE_RAND_EGD */
+
+/* Define to 1 if you have the `RAND_screen' function. */
+/* #undef HAVE_RAND_SCREEN */
+
+/* Define to 1 if you have the `RAND_status' function. */
+/* #undef HAVE_RAND_STATUS */
+
+/* Define to 1 if you have the <rsa.h> header file. */
+/* #undef HAVE_RSA_H */
+
+/* Define to 1 if you have the `select' function. */
+#define HAVE_SELECT 1
+
+/* Define to 1 if you have the <setjmp.h> header file. */
+#define HAVE_SETJMP_H 1
+
+/* Define to 1 if you have the `setvbuf' function. */
+#define HAVE_SETVBUF 1
+
+/* Define to 1 if you have the <sgtty.h> header file. */
+#define HAVE_SGTTY_H 1
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if you have the `signal' function. */
+#define HAVE_SIGNAL 1
+
+/* If you have sigsetjmp */
+#define HAVE_SIGSETJMP 1
+
+/* Define to 1 if you have the `socket' function. */
+#define HAVE_SOCKET 1
+
+/* Define to 1 if you have the <ssl.h> header file. */
+/* #undef HAVE_SSL_H */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+/* #undef HAVE_STDINT_H */
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the `strcmpi' function. */
+/* #undef HAVE_STRCMPI */
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the `stricmp' function. */
+/* #undef HAVE_STRICMP */
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strlcat' function. */
+/* #undef HAVE_STRLCAT */
+
+/* Define to 1 if you have the `strlcpy' function. */
+/* #undef HAVE_STRLCPY */
+
+/* Define to 1 if you have the `strstr' function. */
+#define HAVE_STRSTR 1
+
+/* Define to 1 if you have the `strtok_r' function. */
+#define HAVE_STRTOK_R 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#define HAVE_SYS_POLL_H 1
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+/* #undef HAVE_SYS_SOCKIO_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+/* #undef HAVE_SYS_UTIME_H */
+
+/* Define to 1 if you have the `tcgetattr' function. */
+#define HAVE_TCGETATTR 1
+
+/* Define to 1 if you have the `tcsetattr' function. */
+#define HAVE_TCSETATTR 1
+
+/* Define to 1 if you have the <termios.h> header file. */
+#define HAVE_TERMIOS_H 1
+
+/* Define to 1 if you have the <termio.h> header file. */
+#define HAVE_TERMIO_H 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the `uname' function. */
+#define HAVE_UNAME 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `utime' function. */
+#define HAVE_UTIME 1
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if you have the <winsock.h> header file. */
+/* #undef HAVE_WINSOCK_H */
+
+/* Define to 1 if you have the <x509.h> header file. */
+/* #undef HAVE_X509_H */
+
+/* if you have the zlib.h header file */
+/* #undef HAVE_ZLIB_H */
+
+/* if you have the Kerberos4 libraries (including -ldes) */
+/* #undef KRB4 */
+
+/* cpu-machine-OS */
+#define OS "powerpc-ibm-aix5.1.0.0"
+
+/* Name of package */
+#define PACKAGE "curl"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION ""
+
+/* a suitable file to read random data from */
+/* #undef RANDOM_FILE */
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Version number of package */
+#define VERSION "7.10.2"
+
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define for large files, on AIX-style hosts. */
+#define _LARGE_FILES 1
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* type to use in place of in_addr_t if not defined */
+/* #undef in_addr_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* type to use in place of socklen_t if not defined */
+/* #undef socklen_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef ssize_t */
diff --git a/Utilities/cmcurl/Testing/CMakeLists.txt b/Utilities/cmcurl/Testing/CMakeLists.txt
new file mode 100644
index 000000000..214410fd5
--- /dev/null
+++ b/Utilities/cmcurl/Testing/CMakeLists.txt
@@ -0,0 +1,19 @@
+SET(CURL_TESTS
+ ftpget
+ ftpgetresp
+ ftpupload
+ getinmemory
+ persistant
+ sepheaders
+ simple
+ )
+
+CONFIGURE_FILE(${LIBCURL_SOURCE_DIR}/Testing/testconfig.h.in
+ ${LIBCURL_BINARY_DIR}/Testing/testconfig.h)
+
+INCLUDE_DIRECTORIES(${LIBCURL_BINARY_DIR}/Testing)
+
+FOREACH(TEST ${CURL_TESTS})
+ ADD_EXECUTABLE(${TEST} ${TEST}.c)
+ TARGET_LINK_LIBRARIES(${TEST} cmcurl)
+ENDFOREACH(TEST)
diff --git a/Utilities/cmcurl/Testing/curlgtk.c b/Utilities/cmcurl/Testing/curlgtk.c
new file mode 100644
index 000000000..7c9ce2a1b
--- /dev/null
+++ b/Utilities/cmcurl/Testing/curlgtk.c
@@ -0,0 +1,95 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+/* Copyright (c) 2000 David Odin (aka DindinX) for MandrakeSoft */
+/* an attempt to use the curl library in concert with a gtk-threaded application */
+
+#include <stdio.h>
+#include <gtk/gtk.h>
+
+#include <curl/curl.h>
+#include <curl/types.h> /* new for v7 */
+#include <curl/easy.h> /* new for v7 */
+
+#include <pthread.h>
+
+GtkWidget *Bar;
+
+size_t my_read_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+ return fread(ptr, size, nmemb, stream);
+}
+
+int my_progress_func(GtkWidget *Bar, int t, int d)
+{
+/* printf("%d / %d (%g %%)\n", d, t, d*100.0/t);*/
+ gdk_threads_enter();
+ gtk_progress_set_value(GTK_PROGRESS(Bar), d*100.0/t);
+ gdk_threads_leave();
+ return 0;
+}
+
+void *curl_thread(void *ptr)
+{
+ CURL *curl;
+ CURLcode res;
+ FILE *outfile;
+ gchar *url = ptr;
+
+ curl = curl_easy_init();
+ if(curl)
+ {
+ outfile = fopen("/tmp/test.curl", "w");
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_FILE, outfile);
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read_func);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, Bar);
+
+ res = curl_easy_perform(curl);
+
+ fclose(outfile);
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ return NULL;
+}
+
+int main(int argc, char **argv)
+{
+ GtkWidget *Window, *Frame, *Frame2;
+ GtkAdjustment *adj;
+ pthread_t curl_tid;
+
+ /* Init thread */
+ g_thread_init(NULL);
+
+ gtk_init(&argc, &argv);
+ Window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ Frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(Frame), GTK_SHADOW_OUT);
+ gtk_container_add(GTK_CONTAINER(Window), Frame);
+ Frame2 = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(Frame2), GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(Frame), Frame2);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame2), 5);
+ adj = (GtkAdjustment*)gtk_adjustment_new(0, 0, 100, 0, 0, 0);
+ Bar = gtk_progress_bar_new_with_adjustment(adj);
+ gtk_container_add(GTK_CONTAINER(Frame2), Bar);
+ gtk_widget_show_all(Window);
+
+ pthread_create(&curl_tid, NULL, curl_thread, argv[1]);
+
+ gdk_threads_enter();
+ gtk_main();
+ gdk_threads_leave();
+ return 0;
+}
+
diff --git a/Utilities/cmcurl/Testing/curltest.c b/Utilities/cmcurl/Testing/curltest.c
new file mode 100644
index 000000000..210868e36
--- /dev/null
+++ b/Utilities/cmcurl/Testing/curltest.c
@@ -0,0 +1,159 @@
+/* Prevent warnings on Visual Studio */
+struct _RPC_ASYNC_STATE;
+
+#include "curl/curl.h"
+#include <stdlib.h>
+#include <string.h>
+
+int GetFtpFile(void)
+{
+ int retVal = 0;
+ CURL *curl;
+ CURLcode res;
+ curl = curl_easy_init();
+ if(curl)
+ {
+ /* Get curl 7.9.2 from sunet.se's FTP site: */
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+ curl_easy_setopt(curl, CURLOPT_HEADER, 1);
+ curl_easy_setopt(curl, CURLOPT_URL,
+ "ftp://public.kitware.com/pub/cmake/cygwin/setup.hint");
+ res = curl_easy_perform(curl);
+ if ( res != 0 )
+ {
+ printf("Error fetching: ftp://public.kitware.com/pub/cmake/cygwin/setup.hint\n");
+ retVal = 1;
+ }
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ else
+ {
+ printf("Cannot create curl object\n");
+ retVal = 1;
+ }
+ return retVal;
+}
+
+int GetWebFiles(char *url1, char *url2)
+{
+ int retVal = 0;
+ CURL *curl;
+ CURLcode res;
+
+ char proxy[1024];
+ int proxy_type = 0;
+
+ if ( getenv("HTTP_PROXY") )
+ {
+ proxy_type = 1;
+ if (getenv("HTTP_PROXY_PORT") )
+ {
+ sprintf(proxy, "%s:%s", getenv("HTTP_PROXY"), getenv("HTTP_PROXY_PORT"));
+ }
+ else
+ {
+ sprintf(proxy, "%s", getenv("HTTP_PROXY"));
+ }
+ if ( getenv("HTTP_PROXY_TYPE") )
+ {
+ /* HTTP/SOCKS4/SOCKS5 */
+ if ( strcmp(getenv("HTTP_PROXY_TYPE"), "HTTP") == 0 )
+ {
+ proxy_type = 1;
+ }
+ else if ( strcmp(getenv("HTTP_PROXY_TYPE"), "SOCKS4") == 0 )
+ {
+ proxy_type = 2;
+ }
+ else if ( strcmp(getenv("HTTP_PROXY_TYPE"), "SOCKS5") == 0 )
+ {
+ proxy_type = 3;
+ }
+ }
+ }
+
+ curl = curl_easy_init();
+ if(curl)
+ {
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+ curl_easy_setopt(curl, CURLOPT_HEADER, 1);
+
+ /* Using proxy */
+ if ( proxy_type > 0 )
+ {
+ curl_easy_setopt(curl, CURLOPT_PROXY, proxy);
+ switch (proxy_type)
+ {
+ case 2:
+ curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
+ break;
+ case 3:
+ curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
+ break;
+ default:
+ curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
+ }
+ }
+
+ /* get the first document */
+ curl_easy_setopt(curl, CURLOPT_URL, url1);
+ res = curl_easy_perform(curl);
+ if ( res != 0 )
+ {
+ printf("Error fetching: %s\n", url1);
+ retVal = 1;
+ }
+
+ /* get another document from the same server using the same
+ connection */
+ /* avoid warnings about url2 since below block is commented out: */
+ (void) url2;
+ /*
+ curl_easy_setopt(curl, CURLOPT_URL, url2);
+ res = curl_easy_perform(curl);
+ if ( res != 0 )
+ {
+ printf("Error fetching: %s\n", url2);
+ retVal = 1;
+ }
+ */
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ else
+ {
+ printf("Cannot create curl object\n");
+ retVal = 1;
+ }
+
+ return retVal;
+}
+
+
+int main(int argc, char **argv)
+{
+ int retVal = 0;
+
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+
+ if(argc>1)
+ {
+ retVal += GetWebFiles(argv[1], 0);
+ }
+ else
+ {
+ printf("error: first argument should be a url to download\n");
+ retVal = 1;
+ }
+
+ /* Do not check the output of FTP socks5 cannot handle FTP yet */
+ /* GetFtpFile(); */
+ /* do not test ftp right now because we don't enable that port */
+
+ curl_global_cleanup();
+
+ return retVal;
+}
diff --git a/Utilities/cmcurl/Testing/ftpget.c b/Utilities/cmcurl/Testing/ftpget.c
new file mode 100644
index 000000000..1a3633ec9
--- /dev/null
+++ b/Utilities/cmcurl/Testing/ftpget.c
@@ -0,0 +1,84 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+#include "curl/curl.h"
+#include "curl/types.h"
+#include "curl/easy.h"
+#include "setup.h"
+
+#include "testconfig.h"
+
+/*
+ * This is an example showing how to get a single file from an FTP server.
+ * It delays the actual destination file creation until the first write
+ * callback so that it won't create an empty file in case the remote file
+ * doesn't exist or something else fails.
+ */
+
+struct FtpFile {
+ char *filename;
+ FILE *stream;
+};
+
+int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
+{
+ struct FtpFile *out=(struct FtpFile *)stream;
+ if(out && !out->stream) {
+ /* open file for writing */
+ out->stream=fopen(out->filename, "wb");
+ if(!out->stream)
+ return -1; /* failure, can't open file to write */
+ }
+ return fwrite(buffer, size, nmemb, out->stream);
+}
+
+
+int main(void)
+{
+ CURL *curl;
+ CURLcode res;
+ struct FtpFile ftpfile={
+ LIBCURL_BINARY_DIR "/Testing/ftpget-download.txt", /* name to store the file as if succesful */
+ NULL
+ };
+
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+
+ curl = curl_easy_init();
+ if(curl) {
+ /* Get curl 7.9.2 from sunet.se's FTP site: */
+ curl_easy_setopt(curl, CURLOPT_URL,
+ "ftp://public.kitware.com/pub/cmake/cygwin/setup.hint");
+ /* Define our callback to get called when there's data to be written */
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
+ /* Set a pointer to our struct to pass to the callback */
+ curl_easy_setopt(curl, CURLOPT_FILE, &ftpfile);
+
+ /* Switch on full protocol/debug output */
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, TRUE);
+
+ res = curl_easy_perform(curl);
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+
+ if(CURLE_OK != res) {
+ /* we failed */
+ fprintf(stderr, "curl told us %d\n", res);
+ }
+ }
+
+ if(ftpfile.stream)
+ fclose(ftpfile.stream); /* close the local file */
+
+ curl_global_cleanup();
+
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/ftpgetresp.c b/Utilities/cmcurl/Testing/ftpgetresp.c
new file mode 100644
index 000000000..9548b2a34
--- /dev/null
+++ b/Utilities/cmcurl/Testing/ftpgetresp.c
@@ -0,0 +1,64 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+#include "curl/curl.h"
+#include "curl/types.h"
+#include "curl/easy.h"
+
+#include "testconfig.h"
+
+/*
+ * Similar to ftpget.c but this also stores the received response-lines
+ * in a separate file using our own callback!
+ *
+ * This functionality was introduced in libcurl 7.9.3.
+ */
+
+size_t
+write_response(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ FILE *writehere = (FILE *)data;
+ return fwrite(ptr, size, nmemb, writehere);
+}
+
+int main(int argc, char **argv)
+{
+ CURL *curl;
+ CURLcode res;
+ FILE *ftpfile;
+ FILE *respfile;
+ (void)argc; (void)argv;
+
+ /* local file name to store the file as */
+ ftpfile = fopen(LIBCURL_BINARY_DIR "/Testing/ftpgetresp-list.txt", "wb"); /* b is binary, needed on win32 */
+
+ /* local file name to store the FTP server's response lines in */
+ respfile = fopen(LIBCURL_BINARY_DIR "/Testing/ftpgetresp-responses.txt", "wb"); /* b is binary, needed on win32 */
+
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+
+ curl = curl_easy_init();
+ if(curl) {
+ /* Get a file listing from sunet */
+ curl_easy_setopt(curl, CURLOPT_URL, "ftp://public.kitware.com/");
+ curl_easy_setopt(curl, CURLOPT_FILE, ftpfile);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_response);
+ curl_easy_setopt(curl, CURLOPT_WRITEHEADER, respfile);
+ res = curl_easy_perform(curl);
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+
+ fclose(ftpfile); /* close the local file */
+ fclose(respfile); /* close the response file */
+
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/ftpupload.c b/Utilities/cmcurl/Testing/ftpupload.c
new file mode 100644
index 000000000..780c9cd27
--- /dev/null
+++ b/Utilities/cmcurl/Testing/ftpupload.c
@@ -0,0 +1,93 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+#include "curl/curl.h"
+#include "setup.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "testconfig.h"
+
+/*
+ * This example shows an FTP upload, with a rename of the file just after
+ * a successful upload.
+ *
+ * Example based on source code provided by Erick Nuwendam. Thanks!
+ */
+
+#define LOCAL_FILE LIBCURL_SOURCE_DIR "/Testing/ftpupload.c"
+#define UPLOAD_FILE_AS "while-uploading.txt"
+#define REMOTE_URL "ftp://public.kitware.com/incoming/" UPLOAD_FILE_AS
+#define RENAME_FILE_TO "renamed-and-fine.txt"
+
+int main(int argc, char **argv)
+{
+ CURL *curl;
+ CURLcode res;
+ FILE *ftpfile;
+ FILE * hd_src ;
+ int hd ;
+ struct stat file_info;
+
+ struct curl_slist *headerlist=NULL;
+ char buf_1 [] = "RNFR " UPLOAD_FILE_AS;
+ char buf_2 [] = "RNTO " RENAME_FILE_TO;
+
+ /* get the file size of the local file */
+ hd = open(LOCAL_FILE, O_RDONLY) ;
+ fstat(hd, &file_info);
+ close(hd) ;
+
+ /* get a FILE * of the same file, could also be made with
+ fdopen() from the previous descriptor, but hey this is just
+ an example! */
+ hd_src = fopen(LOCAL_FILE, "rb");
+
+ /* In windows, this will init the winsock stuff */
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ /* get a curl handle */
+ curl = curl_easy_init();
+ if(curl) {
+ /* build a list of commands to pass to libcurl */
+ headerlist = curl_slist_append(headerlist, buf_1);
+ headerlist = curl_slist_append(headerlist, buf_2);
+
+ /* enable uploading */
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, TRUE) ;
+
+ /* specify target */
+ curl_easy_setopt(curl,CURLOPT_URL, REMOTE_URL);
+
+ /* pass in that last of FTP commands to run after the transfer */
+ curl_easy_setopt(curl, CURLOPT_POSTQUOTE, headerlist);
+
+ /* now specify which file to upload */
+ curl_easy_setopt(curl, CURLOPT_INFILE, hd_src);
+
+ /* and give the size of the upload (optional) */
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE, (long)file_info.st_size);
+
+ /* Now run off and do what you've been told! */
+ res = curl_easy_perform(curl);
+
+ /* clean up the FTP commands list */
+ curl_slist_free_all (headerlist);
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ fclose(hd_src); /* close the local file */
+
+ curl_global_cleanup();
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/getinmemory.c b/Utilities/cmcurl/Testing/getinmemory.c
new file mode 100644
index 000000000..a8872da77
--- /dev/null
+++ b/Utilities/cmcurl/Testing/getinmemory.c
@@ -0,0 +1,83 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ *
+ * Example source code to show how the callback function can be used to
+ * download data into a chunk of memory instead of storing it in a file.
+ *
+ * This exact source code has not been verified to work.
+ */
+
+/* to make this work under windows, use the win32-functions from the
+ win32socket.c file as well */
+
+#include "curl/curl.h"
+#include "curl/types.h"
+#include "curl/easy.h"
+
+struct MemoryStruct {
+ char *memory;
+ size_t size;
+};
+
+size_t
+WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ register int realsize = size * nmemb;
+ struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+ mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+ if (mem->memory) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+ return realsize;
+}
+
+int main(int argc, char **argv)
+{
+ CURL *curl_handle;
+
+ struct MemoryStruct chunk;
+
+ chunk.memory=NULL; /* we expect realloc(NULL, size) to work */
+ chunk.size = 0; /* no data at this point */
+
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+
+ /* init the curl session */
+ curl_handle = curl_easy_init();
+
+ /* specify URL to get */
+ curl_easy_setopt(curl_handle, CURLOPT_URL, "http://www.cmake.org/HTML/Index.html");
+
+ /* send all data to this function */
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+
+ /* we pass our 'chunk' struct to the callback function */
+ curl_easy_setopt(curl_handle, CURLOPT_FILE, (void *)&chunk);
+
+ /* get it! */
+ curl_easy_perform(curl_handle);
+
+ /* cleanup curl stuff */
+ curl_easy_cleanup(curl_handle);
+
+ /*
+ * Now, our chunk.memory points to a memory block that is chunk.size
+ * bytes big and contains the remote file.
+ *
+ * Do something nice with it!
+ */
+
+ /* For example display it... */
+ write(1, chunk.memory, chunk.size);
+
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/http-post.c b/Utilities/cmcurl/Testing/http-post.c
new file mode 100644
index 000000000..1b4154fbf
--- /dev/null
+++ b/Utilities/cmcurl/Testing/http-post.c
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <curl/curl.h>
+
+int main(void)
+{
+ CURL *curl;
+ CURLcode res;
+
+ curl = curl_easy_init();
+ if(curl) {
+ /* First set the URL that is about to receive our POST. This URL can
+ just as well be a https:// URL if that is what should receive the
+ data. */
+ curl_easy_setopt(curl, CURLOPT_URL, "http://postit.example.com/moo.cgi");
+ /* Now specify the POST data */
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=daniel&project=curl");
+
+ /* Perform the request, res will get the return code */
+ res = curl_easy_perform(curl);
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/httpput.c b/Utilities/cmcurl/Testing/httpput.c
new file mode 100644
index 000000000..78275c40a
--- /dev/null
+++ b/Utilities/cmcurl/Testing/httpput.c
@@ -0,0 +1,100 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <curl/curl.h>
+
+/*
+ * This example shows a HTTP PUT operation. PUTs a file given as a command
+ * line argument to the URL also given on the command line.
+ *
+ * This example also uses its own read callback.
+ */
+
+size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+ size_t retcode;
+
+ /* in real-world cases, this would probably get this data differently
+ as this fread() stuff is exactly what the library already would do
+ by default internally */
+ retcode = fread(ptr, size, nmemb, stream);
+
+ fprintf(stderr, "*** We read %d bytes from file\n", retcode);
+
+ return retcode;
+}
+
+int main(int argc, char **argv)
+{
+ CURL *curl;
+ CURLcode res;
+ FILE *ftpfile;
+ FILE * hd_src ;
+ int hd ;
+ struct stat file_info;
+
+ char *file;
+ char *url;
+
+ if(argc < 3)
+ return 1;
+
+ file= argv[1];
+ url = argv[2];
+
+ /* get the file size of the local file */
+ hd = open(file, O_RDONLY) ;
+ fstat(hd, &file_info);
+ close(hd) ;
+
+ /* get a FILE * of the same file, could also be made with
+ fdopen() from the previous descriptor, but hey this is just
+ an example! */
+ hd_src = fopen(file, "rb");
+
+ /* In windows, this will init the winsock stuff */
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ /* get a curl handle */
+ curl = curl_easy_init();
+ if(curl) {
+ /* we want to use our own read function */
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
+
+ /* enable uploading */
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, TRUE) ;
+
+ /* HTTP PUT please */
+ curl_easy_setopt(curl, CURLOPT_PUT, TRUE);
+
+ /* specify target */
+ curl_easy_setopt(curl,CURLOPT_URL, url);
+
+ /* now specify which file to upload */
+ curl_easy_setopt(curl, CURLOPT_INFILE, hd_src);
+
+ /* and give the size of the upload (optional) */
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE, file_info.st_size);
+
+ /* Now run off and do what you've been told! */
+ res = curl_easy_perform(curl);
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ fclose(hd_src); /* close the local file */
+
+ curl_global_cleanup();
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/multithread.c b/Utilities/cmcurl/Testing/multithread.c
new file mode 100644
index 000000000..c3936ef4a
--- /dev/null
+++ b/Utilities/cmcurl/Testing/multithread.c
@@ -0,0 +1,70 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+/* A multi-threaded example that uses pthreads extensively to fetch
+ * X remote files at once */
+
+#include <stdio.h>
+#include <pthread.h>
+#include <curl/curl.h>
+
+/* silly list of test-URLs */
+char *urls[]= {
+ "http://curl.haxx.se/",
+ "ftp://cool.haxx.se/",
+ "http://www.contactor.se/",
+ "www.haxx.se"
+};
+
+void *pull_one_url(void *url)
+{
+ CURL *curl;
+
+ curl = curl_easy_init();
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+
+ return NULL;
+}
+
+
+/*
+ int pthread_create(pthread_t *new_thread_ID,
+ const pthread_attr_t *attr,
+ void * (*start_func)(void *), void *arg);
+*/
+
+int main(int argc, char **argv)
+{
+ pthread_t tid[4];
+ int i;
+ int error;
+ for(i=0; i< 4; i++) {
+ error = pthread_create(&tid[i],
+ NULL, /* default attributes please */
+ pull_one_url,
+ urls[i]);
+ if(0 != error)
+ fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error);
+ else
+ fprintf(stderr, "Thread %d, gets %s\n", i, urls[i]);
+ }
+
+ /* now wait for all threads to terminate */
+ for(i=0; i< 4; i++) {
+ error = pthread_join(tid[i], NULL);
+ fprintf(stderr, "Thread %d terminated\n", i);
+ }
+
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/persistant.c b/Utilities/cmcurl/Testing/persistant.c
new file mode 100644
index 000000000..853470345
--- /dev/null
+++ b/Utilities/cmcurl/Testing/persistant.c
@@ -0,0 +1,53 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+
+#include "curl/curl.h"
+
+/* to make this work under windows, use the win32-functions from the
+ docs/examples/win32socket.c file as well */
+
+/* This example REQUIRES libcurl 7.7 or later */
+#if (LIBCURL_VERSION_NUM < 0x070700)
+#error Too old libcurl version, upgrade or stay away.
+#endif
+
+int main(int argc, char **argv)
+{
+ CURL *curl;
+ CURLcode res;
+
+#ifdef MALLOCDEBUG
+ /* this sends all memory debug messages to a specified logfile */
+ curl_memdebug("memdump");
+#endif
+
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+ curl = curl_easy_init();
+ if(curl) {
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+ curl_easy_setopt(curl, CURLOPT_HEADER, 1);
+
+ /* get the first document */
+ curl_easy_setopt(curl, CURLOPT_URL, "http://www.cmake.org/");
+ res = curl_easy_perform(curl);
+
+ /* get another document from the same server using the same
+ connection */
+ curl_easy_setopt(curl, CURLOPT_URL, "http://www.cmake.org/HTML/Index.html");
+ res = curl_easy_perform(curl);
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/postit2.c b/Utilities/cmcurl/Testing/postit2.c
new file mode 100644
index 000000000..9b7cda07e
--- /dev/null
+++ b/Utilities/cmcurl/Testing/postit2.c
@@ -0,0 +1,92 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ *
+ * Example code that uploads a file name 'foo' to a remote script that accepts
+ * "HTML form based" (as described in RFC1738) uploads using HTTP POST.
+ *
+ * The imaginary form we'll fill in looks like:
+ *
+ * <form method="post" enctype="multipart/form-data" action="examplepost.cgi">
+ * Enter file: <input type="file" name="sendfile" size="40">
+ * Enter file name: <input type="text" name="filename" size="30">
+ * <input type="submit" value="send" name="submit">
+ * </form>
+ *
+ * This exact source code has not been verified to work.
+ */
+
+/* to make this work under windows, use the win32-functions from the
+ win32socket.c file as well */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+
+#if LIBCURL_VERSION_NUM < 0x070900
+#error "curl_formadd() is not introduced until libcurl 7.9 and later"
+#endif
+
+int main(int argc, char *argv[])
+{
+ CURL *curl;
+ CURLcode res;
+
+ struct HttpPost *formpost=NULL;
+ struct HttpPost *lastptr=NULL;
+ struct curl_slist *headerlist=NULL;
+ char buf[] = "Expect:";
+
+ /* Fill in the file upload field */
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "sendfile",
+ CURLFORM_FILE, "postit2.c",
+ CURLFORM_END);
+
+ /* Fill in the filename field */
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "filename",
+ CURLFORM_COPYCONTENTS, "postit2.c",
+ CURLFORM_END);
+
+
+ /* Fill in the submit field too, even if this is rarely needed */
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "submit",
+ CURLFORM_COPYCONTENTS, "send",
+ CURLFORM_END);
+
+ curl = curl_easy_init();
+ /* initalize custom header list (stating that Expect: 100-continue is not
+ wanted */
+ headerlist = curl_slist_append(headerlist, buf);
+ if(curl) {
+ /* what URL that receives this POST */
+ curl_easy_setopt(curl, CURLOPT_URL, "http://curl.haxx.se/examplepost.cgi");
+ if ( (argc == 2) && (!strcmp(argv[1], "noexpectheader")) )
+ /* only disable 100-continue header if explicitly requested */
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
+ curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
+ res = curl_easy_perform(curl);
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+
+ /* then cleanup the formpost chain */
+ curl_formfree(formpost);
+ /* free slist */
+ curl_slist_free_all (headerlist);
+ }
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/sepheaders.c b/Utilities/cmcurl/Testing/sepheaders.c
new file mode 100644
index 000000000..818578708
--- /dev/null
+++ b/Utilities/cmcurl/Testing/sepheaders.c
@@ -0,0 +1,78 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+/* to make this work under windows, use the win32-functions from the
+ win32socket.c file as well */
+
+#include "curl/curl.h"
+#include "curl/types.h"
+#include "curl/easy.h"
+
+#include "testconfig.h"
+
+size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+ int written = fwrite(ptr, size, nmemb, (FILE *)stream);
+ return written;
+}
+
+int main(int argc, char **argv)
+{
+ CURL *curl_handle;
+ char *headerfilename = LIBCURL_BINARY_DIR "/Testing/sepheaders-head.out";
+ FILE *headerfile;
+ char *bodyfilename = LIBCURL_BINARY_DIR "/Testing/sepheaders-body.out";
+ FILE *bodyfile;
+
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+ /* init the curl session */
+ curl_handle = curl_easy_init();
+
+ /* set URL to get */
+ curl_easy_setopt(curl_handle, CURLOPT_URL, "http://www.cmake.org/HTML/Index.html");
+
+ /* no progress meter please */
+ curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
+
+ /* shut up completely */
+ //curl_easy_setopt(curl_handle, CURLOPT_MUTE, 1);
+
+ /* send all data to this function */
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);
+
+ /* open the files */
+ headerfile = fopen(headerfilename,"w");
+ if (headerfile == NULL) {
+ curl_easy_cleanup(curl_handle);
+ return -1;
+ }
+ bodyfile = fopen(bodyfilename,"w");
+ if (bodyfile == NULL) {
+ curl_easy_cleanup(curl_handle);
+ return -1;
+ }
+
+ /* we want the headers to this file handle */
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER ,headerfile);
+
+ /* we want the body to this file handle */
+ curl_easy_setopt(curl_handle, CURLOPT_FILE ,bodyfile);
+
+ /* get it! */
+ curl_easy_perform(curl_handle);
+
+ /* close the header file */
+ fclose(headerfile);
+
+ /* cleanup curl stuff */
+ curl_easy_cleanup(curl_handle);
+
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/simple.c b/Utilities/cmcurl/Testing/simple.c
new file mode 100644
index 000000000..6dd6050ec
--- /dev/null
+++ b/Utilities/cmcurl/Testing/simple.c
@@ -0,0 +1,28 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+#include "curl/curl.h"
+
+int main(void)
+{
+ CURL *curl;
+ CURLcode res;
+
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+ curl = curl_easy_init();
+ if(curl) {
+ curl_easy_setopt(curl, CURLOPT_URL, "http://www.cmake.org/HTML/Index.html");
+ res = curl_easy_perform(curl);
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/simplessl.c b/Utilities/cmcurl/Testing/simplessl.c
new file mode 100644
index 000000000..9a53603f4
--- /dev/null
+++ b/Utilities/cmcurl/Testing/simplessl.c
@@ -0,0 +1,118 @@
+/*****************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+
+
+/* some requirements for this to work:
+ 1. set pCertFile to the file with the client certificate
+ 2. if the key is passphrase protected, set pPassphrase to the
+ passphrase you use
+ 3. if you are using a crypto engine:
+ 3.1. set a #define USE_ENGINE
+ 3.2. set pEngine to the name of the crypto engine you use
+ 3.3. set pKeyName to the key identifier you want to use
+ 4. if you don't use a crypto engine:
+ 4.1. set pKeyName to the file name of your client key
+ 4.2. if the format of the key file is DER, set pKeyType to "DER"
+
+ !! verify of the server certificate is not implemented here !!
+
+ **** This example only works with libcurl 7.9.3 and later! ****
+
+*/
+
+int main(int argc, char **argv)
+{
+ CURL *curl;
+ CURLcode res;
+ FILE *headerfile;
+
+ const char *pCertFile = "testcert.pem";
+ const char *pCACertFile="cacert.pem"
+
+ const char *pKeyName;
+ const char *pKeyType;
+
+ const char *pEngine;
+
+#if USE_ENGINE
+ pKeyName = "rsa_test";
+ pKeyType = "ENG";
+ pEngine = "chil"; /* for nChiper HSM... */
+#else
+ pKeyName = "testkey.pem";
+ pKeyType = "PEM";
+ pEngine = NULL;
+#endif
+
+ const char *pPassphrase = NULL;
+
+ headerfile = fopen("dumpit", "w");
+
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+
+ curl = curl_easy_init();
+ if(curl) {
+ /* what call to write: */
+ curl_easy_setopt(curl, CURLOPT_URL, "HTTPS://curl.haxx.se");
+ curl_easy_setopt(curl, CURLOPT_WRITEHEADER, headerfile);
+
+ while(1) /* do some ugly short cut... */
+ {
+ if (pEngine) /* use crypto engine */
+ {
+ if (curl_easy_setopt(curl, CURLOPT_SSLENGINE,pEngine) != CURLE_OK)
+ { /* load the crypto engine */
+ fprintf(stderr,"can't set crypto engine\n");
+ break;
+ }
+ if (curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT,1) != CURLE_OK)
+ { /* set the crypto engine as default */
+ /* only needed for the first time you load
+ a engine in a curl object... */
+ fprintf(stderr,"can't set crypto engine as default\n");
+ break;
+ }
+ }
+ /* cert is stored PEM coded in file... */
+ /* since PEM is default, we needn't set it for PEM */
+ curl_easy_setopt(curl,CURLOPT_SSLCERTTYPE,"PEM");
+ /* set the cert for client authentication */
+ curl_easy_setopt(curl,CURLOPT_SSLCERT,pCertFile);
+ /* sorry, for engine we must set the passphrase
+ (if the key has one...) */
+ if (pPassphrase)
+ curl_easy_setopt(curl,CURLOPT_SSLKEYPASSWD,pPassphrase);
+ /* if we use a key stored in a crypto engine,
+ we must set the key type to "ENG" */
+ curl_easy_setopt(curl,CURLOPT_SSLKEYTYPE,pKeyType);
+ /* set the private key (file or ID in engine) */
+ curl_easy_setopt(curl,CURLOPT_SSLKEY,pKeyName);
+ /* set the file with the certs vaildating the server */
+ curl_easy_setopt(curl,CURLOPT_CAINFO,pCACertFile);
+ /* disconnect if we can't validate server's cert */
+ curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,1);
+
+ res = curl_easy_perform(curl);
+ break; /* we are done... */
+ }
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+
+ curl_global_cleanup();
+
+ return 0;
+}
diff --git a/Utilities/cmcurl/Testing/testconfig.h.in b/Utilities/cmcurl/Testing/testconfig.h.in
new file mode 100644
index 000000000..faab46223
--- /dev/null
+++ b/Utilities/cmcurl/Testing/testconfig.h.in
@@ -0,0 +1,7 @@
+#ifndef __testconfig_h__
+#define __testconfig_h__
+
+#define LIBCURL_SOURCE_DIR "${LIBCURL_SOURCE_DIR}"
+#define LIBCURL_BINARY_DIR "${LIBCURL_BINARY_DIR}"
+
+#endif /* __testconfig_h__ */
diff --git a/Utilities/cmcurl/Testing/win32sockets.c b/Utilities/cmcurl/Testing/win32sockets.c
new file mode 100644
index 000000000..5f791c8b5
--- /dev/null
+++ b/Utilities/cmcurl/Testing/win32sockets.c
@@ -0,0 +1,49 @@
+
+/*
+ * Note: This is only required if you use curl 7.8 or lower, later
+ * versions provide an option to curl_global_init() that does the
+ * win32 initialization for you.
+ */
+
+/*
+ * These are example functions doing socket init that Windows
+ * require. If you don't use windows, you can safely ignore this crap.
+ */
+
+#include <windows.h>
+
+void win32_cleanup(void)
+{
+ WSACleanup();
+}
+
+int win32_init(void)
+{
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+ wVersionRequested = MAKEWORD(1, 1);
+
+ err = WSAStartup(wVersionRequested, &wsaData);
+
+ if (err != 0)
+ /* Tell the user that we couldn't find a useable */
+ /* winsock.dll. */
+ return 1;
+
+ /* Confirm that the Windows Sockets DLL supports 1.1.*/
+ /* Note that if the DLL supports versions greater */
+ /* than 1.1 in addition to 1.1, it will still return */
+ /* 1.1 in wVersion since that is the version we */
+ /* requested. */
+
+ if ( LOBYTE( wsaData.wVersion ) != 1 ||
+ HIBYTE( wsaData.wVersion ) != 1 ) {
+ /* Tell the user that we couldn't find a useable */
+
+ /* winsock.dll. */
+ WSACleanup();
+ return 1;
+ }
+ return 0; /* 0 is ok */
+}
diff --git a/Utilities/cmcurl/amigaos.c b/Utilities/cmcurl/amigaos.c
new file mode 100644
index 000000000..7106f8daf
--- /dev/null
+++ b/Utilities/cmcurl/amigaos.c
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "amigaos.h"
+#include <amitcp/socketbasetags.h>
+
+struct Library *SocketBase = NULL;
+extern int errno, h_errno;
+
+#ifdef __libnix__
+#include <stabs.h>
+void __request(const char *msg);
+#else
+# define __request( msg ) Printf( msg "\n\a")
+#endif
+
+void amiga_cleanup()
+{
+ if(SocketBase) {
+ CloseLibrary(SocketBase);
+ SocketBase = NULL;
+ }
+}
+
+BOOL amiga_init()
+{
+ if(!SocketBase)
+ SocketBase = OpenLibrary("bsdsocket.library", 4);
+
+ if(!SocketBase) {
+ __request("No TCP/IP Stack running!");
+ return FALSE;
+ }
+
+ if(SocketBaseTags(
+ SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG) &errno,
+// SBTM_SETVAL(SBTC_HERRNOLONGPTR), (ULONG) &h_errno,
+ SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG) "cURL",
+ TAG_DONE)) {
+
+ __request("SocketBaseTags ERROR");
+ return FALSE;
+ }
+
+#ifndef __libnix__
+ atexit(amiga_cleanup);
+#endif
+
+ return TRUE;
+}
+
+#ifdef __libnix__
+ADD2EXIT(amiga_cleanup,-50);
+#endif
diff --git a/Utilities/cmcurl/amigaos.h b/Utilities/cmcurl/amigaos.h
new file mode 100644
index 000000000..e5786d482
--- /dev/null
+++ b/Utilities/cmcurl/amigaos.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#ifndef LIBCURL_AMIGAOS_H
+#define LIBCURL_AMIGAOS_H
+
+#ifndef __ixemul__
+
+#include <exec/types.h>
+#include <exec/execbase.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+
+#include <sys/socket.h>
+
+#include "config-amigaos.h"
+
+#ifndef select
+# define select(args...) WaitSelect( args, NULL)
+#endif
+#ifndef inet_ntoa
+# define inet_ntoa(x) Inet_NtoA( x ## .s_addr)
+#endif
+#ifndef ioctl
+# define ioctl(a,b,c,d) IoctlSocket( (LONG)a, (ULONG)b, (char*)c)
+#endif
+#define _AMIGASF 1
+
+extern void amiga_cleanup();
+extern BOOL amiga_init();
+
+#else /* __ixemul__ */
+
+#warning compiling with ixemul...
+
+#endif /* __ixemul__ */
+#endif /* LIBCURL_AMIGAOS_H */
diff --git a/Utilities/cmcurl/arpa_telnet.h b/Utilities/cmcurl/arpa_telnet.h
new file mode 100644
index 000000000..e6a04dcef
--- /dev/null
+++ b/Utilities/cmcurl/arpa_telnet.h
@@ -0,0 +1,101 @@
+#ifndef __ARPA_TELNET_H
+#define __ARPA_TELNET_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#ifndef CURL_DISABLE_TELNET
+/*
+ * Telnet option defines. Add more here if in need.
+ */
+#define CURL_TELOPT_BINARY 0 /* binary 8bit data */
+#define CURL_TELOPT_SGA 3 /* Supress Go Ahead */
+#define CURL_TELOPT_EXOPL 255 /* EXtended OPtions List */
+#define CURL_TELOPT_TTYPE 24 /* Terminal TYPE */
+#define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */
+
+#define CURL_TELOPT_NEW_ENVIRON 39 /* NEW ENVIRONment variables */
+#define CURL_NEW_ENV_VAR 0
+#define CURL_NEW_ENV_VALUE 1
+
+/*
+ * The telnet options represented as strings
+ */
+static const char * const 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 CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON
+
+#define CURL_TELOPT_OK(x) ((x) <= CURL_TELOPT_MAXIMUM)
+#define CURL_TELOPT(x) telnetoptions[x]
+
+#define CURL_NTELOPTS 40
+
+/*
+ * First some defines
+ */
+#define CURL_xEOF 236 /* End Of File */
+#define CURL_SE 240 /* Sub negotiation End */
+#define CURL_NOP 241 /* No OPeration */
+#define CURL_DM 242 /* Data Mark */
+#define CURL_GA 249 /* Go Ahead, reverse the line */
+#define CURL_SB 250 /* SuBnegotiation */
+#define CURL_WILL 251 /* Our side WILL use this option */
+#define CURL_WONT 252 /* Our side WON'T use this option */
+#define CURL_DO 253 /* DO use this option! */
+#define CURL_DONT 254 /* DON'T use this option! */
+#define CURL_IAC 255 /* Interpret As Command */
+
+/*
+ * Then those numbers represented as strings:
+ */
+static const char * const telnetcmds[]=
+{
+ "EOF", "SUSP", "ABORT", "EOR", "SE",
+ "NOP", "DMARK", "BRK", "IP", "AO",
+ "AYT", "EC", "EL", "GA", "SB",
+ "WILL", "WONT", "DO", "DONT", "IAC"
+};
+
+#define CURL_TELCMD_MINIMUM CURL_xEOF /* the first one */
+#define CURL_TELCMD_MAXIMUM CURL_IAC /* surprise, 255 is the last one! ;-) */
+
+#define CURL_TELQUAL_IS 0
+#define CURL_TELQUAL_SEND 1
+#define CURL_TELQUAL_INFO 2
+#define CURL_TELQUAL_NAME 3
+
+#define CURL_TELCMD_OK(x) ( ((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \
+ ((unsigned int)(x) <= CURL_TELCMD_MAXIMUM) )
+#define CURL_TELCMD(x) telnetcmds[(x)-CURL_TELCMD_MINIMUM]
+#endif
+#endif
diff --git a/Utilities/cmcurl/base64.c b/Utilities/cmcurl/base64.c
new file mode 100644
index 000000000..aa03f8346
--- /dev/null
+++ b/Utilities/cmcurl/base64.c
@@ -0,0 +1,366 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $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 "urldata.h" /* for the SessionHandle definition */
+#include "easyif.h" /* for Curl_convert_... prototypes */
+#include "base64.h"
+#include "memory.h"
+
+/* include memdebug.h last */
+#include "memdebug.h"
+
+/* ---- Base64 Encoding/Decoding Table --- */
+static const char table64[]=
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void decodeQuantum(unsigned char *dest, const char *src)
+{
+ unsigned int x = 0;
+ int i;
+ char *found;
+
+ for(i = 0; i < 4; i++) {
+ if((found = strchr(table64, src[i])))
+ x = (x << 6) + (unsigned int)(found - table64);
+ else if(src[i] == '=')
+ x = (x << 6);
+ }
+
+ dest[2] = (unsigned char)(x & 255);
+ x >>= 8;
+ dest[1] = (unsigned char)(x & 255);
+ x >>= 8;
+ dest[0] = (unsigned char)(x & 255);
+}
+
+/*
+ * Curl_base64_decode()
+ *
+ * Given a base64 string at src, decode it and return an allocated memory in
+ * the *outptr. Returns the length of the decoded data.
+ */
+size_t Curl_base64_decode(const char *src, unsigned char **outptr)
+{
+ int length = 0;
+ int equalsTerm = 0;
+ int i;
+ int numQuantums;
+ unsigned char lastQuantum[3];
+ size_t rawlen=0;
+ unsigned char *newstr;
+
+ *outptr = NULL;
+
+ while((src[length] != '=') && src[length])
+ length++;
+ /* A maximum of two = padding characters is allowed */
+ if(src[length] == '=') {
+ equalsTerm++;
+ if(src[length+equalsTerm] == '=')
+ equalsTerm++;
+ }
+ numQuantums = (length + equalsTerm) / 4;
+
+ /* Don't allocate a buffer if the decoded length is 0 */
+ if (numQuantums <= 0)
+ return 0;
+
+ rawlen = (numQuantums * 3) - equalsTerm;
+
+ /* The buffer must be large enough to make room for the last quantum
+ (which may be partially thrown out) and the zero terminator. */
+ newstr = malloc(rawlen+4);
+ if(!newstr)
+ return 0;
+
+ *outptr = newstr;
+
+ /* Decode all but the last quantum (which may not decode to a
+ multiple of 3 bytes) */
+ for(i = 0; i < numQuantums - 1; i++) {
+ decodeQuantum((unsigned char *)newstr, src);
+ newstr += 3; src += 4;
+ }
+
+ /* This final decode may actually read slightly past the end of the buffer
+ if the input string is missing pad bytes. This will almost always be
+ harmless. */
+ decodeQuantum(lastQuantum, src);
+ for(i = 0; i < 3 - equalsTerm; i++)
+ newstr[i] = lastQuantum[i];
+
+ newstr[i] = 0; /* zero terminate */
+ return rawlen;
+}
+
+/*
+ * 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.
+ *
+ */
+size_t Curl_base64_encode(struct SessionHandle *data,
+ const char *inp, size_t insize, char **outptr)
+{
+ unsigned char ibuf[3];
+ unsigned char obuf[4];
+ int i;
+ int inputparts;
+ char *output;
+ char *base64data;
+#ifdef CURL_DOES_CONVERSIONS
+ char *convbuf;
+#endif
+
+ char *indata = (char *)inp;
+
+ *outptr = NULL; /* set to NULL in case of failure before we reach the end */
+
+ if(0 == insize)
+ insize = strlen(indata);
+
+ base64data = output = (char*)malloc(insize*4/3+4);
+ if(NULL == output)
+ return 0;
+
+#ifdef CURL_DOES_CONVERSIONS
+ /*
+ * The base64 data needs to be created using the network encoding
+ * not the host encoding. And we can't change the actual input
+ * so we copy it to a buffer, translate it, and use that instead.
+ */
+ if(data) {
+ convbuf = (char*)malloc(insize);
+ if(!convbuf) {
+ return 0;
+ }
+ memcpy(convbuf, indata, insize);
+ if(CURLE_OK != Curl_convert_to_network(data, convbuf, insize)) {
+ free(convbuf);
+ return 0;
+ }
+ indata = convbuf; /* switch to the converted buffer */
+ }
+#else
+ (void)data;
+#endif
+
+ while(insize > 0) {
+ for (i = inputparts = 0; i < 3; i++) {
+ if(insize > 0) {
+ inputparts++;
+ ibuf[i] = *indata;
+ indata++;
+ insize--;
+ }
+ else
+ ibuf[i] = 0;
+ }
+
+ obuf[0] = (unsigned char) ((ibuf[0] & 0xFC) >> 2);
+ obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
+ ((ibuf[1] & 0xF0) >> 4));
+ obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
+ ((ibuf[2] & 0xC0) >> 6));
+ obuf[3] = (unsigned char) (ibuf[2] & 0x3F);
+
+ switch(inputparts) {
+ case 1: /* only one byte read */
+ snprintf(output, 5, "%c%c==",
+ table64[obuf[0]],
+ table64[obuf[1]]);
+ break;
+ case 2: /* two bytes read */
+ snprintf(output, 5, "%c%c%c=",
+ table64[obuf[0]],
+ table64[obuf[1]],
+ table64[obuf[2]]);
+ break;
+ default:
+ snprintf(output, 5, "%c%c%c%c",
+ table64[obuf[0]],
+ table64[obuf[1]],
+ table64[obuf[2]],
+ table64[obuf[3]] );
+ break;
+ }
+ output += 4;
+ }
+ *output=0;
+ *outptr = base64data; /* make it return the actual data memory */
+
+#ifdef CURL_DOES_CONVERSIONS
+ if(data)
+ free(convbuf);
+#endif
+ return strlen(base64data); /* return the length of the new data */
+}
+/* ---- End of Base64 Encoding ---- */
+
+/************* 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;
+ size_t base64Len;
+ unsigned char *data;
+ int dataLen;
+ struct SessionHandle *handle = NULL;
+
+#ifdef CURL_DOES_CONVERSIONS
+ /* get a Curl handle so Curl_base64_encode can translate properly */
+ handle = curl_easy_init();
+ if(handle == NULL) {
+ fprintf(stderr, "Error: curl_easy_init failed\n");
+ return 0;
+ }
+#endif
+ data = (unsigned char *)suck(&dataLen);
+ base64Len = Curl_base64_encode(handle, data, dataLen, &base64);
+
+ fprintf(stderr, "%d\n", base64Len);
+ fprintf(stdout, "%s\n", base64);
+
+ free(base64); free(data);
+#ifdef CURL_DOES_CONVERSIONS
+ curl_easy_cleanup(handle);
+#endif
+ 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
+ *
+ * gcc -DTEST_DECODE base64.c -o base64 mprintf.o memdebug.o
+ */
+#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;
+ int i, j;
+#ifdef CURL_DOES_CONVERSIONS
+ /* get a Curl handle so main can translate properly */
+ struct SessionHandle *handle = curl_easy_init();
+ if(handle == NULL) {
+ fprintf(stderr, "Error: curl_easy_init failed\n");
+ return 0;
+ }
+#endif
+
+ base64 = (char *)suck(&base64Len);
+ dataLen = Curl_base64_decode(base64, &data);
+
+ fprintf(stderr, "%d\n", dataLen);
+
+ for(i=0; i < dataLen; i+=0x10) {
+ printf("0x%02x: ", i);
+ for(j=0; j < 0x10; j++)
+ if((j+i) < dataLen)
+ printf("%02x ", data[i+j]);
+ else
+ printf(" ");
+
+ printf(" | ");
+
+ for(j=0; j < 0x10; j++)
+ if((j+i) < dataLen) {
+#ifdef CURL_DOES_CONVERSIONS
+ if(CURLE_OK !=
+ Curl_convert_from_network(handle, &data[i+j], (size_t)1))
+ data[i+j] = '.';
+#endif /* CURL_DOES_CONVERSIONS */
+ printf("%c", ISGRAPH(data[i+j])?data[i+j]:'.');
+ } else
+ break;
+ puts("");
+ }
+
+#ifdef CURL_DOES_CONVERSIONS
+ curl_easy_cleanup(handle);
+#endif
+ 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
diff --git a/Utilities/cmcurl/base64.h b/Utilities/cmcurl/base64.h
new file mode 100644
index 000000000..59742bcd3
--- /dev/null
+++ b/Utilities/cmcurl/base64.h
@@ -0,0 +1,28 @@
+#ifndef __BASE64_H
+#define __BASE64_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+size_t Curl_base64_encode(struct SessionHandle *data,
+ const char *input, size_t size, char **str);
+size_t Curl_base64_decode(const char *source, unsigned char **outptr);
+#endif
diff --git a/Utilities/cmcurl/ca-bundle.h b/Utilities/cmcurl/ca-bundle.h
new file mode 100644
index 000000000..12390bc7b
--- /dev/null
+++ b/Utilities/cmcurl/ca-bundle.h
@@ -0,0 +1 @@
+/* ca bundle path set in here*/
diff --git a/Utilities/cmcurl/config.h.in b/Utilities/cmcurl/config.h.in
new file mode 100644
index 000000000..6e74935a3
--- /dev/null
+++ b/Utilities/cmcurl/config.h.in
@@ -0,0 +1,726 @@
+/* lib/config.h.in. Generated from configure.ac by autoheader. */
+
+/* when building libcurl itself */
+#cmakedefine BUILDING_LIBCURL ${BUILDING_LIBCURL}
+
+/* to disable cookies support */
+#cmakedefine CURL_DISABLE_COOKIES ${CURL_DISABLE_COOKIES}
+
+/* to disable cryptographic authentication */
+#cmakedefine CURL_DISABLE_CRYPTO_AUTH ${CURL_DISABLE_CRYPTO_AUTH}
+
+/* to disable DICT */
+#cmakedefine CURL_DISABLE_DICT ${CURL_DISABLE_DICT}
+
+/* to disable FILE */
+#cmakedefine CURL_DISABLE_FILE ${CURL_DISABLE_FILE}
+
+/* to disable FTP */
+#cmakedefine CURL_DISABLE_FTP ${CURL_DISABLE_FTP}
+
+/* to disable HTTP */
+#cmakedefine CURL_DISABLE_HTTP ${CURL_DISABLE_HTTP}
+
+/* to disable LDAP */
+#cmakedefine CURL_DISABLE_LDAP ${CURL_DISABLE_LDAP}
+
+/* to disable TELNET */
+#cmakedefine CURL_DISABLE_TELNET ${CURL_DISABLE_TELNET}
+
+/* to disable TFTP */
+#cmakedefine CURL_DISABLE_TFTP ${CURL_DISABLE_TFTP}
+
+/* to disable verbose strings */
+#cmakedefine CURL_DISABLE_VERBOSE_STRINGS ${CURL_DISABLE_VERBOSE_STRINGS}
+
+/* to make a symbol visible */
+#cmakedefine CURL_EXTERN_SYMBOL ${CURL_EXTERN_SYMBOL}
+
+/* to enable hidden symbols */
+#cmakedefine CURL_HIDDEN_SYMBOLS ${CURL_HIDDEN_SYMBOLS}
+
+/* when not building a shared library */
+#cmakedefine CURL_STATICLIB ${CURL_STATICLIB}
+
+/* Set to explicitly specify we don't want to use thread-safe functions */
+#cmakedefine DISABLED_THREADSAFE ${DISABLED_THREADSAFE}
+
+/* lber dynamic library file */
+#cmakedefine DL_LBER_FILE ${DL_LBER_FILE}
+
+/* ldap dynamic library file */
+#cmakedefine DL_LDAP_FILE ${DL_LDAP_FILE}
+
+/* your Entropy Gathering Daemon socket pathname */
+#cmakedefine EGD_SOCKET ${EGD_SOCKET}
+
+/* Define if you want to enable IPv6 support */
+#cmakedefine ENABLE_IPV6 ${ENABLE_IPV6}
+
+/* Define to the type qualifier of arg 1 for getnameinfo. */
+#cmakedefine GETNAMEINFO_QUAL_ARG1 ${GETNAMEINFO_QUAL_ARG1}
+
+/* Define to the type of arg 1 for getnameinfo. */
+#cmakedefine GETNAMEINFO_TYPE_ARG1 ${GETNAMEINFO_TYPE_ARG1}
+
+/* Define to the type of arg 2 for getnameinfo. */
+#cmakedefine GETNAMEINFO_TYPE_ARG2 ${GETNAMEINFO_TYPE_ARG2}
+
+/* Define to the type of args 4 and 6 for getnameinfo. */
+#cmakedefine GETNAMEINFO_TYPE_ARG46 ${GETNAMEINFO_TYPE_ARG46}
+
+/* Define to the type of arg 7 for getnameinfo. */
+#cmakedefine GETNAMEINFO_TYPE_ARG7 ${GETNAMEINFO_TYPE_ARG7}
+
+/* Define to 1 if you have the <alloca.h> header file. */
+#cmakedefine HAVE_ALLOCA_H ${HAVE_ALLOCA_H}
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#cmakedefine HAVE_ARPA_INET_H ${HAVE_ARPA_INET_H}
+
+/* Define to 1 if you have the <arpa/tftp.h> header file. */
+#cmakedefine HAVE_ARPA_TFTP_H ${HAVE_ARPA_TFTP_H}
+
+/* Define to 1 if you have the <assert.h> header file. */
+#cmakedefine HAVE_ASSERT_H ${HAVE_ASSERT_H}
+
+/* Define to 1 if you have the `basename' function. */
+#cmakedefine HAVE_BASENAME ${HAVE_BASENAME}
+
+/* Define to 1 if you have the `closesocket' function. */
+#cmakedefine HAVE_CLOSESOCKET ${HAVE_CLOSESOCKET}
+
+/* Define to 1 if you have the `CRYPTO_cleanup_all_ex_data' function. */
+#cmakedefine HAVE_CRYPTO_CLEANUP_ALL_EX_DATA ${HAVE_CRYPTO_CLEANUP_ALL_EX_DATA}
+
+/* Define to 1 if you have the <crypto.h> header file. */
+#cmakedefine HAVE_CRYPTO_H ${HAVE_CRYPTO_H}
+
+/* Define to 1 if you have the <des.h> header file. */
+#cmakedefine HAVE_DES_H ${HAVE_DES_H}
+
+/* disabled non-blocking sockets */
+#cmakedefine HAVE_DISABLED_NONBLOCKING ${HAVE_DISABLED_NONBLOCKING}
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#cmakedefine HAVE_DLFCN_H ${HAVE_DLFCN_H}
+
+/* Define to 1 if you have the `dlopen' function. */
+#cmakedefine HAVE_DLOPEN ${HAVE_DLOPEN}
+
+/* Define to 1 if you have the `ENGINE_load_builtin_engines' function. */
+#cmakedefine HAVE_ENGINE_LOAD_BUILTIN_ENGINES ${HAVE_ENGINE_LOAD_BUILTIN_ENGINES}
+
+/* Define to 1 if you have the <errno.h> header file. */
+#cmakedefine HAVE_ERRNO_H ${HAVE_ERRNO_H}
+
+/* Define to 1 if you have the <err.h> header file. */
+#cmakedefine HAVE_ERR_H ${HAVE_ERR_H}
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#cmakedefine HAVE_FCNTL_H ${HAVE_FCNTL_H}
+
+/* use FIONBIO for non-blocking sockets */
+#cmakedefine HAVE_FIONBIO ${HAVE_FIONBIO}
+
+/* Define to 1 if you have the `fork' function. */
+#cmakedefine HAVE_FORK ${HAVE_FORK}
+
+/* Define to 1 if you have the `ftruncate' function. */
+#cmakedefine HAVE_FTRUNCATE ${HAVE_FTRUNCATE}
+
+/* Define if getaddrinfo exists and works */
+#cmakedefine HAVE_GETADDRINFO ${HAVE_GETADDRINFO}
+
+/* Define to 1 if you have the `geteuid' function. */
+#cmakedefine HAVE_GETEUID ${HAVE_GETEUID}
+
+/* Define to 1 if you have the `gethostbyaddr' function. */
+#cmakedefine HAVE_GETHOSTBYADDR ${HAVE_GETHOSTBYADDR}
+
+/* If you have gethostbyname */
+#cmakedefine HAVE_GETHOSTBYNAME ${HAVE_GETHOSTBYNAME}
+
+/* Define to 1 if you have the `gethostbyname_r' function. */
+#cmakedefine HAVE_GETHOSTBYNAME_R ${HAVE_GETHOSTBYNAME_R}
+
+/* gethostbyname_r() takes 3 args */
+#cmakedefine HAVE_GETHOSTBYNAME_R_3 ${HAVE_GETHOSTBYNAME_R_3}
+
+/* gethostbyname_r() takes 5 args */
+#cmakedefine HAVE_GETHOSTBYNAME_R_5 ${HAVE_GETHOSTBYNAME_R_5}
+
+/* gethostbyname_r() takes 6 args */
+#cmakedefine HAVE_GETHOSTBYNAME_R_6 ${HAVE_GETHOSTBYNAME_R_6}
+
+/* Define to 1 if you have the getnameinfo function. */
+#cmakedefine HAVE_GETNAMEINFO ${HAVE_GETNAMEINFO}
+
+/* Define to 1 if you have the `getpass_r' function. */
+#cmakedefine HAVE_GETPASS_R ${HAVE_GETPASS_R}
+
+/* Define to 1 if you have the `getprotobyname' function. */
+#cmakedefine HAVE_GETPROTOBYNAME ${HAVE_GETPROTOBYNAME}
+
+/* Define to 1 if you have the `getpwuid' function. */
+#cmakedefine HAVE_GETPWUID ${HAVE_GETPWUID}
+
+/* Define to 1 if you have the `getrlimit' function. */
+#cmakedefine HAVE_GETRLIMIT ${HAVE_GETRLIMIT}
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#cmakedefine HAVE_GETTIMEOFDAY ${HAVE_GETTIMEOFDAY}
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#cmakedefine HAVE_GMTIME_R ${HAVE_GMTIME_R}
+
+/* if you have the gssapi libraries */
+#cmakedefine HAVE_GSSAPI ${HAVE_GSSAPI}
+
+/* if you have the GNU gssapi libraries */
+#cmakedefine HAVE_GSSGNU ${HAVE_GSSGNU}
+
+/* if you have the Heimdal gssapi libraries */
+#cmakedefine HAVE_GSSHEIMDAL ${HAVE_GSSHEIMDAL}
+
+/* if you have the MIT gssapi libraries */
+#cmakedefine HAVE_GSSMIT ${HAVE_GSSMIT}
+
+/* Define to 1 if you have the `idna_strerror' function. */
+#cmakedefine HAVE_IDNA_STRERROR ${HAVE_IDNA_STRERROR}
+
+/* Define to 1 if you have the `idn_free' function. */
+#cmakedefine HAVE_IDN_FREE ${HAVE_IDN_FREE}
+
+/* Define to 1 if you have the <idn-free.h> header file. */
+#cmakedefine HAVE_IDN_FREE_H ${HAVE_IDN_FREE_H}
+
+/* Define to 1 if you have the `inet_addr' function. */
+#cmakedefine HAVE_INET_ADDR ${HAVE_INET_ADDR}
+
+/* Define to 1 if you have the `inet_ntoa' function. */
+#cmakedefine HAVE_INET_NTOA ${HAVE_INET_NTOA}
+
+/* Define to 1 if you have the `inet_ntoa_r' function. */
+#cmakedefine HAVE_INET_NTOA_R ${HAVE_INET_NTOA_R}
+
+/* inet_ntoa_r() is declared */
+#cmakedefine HAVE_INET_NTOA_R_DECL ${HAVE_INET_NTOA_R_DECL}
+
+/* Define to 1 if you have the `inet_pton' function. */
+#cmakedefine HAVE_INET_PTON ${HAVE_INET_PTON}
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#cmakedefine HAVE_INTTYPES_H ${HAVE_INTTYPES_H}
+
+/* use ioctlsocket() for non-blocking sockets */
+#cmakedefine HAVE_IOCTLSOCKET ${HAVE_IOCTLSOCKET}
+
+/* use Ioctlsocket() for non-blocking sockets */
+#cmakedefine HAVE_IOCTLSOCKET_CASE ${HAVE_IOCTLSOCKET_CASE}
+
+/* Define to 1 if you have the <io.h> header file. */
+#cmakedefine HAVE_IO_H ${HAVE_IO_H}
+
+/* if you have the Kerberos4 libraries (including -ldes) */
+#cmakedefine HAVE_KRB4 ${HAVE_KRB4}
+
+/* Define to 1 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 to 1 if you have the <krb.h> header file. */
+#cmakedefine HAVE_KRB_H ${HAVE_KRB_H}
+
+/* Define to 1 if you have the `dl' library (-ldl). */
+#cmakedefine HAVE_LIBDL ${HAVE_LIBDL}
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#cmakedefine HAVE_LIBGEN_H ${HAVE_LIBGEN_H}
+
+/* Define to 1 if you have the `idn' library (-lidn). */
+#cmakedefine HAVE_LIBIDN ${HAVE_LIBIDN}
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+#cmakedefine HAVE_LIBRESOLV ${HAVE_LIBRESOLV}
+
+/* Define to 1 if you have the `resolve' library (-lresolve). */
+#cmakedefine HAVE_LIBRESOLVE ${HAVE_LIBRESOLVE}
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+#cmakedefine HAVE_LIBSOCKET ${HAVE_LIBSOCKET}
+
+/* Define to 1 if you have the `ssh2' library (-lssh2). */
+#cmakedefine HAVE_LIBSSH2 ${HAVE_LIBSSH2}
+
+/* Define to 1 if you have the <libssh2.h> header file. */
+#cmakedefine HAVE_LIBSSH2_H ${HAVE_LIBSSH2_H}
+
+/* Define to 1 if you have the `ssl' library (-lssl). */
+#cmakedefine HAVE_LIBSSL ${HAVE_LIBSSL}
+
+/* if zlib is available */
+#cmakedefine HAVE_LIBZ ${HAVE_LIBZ}
+
+/* Define to 1 if you have the <limits.h> header file. */
+#cmakedefine HAVE_LIMITS_H ${HAVE_LIMITS_H}
+
+/* if your compiler supports LL */
+#cmakedefine HAVE_LL ${HAVE_LL}
+
+/* Define to 1 if you have the <locale.h> header file. */
+#cmakedefine HAVE_LOCALE_H ${HAVE_LOCALE_H}
+
+/* Define to 1 if you have the `localtime_r' function. */
+#cmakedefine HAVE_LOCALTIME_R ${HAVE_LOCALTIME_R}
+
+/* if your compiler supports long long */
+#cmakedefine HAVE_LONGLONG ${HAVE_LONGLONG}
+
+/* Define to 1 if you have the malloc.h header file. */
+#cmakedefine HAVE_MALLOC_H ${HAVE_MALLOC_H}
+
+/* Define to 1 if you have the <memory.h> header file. */
+#cmakedefine HAVE_MEMORY_H ${HAVE_MEMORY_H}
+
+/* Define to 1 if you have the MSG_NOSIGNAL flag. */
+#cmakedefine HAVE_MSG_NOSIGNAL ${HAVE_MSG_NOSIGNAL}
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#cmakedefine HAVE_NETDB_H ${HAVE_NETDB_H}
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#cmakedefine HAVE_NETINET_IN_H ${HAVE_NETINET_IN_H}
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+#cmakedefine HAVE_NETINET_TCP_H ${HAVE_NETINET_TCP_H}
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#cmakedefine HAVE_NET_IF_H ${HAVE_NET_IF_H}
+
+/* Define to 1 if NI_WITHSCOPEID exists and works. */
+#cmakedefine HAVE_NI_WITHSCOPEID ${HAVE_NI_WITHSCOPEID}
+
+/* Defined if no inet_pton() prototype available */
+#cmakedefine HAVE_NO_INET_PTON_PROTO ${HAVE_NO_INET_PTON_PROTO}
+
+/* we have no strerror_r() proto */
+#cmakedefine HAVE_NO_STRERROR_R_DECL ${HAVE_NO_STRERROR_R_DECL}
+
+/* Define to 1 if you have the <openssl/crypto.h> header file. */
+#cmakedefine HAVE_OPENSSL_CRYPTO_H ${HAVE_OPENSSL_CRYPTO_H}
+
+/* Define to 1 if you have the <openssl/engine.h> header file. */
+#cmakedefine HAVE_OPENSSL_ENGINE_H ${HAVE_OPENSSL_ENGINE_H}
+
+/* Define to 1 if you have the <openssl/err.h> header file. */
+#cmakedefine HAVE_OPENSSL_ERR_H ${HAVE_OPENSSL_ERR_H}
+
+/* Define to 1 if you have the <openssl/pem.h> header file. */
+#cmakedefine HAVE_OPENSSL_PEM_H ${HAVE_OPENSSL_PEM_H}
+
+/* Define to 1 if you have the <openssl/pkcs12.h> header file. */
+#cmakedefine HAVE_OPENSSL_PKCS12_H ${HAVE_OPENSSL_PKCS12_H}
+
+/* Define to 1 if you have the <openssl/rsa.h> header file. */
+#cmakedefine HAVE_OPENSSL_RSA_H ${HAVE_OPENSSL_RSA_H}
+
+/* Define to 1 if you have the <openssl/ssl.h> header file. */
+#cmakedefine HAVE_OPENSSL_SSL_H ${HAVE_OPENSSL_SSL_H}
+
+/* Define to 1 if you have the <openssl/x509.h> header file. */
+#cmakedefine HAVE_OPENSSL_X509_H ${HAVE_OPENSSL_X509_H}
+
+/* use O_NONBLOCK for non-blocking sockets */
+#cmakedefine HAVE_O_NONBLOCK ${HAVE_O_NONBLOCK}
+
+/* Define to 1 if you have the <pem.h> header file. */
+#cmakedefine HAVE_PEM_H ${HAVE_PEM_H}
+
+/* Define to 1 if you have the `perror' function. */
+#cmakedefine HAVE_PERROR ${HAVE_PERROR}
+
+/* Define to 1 if you have the `pipe' function. */
+#cmakedefine HAVE_PIPE ${HAVE_PIPE}
+
+/* Define to 1 if you have the `poll' function. */
+#cmakedefine HAVE_POLL ${HAVE_POLL}
+
+/* Define to 1 if you have the <process.h> header file. */
+#cmakedefine HAVE_PROCESS_H ${HAVE_PROCESS_H}
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#cmakedefine HAVE_PWD_H ${HAVE_PWD_H}
+
+/* Define to 1 if you have the `RAND_egd' function. */
+#cmakedefine HAVE_RAND_EGD ${HAVE_RAND_EGD}
+
+/* Define to 1 if you have the `RAND_screen' function. */
+#cmakedefine HAVE_RAND_SCREEN ${HAVE_RAND_SCREEN}
+
+/* Define to 1 if you have the `RAND_status' function. */
+#cmakedefine HAVE_RAND_STATUS ${HAVE_RAND_STATUS}
+
+/* Define to 1 if you have the recv function. */
+#cmakedefine HAVE_RECV ${HAVE_RECV}
+
+/* Define to 1 if you have the <rsa.h> header file. */
+#cmakedefine HAVE_RSA_H ${HAVE_RSA_H}
+
+/* Define to 1 if you have the select function. */
+#cmakedefine HAVE_SELECT ${HAVE_SELECT}
+
+/* Define to 1 if you have the send function. */
+#cmakedefine HAVE_SEND ${HAVE_SEND}
+
+/* Define to 1 if you have the <setjmp.h> header file. */
+#cmakedefine HAVE_SETJMP_H ${HAVE_SETJMP_H}
+
+/* Define to 1 if you have the `setlocale' function. */
+#cmakedefine HAVE_SETLOCALE ${HAVE_SETLOCALE}
+
+/* Define to 1 if you have the `setrlimit' function. */
+#cmakedefine HAVE_SETRLIMIT ${HAVE_SETRLIMIT}
+
+/* Define to 1 if you have the <sgtty.h> header file. */
+#cmakedefine HAVE_SGTTY_H ${HAVE_SGTTY_H}
+
+/* Define to 1 if you have the `sigaction' function. */
+#cmakedefine HAVE_SIGACTION ${HAVE_SIGACTION}
+
+/* Define to 1 if you have the `siginterrupt' function. */
+#cmakedefine HAVE_SIGINTERRUPT ${HAVE_SIGINTERRUPT}
+
+/* Define to 1 if you have the `signal' function. */
+#cmakedefine HAVE_SIGNAL ${HAVE_SIGNAL}
+
+/* Define to 1 if you have the <signal.h> header file. */
+#cmakedefine HAVE_SIGNAL_H ${HAVE_SIGNAL_H}
+
+/* If you have sigsetjmp */
+#cmakedefine HAVE_SIGSETJMP ${HAVE_SIGSETJMP}
+
+/* Define to 1 if sig_atomic_t is an available typedef. */
+#cmakedefine HAVE_SIG_ATOMIC_T ${HAVE_SIG_ATOMIC_T}
+
+/* Define to 1 if sig_atomic_t is already defined as volatile. */
+#cmakedefine HAVE_SIG_ATOMIC_T_VOLATILE ${HAVE_SIG_ATOMIC_T_VOLATILE}
+
+/* Define to 1 if you have the `socket' function. */
+#cmakedefine HAVE_SOCKET ${HAVE_SOCKET}
+
+/* use SO_NONBLOCK for non-blocking sockets */
+#cmakedefine HAVE_SO_NONBLOCK ${HAVE_SO_NONBLOCK}
+
+/* Define this if you have the SPNEGO library fbopenssl */
+#cmakedefine HAVE_SPNEGO ${HAVE_SPNEGO}
+
+/* Define to 1 if you have the <ssl.h> header file. */
+#cmakedefine HAVE_SSL_H ${HAVE_SSL_H}
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#cmakedefine HAVE_STDINT_H ${HAVE_STDINT_H}
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#cmakedefine HAVE_STDIO_H ${HAVE_STDIO_H}
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#cmakedefine HAVE_STDLIB_H ${HAVE_STDLIB_H}
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#cmakedefine HAVE_STRCASECMP ${HAVE_STRCASECMP}
+
+/* Define to 1 if you have the `strcmpi' function. */
+#cmakedefine HAVE_STRCMPI ${HAVE_STRCMPI}
+
+/* Define to 1 if you have the `strdup' function. */
+#cmakedefine HAVE_STRDUP ${HAVE_STRDUP}
+
+/* Define to 1 if you have the `stricmp' function. */
+#cmakedefine HAVE_STRICMP ${HAVE_STRICMP}
+
+/* Define to 1 if you have the <strings.h> header file. */
+#cmakedefine HAVE_STRINGS_H ${HAVE_STRINGS_H}
+
+/* Define to 1 if you have the <string.h> header file. */
+#cmakedefine HAVE_STRING_H ${HAVE_STRING_H}
+
+/* Define to 1 if you have the `strlcat' function. */
+#cmakedefine HAVE_STRLCAT ${HAVE_STRLCAT}
+
+/* Define to 1 if you have the `strlcpy' function. */
+#cmakedefine HAVE_STRLCPY ${HAVE_STRLCPY}
+
+/* Define to 1 if you have the `strstr' function. */
+#cmakedefine HAVE_STRSTR ${HAVE_STRSTR}
+
+/* Define to 1 if you have the `strtok_r' function. */
+#cmakedefine HAVE_STRTOK_R ${HAVE_STRTOK_R}
+
+/* Define to 1 if you have the `strtoll' function. */
+#cmakedefine HAVE_STRTOLL ${HAVE_STRTOLL}
+
+/* if struct sockaddr_storage is defined */
+#cmakedefine HAVE_STRUCT_SOCKADDR_STORAGE ${HAVE_STRUCT_SOCKADDR_STORAGE}
+
+/* Define to 1 if you have the timeval struct. */
+#cmakedefine HAVE_STRUCT_TIMEVAL ${HAVE_STRUCT_TIMEVAL}
+
+/* Define to 1 if you have the <sys/filio.h> header file. */
+#cmakedefine HAVE_SYS_FILIO_H ${HAVE_SYS_FILIO_H}
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#cmakedefine HAVE_SYS_IOCTL_H ${HAVE_SYS_IOCTL_H}
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#cmakedefine HAVE_SYS_PARAM_H ${HAVE_SYS_PARAM_H}
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#cmakedefine HAVE_SYS_POLL_H ${HAVE_SYS_POLL_H}
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#cmakedefine HAVE_SYS_RESOURCE_H ${HAVE_SYS_RESOURCE_H}
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#cmakedefine HAVE_SYS_SELECT_H ${HAVE_SYS_SELECT_H}
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#cmakedefine HAVE_SYS_SOCKET_H ${HAVE_SYS_SOCKET_H}
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+#cmakedefine HAVE_SYS_SOCKIO_H ${HAVE_SYS_SOCKIO_H}
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#cmakedefine HAVE_SYS_STAT_H ${HAVE_SYS_STAT_H}
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#cmakedefine HAVE_SYS_TIME_H ${HAVE_SYS_TIME_H}
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#cmakedefine HAVE_SYS_TYPES_H ${HAVE_SYS_TYPES_H}
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+#cmakedefine HAVE_SYS_UTIME_H ${HAVE_SYS_UTIME_H}
+
+/* Define to 1 if you have the <termios.h> header file. */
+#cmakedefine HAVE_TERMIOS_H ${HAVE_TERMIOS_H}
+
+/* Define to 1 if you have the <termio.h> header file. */
+#cmakedefine HAVE_TERMIO_H ${HAVE_TERMIO_H}
+
+/* Define to 1 if you have the <time.h> header file. */
+#cmakedefine HAVE_TIME_H ${HAVE_TIME_H}
+
+/* Define to 1 if you have the <tld.h> header file. */
+#cmakedefine HAVE_TLD_H ${HAVE_TLD_H}
+
+/* Define to 1 if you have the `tld_strerror' function. */
+#cmakedefine HAVE_TLD_STRERROR ${HAVE_TLD_STRERROR}
+
+/* Define to 1 if you have the `uname' function. */
+#cmakedefine HAVE_UNAME ${HAVE_UNAME}
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H}
+
+/* Define to 1 if you have the `utime' function. */
+#cmakedefine HAVE_UTIME ${HAVE_UTIME}
+
+/* Define to 1 if you have the <utime.h> header file. */
+#cmakedefine HAVE_UTIME_H ${HAVE_UTIME_H}
+
+/* Define to 1 if you have the windows.h header file. */
+#cmakedefine HAVE_WINDOWS_H ${HAVE_WINDOWS_H}
+
+/* Define to 1 if you have the winsock2.h header file. */
+#cmakedefine HAVE_WINSOCK2_H ${HAVE_WINSOCK2_H}
+
+/* Define to 1 if you have the winsock.h header file. */
+#cmakedefine HAVE_WINSOCK_H ${HAVE_WINSOCK_H}
+
+/* Define this symbol if your OS supports changing the contents of argv */
+#cmakedefine HAVE_WRITABLE_ARGV ${HAVE_WRITABLE_ARGV}
+
+/* Define to 1 if you have the ws2tcpip.h header file. */
+#cmakedefine HAVE_WS2TCPIP_H ${HAVE_WS2TCPIP_H}
+
+/* Define to 1 if you have the <x509.h> header file. */
+#cmakedefine HAVE_X509_H ${HAVE_X509_H}
+
+/* if you have the zlib.h header file */
+#cmakedefine HAVE_ZLIB_H ${HAVE_ZLIB_H}
+
+/* If you lack a fine basename() prototype */
+#cmakedefine NEED_BASENAME_PROTO ${NEED_BASENAME_PROTO}
+
+/* Define to 1 if you need the malloc.h header file even with stdlib.h */
+#cmakedefine NEED_MALLOC_H ${NEED_MALLOC_H}
+
+/* need REENTRANT defined */
+#cmakedefine NEED_REENTRANT ${NEED_REENTRANT}
+
+/* cpu-machine-OS */
+#define OS "${OPERATING_SYSTEM}"
+
+/* Name of package */
+#cmakedefine PACKAGE "${PACKAGE}"
+
+/* Define to the address where bug reports for this package should be sent. */
+#cmakedefine PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}"
+
+/* Define to the full name of this package. */
+#cmakedefine PACKAGE_NAME "${PACKAGE_NAME}"
+
+/* Define to the full name and version of this package. */
+#cmakedefine PACKAGE_STRING "${PACKAGE_STRING}"
+
+/* Define to the one symbol short name of this package. */
+#cmakedefine PACKAGE_TARNAME "${PACKAGE_TARNAME}"
+
+/* Define to the version of this package. */
+#cmakedefine PACKAGE_VERSION "${PACKAGE_VERSION}"
+
+/* a suitable file to read random data from */
+#cmakedefine RANDOM_FILE "${RANDOM_FILE}"
+
+/* Define to the type of arg 1 for recv. */
+#cmakedefine RECV_TYPE_ARG1 ${RECV_TYPE_ARG1}
+
+/* Define to the type of arg 2 for recv. */
+#cmakedefine RECV_TYPE_ARG2 ${RECV_TYPE_ARG2}
+
+/* Define to the type of arg 3 for recv. */
+#cmakedefine RECV_TYPE_ARG3 ${RECV_TYPE_ARG3}
+
+/* Define to the type of arg 4 for recv. */
+#cmakedefine RECV_TYPE_ARG4 ${RECV_TYPE_ARG4}
+
+/* Define to the function return type for recv. */
+#cmakedefine RECV_TYPE_RETV ${RECV_TYPE_RETV}
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#cmakedefine RETSIGTYPE ${RETSIGTYPE}
+
+/* Define to the type of arg 1 for `select'. */
+#cmakedefine SELECT_TYPE_ARG1 ${SELECT_TYPE_ARG1}
+
+/* Define to the type of args 2, 3 and 4 for `select'. */
+#cmakedefine SELECT_TYPE_ARG234 ${SELECT_TYPE_ARG234}
+
+/* Define to the type of arg 5 for `select'. */
+#cmakedefine SELECT_TYPE_ARG5 ${SELECT_TYPE_ARG5}
+
+/* Define to the type qualifier of arg 2 for send. */
+#cmakedefine SEND_QUAL_ARG2 ${SEND_QUAL_ARG2}
+
+/* Define to the type of arg 1 for send. */
+#cmakedefine SEND_TYPE_ARG1 ${SEND_TYPE_ARG1}
+
+/* Define to the type of arg 2 for send. */
+#cmakedefine SEND_TYPE_ARG2 ${SEND_TYPE_ARG2}
+
+/* Define to the type of arg 3 for send. */
+#cmakedefine SEND_TYPE_ARG3 ${SEND_TYPE_ARG3}
+
+/* Define to the type of arg 4 for send. */
+#cmakedefine SEND_TYPE_ARG4 ${SEND_TYPE_ARG4}
+
+/* Define to the function return type for send. */
+#cmakedefine SEND_TYPE_RETV ${SEND_TYPE_RETV}
+
+/* The size of `curl_off_t', as computed by sizeof. */
+@SIZEOF_CURL_OFF_T_CODE@
+
+/* The size of `long', as computed by sizeof. */
+@SIZEOF_LONG_CODE@
+
+/* The size of `long long', as computed by sizeof. */
+@SIZEOF_LONG_LONG_CODE@
+
+/* The size of `__int64', as computed by sizeof. */
+@SIZEOF___INT64_CODE@
+
+/* The size of `size_t', as computed by sizeof. */
+@SIZEOF_SIZE_T_CODE@
+
+/* The size of `ssize_t', as computed by sizeof. */
+@SIZEOF_SSIZE_T_CODE@
+
+/* The size of `time_t', as computed by sizeof. */
+@SIZEOF_TIME_T_CODE@
+
+/* Define to 1 if you have the ANSI C header files. */
+#cmakedefine STDC_HEADERS ${STDC_HEADERS}
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#cmakedefine TIME_WITH_SYS_TIME ${TIME_WITH_SYS_TIME}
+
+/* Define if you want to enable ares support */
+#cmakedefine USE_ARES ${USE_ARES}
+
+/* if GnuTLS is enabled */
+#cmakedefine USE_GNUTLS ${USE_GNUTLS}
+
+/* if libSSH2 is in use */
+#cmakedefine USE_LIBSSH2 ${USE_LIBSSH2}
+
+/* If you want to build curl with the built-in manual */
+#cmakedefine USE_MANUAL ${USE_MANUAL}
+
+/* if OpenSSL is in use */
+#cmakedefine USE_OPENSSL ${USE_OPENSSL}
+
+/* if SSL is enabled */
+#cmakedefine USE_SSLEAY ${USE_SSLEAY}
+
+/* to enable SSPI support */
+#cmakedefine USE_WINDOWS_SSPI ${USE_WINDOWS_SSPI}
+
+/* Version number of package */
+#cmakedefine VERSION "${VERSION}"
+
+/* Define to avoid automatic inclusion of winsock.h */
+#cmakedefine WIN32_LEAN_AND_MEAN ${WIN32_LEAN_AND_MEAN}
+
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+#cmakedefine _ALL_SOURCE ${_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 this if you need it to compile thread-safe code */
+#cmakedefine _THREAD_SAFE ${_THREAD_SAFE}
+
+/* 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 int' 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}
+
+/* the signed version of size_t */
+#ifndef SIZEOF_SSIZE_T
+# if SIZEOF_LONG == SIZEOF_SIZE_T
+ typedef long ssize_t;
+# elif SIZEOF_LONG_LONG == SIZEOF_SIZE_T
+ typedef long long ssize_t;
+# elif SIZEOF___INT64 == SIZEOF_SIZE_T
+ typedef __int64 ssize_t;
+# else
+ typedef int ssize_t;
+# endif
+#endif
+
+/* Special handling of zlib library */
+#cmakedefine CURL_SPECIAL_ZLIB_H "${CURL_SPECIAL_ZLIB_H}"
diff --git a/Utilities/cmcurl/connect.c b/Utilities/cmcurl/connect.c
new file mode 100644
index 000000000..2b3897204
--- /dev/null
+++ b/Utilities/cmcurl/connect.c
@@ -0,0 +1,905 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef WIN32
+/* headers for non-win32 */
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#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> /* <netinet/tcp.h> may need it */
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h> /* for TCP_NODELAY */
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_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
+#if (defined(HAVE_FIONBIO) && defined(__NOVELL_LIBC__))
+#include <sys/filio.h>
+#endif
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#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 USE_WINSOCK
+#define EINPROGRESS WSAEINPROGRESS
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define EISCONN WSAEISCONN
+#define ENOTSOCK WSAENOTSOCK
+#define ECONNREFUSED WSAECONNREFUSED
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "if2ip.h"
+#include "strerror.h"
+#include "connect.h"
+#include "memory.h"
+#include "select.h"
+#include "url.h" /* for Curl_safefree() */
+#include "multiif.h"
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "inet_ntop.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+static bool verifyconnect(curl_socket_t sockfd, int *error);
+
+static curl_socket_t
+singleipconnect(struct connectdata *conn,
+ const Curl_addrinfo *ai, /* start connecting to this */
+ long timeout_ms,
+ bool *connected);
+
+/*
+ * Curl_sockerrno() returns the *socket-related* errno (or equivalent) on this
+ * platform to hide platform specific for the function that calls this.
+ */
+int Curl_sockerrno(void)
+{
+#ifdef USE_WINSOCK
+ return (int)WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+/*
+ * Curl_nonblock() set the given socket to either blocking or non-blocking
+ * mode based on the 'nonblock' boolean argument. This function is highly
+ * portable.
+ */
+int Curl_nonblock(curl_socket_t sockfd, /* operate on this */
+ int nonblock /* TRUE or FALSE */)
+{
+#undef SETBLOCK
+#define SETBLOCK 0
+#ifdef HAVE_O_NONBLOCK
+ /* most recent unix versions */
+ int flags;
+
+ flags = fcntl(sockfd, F_GETFL, 0);
+ if (TRUE == nonblock)
+ return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+ else
+ return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
+#undef SETBLOCK
+#define SETBLOCK 1
+#endif
+
+#if defined(HAVE_FIONBIO) && (SETBLOCK == 0)
+ /* older unix versions */
+ int flags;
+
+ flags = nonblock;
+ return ioctl(sockfd, FIONBIO, &flags);
+#undef SETBLOCK
+#define SETBLOCK 2
+#endif
+
+#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0)
+ /* Windows? */
+ unsigned long flags;
+ flags = nonblock;
+
+ return ioctlsocket(sockfd, FIONBIO, &flags);
+#undef SETBLOCK
+#define SETBLOCK 3
+#endif
+
+#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0)
+ /* presumably for Amiga */
+ return IoctlSocket(sockfd, FIONBIO, (long)nonblock);
+#undef SETBLOCK
+#define SETBLOCK 4
+#endif
+
+#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0)
+ /* BeOS */
+ long b = nonblock ? 1 : 0;
+ return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
+#undef SETBLOCK
+#define SETBLOCK 5
+#endif
+
+#ifdef HAVE_DISABLED_NONBLOCKING
+ return 0; /* returns success */
+#undef SETBLOCK
+#define SETBLOCK 6
+#endif
+
+#if (SETBLOCK == 0)
+#error "no non-blocking method was found/used/set"
+#endif
+}
+
+/*
+ * waitconnect() waits for a TCP connect on the given socket for the specified
+ * number if milliseconds. It returns:
+ * 0 fine connect
+ * -1 select() error
+ * 1 select() timeout
+ * 2 select() returned with an error condition fd_set
+ */
+
+#define WAITCONN_CONNECTED 0
+#define WAITCONN_SELECT_ERROR -1
+#define WAITCONN_TIMEOUT 1
+#define WAITCONN_FDSET_ERROR 2
+
+static
+int waitconnect(curl_socket_t sockfd, /* socket */
+ long timeout_msec)
+{
+ int rc;
+#ifdef mpeix
+ /* Call this function once now, and ignore the results. We do this to
+ "clear" the error state on the socket so that we can later read it
+ reliably. This is reported necessary on the MPE/iX operating system. */
+ (void)verifyconnect(sockfd, NULL);
+#endif
+
+ /* now select() until we get connect or timeout */
+ rc = Curl_select(CURL_SOCKET_BAD, sockfd, (int)timeout_msec);
+ if(-1 == rc)
+ /* error, no connect here, try next */
+ return WAITCONN_SELECT_ERROR;
+
+ else if(0 == rc)
+ /* timeout, no connect today */
+ return WAITCONN_TIMEOUT;
+
+ if(rc & CSELECT_ERR)
+ /* error condition caught */
+ return WAITCONN_FDSET_ERROR;
+
+ /* we have a connect! */
+ return WAITCONN_CONNECTED;
+}
+
+static CURLcode bindlocal(struct connectdata *conn,
+ curl_socket_t sockfd)
+{
+ struct SessionHandle *data = conn->data;
+ struct sockaddr_in me;
+ struct sockaddr *sock = NULL; /* bind to this address */
+ socklen_t socksize; /* size of the data sock points to */
+ unsigned short port = data->set.localport; /* use this port number, 0 for
+ "random" */
+ /* how many port numbers to try to bind to, increasing one at a time */
+ int portnum = data->set.localportrange;
+
+ /*************************************************************
+ * Select device to bind socket to
+ *************************************************************/
+ if (data->set.device && (strlen(data->set.device)<255) ) {
+ struct Curl_dns_entry *h=NULL;
+ char myhost[256] = "";
+ in_addr_t in;
+ int rc;
+ bool was_iface = FALSE;
+
+ /* First check if the given name is an IP address */
+ in=inet_addr(data->set.device);
+
+ if((in == CURL_INADDR_NONE) &&
+ Curl_if2ip(data->set.device, myhost, sizeof(myhost))) {
+ /*
+ * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
+ */
+ rc = Curl_resolv(conn, myhost, 0, &h);
+ if(rc == CURLRESOLV_PENDING)
+ (void)Curl_wait_for_resolv(conn, &h);
+
+ if(h) {
+ was_iface = TRUE;
+ Curl_resolv_unlock(data, h);
+ }
+ }
+
+ if(!was_iface) {
+ /*
+ * This was not an interface, resolve the name as a host name
+ * or IP number
+ */
+ rc = Curl_resolv(conn, data->set.device, 0, &h);
+ if(rc == CURLRESOLV_PENDING)
+ (void)Curl_wait_for_resolv(conn, &h);
+
+ if(h) {
+ if(in == CURL_INADDR_NONE)
+ /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
+ Curl_inet_ntop(h->addr->ai_addr->sa_family,
+ &((struct sockaddr_in*)h->addr->ai_addr)->sin_addr,
+ myhost, sizeof myhost);
+ else
+ /* we know data->set.device is shorter than the myhost array */
+ strcpy(myhost, data->set.device);
+ Curl_resolv_unlock(data, h);
+ }
+ }
+
+ if(! *myhost) {
+ /* need to fix this
+ h=Curl_gethost(data,
+ getmyhost(*myhost,sizeof(myhost)),
+ hostent_buf,
+ sizeof(hostent_buf));
+ */
+ failf(data, "Couldn't bind to '%s'", data->set.device);
+ return CURLE_HTTP_PORT_FAILED;
+ }
+
+ infof(data, "Bind local address to %s\n", myhost);
+
+#ifdef SO_BINDTODEVICE
+ /* I am not sure any other OSs than Linux that provide this feature, and
+ * at the least I cannot test. --Ben
+ *
+ * This feature allows one to tightly bind the local socket to a
+ * particular interface. This will force even requests to other local
+ * interfaces to go out the external interface.
+ *
+ */
+ if (was_iface) {
+ /* Only bind to the interface when specified as interface, not just as a
+ * hostname or ip address.
+ */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
+ data->set.device, strlen(data->set.device)+1) != 0) {
+ /* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n",
+ sockfd, data->set.device, Curl_strerror(Curl_sockerrno())); */
+ infof(data, "SO_BINDTODEVICE %s failed\n",
+ data->set.device);
+ /* This is typically "errno 1, error: Operation not permitted" if
+ you're not running as root or another suitable privileged user */
+ }
+ }
+#endif
+
+ in=inet_addr(myhost);
+ if (CURL_INADDR_NONE == in) {
+ failf(data,"couldn't find my own IP address (%s)", myhost);
+ return CURLE_HTTP_PORT_FAILED;
+ } /* end of inet_addr */
+
+ if ( h ) {
+ Curl_addrinfo *addr = h->addr;
+ sock = addr->ai_addr;
+ socksize = addr->ai_addrlen;
+ }
+ else
+ return CURLE_HTTP_PORT_FAILED;
+
+ }
+ else if(port) {
+ /* if a local port number is requested but no local IP, extract the
+ address from the socket */
+ memset(&me, 0, sizeof(struct sockaddr));
+ me.sin_family = AF_INET;
+ me.sin_addr.s_addr = INADDR_ANY;
+
+ sock = (struct sockaddr *)&me;
+ socksize = sizeof(struct sockaddr);
+
+ }
+ else
+ /* no local kind of binding was requested */
+ return CURLE_OK;
+
+ do {
+
+ /* Set port number to bind to, 0 makes the system pick one */
+ if(sock->sa_family == AF_INET)
+ ((struct sockaddr_in *)sock)->sin_port = htons(port);
+#ifdef ENABLE_IPV6
+ else
+ ((struct sockaddr_in6 *)sock)->sin6_port = htons(port);
+#endif
+
+ if( bind(sockfd, sock, socksize) >= 0) {
+ /* we succeeded to bind */
+ struct Curl_sockaddr_storage add;
+ socklen_t size;
+
+ size = sizeof(add);
+ if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
+ failf(data, "getsockname() failed");
+ return CURLE_HTTP_PORT_FAILED;
+ }
+ /* We re-use/clobber the port variable here below */
+ if(((struct sockaddr *)&add)->sa_family == AF_INET)
+ port = ntohs(((struct sockaddr_in *)&add)->sin_port);
+#ifdef ENABLE_IPV6
+ else
+ port = ntohs(((struct sockaddr_in6 *)&add)->sin6_port);
+#endif
+ infof(data, "Local port: %d\n", port);
+ return CURLE_OK;
+ }
+ if(--portnum > 0) {
+ infof(data, "Bind to local port %d failed, trying next\n", port);
+ port++; /* try next port */
+ }
+ else
+ break;
+ } while(1);
+
+ data->state.os_errno = Curl_sockerrno();
+ failf(data, "bind failure: %s",
+ Curl_strerror(conn, data->state.os_errno));
+ return CURLE_HTTP_PORT_FAILED;
+
+}
+
+/*
+ * verifyconnect() returns TRUE if the connect really has happened.
+ */
+static bool verifyconnect(curl_socket_t sockfd, int *error)
+{
+ bool rc = TRUE;
+#ifdef SO_ERROR
+ int err = 0;
+ socklen_t errSize = sizeof(err);
+
+#ifdef WIN32
+ /*
+ * In October 2003 we effectively nullified this function on Windows due to
+ * problems with it using all CPU in multi-threaded cases.
+ *
+ * In May 2004, we bring it back to offer more info back on connect failures.
+ * Gisle Vanem could reproduce the former problems with this function, but
+ * could avoid them by adding this SleepEx() call below:
+ *
+ * "I don't have Rational Quantify, but the hint from his post was
+ * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
+ * just Sleep(0) would be enough?) would release whatever
+ * mutex/critical-section the ntdll call is waiting on.
+ *
+ * Someone got to verify this on Win-NT 4.0, 2000."
+ */
+
+#ifdef _WIN32_WCE
+ Sleep(0);
+#else
+ SleepEx(0, FALSE);
+#endif
+
+#endif
+
+ if( -1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR,
+ (void *)&err, &errSize))
+ err = Curl_sockerrno();
+
+#ifdef _WIN32_WCE
+ /* Always returns this error, bug in CE? */
+ if(WSAENOPROTOOPT==err)
+ err=0;
+#endif
+
+ if ((0 == err) || (EISCONN == err))
+ /* we are connected, awesome! */
+ rc = TRUE;
+ else
+ /* This wasn't a successful connect */
+ rc = FALSE;
+ if (error)
+ *error = err;
+#else
+ (void)sockfd;
+ if (error)
+ *error = Curl_sockerrno();
+#endif
+ return rc;
+}
+
+CURLcode Curl_store_ip_addr(struct connectdata *conn)
+{
+ char addrbuf[256];
+ Curl_printable_address(conn->ip_addr, addrbuf, sizeof(addrbuf));
+
+ /* save the string */
+ Curl_safefree(conn->ip_addr_str);
+ conn->ip_addr_str = strdup(addrbuf);
+ if(!conn->ip_addr_str)
+ return CURLE_OUT_OF_MEMORY; /* FAIL */
+
+#ifdef PF_INET6
+ if(conn->ip_addr->ai_family == PF_INET6)
+ conn->bits.ipv6 = TRUE;
+#endif
+
+ return CURLE_OK;
+}
+
+/* Used within the multi interface. Try next IP address, return TRUE if no
+ more address exists */
+static bool trynextip(struct connectdata *conn,
+ int sockindex,
+ bool *connected)
+{
+ curl_socket_t sockfd;
+ Curl_addrinfo *ai;
+
+ /* first close the failed socket */
+ sclose(conn->sock[sockindex]);
+ conn->sock[sockindex] = CURL_SOCKET_BAD;
+ *connected = FALSE;
+
+ if(sockindex != FIRSTSOCKET)
+ return TRUE; /* no next */
+
+ /* try the next address */
+ ai = conn->ip_addr->ai_next;
+
+ while (ai) {
+ sockfd = singleipconnect(conn, ai, 0L, connected);
+ if(sockfd != CURL_SOCKET_BAD) {
+ /* store the new socket descriptor */
+ conn->sock[sockindex] = sockfd;
+ conn->ip_addr = ai;
+
+ Curl_store_ip_addr(conn);
+ return FALSE;
+ }
+ ai = ai->ai_next;
+ }
+ return TRUE;
+}
+
+/*
+ * Curl_is_connected() is used from the multi interface to check if the
+ * firstsocket has connected.
+ */
+
+CURLcode Curl_is_connected(struct connectdata *conn,
+ int sockindex,
+ bool *connected)
+{
+ int rc;
+ struct SessionHandle *data = conn->data;
+ CURLcode code = CURLE_OK;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ long allow = DEFAULT_CONNECT_TIMEOUT;
+ long allow_total = 0;
+ long has_passed;
+
+ curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
+
+ *connected = FALSE; /* a very negative world view is best */
+
+ /* Evaluate in milliseconds how much time that has passed */
+ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
+
+ /* subtract the most strict timeout of the ones */
+ if(data->set.timeout && data->set.connecttimeout) {
+ if (data->set.timeout < data->set.connecttimeout)
+ allow_total = allow = data->set.timeout*1000;
+ else
+ allow = data->set.connecttimeout*1000;
+ }
+ else if(data->set.timeout) {
+ allow_total = allow = data->set.timeout*1000;
+ }
+ else if(data->set.connecttimeout) {
+ allow = data->set.connecttimeout*1000;
+ }
+
+ if(has_passed > allow ) {
+ /* time-out, bail out, go home */
+ failf(data, "Connection time-out after %ld ms", has_passed);
+ return CURLE_OPERATION_TIMEOUTED;
+ }
+ if(conn->bits.tcpconnect) {
+ /* we are connected already! */
+ Curl_expire(data, allow_total);
+ *connected = TRUE;
+ return CURLE_OK;
+ }
+
+ Curl_expire(data, allow);
+
+ /* check for connect without timeout as we want to return immediately */
+ rc = waitconnect(sockfd, 0);
+
+ if(WAITCONN_CONNECTED == rc) {
+ int error;
+ if (verifyconnect(sockfd, &error)) {
+ /* we are connected, awesome! */
+ *connected = TRUE;
+ return CURLE_OK;
+ }
+ /* nope, not connected for real */
+ data->state.os_errno = error;
+ infof(data, "Connection failed\n");
+ if(trynextip(conn, sockindex, connected)) {
+ code = CURLE_COULDNT_CONNECT;
+ }
+ }
+ else if(WAITCONN_TIMEOUT != rc) {
+ int error = 0;
+
+ /* nope, not connected */
+ if (WAITCONN_FDSET_ERROR == rc) {
+ (void)verifyconnect(sockfd, &error);
+ data->state.os_errno = error;
+ infof(data, "%s\n",Curl_strerror(conn,error));
+ }
+ else
+ infof(data, "Connection failed\n");
+
+ if(trynextip(conn, sockindex, connected)) {
+ error = Curl_sockerrno();
+ data->state.os_errno = error;
+ failf(data, "Failed connect to %s:%d; %s",
+ conn->host.name, conn->port, Curl_strerror(conn,error));
+ code = CURLE_COULDNT_CONNECT;
+ }
+ }
+ /*
+ * If the connection failed here, we should attempt to connect to the "next
+ * address" for the given host.
+ */
+
+ return code;
+}
+
+static void tcpnodelay(struct connectdata *conn,
+ curl_socket_t sockfd)
+{
+#ifdef TCP_NODELAY
+ struct SessionHandle *data= conn->data;
+ socklen_t onoff = (socklen_t) data->set.tcp_nodelay;
+ int proto = IPPROTO_TCP;
+
+#ifdef HAVE_GETPROTOBYNAME
+ struct protoent *pe = getprotobyname("tcp");
+ if (pe)
+ proto = pe->p_proto;
+#endif
+
+ if(setsockopt(sockfd, proto, TCP_NODELAY, (void *)&onoff,
+ sizeof(onoff)) < 0)
+ infof(data, "Could not set TCP_NODELAY: %s\n",
+ Curl_strerror(conn, Curl_sockerrno()));
+ else
+ infof(data,"TCP_NODELAY set\n");
+#else
+ (void)conn;
+ (void)sockfd;
+#endif
+}
+
+#ifdef SO_NOSIGPIPE
+/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
+ sending data to a dead peer (instead of relying on the 4th argument to send
+ being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
+ systems? */
+static void nosigpipe(struct connectdata *conn,
+ curl_socket_t sockfd)
+{
+ struct SessionHandle *data= conn->data;
+ int onoff = 1;
+ if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
+ sizeof(onoff)) < 0)
+ infof(data, "Could not set SO_NOSIGPIPE: %s\n",
+ Curl_strerror(conn, Curl_sockerrno()));
+}
+#else
+#define nosigpipe(x,y)
+#endif
+
+/* singleipconnect() connects to the given IP only, and it may return without
+ having connected if used from the multi interface. */
+static curl_socket_t
+singleipconnect(struct connectdata *conn,
+ const Curl_addrinfo *ai,
+ long timeout_ms,
+ bool *connected)
+{
+ char addr_buf[128];
+ int rc;
+ int error;
+ bool isconnected;
+ struct SessionHandle *data = conn->data;
+ curl_socket_t sockfd;
+ CURLcode res;
+
+ sockfd = socket(ai->ai_family, conn->socktype, ai->ai_protocol);
+ if (sockfd == CURL_SOCKET_BAD)
+ return CURL_SOCKET_BAD;
+
+ *connected = FALSE; /* default is not connected */
+
+ Curl_printable_address(ai, addr_buf, sizeof(addr_buf));
+ infof(data, " Trying %s... ", addr_buf);
+
+ if(data->set.tcp_nodelay)
+ tcpnodelay(conn, sockfd);
+
+ nosigpipe(conn, sockfd);
+
+ if(data->set.fsockopt) {
+ /* activate callback for setting socket options */
+ error = data->set.fsockopt(data->set.sockopt_client,
+ sockfd,
+ CURLSOCKTYPE_IPCXN);
+ if (error) {
+ sclose(sockfd); /* close the socket and bail out */
+ return CURL_SOCKET_BAD;
+ }
+ }
+
+ /* possibly bind the local end to an IP, interface or port */
+ res = bindlocal(conn, sockfd);
+ if(res) {
+ sclose(sockfd); /* close socket and bail out */
+ return CURL_SOCKET_BAD;
+ }
+
+ /* set socket non-blocking */
+ Curl_nonblock(sockfd, TRUE);
+
+ /* Connect TCP sockets, bind UDP */
+ if(conn->socktype == SOCK_STREAM)
+ rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
+ else
+ rc = 0;
+
+ if(-1 == rc) {
+ error = Curl_sockerrno();
+
+ 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
+ rc = waitconnect(sockfd, timeout_ms);
+ break;
+ default:
+ /* unknown error, fallthrough and try another address! */
+ failf(data, "Failed to connect to %s: %s",
+ addr_buf, Curl_strerror(conn,error));
+ data->state.os_errno = error;
+ break;
+ }
+ }
+
+ /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from
+ connect(). We can be sure of this since connect() cannot return 1. */
+ if((WAITCONN_TIMEOUT == rc) &&
+ (data->state.used_interface == Curl_if_multi)) {
+ /* Timeout when running the multi interface */
+ return sockfd;
+ }
+
+ isconnected = verifyconnect(sockfd, &error);
+
+ if(!rc && isconnected) {
+ /* we are connected, awesome! */
+ *connected = TRUE; /* this is a true connect */
+ infof(data, "connected\n");
+ return sockfd;
+ }
+ else if(WAITCONN_TIMEOUT == rc)
+ infof(data, "Timeout\n");
+ else {
+ data->state.os_errno = error;
+ infof(data, "%s\n", Curl_strerror(conn, error));
+ }
+
+ /* connect failed or timed out */
+ sclose(sockfd);
+
+ return CURL_SOCKET_BAD;
+}
+
+/*
+ * TCP connect to the given host with timeout, proxy or remote doesn't matter.
+ * There might be more than one IP address to try out. Fill in the passed
+ * pointer with the connected socket.
+ */
+
+CURLcode Curl_connecthost(struct connectdata *conn, /* context */
+ const struct Curl_dns_entry *remotehost, /* use this one */
+ curl_socket_t *sockconn, /* the connected socket */
+ Curl_addrinfo **addr, /* the one we used */
+ bool *connected) /* really connected? */
+{
+ struct SessionHandle *data = conn->data;
+ curl_socket_t sockfd = CURL_SOCKET_BAD;
+ int aliasindex;
+ int num_addr;
+ Curl_addrinfo *ai;
+ Curl_addrinfo *curr_addr;
+
+ struct timeval after;
+ struct timeval before = Curl_tvnow();
+
+ /*************************************************************
+ * Figure out what maximum time we have left
+ *************************************************************/
+ long timeout_ms= DEFAULT_CONNECT_TIMEOUT;
+ long timeout_per_addr;
+
+ *connected = FALSE; /* default to not connected */
+
+ if(data->set.timeout || data->set.connecttimeout) {
+ long has_passed;
+
+ /* Evaluate in milliseconds how much time that has passed */
+ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
+
+#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 -= 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;
+ }
+ }
+ Curl_expire(data, timeout_ms);
+
+ /* Max time for each address */
+ num_addr = Curl_num_addresses(remotehost->addr);
+ timeout_per_addr = timeout_ms / num_addr;
+
+ ai = remotehost->addr;
+
+ /* Below is the loop that attempts to connect to all IP-addresses we
+ * know for the given host. One by one until one IP succeeds.
+ */
+
+ if(data->state.used_interface == Curl_if_multi)
+ /* don't hang when doing multi */
+ timeout_per_addr = 0;
+
+ /*
+ * Connecting with a Curl_addrinfo chain
+ */
+ for (curr_addr = ai, aliasindex=0; curr_addr;
+ curr_addr = curr_addr->ai_next, aliasindex++) {
+
+ /* start connecting to the IP curr_addr points to */
+ sockfd = singleipconnect(conn, curr_addr, timeout_per_addr, connected);
+
+ if(sockfd != CURL_SOCKET_BAD)
+ break;
+
+ /* get a new timeout for next attempt */
+ after = Curl_tvnow();
+ timeout_ms -= Curl_tvdiff(after, before);
+ if(timeout_ms < 0) {
+ failf(data, "connect() timed out!");
+ return CURLE_OPERATION_TIMEOUTED;
+ }
+ before = after;
+ } /* end of connect-to-each-address loop */
+
+ if (sockfd == CURL_SOCKET_BAD) {
+ /* no good connect was made */
+ *sockconn = CURL_SOCKET_BAD;
+ failf(data, "couldn't connect to host");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ /* leave the socket in non-blocking mode */
+
+ /* store the address we use */
+ if(addr)
+ *addr = curr_addr;
+
+ /* allow NULL-pointers to get passed in */
+ if(sockconn)
+ *sockconn = sockfd; /* the socket descriptor we've connected */
+
+ data->info.numconnects++; /* to track the number of connections made */
+
+ return CURLE_OK;
+}
diff --git a/Utilities/cmcurl/connect.h b/Utilities/cmcurl/connect.h
new file mode 100644
index 000000000..599572b27
--- /dev/null
+++ b/Utilities/cmcurl/connect.h
@@ -0,0 +1,46 @@
+#ifndef __CONNECT_H
+#define __CONNECT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+int Curl_nonblock(curl_socket_t sockfd, /* operate on this */
+ int nonblock /* TRUE or FALSE */);
+
+CURLcode Curl_is_connected(struct connectdata *conn,
+ int sockindex,
+ bool *connected);
+
+CURLcode Curl_connecthost(struct connectdata *conn,
+ const struct Curl_dns_entry *host, /* connect to this */
+ curl_socket_t *sockconn, /* not set if error */
+ Curl_addrinfo **addr, /* the one we used */
+ bool *connected /* truly connected? */
+ );
+
+int Curl_sockerrno(void);
+
+CURLcode Curl_store_ip_addr(struct connectdata *conn);
+
+#define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
+
+#endif
diff --git a/Utilities/cmcurl/content_encoding.c b/Utilities/cmcurl/content_encoding.c
new file mode 100644
index 000000000..97f834177
--- /dev/null
+++ b/Utilities/cmcurl/content_encoding.c
@@ -0,0 +1,424 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifdef HAVE_LIBZ
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "sendf.h"
+#include "content_encoding.h"
+#include "memory.h"
+
+#include "memdebug.h"
+
+/* Comment this out if zlib is always going to be at least ver. 1.2.0.4
+ (doing so will reduce code size slightly). */
+#define OLD_ZLIB_SUPPORT 1
+
+#define DSIZ 0x10000 /* buffer size for decompressed data */
+
+#define GZIP_MAGIC_0 0x1f
+#define GZIP_MAGIC_1 0x8b
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+enum zlibState {
+ ZLIB_UNINIT, /* uninitialized */
+ ZLIB_INIT, /* initialized */
+ ZLIB_GZIP_HEADER, /* reading gzip header */
+ ZLIB_GZIP_INFLATING, /* inflating gzip stream */
+ ZLIB_INIT_GZIP /* initialized in transparent gzip mode */
+};
+
+static CURLcode
+process_zlib_error(struct connectdata *conn, z_stream *z)
+{
+ struct SessionHandle *data = conn->data;
+ if (z->msg)
+ failf (data, "Error while processing content unencoding: %s",
+ z->msg);
+ else
+ failf (data, "Error while processing content unencoding: "
+ "Unknown failure within decompression software.");
+
+ return CURLE_BAD_CONTENT_ENCODING;
+}
+
+static CURLcode
+exit_zlib(z_stream *z, bool *zlib_init, CURLcode result)
+{
+ inflateEnd(z);
+ *zlib_init = ZLIB_UNINIT;
+ return result;
+}
+
+static CURLcode
+inflate_stream(struct connectdata *conn,
+ struct Curl_transfer_keeper *k)
+{
+ int allow_restart = 1;
+ z_stream *z = &k->z; /* zlib state structure */
+ uInt nread = z->avail_in;
+ Bytef *orig_in = z->next_in;
+ int status; /* zlib status */
+ CURLcode result = CURLE_OK; /* Curl_client_write status */
+ char *decomp; /* Put the decompressed data here. */
+
+ /* Dynamically allocate a buffer for decompression because it's uncommonly
+ large to hold on the stack */
+ decomp = (char*)malloc(DSIZ);
+ if (decomp == NULL) {
+ return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
+ }
+
+ /* because the buffer size is fixed, iteratively decompress and transfer to
+ the client via client_write. */
+ for (;;) {
+ /* (re)set buffer for decompressed output for every iteration */
+ z->next_out = (Bytef *)decomp;
+ z->avail_out = DSIZ;
+
+ status = inflate(z, Z_SYNC_FLUSH);
+ if (status == Z_OK || status == Z_STREAM_END) {
+ allow_restart = 0;
+ if(DSIZ - z->avail_out) {
+ result = Curl_client_write(conn, CLIENTWRITE_BODY, decomp,
+ DSIZ - z->avail_out);
+ /* if !CURLE_OK, clean up, return */
+ if (result) {
+ free(decomp);
+ return exit_zlib(z, &k->zlib_init, result);
+ }
+ }
+
+ /* Done? clean up, return */
+ if (status == Z_STREAM_END) {
+ free(decomp);
+ if (inflateEnd(z) == Z_OK)
+ return exit_zlib(z, &k->zlib_init, result);
+ else
+ return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
+ }
+
+ /* Done with these bytes, exit */
+ if (status == Z_OK && z->avail_in == 0) {
+ free(decomp);
+ return result;
+ }
+ }
+ else if (allow_restart && status == Z_DATA_ERROR) {
+ /* some servers seem to not generate zlib headers, so this is an attempt
+ to fix and continue anyway */
+
+ inflateReset(z);
+ if (inflateInit2(z, -MAX_WBITS) != Z_OK) {
+ return process_zlib_error(conn, z);
+ }
+ z->next_in = orig_in;
+ z->avail_in = nread;
+ allow_restart = 0;
+ continue;
+ }
+ else { /* Error; exit loop, handle below */
+ free(decomp);
+ return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
+ }
+ }
+ /* Will never get here */
+}
+
+CURLcode
+Curl_unencode_deflate_write(struct connectdata *conn,
+ struct Curl_transfer_keeper *k,
+ ssize_t nread)
+{
+ z_stream *z = &k->z; /* zlib state structure */
+
+ /* Initialize zlib? */
+ if (k->zlib_init == ZLIB_UNINIT) {
+ z->zalloc = (alloc_func)Z_NULL;
+ z->zfree = (free_func)Z_NULL;
+ z->opaque = 0;
+ z->next_in = NULL;
+ z->avail_in = 0;
+ if (inflateInit(z) != Z_OK)
+ return process_zlib_error(conn, z);
+ k->zlib_init = ZLIB_INIT;
+ }
+
+ /* Set the compressed input when this function is called */
+ z->next_in = (Bytef *)k->str;
+ z->avail_in = (uInt)nread;
+
+ /* Now uncompress the data */
+ return inflate_stream(conn, k);
+}
+
+#ifdef OLD_ZLIB_SUPPORT
+/* Skip over the gzip header */
+static enum {
+ GZIP_OK,
+ GZIP_BAD,
+ GZIP_UNDERFLOW
+} check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen)
+{
+ int method, flags;
+ const ssize_t totallen = len;
+
+ /* The shortest header is 10 bytes */
+ if (len < 10)
+ return GZIP_UNDERFLOW;
+
+ if ((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1))
+ return GZIP_BAD;
+
+ method = data[2];
+ flags = data[3];
+
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ /* Can't handle this compression method or unknown flag */
+ return GZIP_BAD;
+ }
+
+ /* Skip over time, xflags, OS code and all previous bytes */
+ len -= 10;
+ data += 10;
+
+ if (flags & EXTRA_FIELD) {
+ ssize_t extra_len;
+
+ if (len < 2)
+ return GZIP_UNDERFLOW;
+
+ extra_len = (data[1] << 8) | data[0];
+
+ if (len < (extra_len+2))
+ return GZIP_UNDERFLOW;
+
+ len -= (extra_len + 2);
+ data += (extra_len + 2);
+ }
+
+ if (flags & ORIG_NAME) {
+ /* Skip over NUL-terminated file name */
+ while (len && *data) {
+ --len;
+ ++data;
+ }
+ if (!len || *data)
+ return GZIP_UNDERFLOW;
+
+ /* Skip over the NUL */
+ --len;
+ ++data;
+ }
+
+ if (flags & COMMENT) {
+ /* Skip over NUL-terminated comment */
+ while (len && *data) {
+ --len;
+ ++data;
+ }
+ if (!len || *data)
+ return GZIP_UNDERFLOW;
+
+ /* Skip over the NUL */
+ --len;
+ ++data;
+ }
+
+ if (flags & HEAD_CRC) {
+ if (len < 2)
+ return GZIP_UNDERFLOW;
+
+ len -= 2;
+ data += 2;
+ }
+
+ *headerlen = totallen - len;
+ return GZIP_OK;
+}
+#endif
+
+CURLcode
+Curl_unencode_gzip_write(struct connectdata *conn,
+ struct Curl_transfer_keeper *k,
+ ssize_t nread)
+{
+ z_stream *z = &k->z; /* zlib state structure */
+
+ /* Initialize zlib? */
+ if (k->zlib_init == ZLIB_UNINIT) {
+ z->zalloc = (alloc_func)Z_NULL;
+ z->zfree = (free_func)Z_NULL;
+ z->opaque = 0;
+ z->next_in = NULL;
+ z->avail_in = 0;
+
+ if (strcmp(zlibVersion(), "1.2.0.4") >= 0) {
+ /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */
+ if (inflateInit2(z, MAX_WBITS+32) != Z_OK) {
+ return process_zlib_error(conn, z);
+ }
+ k->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
+
+ } else {
+ /* we must parse the gzip header ourselves */
+ if (inflateInit2(z, -MAX_WBITS) != Z_OK) {
+ return process_zlib_error(conn, z);
+ }
+ k->zlib_init = ZLIB_INIT; /* Initial call state */
+ }
+ }
+
+ if (k->zlib_init == ZLIB_INIT_GZIP) {
+ /* Let zlib handle the gzip decompression entirely */
+ z->next_in = (Bytef *)k->str;
+ z->avail_in = (uInt)nread;
+ /* Now uncompress the data */
+ return inflate_stream(conn, k);
+ }
+
+#ifndef OLD_ZLIB_SUPPORT
+ /* Support for old zlib versions is compiled away and we are running with
+ an old version, so return an error. */
+ return exit_zlib(z, &k->zlib_init, CURLE_FUNCTION_NOT_FOUND);
+
+#else
+ /* This next mess is to get around the potential case where there isn't
+ * enough data passed in to skip over the gzip header. If that happens, we
+ * malloc a block and copy what we have then wait for the next call. If
+ * there still isn't enough (this is definitely a worst-case scenario), we
+ * make the block bigger, copy the next part in and keep waiting.
+ *
+ * This is only required with zlib versions < 1.2.0.4 as newer versions
+ * can handle the gzip header themselves.
+ */
+
+ switch (k->zlib_init) {
+ /* Skip over gzip header? */
+ case ZLIB_INIT:
+ {
+ /* Initial call state */
+ ssize_t hlen;
+
+ switch (check_gzip_header((unsigned char *)k->str, nread, &hlen)) {
+ case GZIP_OK:
+ z->next_in = (Bytef *)k->str + hlen;
+ z->avail_in = (uInt)(nread - hlen);
+ k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
+ break;
+
+ case GZIP_UNDERFLOW:
+ /* We need more data so we can find the end of the gzip header. It's
+ * possible that the memory block we malloc here will never be freed if
+ * the transfer abruptly aborts after this point. Since it's unlikely
+ * that circumstances will be right for this code path to be followed in
+ * the first place, and it's even more unlikely for a transfer to fail
+ * immediately afterwards, it should seldom be a problem.
+ */
+ z->avail_in = (uInt)nread;
+ z->next_in = malloc(z->avail_in);
+ if (z->next_in == NULL) {
+ return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
+ }
+ memcpy(z->next_in, k->str, z->avail_in);
+ k->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */
+ /* We don't have any data to inflate yet */
+ return CURLE_OK;
+
+ case GZIP_BAD:
+ default:
+ return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
+ }
+
+ }
+ break;
+
+ case ZLIB_GZIP_HEADER:
+ {
+ /* Need more gzip header data state */
+ ssize_t hlen;
+ unsigned char *oldblock = z->next_in;
+
+ z->avail_in += nread;
+ z->next_in = realloc(z->next_in, z->avail_in);
+ if (z->next_in == NULL) {
+ free(oldblock);
+ return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
+ }
+ /* Append the new block of data to the previous one */
+ memcpy(z->next_in + z->avail_in - nread, k->str, nread);
+
+ switch (check_gzip_header(z->next_in, z->avail_in, &hlen)) {
+ case GZIP_OK:
+ /* This is the zlib stream data */
+ free(z->next_in);
+ /* Don't point into the malloced block since we just freed it */
+ z->next_in = (Bytef *)k->str + hlen + nread - z->avail_in;
+ z->avail_in = (uInt)(z->avail_in - hlen);
+ k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
+ break;
+
+ case GZIP_UNDERFLOW:
+ /* We still don't have any data to inflate! */
+ return CURLE_OK;
+
+ case GZIP_BAD:
+ default:
+ free(z->next_in);
+ return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
+ }
+
+ }
+ break;
+
+ case ZLIB_GZIP_INFLATING:
+ default:
+ /* Inflating stream state */
+ z->next_in = (Bytef *)k->str;
+ z->avail_in = (uInt)nread;
+ break;
+ }
+
+ if (z->avail_in == 0) {
+ /* We don't have any data to inflate; wait until next time */
+ return CURLE_OK;
+ }
+
+ /* We've parsed the header, now uncompress the data */
+ return inflate_stream(conn, k);
+#endif
+}
+#endif /* HAVE_LIBZ */
diff --git a/Utilities/cmcurl/content_encoding.h b/Utilities/cmcurl/content_encoding.h
new file mode 100644
index 000000000..b31669be4
--- /dev/null
+++ b/Utilities/cmcurl/content_encoding.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#include "setup.h"
+
+/*
+ * Comma-separated list all supported Content-Encodings ('identity' is implied)
+ */
+#ifdef HAVE_LIBZ
+#define ALL_CONTENT_ENCODINGS "deflate, gzip"
+#else
+#define ALL_CONTENT_ENCODINGS "identity"
+#endif
+
+CURLcode Curl_unencode_deflate_write(struct connectdata *conn,
+ struct Curl_transfer_keeper *k,
+ ssize_t nread);
+
+CURLcode
+Curl_unencode_gzip_write(struct connectdata *conn,
+ struct Curl_transfer_keeper *k,
+ ssize_t nread);
diff --git a/Utilities/cmcurl/cookie.c b/Utilities/cmcurl/cookie.c
new file mode 100644
index 000000000..2856ad882
--- /dev/null
+++ b/Utilities/cmcurl/cookie.c
@@ -0,0 +1,1017 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $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"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+
+#include <stdlib.h>
+#include <string.h>
+
+#define _MPRINTF_REPLACE /* without this on windows OS we get undefined reference to snprintf */
+#include <curl/mprintf.h>
+
+#include "urldata.h"
+#include "cookie.h"
+#include "strequal.h"
+#include "strtok.h"
+#include "sendf.h"
+#include "memory.h"
+#include "share.h"
+#include "strtoofft.h"
+
+/* The last #include file should be: */
+#ifdef CURLDEBUG
+#include "memdebug.h"
+#endif
+
+#define my_isspace(x) ((x == ' ') || (x == '\t'))
+
+static void freecookie(struct Cookie *co)
+{
+ if(co->expirestr)
+ free(co->expirestr);
+ if(co->domain)
+ free(co->domain);
+ if(co->path)
+ free(co->path);
+ if(co->name)
+ free(co->name);
+ if(co->value)
+ free(co->value);
+ if(co->maxage)
+ free(co->maxage);
+ if(co->version)
+ free(co->version);
+
+ free(co);
+}
+
+static bool tailmatch(const char *little, const char *bigone)
+{
+ size_t littlelen = strlen(little);
+ size_t biglen = strlen(bigone);
+
+ if(littlelen > biglen)
+ return FALSE;
+
+ return (bool)strequal(little, bigone+biglen-littlelen);
+}
+
+/*
+ * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
+ */
+void Curl_cookie_loadfiles(struct SessionHandle *data)
+{
+ struct curl_slist *list = data->change.cookielist;
+ if(list) {
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+ while(list) {
+ data->cookies = Curl_cookie_init(data,
+ list->data,
+ data->cookies,
+ data->set.cookiesession);
+ list = list->next;
+ }
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+ curl_slist_free_all(data->change.cookielist); /* clean up list */
+ data->change.cookielist = NULL; /* don't do this again! */
+ }
+}
+
+/****************************************************************************
+ *
+ * Curl_cookie_add()
+ *
+ * Add a single cookie line to the cookie keeping object.
+ *
+ ***************************************************************************/
+
+struct Cookie *
+Curl_cookie_add(struct SessionHandle *data,
+ /* The 'data' pointer here may be NULL at times, and thus
+ must only be used very carefully for things that can deal
+ with data being NULL. Such as infof() and similar */
+
+ struct CookieInfo *c,
+ bool httpheader, /* TRUE if HTTP header-style line */
+ char *lineptr, /* first character of the line */
+ char *domain, /* default domain */
+ char *path) /* full path used when this cookie is set,
+ used to get default path for the cookie
+ unless set */
+{
+ struct Cookie *clist;
+ char *what;
+ char name[MAX_NAME];
+ char *ptr;
+ char *semiptr;
+ struct Cookie *co;
+ struct Cookie *lastc=NULL;
+ time_t now = time(NULL);
+ bool replace_old = FALSE;
+ bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
+
+ /* First, alloc and init a new struct for it */
+ co = (struct Cookie *)calloc(sizeof(struct Cookie), 1);
+ if(!co)
+ return NULL; /* bail out if we're this low on memory */
+
+ if(httpheader) {
+ /* This line was read off a HTTP-header */
+ char *sep;
+
+ what = malloc(MAX_COOKIE_LINE);
+ if(!what) {
+ free(co);
+ return NULL;
+ }
+
+ semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
+
+ while(*lineptr && my_isspace(*lineptr))
+ lineptr++;
+
+ 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 */
+
+ char *whatptr;
+
+ /* Strip off trailing whitespace from the 'what' */
+ size_t len=strlen(what);
+ while(len && my_isspace(what[len-1])) {
+ what[len-1]=0;
+ len--;
+ }
+
+ /* Skip leading whitespace from the 'what' */
+ whatptr=what;
+ while(my_isspace(*whatptr)) {
+ whatptr++;
+ }
+
+ if(strequal("path", name)) {
+ co->path=strdup(whatptr);
+ if(!co->path) {
+ badcookie = TRUE; /* out of memory bad */
+ break;
+ }
+ }
+ else if(strequal("domain", name)) {
+ /* note that this name may or may not have a preceeding dot, but
+ we don't care about that, we treat the names the same anyway */
+
+ const char *domptr=whatptr;
+ int dotcount=1;
+
+ /* Count the dots, we need to make sure that there are enough
+ of them. */
+
+ if('.' == whatptr[0])
+ /* don't count the initial dot, assume it */
+ domptr++;
+
+ do {
+ domptr = strchr(domptr, '.');
+ if(domptr) {
+ domptr++;
+ dotcount++;
+ }
+ } while(domptr);
+
+ /* The original Netscape cookie spec defined that this domain name
+ MUST have three dots (or two if one of the seven holy TLDs),
+ but it seems that these kinds of cookies are in use "out there"
+ so we cannot be that strict. I've therefore lowered the check
+ to not allow less than two dots. */
+
+ if(dotcount < 2) {
+ /* Received and skipped a cookie with a domain using too few
+ dots. */
+ badcookie=TRUE; /* mark this as a bad cookie */
+ infof(data, "skipped cookie with illegal dotcount domain: %s\n",
+ whatptr);
+ }
+ else {
+ /* Now, we make sure that our host is within the given domain,
+ or the given domain is not valid and thus cannot be set. */
+
+ if('.' == whatptr[0])
+ whatptr++; /* ignore preceeding dot */
+
+ if(!domain || tailmatch(whatptr, domain)) {
+ const char *tailptr=whatptr;
+ if(tailptr[0] == '.')
+ tailptr++;
+ co->domain=strdup(tailptr); /* don't prefix w/dots
+ internally */
+ if(!co->domain) {
+ badcookie = TRUE;
+ break;
+ }
+ co->tailmatch=TRUE; /* we always do that if the domain name was
+ given */
+ }
+ else {
+ /* we did not get a tailmatch and then the attempted set domain
+ is not a domain to which the current host belongs. Mark as
+ bad. */
+ badcookie=TRUE;
+ infof(data, "skipped cookie with bad tailmatch domain: %s\n",
+ whatptr);
+ }
+ }
+ }
+ else if(strequal("version", name)) {
+ co->version=strdup(whatptr);
+ if(!co->version) {
+ badcookie = TRUE;
+ break;
+ }
+ }
+ 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(whatptr);
+ if(!co->maxage) {
+ badcookie = TRUE;
+ break;
+ }
+ co->expires =
+ atoi((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0]) + (long)now;
+ }
+ else if(strequal("expires", name)) {
+ co->expirestr=strdup(whatptr);
+ if(!co->expirestr) {
+ badcookie = TRUE;
+ break;
+ }
+ co->expires = curl_getdate(what, &now);
+ }
+ else if(!co->name) {
+ co->name = strdup(name);
+ co->value = strdup(whatptr);
+ if(!co->name || !co->value) {
+ badcookie = TRUE;
+ break;
+ }
+ }
+ /*
+ else this is the second (or more) name we don't know
+ about! */
+ }
+ else {
+ /* this is an "illegal" <what>=<this> pair */
+ }
+ }
+ 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 && my_isspace(*ptr))
+ ptr++;
+ semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
+
+ if(!semiptr && *ptr)
+ /* There are no more semicolons, but there's a final name=value pair
+ coming up */
+ semiptr=strchr(ptr, '\0');
+ } while(semiptr);
+
+ if(!badcookie && !co->domain) {
+ if(domain) {
+ /* no domain was given in the header line, set the default */
+ co->domain=strdup(domain);
+ if(!co->domain)
+ badcookie = TRUE;
+ }
+ }
+
+ if(!badcookie && !co->path && path) {
+ /* no path was given in the header line, set the default */
+ char *endslash = strrchr(path, '/');
+ if(endslash) {
+ size_t pathlen = endslash-path+1; /* include the ending slash */
+ co->path=malloc(pathlen+1); /* one extra for the zero byte */
+ if(co->path) {
+ memcpy(co->path, path, pathlen);
+ co->path[pathlen]=0; /* zero terminate */
+ }
+ else
+ badcookie = TRUE;
+ }
+ }
+
+ free(what);
+
+ if(badcookie || !co->name) {
+ /* we didn't get a cookie name or a bad one,
+ this is an illegal line, bail out */
+ freecookie(co);
+ return NULL;
+ }
+
+ }
+ else {
+ /* This line is NOT a HTTP header style line, we do offer support for
+ reading the odd netscape cookies-file format here */
+ char *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); /* tokenize it on the TAB */
+
+ /* Here's a quick check to eliminate normal HTTP-headers from this */
+ if(!firstptr || strchr(firstptr, ':')) {
+ free(co);
+ return NULL;
+ }
+
+ /* Now loop through the fields and init the struct we already have
+ allocated */
+ for(ptr=firstptr, fields=0; ptr && !badcookie;
+ ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
+ switch(fields) {
+ case 0:
+ if(ptr[0]=='.') /* skip preceeding dots */
+ ptr++;
+ co->domain = strdup(ptr);
+ if(!co->domain)
+ badcookie = TRUE;
+ break;
+ case 1:
+ /* This field got its explanation on the 23rd of May 2001 by
+ Andrés García:
+
+ flag: A TRUE/FALSE value indicating if all machines within a given
+ domain can access the variable. This value is set automatically by
+ the browser, depending on the value you set for the domain.
+
+ As far as I can see, it is set to true when the cookie says
+ .domain.com and to false when the domain is complete www.domain.com
+ */
+ co->tailmatch=(bool)strequal(ptr, "TRUE"); /* 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);
+ if(!co->path)
+ badcookie = TRUE;
+ break;
+ }
+ /* this doesn't look like a path, make one up! */
+ co->path = strdup("/");
+ if(!co->path)
+ badcookie = TRUE;
+ fields++; /* add a field and fall down to secure */
+ /* FALLTHROUGH */
+ case 3:
+ co->secure = (bool)strequal(ptr, "TRUE");
+ break;
+ case 4:
+ co->expires = curlx_strtoofft(ptr, NULL, 10);
+ break;
+ case 5:
+ co->name = strdup(ptr);
+ if(!co->name)
+ badcookie = TRUE;
+ break;
+ case 6:
+ co->value = strdup(ptr);
+ if(!co->value)
+ badcookie = TRUE;
+ break;
+ }
+ }
+ if(6 == fields) {
+ /* we got a cookie with blank contents, fix it */
+ co->value = strdup("");
+ if(!co->value)
+ badcookie = TRUE;
+ else
+ fields++;
+ }
+
+ if(!badcookie && (7 != fields))
+ /* we did not find the sufficient number of fields */
+ badcookie = TRUE;
+
+ if(badcookie) {
+ freecookie(co);
+ return NULL;
+ }
+
+ }
+
+ if(!c->running && /* read from a file */
+ c->newsession && /* clean session cookies */
+ !co->expires) { /* this is a session cookie since it doesn't expire! */
+ freecookie(co);
+ return NULL;
+ }
+
+ co->livecookie = c->running;
+
+ /* now, we have parsed the incoming line, we must now check if this
+ superceeds an already existing cookie, which it may if the previous have
+ the same domain and path as this */
+
+ clist = c->cookies;
+ replace_old = FALSE;
+ while(clist) {
+ if(strequal(clist->name, co->name)) {
+ /* the names are identical */
+
+ if(clist->domain && co->domain) {
+ if(strequal(clist->domain, co->domain))
+ /* The domains are identical */
+ replace_old=TRUE;
+ }
+ else if(!clist->domain && !co->domain)
+ replace_old = TRUE;
+
+ if(replace_old) {
+ /* the domains were identical */
+
+ if(clist->path && co->path) {
+ if(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! */
+ freecookie(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(c->running)
+ /* Only show this when NOT reading the cookies from a file */
+ infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, expire %d\n",
+ replace_old?"Replaced":"Added", co->name, co->value,
+ co->domain, co->path, co->expires);
+
+ if(!replace_old) {
+ /* then make the last item point on this new one */
+ if(lastc)
+ lastc->next = co;
+ else
+ c->cookies = co;
+ }
+
+ c->numcookies++; /* one more cookie in the jar */
+ return co;
+}
+
+/*****************************************************************************
+ *
+ * Curl_cookie_init()
+ *
+ * Inits a cookie struct to read data from a local file. This is always
+ * called before any cookies are set. File may be NULL.
+ *
+ * If 'newsession' is TRUE, discard all "session cookies" on read from file.
+ *
+ ****************************************************************************/
+struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
+ char *file,
+ struct CookieInfo *inc,
+ bool newsession)
+{
+ struct CookieInfo *c;
+ FILE *fp;
+ bool fromfile=TRUE;
+
+ if(NULL == inc) {
+ /* we didn't get a struct, create one */
+ c = (struct CookieInfo *)calloc(1, sizeof(struct CookieInfo));
+ if(!c)
+ return NULL; /* failed to get memory */
+ c->filename = strdup(file?file:"none"); /* copy the name just in case */
+ }
+ else {
+ /* we got an already existing one, use that */
+ c = inc;
+ }
+ c->running = FALSE; /* this is not running, this is init */
+
+ if(file && strequal(file, "-")) {
+ fp = stdin;
+ fromfile=FALSE;
+ }
+ else if(file && !*file) {
+ /* points to a "" string */
+ fp = NULL;
+ }
+ else
+ fp = file?fopen(file, "r"):NULL;
+
+ c->newsession = newsession; /* new session? */
+
+ if(fp) {
+ char *lineptr;
+ bool headerline;
+
+ char *line = (char *)malloc(MAX_COOKIE_LINE);
+ if(line) {
+ while(fgets(line, MAX_COOKIE_LINE, fp)) {
+ if(checkprefix("Set-Cookie:", line)) {
+ /* This is a cookie line, get it! */
+ lineptr=&line[11];
+ headerline=TRUE;
+ }
+ else {
+ lineptr=line;
+ headerline=FALSE;
+ }
+ while(*lineptr && my_isspace(*lineptr))
+ lineptr++;
+
+ Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
+ }
+ free(line); /* free the line buffer */
+ }
+ if(fromfile)
+ fclose(fp);
+ }
+
+ c->running = TRUE; /* now, we're running */
+
+ return c;
+}
+
+/*****************************************************************************
+ *
+ * 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);
+ 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 */
+ if(!co->domain ||
+ (co->tailmatch && tailmatch(co->domain, host)) ||
+ (!co->tailmatch && strequal(host, co->domain)) ) {
+ /* the right part of the host matches the domain stuff in the
+ cookie data */
+
+ /* now check the left part of the path with the cookies path
+ requirement */
+ if(!co->path ||
+ /* not using checkprefix() because matching should be
+ case-sensitive */
+ !strncmp(co->path, path, strlen(co->path)) ) {
+
+ /* and now, we know this is a match and we should create an
+ entry for the return-linked-list */
+
+ newco = (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;
+ }
+ else {
+ /* failure, clear up the allocated chain and return NULL */
+ while(mainco) {
+ co = mainco->next;
+ free(mainco);
+ mainco = co;
+ }
+
+ return NULL;
+ }
+ }
+ }
+ }
+ co = co->next;
+ }
+
+ return mainco; /* return the new list */
+}
+
+/*****************************************************************************
+ *
+ * Curl_cookie_clearall()
+ *
+ * Clear all existing cookies and reset the counter.
+ *
+ ****************************************************************************/
+void Curl_cookie_clearall(struct CookieInfo *cookies)
+{
+ if(cookies) {
+ Curl_cookie_freelist(cookies->cookies);
+ cookies->cookies = NULL;
+ cookies->numcookies = 0;
+ }
+}
+
+/*****************************************************************************
+ *
+ * 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_clearsess()
+ *
+ * Free all session cookies in the cookies list.
+ *
+ ****************************************************************************/
+void Curl_cookie_clearsess(struct CookieInfo *cookies)
+{
+ struct Cookie *first, *curr, *next, *prev = NULL;
+
+ if(!cookies->cookies)
+ return;
+
+ first = curr = prev = cookies->cookies;
+
+ for(; curr; curr = next) {
+ next = curr->next;
+ if(!curr->expires) {
+ if(first == curr)
+ first = next;
+
+ if(prev == curr)
+ prev = next;
+ else
+ prev->next = next;
+
+ free(curr);
+ cookies->numcookies--;
+ }
+ else
+ prev = curr;
+ }
+
+ cookies->cookies = first;
+}
+
+
+/*****************************************************************************
+ *
+ * Curl_cookie_cleanup()
+ *
+ * Free a "cookie object" previous created with cookie_init().
+ *
+ ****************************************************************************/
+void Curl_cookie_cleanup(struct CookieInfo *c)
+{
+ struct Cookie *co;
+ struct Cookie *next;
+ if(c) {
+ if(c->filename)
+ free(c->filename);
+ co = c->cookies;
+
+ while(co) {
+ next = co->next;
+ freecookie(co);
+ co = next;
+ }
+ free(c); /* free the base struct as well */
+ }
+}
+
+/* get_netscape_format()
+ *
+ * Formats a string for Netscape output file, w/o a newline at the end.
+ *
+ * Function returns a char * to a formatted line. Has to be free()d
+*/
+static char *get_netscape_format(const struct Cookie *co)
+{
+ return aprintf(
+ "%s%s\t" /* domain */
+ "%s\t" /* tailmatch */
+ "%s\t" /* path */
+ "%s\t" /* secure */
+ "%" FORMAT_OFF_T "\t" /* expires */
+ "%s\t" /* name */
+ "%s", /* value */
+ /* Make sure all domains are prefixed with a dot if they allow
+ tailmatching. This is Mozilla-style. */
+ (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
+ co->domain?co->domain:"unknown",
+ co->tailmatch?"TRUE":"FALSE",
+ co->path?co->path:"/",
+ co->secure?"TRUE":"FALSE",
+ co->expires,
+ co->name,
+ co->value?co->value:"");
+}
+
+/*
+ * 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) {
+ char *format_ptr;
+
+ fputs("# Netscape HTTP Cookie File\n"
+ "# http://curlm.haxx.se/rfc/cookie_spec.html\n"
+ "# This file was generated by libcurl! Edit at your own risk.\n\n",
+ out);
+ co = c->cookies;
+
+ while(co) {
+ format_ptr = get_netscape_format(co);
+ if (format_ptr == NULL) {
+ fprintf(out, "#\n# Fatal libcurl error\n");
+ return 1;
+ }
+ fprintf(out, "%s\n", format_ptr);
+ free(format_ptr);
+ co=co->next;
+ }
+ }
+
+ if(!use_stdout)
+ fclose(out);
+
+ return 0;
+}
+
+struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
+{
+ struct curl_slist *list = NULL;
+ struct curl_slist *beg;
+ struct Cookie *c;
+ char *line;
+
+ if ((data->cookies == NULL) ||
+ (data->cookies->numcookies == 0))
+ return NULL;
+
+ c = data->cookies->cookies;
+
+ beg = list;
+ while (c) {
+ /* fill the list with _all_ the cookies we know */
+ line = get_netscape_format(c);
+ if (line == NULL) {
+ /* get_netscape_format returns null only if we run out of memory */
+
+ curl_slist_free_all(beg); /* free some memory */
+ return NULL;
+ }
+ list = curl_slist_append(list, line);
+ free(line);
+ c = c->next;
+ }
+
+ return list;
+}
+
+#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */
diff --git a/Utilities/cmcurl/cookie.h b/Utilities/cmcurl/cookie.h
new file mode 100644
index 000000000..57c3acbdd
--- /dev/null
+++ b/Utilities/cmcurl/cookie.h
@@ -0,0 +1,107 @@
+#ifndef __COOKIE_H
+#define __COOKIE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include <stdio.h>
+#if defined(WIN32)
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#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> */
+ curl_off_t expires; /* expires = <this> */
+ char *expirestr; /* the plain text version */
+ bool tailmatch; /* weather we do tail-matchning of the domain name */
+
+ /* 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" */
+ bool newsession; /* new session, discard session cookies on load */
+};
+
+/* This is the maximum line length we accept for a cookie line. RFC 2109
+ section 6.3 says:
+
+ "at least 4096 bytes per cookie (as measured by the size of the characters
+ that comprise the cookie non-terminal in the syntax description of the
+ Set-Cookie header)"
+
+*/
+#define MAX_COOKIE_LINE 5000
+#define MAX_COOKIE_LINE_TXT "4999"
+
+/* This is the maximum length of a cookie name we deal with: */
+#define MAX_NAME 1024
+#define MAX_NAME_TXT "1023"
+
+struct SessionHandle;
+/*
+ * Add a cookie to the internal list of cookies. The domain and path arguments
+ * are only used if the header boolean is TRUE.
+ */
+
+struct Cookie *Curl_cookie_add(struct SessionHandle *data,
+ struct CookieInfo *, bool header, char *line,
+ char *domain, char *path);
+
+struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
+ char *, struct CookieInfo *, bool);
+struct Cookie *Curl_cookie_getlist(struct CookieInfo *, char *, char *, bool);
+void Curl_cookie_freelist(struct Cookie *);
+void Curl_cookie_clearall(struct CookieInfo *cookies);
+void Curl_cookie_clearsess(struct CookieInfo *cookies);
+void Curl_cookie_cleanup(struct CookieInfo *);
+int Curl_cookie_output(struct CookieInfo *, char *);
+
+#if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_COOKIES)
+#define Curl_cookie_list(x) NULL
+#define Curl_cookie_loadfiles(x) do { } while (0)
+#else
+struct curl_slist *Curl_cookie_list(struct SessionHandle *data);
+void Curl_cookie_loadfiles(struct SessionHandle *data);
+#endif
+
+#endif
diff --git a/Utilities/cmcurl/curl/curl.h b/Utilities/cmcurl/curl/curl.h
new file mode 100644
index 000000000..157c634a4
--- /dev/null
+++ b/Utilities/cmcurl/curl/curl.h
@@ -0,0 +1,1644 @@
+#ifndef __CURL_CURL_H
+#define __CURL_CURL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/* If you have problems, all libcurl docs and details are found here:
+ http://curl.haxx.se/libcurl/
+*/
+
+#include "curlver.h" /* the libcurl version defines */
+
+#include <stdio.h>
+#include <limits.h>
+
+/* The include stuff here below is mainly for time_t! */
+#ifdef vms
+# include <types.h>
+# include <time.h>
+#else
+# include <sys/types.h>
+# include <time.h>
+#endif /* defined (vms) */
+
+typedef void CURL;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Decorate exportable functions for Win32 DLL linking.
+ * This avoids using a .def file for building libcurl.dll.
+ */
+#if (defined(WIN32) || defined(_WIN32)) && !defined(CURL_STATICLIB)
+#if defined(BUILDING_LIBCURL)
+#define CURL_EXTERN __declspec(dllexport)
+#else
+#define CURL_EXTERN __declspec(dllimport)
+#endif
+#else
+
+#ifdef CURL_HIDDEN_SYMBOLS
+/*
+ * This definition is used to make external definitions visibile in the
+ * shared library when symbols are hidden by default. It makes no
+ * difference when compiling applications whether this is set or not,
+ * only when compiling the library.
+ */
+#define CURL_EXTERN CURL_EXTERN_SYMBOL
+#else
+#define CURL_EXTERN
+#endif
+#endif
+
+/*
+ * We want the typedef curl_off_t setup for large file support on all
+ * platforms. We also provide a CURL_FORMAT_OFF_T define to use in *printf
+ * format strings when outputting a variable of type curl_off_t.
+ *
+ * Note: "pocc -Ze" is MSVC compatibily mode and this sets _MSC_VER!
+ */
+
+#if (defined(_MSC_VER) && !defined(__POCC__)) || (defined(__LCC__) && defined(WIN32))
+/* MSVC */
+#ifdef _WIN32_WCE
+ typedef long curl_off_t;
+#define CURL_FORMAT_OFF_T "%ld"
+#else
+ typedef signed __int64 curl_off_t;
+#define CURL_FORMAT_OFF_T "%I64d"
+#endif
+#else /* (_MSC_VER && !__POCC__) || (__LCC__ && WIN32) */
+#if (defined(__GNUC__) && defined(WIN32)) || defined(__WATCOMC__)
+/* gcc on windows or Watcom */
+ typedef long long curl_off_t;
+#define CURL_FORMAT_OFF_T "%I64d"
+#else /* GCC or Watcom on Windows */
+
+/* "normal" POSIX approach, do note that this does not necessarily mean that
+ the type is >32 bits, see the SIZEOF_CURL_OFF_T define for that! */
+ typedef off_t curl_off_t;
+
+/* Check a range of defines to detect large file support. On Linux it seems
+ none of these are set by default, so if you don't explicitly switches on
+ large file support, this define will be made for "small file" support. */
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 0 /* to prevent warnings in the check below */
+#define UNDEF_FILE_OFFSET_BITS
+#endif
+#ifndef FILESIZEBITS
+#define FILESIZEBITS 0 /* to prevent warnings in the check below */
+#define UNDEF_FILESIZEBITS
+#endif
+
+#if defined(_LARGE_FILES) || (_FILE_OFFSET_BITS > 32) || (FILESIZEBITS > 32) \
+ || defined(_LARGEFILE_SOURCE) || defined(_LARGEFILE64_SOURCE)
+ /* For now, we assume at least one of these to be set for large files to
+ work! */
+#define CURL_FORMAT_OFF_T "%lld"
+#else /* LARGE_FILE support */
+#define CURL_FORMAT_OFF_T "%ld"
+#endif
+#endif /* GCC or Watcom on Windows */
+#endif /* (_MSC_VER && !__POCC__) || (__LCC__ && WIN32) */
+
+#ifdef UNDEF_FILE_OFFSET_BITS
+/* this was defined above for our checks, undefine it again */
+#undef _FILE_OFFSET_BITS
+#endif
+
+#ifdef UNDEF_FILESIZEBITS
+/* this was defined above for our checks, undefine it again */
+#undef FILESIZEBITS
+#endif
+
+#if defined(_WIN32) && !defined(WIN32)
+/* Chris Lewis mentioned that he doesn't get WIN32 defined, only _WIN32 so we
+ make this adjustment to catch this. */
+#define WIN32 1
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__GNUC__) && \
+ !defined(__CYGWIN__) || defined(__MINGW32__)
+#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H))
+/* The check above prevents the winsock2 inclusion if winsock.h already was
+ included, since they can't co-exist without problems */
+#include <winsock2.h>
+#endif
+#else
+
+/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish
+ libc5-based Linux systems. Only include it on system that are known to
+ require it! */
+#if defined(_AIX) || defined(NETWARE) || defined(__NetBSD__) || defined(__minix)
+#include <sys/select.h>
+#endif
+
+#ifndef _WIN32_WCE
+#include <sys/socket.h>
+#endif
+#ifndef __WATCOMC__
+#include <sys/time.h>
+#endif
+#include <sys/types.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef curl_socket_typedef
+/* socket typedef */
+#ifdef WIN32
+typedef SOCKET curl_socket_t;
+#define CURL_SOCKET_BAD INVALID_SOCKET
+#else
+typedef int curl_socket_t;
+#define CURL_SOCKET_BAD -1
+#endif
+#define curl_socket_typedef
+#endif /* curl_socket_typedef */
+
+struct curl_httppost {
+ struct curl_httppost *next; /* next entry in the list */
+ char *name; /* pointer to allocated name */
+ long namelength; /* length of name length */
+ char *contents; /* pointer to allocated data contents */
+ long contentslength; /* length of contents field */
+ char *buffer; /* pointer to allocated buffer contents */
+ long bufferlength; /* length of buffer field */
+ char *contenttype; /* Content-Type */
+ struct curl_slist* contentheader; /* list of extra headers for this form */
+ struct curl_httppost *more; /* if one field name has more than one
+ file, this link should link to following
+ files */
+ long flags; /* as defined below */
+#define HTTPPOST_FILENAME (1<<0) /* specified content is a file name */
+#define HTTPPOST_READFILE (1<<1) /* specified content is a file name */
+#define HTTPPOST_PTRNAME (1<<2) /* name is only stored pointer
+ do not free in formfree */
+#define HTTPPOST_PTRCONTENTS (1<<3) /* contents is only stored pointer
+ do not free in formfree */
+#define HTTPPOST_BUFFER (1<<4) /* upload file from buffer */
+#define HTTPPOST_PTRBUFFER (1<<5) /* upload file from pointer contents */
+
+ char *showfilename; /* The file name to show. If not set, the
+ actual file name will be used (if this
+ is a file part) */
+};
+
+typedef int (*curl_progress_callback)(void *clientp,
+ double dltotal,
+ double dlnow,
+ double ultotal,
+ double ulnow);
+
+ /* Tests have proven that 20K is a very bad buffer size for uploads on
+ Windows, while 16K for some odd reason performed a lot better. */
+#define CURL_MAX_WRITE_SIZE 16384
+
+typedef size_t (*curl_write_callback)(char *buffer,
+ size_t size,
+ size_t nitems,
+ void *outstream);
+
+/* This is a return code for the read callback that, when returned, will
+ signal libcurl to immediately abort the current transfer. */
+#define CURL_READFUNC_ABORT 0x10000000
+typedef size_t (*curl_read_callback)(char *buffer,
+ size_t size,
+ size_t nitems,
+ void *instream);
+
+typedef enum {
+ CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */
+ CURLSOCKTYPE_LAST /* never use */
+} curlsocktype;
+
+typedef int (*curl_sockopt_callback)(void *clientp,
+ curl_socket_t curlfd,
+ curlsocktype purpose);
+
+#ifndef CURL_NO_OLDIES
+ /* not used since 7.10.8, will be removed in a future release */
+typedef int (*curl_passwd_callback)(void *clientp,
+ const char *prompt,
+ char *buffer,
+ int buflen);
+#endif
+
+typedef enum {
+ CURLIOE_OK, /* I/O operation successful */
+ CURLIOE_UNKNOWNCMD, /* command was unknown to callback */
+ CURLIOE_FAILRESTART, /* failed to restart the read */
+ CURLIOE_LAST /* never use */
+} curlioerr;
+
+typedef enum {
+ CURLIOCMD_NOP, /* no operation */
+ CURLIOCMD_RESTARTREAD, /* restart the read stream from start */
+ CURLIOCMD_LAST /* never use */
+} curliocmd;
+
+typedef curlioerr (*curl_ioctl_callback)(CURL *handle,
+ int cmd,
+ void *clientp);
+
+/*
+ * The following typedef's are signatures of malloc, free, realloc, strdup and
+ * calloc respectively. Function pointers of these types can be passed to the
+ * curl_global_init_mem() function to set user defined memory management
+ * callback routines.
+ */
+typedef void *(*curl_malloc_callback)(size_t size);
+typedef void (*curl_free_callback)(void *ptr);
+typedef void *(*curl_realloc_callback)(void *ptr, size_t size);
+typedef char *(*curl_strdup_callback)(const char *str);
+typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size);
+
+/* the kind of data that is passed to information_callback*/
+typedef enum {
+ CURLINFO_TEXT = 0,
+ CURLINFO_HEADER_IN, /* 1 */
+ CURLINFO_HEADER_OUT, /* 2 */
+ CURLINFO_DATA_IN, /* 3 */
+ CURLINFO_DATA_OUT, /* 4 */
+ CURLINFO_SSL_DATA_IN, /* 5 */
+ CURLINFO_SSL_DATA_OUT, /* 6 */
+ CURLINFO_END
+} curl_infotype;
+
+typedef int (*curl_debug_callback)
+ (CURL *handle, /* the handle/transfer this concerns */
+ curl_infotype type, /* what kind of data */
+ char *data, /* points to the data */
+ size_t size, /* size of the data pointed to */
+ void *userptr); /* whatever the user please */
+
+/* All possible error codes from all sorts of curl functions. Future versions
+ may return other values, stay prepared.
+
+ Always add new return codes last. Never *EVER* remove any. The return
+ codes must remain the same!
+ */
+
+typedef enum {
+ CURLE_OK = 0,
+ CURLE_UNSUPPORTED_PROTOCOL, /* 1 */
+ CURLE_FAILED_INIT, /* 2 */
+ CURLE_URL_MALFORMAT, /* 3 */
+ CURLE_URL_MALFORMAT_USER, /* 4 - NOT USED */
+ CURLE_COULDNT_RESOLVE_PROXY, /* 5 */
+ CURLE_COULDNT_RESOLVE_HOST, /* 6 */
+ CURLE_COULDNT_CONNECT, /* 7 */
+ CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */
+ CURLE_FTP_ACCESS_DENIED, /* 9 a service was denied by the FTP server
+ due to lack of access - when login fails
+ this is not returned. */
+ CURLE_FTP_USER_PASSWORD_INCORRECT, /* 10 - NOT USED */
+ CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */
+ CURLE_FTP_WEIRD_USER_REPLY, /* 12 */
+ CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */
+ CURLE_FTP_WEIRD_227_FORMAT, /* 14 */
+ CURLE_FTP_CANT_GET_HOST, /* 15 */
+ CURLE_FTP_CANT_RECONNECT, /* 16 */
+ CURLE_FTP_COULDNT_SET_BINARY, /* 17 */
+ CURLE_PARTIAL_FILE, /* 18 */
+ CURLE_FTP_COULDNT_RETR_FILE, /* 19 */
+ CURLE_FTP_WRITE_ERROR, /* 20 */
+ CURLE_FTP_QUOTE_ERROR, /* 21 */
+ CURLE_HTTP_RETURNED_ERROR, /* 22 */
+ CURLE_WRITE_ERROR, /* 23 */
+ CURLE_MALFORMAT_USER, /* 24 - NOT USED */
+ CURLE_FTP_COULDNT_STOR_FILE, /* 25 - failed FTP upload */
+ CURLE_READ_ERROR, /* 26 - could open/read from file */
+ CURLE_OUT_OF_MEMORY, /* 27 */
+ /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error
+ instead of a memory allocation error if CURL_DOES_CONVERSIONS
+ is defined
+ */
+ CURLE_OPERATION_TIMEOUTED, /* 28 - the timeout time was reached */
+ CURLE_FTP_COULDNT_SET_ASCII, /* 29 - TYPE A failed */
+ CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */
+ CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */
+ CURLE_FTP_COULDNT_GET_SIZE, /* 32 - the SIZE command failed */
+ CURLE_HTTP_RANGE_ERROR, /* 33 - RANGE "command" didn't work */
+ CURLE_HTTP_POST_ERROR, /* 34 */
+ CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */
+ CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */
+ CURLE_FILE_COULDNT_READ_FILE, /* 37 */
+ CURLE_LDAP_CANNOT_BIND, /* 38 */
+ CURLE_LDAP_SEARCH_FAILED, /* 39 */
+ CURLE_LIBRARY_NOT_FOUND, /* 40 */
+ CURLE_FUNCTION_NOT_FOUND, /* 41 */
+ CURLE_ABORTED_BY_CALLBACK, /* 42 */
+ CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */
+ CURLE_BAD_CALLING_ORDER, /* 44 - NOT USED */
+ CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */
+ CURLE_BAD_PASSWORD_ENTERED, /* 46 - NOT USED */
+ CURLE_TOO_MANY_REDIRECTS , /* 47 - catch endless re-direct loops */
+ CURLE_UNKNOWN_TELNET_OPTION, /* 48 - User specified an unknown option */
+ CURLE_TELNET_OPTION_SYNTAX , /* 49 - Malformed telnet option */
+ CURLE_OBSOLETE, /* 50 - NOT USED */
+ CURLE_SSL_PEER_CERTIFICATE, /* 51 - peer's certificate wasn't ok */
+ CURLE_GOT_NOTHING, /* 52 - when this is a specific error */
+ CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */
+ CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as
+ default */
+ CURLE_SEND_ERROR, /* 55 - failed sending network data */
+ CURLE_RECV_ERROR, /* 56 - failure in receiving network data */
+ CURLE_SHARE_IN_USE, /* 57 - share is in use */
+ CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */
+ CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */
+ CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */
+ CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized transfer encoding */
+ CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */
+ CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */
+ CURLE_FTP_SSL_FAILED, /* 64 - Requested FTP SSL level failed */
+ CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind
+ that failed */
+ CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */
+ CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not
+ accepted and we failed to login */
+ CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */
+ CURLE_TFTP_PERM, /* 69 - permission problem on server */
+ CURLE_TFTP_DISKFULL, /* 70 - out of disk space on server */
+ CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */
+ CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */
+ CURLE_TFTP_EXISTS, /* 73 - File already exists */
+ CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */
+ CURLE_CONV_FAILED, /* 75 - conversion failed */
+ CURLE_CONV_REQD, /* 76 - caller must register conversion
+ callbacks using curl_easy_setopt options
+ CURLOPT_CONV_FROM_NETWORK_FUNCTION,
+ CURLOPT_CONV_TO_NETWORK_FUNCTION, and
+ CURLOPT_CONV_FROM_UTF8_FUNCTION */
+ CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing
+ or wrong format */
+ CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */
+ CURLE_SSH, /* 79 - error from the SSH layer, somewhat
+ generic so the error message will be of
+ interest when this has happened */
+
+ CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL
+ connection */
+ CURL_LAST /* never use! */
+} CURLcode;
+
+/* This prototype applies to all conversion callbacks */
+typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length);
+
+typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */
+ void *ssl_ctx, /* actually an
+ OpenSSL SSL_CTX */
+ void *userptr);
+
+/* Make a spelling correction for the operation timed-out define */
+#define CURLE_OPERATION_TIMEDOUT CURLE_OPERATION_TIMEOUTED
+
+#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
+ the obsolete stuff removed! */
+/* backwards compatibility with older names */
+#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR
+#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED
+#endif
+
+typedef enum {
+ CURLPROXY_HTTP = 0,
+ CURLPROXY_SOCKS4 = 4,
+ CURLPROXY_SOCKS5 = 5
+} curl_proxytype;
+
+#define CURLAUTH_NONE 0 /* nothing */
+#define CURLAUTH_BASIC (1<<0) /* Basic (default) */
+#define CURLAUTH_DIGEST (1<<1) /* Digest */
+#define CURLAUTH_GSSNEGOTIATE (1<<2) /* GSS-Negotiate */
+#define CURLAUTH_NTLM (1<<3) /* NTLM */
+#define CURLAUTH_ANY ~0 /* all types set */
+#define CURLAUTH_ANYSAFE (~CURLAUTH_BASIC)
+
+#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */
+#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */
+#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */
+#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */
+#define CURLSSH_AUTH_HOST (1<<2) /* host key files */
+#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */
+#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY
+
+#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
+ the obsolete stuff removed! */
+/* this was the error code 50 in 7.7.3 and a few earlier versions, this
+ is no longer used by libcurl but is instead #defined here only to not
+ make programs break */
+#define CURLE_ALREADY_COMPLETE 99999
+
+/* These are just to make older programs not break: */
+#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE
+#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME
+#endif
+
+#define CURL_ERROR_SIZE 256
+
+/* parameter for the CURLOPT_FTP_SSL option */
+typedef enum {
+ CURLFTPSSL_NONE, /* do not attempt to use SSL */
+ CURLFTPSSL_TRY, /* try using SSL, proceed anyway otherwise */
+ CURLFTPSSL_CONTROL, /* SSL for the control connection or fail */
+ CURLFTPSSL_ALL, /* SSL for all communication or fail */
+ CURLFTPSSL_LAST /* not an option, never use */
+} curl_ftpssl;
+
+/* parameter for the CURLOPT_FTPSSLAUTH option */
+typedef enum {
+ CURLFTPAUTH_DEFAULT, /* let libcurl decide */
+ CURLFTPAUTH_SSL, /* use "AUTH SSL" */
+ CURLFTPAUTH_TLS, /* use "AUTH TLS" */
+ CURLFTPAUTH_LAST /* not an option, never use */
+} curl_ftpauth;
+
+/* parameter for the CURLOPT_FTP_FILEMETHOD option */
+typedef enum {
+ CURLFTPMETHOD_DEFAULT, /* let libcurl pick */
+ CURLFTPMETHOD_MULTICWD, /* single CWD operation for each path part */
+ CURLFTPMETHOD_NOCWD, /* no CWD at all */
+ CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */
+ CURLFTPMETHOD_LAST /* not an option, never use */
+} curl_ftpmethod;
+
+/* long may be 32 or 64 bits, but we should never depend on anything else
+ but 32 */
+#define CURLOPTTYPE_LONG 0
+#define CURLOPTTYPE_OBJECTPOINT 10000
+#define CURLOPTTYPE_FUNCTIONPOINT 20000
+#define CURLOPTTYPE_OFF_T 30000
+
+/* name is uppercase CURLOPT_<name>,
+ type is one of the defined CURLOPTTYPE_<type>
+ number is unique identifier */
+#ifdef CINIT
+#undef CINIT
+#endif
+/*
+ * Figure out if we can use the ## operator, which is supported by ISO/ANSI C
+ * and C++. Some compilers support it without setting __STDC__ or __cplusplus
+ * so we need to carefully check for them too. We don't use configure-checks
+ * for these since we want these headers to remain generic and working for all
+ * platforms.
+ */
+#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \
+ defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \
+ defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__)
+ /* This compiler is believed to have an ISO compatible preprocessor */
+#define CURL_ISOCPP
+#else
+ /* This compiler is believed NOT to have an ISO compatible preprocessor */
+#undef CURL_ISOCPP
+#endif
+
+#ifdef CURL_ISOCPP
+#define CINIT(name,type,number) CURLOPT_ ## name = CURLOPTTYPE_ ## type + number
+#else
+/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */
+#define LONG CURLOPTTYPE_LONG
+#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT
+#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT
+#define OFF_T CURLOPTTYPE_OFF_T
+#define CINIT(name,type,number) CURLOPT_/**/name = type + number
+#endif
+
+/*
+ * This macro-mania below setups the CURLOPT_[what] enum, to be used with
+ * curl_easy_setopt(). The first argument in the CINIT() macro is the [what]
+ * word.
+ */
+
+typedef enum {
+ /* This is the FILE * or void * the regular output should be written to. */
+ CINIT(FILE, OBJECTPOINT, 1),
+
+ /* The full URL to get/put */
+ CINIT(URL, OBJECTPOINT, 2),
+
+ /* Port number to connect to, if other than default. */
+ CINIT(PORT, LONG, 3),
+
+ /* Name of proxy to use. */
+ CINIT(PROXY, OBJECTPOINT, 4),
+
+ /* "name:password" to use when fetching. */
+ CINIT(USERPWD, OBJECTPOINT, 5),
+
+ /* "name:password" to use with proxy. */
+ CINIT(PROXYUSERPWD, OBJECTPOINT, 6),
+
+ /* Range to get, specified as an ASCII string. */
+ CINIT(RANGE, OBJECTPOINT, 7),
+
+ /* not used */
+
+ /* Specified file stream to upload from (use as input): */
+ CINIT(INFILE, OBJECTPOINT, 9),
+
+ /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE
+ * bytes big. If this is not used, error messages go to stderr instead: */
+ CINIT(ERRORBUFFER, OBJECTPOINT, 10),
+
+ /* Function that will be called to store the output (instead of fwrite). The
+ * parameters will use fwrite() syntax, make sure to follow them. */
+ CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11),
+
+ /* Function that will be called to read the input (instead of fread). The
+ * parameters will use fread() syntax, make sure to follow them. */
+ CINIT(READFUNCTION, FUNCTIONPOINT, 12),
+
+ /* Time-out the read operation after this amount of seconds */
+ CINIT(TIMEOUT, LONG, 13),
+
+ /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about
+ * how large the file being sent really is. That allows better error
+ * checking and better verifies that the upload was succcessful. -1 means
+ * unknown size.
+ *
+ * For large file support, there is also a _LARGE version of the key
+ * which takes an off_t type, allowing platforms with larger off_t
+ * sizes to handle larger files. See below for INFILESIZE_LARGE.
+ */
+ CINIT(INFILESIZE, LONG, 14),
+
+ /* POST input fields. */
+ CINIT(POSTFIELDS, OBJECTPOINT, 15),
+
+ /* Set the referer page (needed by some CGIs) */
+ CINIT(REFERER, OBJECTPOINT, 16),
+
+ /* Set the FTP PORT string (interface name, named or numerical IP address)
+ Use i.e '-' to use default address. */
+ CINIT(FTPPORT, OBJECTPOINT, 17),
+
+ /* Set the User-Agent string (examined by some CGIs) */
+ CINIT(USERAGENT, OBJECTPOINT, 18),
+
+ /* If the download receives less than "low speed limit" bytes/second
+ * during "low speed time" seconds, the operations is aborted.
+ * You could i.e if you have a pretty high speed connection, abort if
+ * it is less than 2000 bytes/sec during 20 seconds.
+ */
+
+ /* Set the "low speed limit" */
+ CINIT(LOW_SPEED_LIMIT, LONG , 19),
+
+ /* Set the "low speed time" */
+ CINIT(LOW_SPEED_TIME, LONG, 20),
+
+ /* Set the continuation offset.
+ *
+ * Note there is also a _LARGE version of this key which uses
+ * off_t types, allowing for large file offsets on platforms which
+ * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE.
+ */
+ CINIT(RESUME_FROM, LONG, 21),
+
+ /* Set cookie in request: */
+ CINIT(COOKIE, OBJECTPOINT, 22),
+
+ /* This points to a linked list of headers, struct curl_slist kind */
+ CINIT(HTTPHEADER, OBJECTPOINT, 23),
+
+ /* This points to a linked list of post entries, struct HttpPost */
+ CINIT(HTTPPOST, OBJECTPOINT, 24),
+
+ /* name of the file keeping your private SSL-certificate */
+ CINIT(SSLCERT, OBJECTPOINT, 25),
+
+ /* password for the SSL-private key, keep this for compatibility */
+ CINIT(SSLCERTPASSWD, OBJECTPOINT, 26),
+ /* password for the SSL private key */
+ CINIT(SSLKEYPASSWD, OBJECTPOINT, 26),
+
+ /* send TYPE parameter? */
+ CINIT(CRLF, LONG, 27),
+
+ /* send linked-list of QUOTE commands */
+ CINIT(QUOTE, OBJECTPOINT, 28),
+
+ /* send FILE * or void * to store headers to, if you use a callback it
+ is simply passed to the callback unmodified */
+ CINIT(WRITEHEADER, OBJECTPOINT, 29),
+
+ /* point to a file to read the initial cookies from, also enables
+ "cookie awareness" */
+ CINIT(COOKIEFILE, OBJECTPOINT, 31),
+
+ /* What version to specifly try to use.
+ See CURL_SSLVERSION defines below. */
+ CINIT(SSLVERSION, LONG, 32),
+
+ /* What kind of HTTP time condition to use, see defines */
+ CINIT(TIMECONDITION, LONG, 33),
+
+ /* Time to use with the above condition. Specified in number of seconds
+ since 1 Jan 1970 */
+ CINIT(TIMEVALUE, LONG, 34),
+
+ /* 35 = OBSOLETE */
+
+ /* Custom request, for customizing the get command like
+ HTTP: DELETE, TRACE and others
+ FTP: to use a different list command
+ */
+ CINIT(CUSTOMREQUEST, OBJECTPOINT, 36),
+
+ /* HTTP request, for odd commands like DELETE, TRACE and others */
+ CINIT(STDERR, OBJECTPOINT, 37),
+
+ /* 38 is not used */
+
+ /* send linked-list of post-transfer QUOTE commands */
+ CINIT(POSTQUOTE, OBJECTPOINT, 39),
+
+ /* Pass a pointer to string of the output using full variable-replacement
+ as described elsewhere. */
+ CINIT(WRITEINFO, OBJECTPOINT, 40),
+
+ CINIT(VERBOSE, LONG, 41), /* talk a lot */
+ CINIT(HEADER, LONG, 42), /* throw the header out too */
+ CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */
+ CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */
+ CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 300 */
+ CINIT(UPLOAD, LONG, 46), /* this is an upload */
+ CINIT(POST, LONG, 47), /* HTTP POST method */
+ CINIT(FTPLISTONLY, LONG, 48), /* Use NLST when listing ftp dir */
+
+ CINIT(FTPAPPEND, LONG, 50), /* Append instead of overwrite on upload! */
+
+ /* Specify whether to read the user+password from the .netrc or the URL.
+ * This must be one of the CURL_NETRC_* enums below. */
+ CINIT(NETRC, LONG, 51),
+
+ CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */
+
+ CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */
+ CINIT(PUT, LONG, 54), /* HTTP PUT */
+
+ /* 55 = OBSOLETE */
+
+ /* Function that will be called instead of the internal progress display
+ * function. This function should be defined as the curl_progress_callback
+ * prototype defines. */
+ CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56),
+
+ /* Data passed to the progress callback */
+ CINIT(PROGRESSDATA, OBJECTPOINT, 57),
+
+ /* We want the referer field set automatically when following locations */
+ CINIT(AUTOREFERER, LONG, 58),
+
+ /* Port of the proxy, can be set in the proxy string as well with:
+ "[host]:[port]" */
+ CINIT(PROXYPORT, LONG, 59),
+
+ /* size of the POST input data, if strlen() is not good to use */
+ CINIT(POSTFIELDSIZE, LONG, 60),
+
+ /* tunnel non-http operations through a HTTP proxy */
+ CINIT(HTTPPROXYTUNNEL, LONG, 61),
+
+ /* Set the interface string to use as outgoing network interface */
+ CINIT(INTERFACE, OBJECTPOINT, 62),
+
+ /* Set the krb4 security level, this also enables krb4 awareness. This is a
+ * string, 'clear', 'safe', 'confidential' or 'private'. If the string is
+ * set but doesn't match one of these, 'private' will be used. */
+ CINIT(KRB4LEVEL, OBJECTPOINT, 63),
+
+ /* Set if we should verify the peer in ssl handshake, set 1 to verify. */
+ CINIT(SSL_VERIFYPEER, LONG, 64),
+
+ /* The CApath or CAfile used to validate the peer certificate
+ this option is used only if SSL_VERIFYPEER is true */
+ CINIT(CAINFO, OBJECTPOINT, 65),
+
+ /* 66 = OBSOLETE */
+ /* 67 = OBSOLETE */
+
+ /* Maximum number of http redirects to follow */
+ CINIT(MAXREDIRS, LONG, 68),
+
+ /* Pass a long set to 1 to get the date of the requested document (if
+ possible)! Pass a zero to shut it off. */
+ CINIT(FILETIME, LONG, 69),
+
+ /* This points to a linked list of telnet options */
+ CINIT(TELNETOPTIONS, OBJECTPOINT, 70),
+
+ /* Max amount of cached alive connections */
+ CINIT(MAXCONNECTS, LONG, 71),
+
+ /* What policy to use when closing connections when the cache is filled
+ up */
+ CINIT(CLOSEPOLICY, LONG, 72),
+
+ /* 73 = OBSOLETE */
+
+ /* Set to explicitly use a new connection for the upcoming transfer.
+ Do not use this unless you're absolutely sure of this, as it makes the
+ operation slower and is less friendly for the network. */
+ CINIT(FRESH_CONNECT, LONG, 74),
+
+ /* Set to explicitly forbid the upcoming transfer's connection to be re-used
+ when done. Do not use this unless you're absolutely sure of this, as it
+ makes the operation slower and is less friendly for the network. */
+ CINIT(FORBID_REUSE, LONG, 75),
+
+ /* Set to a file name that contains random data for libcurl to use to
+ seed the random engine when doing SSL connects. */
+ CINIT(RANDOM_FILE, OBJECTPOINT, 76),
+
+ /* Set to the Entropy Gathering Daemon socket pathname */
+ CINIT(EGDSOCKET, OBJECTPOINT, 77),
+
+ /* Time-out connect operations after this amount of seconds, if connects
+ are OK within this time, then fine... This only aborts the connect
+ phase. [Only works on unix-style/SIGALRM operating systems] */
+ CINIT(CONNECTTIMEOUT, LONG, 78),
+
+ /* Function that will be called to store headers (instead of fwrite). The
+ * parameters will use fwrite() syntax, make sure to follow them. */
+ CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79),
+
+ /* Set this to force the HTTP request to get back to GET. Only really usable
+ if POST, PUT or a custom request have been used first.
+ */
+ CINIT(HTTPGET, LONG, 80),
+
+ /* Set if we should verify the Common name from the peer certificate in ssl
+ * handshake, set 1 to check existence, 2 to ensure that it matches the
+ * provided hostname. */
+ CINIT(SSL_VERIFYHOST, LONG, 81),
+
+ /* Specify which file name to write all known cookies in after completed
+ operation. Set file name to "-" (dash) to make it go to stdout. */
+ CINIT(COOKIEJAR, OBJECTPOINT, 82),
+
+ /* Specify which SSL ciphers to use */
+ CINIT(SSL_CIPHER_LIST, OBJECTPOINT, 83),
+
+ /* Specify which HTTP version to use! This must be set to one of the
+ CURL_HTTP_VERSION* enums set below. */
+ CINIT(HTTP_VERSION, LONG, 84),
+
+ /* Specificly switch on or off the FTP engine's use of the EPSV command. By
+ default, that one will always be attempted before the more traditional
+ PASV command. */
+ CINIT(FTP_USE_EPSV, LONG, 85),
+
+ /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */
+ CINIT(SSLCERTTYPE, OBJECTPOINT, 86),
+
+ /* name of the file keeping your private SSL-key */
+ CINIT(SSLKEY, OBJECTPOINT, 87),
+
+ /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */
+ CINIT(SSLKEYTYPE, OBJECTPOINT, 88),
+
+ /* crypto engine for the SSL-sub system */
+ CINIT(SSLENGINE, OBJECTPOINT, 89),
+
+ /* set the crypto engine for the SSL-sub system as default
+ the param has no meaning...
+ */
+ CINIT(SSLENGINE_DEFAULT, LONG, 90),
+
+ /* Non-zero value means to use the global dns cache */
+ CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* To becomeO BSOLETE soon */
+
+ /* DNS cache timeout */
+ CINIT(DNS_CACHE_TIMEOUT, LONG, 92),
+
+ /* send linked-list of pre-transfer QUOTE commands (Wesley Laxton)*/
+ CINIT(PREQUOTE, OBJECTPOINT, 93),
+
+ /* set the debug function */
+ CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94),
+
+ /* set the data for the debug function */
+ CINIT(DEBUGDATA, OBJECTPOINT, 95),
+
+ /* mark this as start of a cookie session */
+ CINIT(COOKIESESSION, LONG, 96),
+
+ /* The CApath directory used to validate the peer certificate
+ this option is used only if SSL_VERIFYPEER is true */
+ CINIT(CAPATH, OBJECTPOINT, 97),
+
+ /* Instruct libcurl to use a smaller receive buffer */
+ CINIT(BUFFERSIZE, LONG, 98),
+
+ /* Instruct libcurl to not use any signal/alarm handlers, even when using
+ timeouts. This option is useful for multi-threaded applications.
+ See libcurl-the-guide for more background information. */
+ CINIT(NOSIGNAL, LONG, 99),
+
+ /* Provide a CURLShare for mutexing non-ts data */
+ CINIT(SHARE, OBJECTPOINT, 100),
+
+ /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default),
+ CURLPROXY_SOCKS4 and CURLPROXY_SOCKS5. */
+ CINIT(PROXYTYPE, LONG, 101),
+
+ /* Set the Accept-Encoding string. Use this to tell a server you would like
+ the response to be compressed. */
+ CINIT(ENCODING, OBJECTPOINT, 102),
+
+ /* Set pointer to private data */
+ CINIT(PRIVATE, OBJECTPOINT, 103),
+
+ /* Set aliases for HTTP 200 in the HTTP Response header */
+ CINIT(HTTP200ALIASES, OBJECTPOINT, 104),
+
+ /* Continue to send authentication (user+password) when following locations,
+ even when hostname changed. This can potentionally send off the name
+ and password to whatever host the server decides. */
+ CINIT(UNRESTRICTED_AUTH, LONG, 105),
+
+ /* Specificly switch on or off the FTP engine's use of the EPRT command ( it
+ also disables the LPRT attempt). By default, those ones will always be
+ attempted before the good old traditional PORT command. */
+ CINIT(FTP_USE_EPRT, LONG, 106),
+
+ /* Set this to a bitmask value to enable the particular authentications
+ methods you like. Use this in combination with CURLOPT_USERPWD.
+ Note that setting multiple bits may cause extra network round-trips. */
+ CINIT(HTTPAUTH, LONG, 107),
+
+ /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx
+ in second argument. The function must be matching the
+ curl_ssl_ctx_callback proto. */
+ CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108),
+
+ /* Set the userdata for the ssl context callback function's third
+ argument */
+ CINIT(SSL_CTX_DATA, OBJECTPOINT, 109),
+
+ /* FTP Option that causes missing dirs to be created on the remote server */
+ CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110),
+
+ /* Set this to a bitmask value to enable the particular authentications
+ methods you like. Use this in combination with CURLOPT_PROXYUSERPWD.
+ Note that setting multiple bits may cause extra network round-trips. */
+ CINIT(PROXYAUTH, LONG, 111),
+
+ /* FTP option that changes the timeout, in seconds, associated with
+ getting a response. This is different from transfer timeout time and
+ essentially places a demand on the FTP server to acknowledge commands
+ in a timely manner. */
+ CINIT(FTP_RESPONSE_TIMEOUT, LONG , 112),
+
+ /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to
+ tell libcurl to resolve names to those IP versions only. This only has
+ affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */
+ CINIT(IPRESOLVE, LONG, 113),
+
+ /* Set this option to limit the size of a file that will be downloaded from
+ an HTTP or FTP server.
+
+ Note there is also _LARGE version which adds large file support for
+ platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */
+ CINIT(MAXFILESIZE, LONG, 114),
+
+ /* See the comment for INFILESIZE above, but in short, specifies
+ * the size of the file being uploaded. -1 means unknown.
+ */
+ CINIT(INFILESIZE_LARGE, OFF_T, 115),
+
+ /* Sets the continuation offset. There is also a LONG version of this;
+ * look above for RESUME_FROM.
+ */
+ CINIT(RESUME_FROM_LARGE, OFF_T, 116),
+
+ /* Sets the maximum size of data that will be downloaded from
+ * an HTTP or FTP server. See MAXFILESIZE above for the LONG version.
+ */
+ CINIT(MAXFILESIZE_LARGE, OFF_T, 117),
+
+ /* Set this option to the file name of your .netrc file you want libcurl
+ to parse (using the CURLOPT_NETRC option). If not set, libcurl will do
+ a poor attempt to find the user's home directory and check for a .netrc
+ file in there. */
+ CINIT(NETRC_FILE, OBJECTPOINT, 118),
+
+ /* Enable SSL/TLS for FTP, pick one of:
+ CURLFTPSSL_TRY - try using SSL, proceed anyway otherwise
+ CURLFTPSSL_CONTROL - SSL for the control connection or fail
+ CURLFTPSSL_ALL - SSL for all communication or fail
+ */
+ CINIT(FTP_SSL, LONG, 119),
+
+ /* The _LARGE version of the standard POSTFIELDSIZE option */
+ CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120),
+
+ /* Enable/disable the TCP Nagle algorithm */
+ CINIT(TCP_NODELAY, LONG, 121),
+
+ /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+ /* 123 OBSOLETE. Gone in 7.16.0 */
+ /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+ /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+ /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+ /* 127 OBSOLETE. Gone in 7.16.0 */
+ /* 128 OBSOLETE. Gone in 7.16.0 */
+
+ /* When FTP over SSL/TLS is selected (with CURLOPT_FTP_SSL), this option
+ can be used to change libcurl's default action which is to first try
+ "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK
+ response has been received.
+
+ Available parameters are:
+ CURLFTPAUTH_DEFAULT - let libcurl decide
+ CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS
+ CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL
+ */
+ CINIT(FTPSSLAUTH, LONG, 129),
+
+ CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130),
+ CINIT(IOCTLDATA, OBJECTPOINT, 131),
+
+ /* 132 OBSOLETE. Gone in 7.16.0 */
+ /* 133 OBSOLETE. Gone in 7.16.0 */
+
+ /* zero terminated string for pass on to the FTP server when asked for
+ "account" info */
+ CINIT(FTP_ACCOUNT, OBJECTPOINT, 134),
+
+ /* feed cookies into cookie engine */
+ CINIT(COOKIELIST, OBJECTPOINT, 135),
+
+ /* ignore Content-Length */
+ CINIT(IGNORE_CONTENT_LENGTH, LONG, 136),
+
+ /* Set to non-zero to skip the IP address received in a 227 PASV FTP server
+ response. Typically used for FTP-SSL purposes but is not restricted to
+ that. libcurl will then instead use the same IP address it used for the
+ control connection. */
+ CINIT(FTP_SKIP_PASV_IP, LONG, 137),
+
+ /* Select "file method" to use when doing FTP, see the curl_ftpmethod
+ above. */
+ CINIT(FTP_FILEMETHOD, LONG, 138),
+
+ /* Local port number to bind the socket to */
+ CINIT(LOCALPORT, LONG, 139),
+
+ /* Number of ports to try, including the first one set with LOCALPORT.
+ Thus, setting it to 1 will make no additional attempts but the first.
+ */
+ CINIT(LOCALPORTRANGE, LONG, 140),
+
+ /* no transfer, set up connection and let application use the socket by
+ extracting it with CURLINFO_LASTSOCKET */
+ CINIT(CONNECT_ONLY, LONG, 141),
+
+ /* Function that will be called to convert from the
+ network encoding (instead of using the iconv calls in libcurl) */
+ CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142),
+
+ /* Function that will be called to convert to the
+ network encoding (instead of using the iconv calls in libcurl) */
+ CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143),
+
+ /* Function that will be called to convert from UTF8
+ (instead of using the iconv calls in libcurl)
+ Note that this is used only for SSL certificate processing */
+ CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144),
+
+ /* if the connection proceeds too quickly then need to slow it down */
+ /* limit-rate: maximum number of bytes per second to send or receive */
+ CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145),
+ CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146),
+
+ /* Pointer to command string to send if USER/PASS fails. */
+ CINIT(FTP_ALTERNATIVE_TO_USER, OBJECTPOINT, 147),
+
+ /* callback function for setting socket options */
+ CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148),
+ CINIT(SOCKOPTDATA, OBJECTPOINT, 149),
+
+ /* set to 0 to disable session ID re-use for this transfer, default is
+ enabled (== 1) */
+ CINIT(SSL_SESSIONID_CACHE, LONG, 150),
+
+ /* allowed SSH authentication methods */
+ CINIT(SSH_AUTH_TYPES, LONG, 151),
+
+ /* Used by scp/sftp to do public/private key authentication */
+ CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152),
+ CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153),
+
+ /* Send CCC (Clear Command Channel) after authentication */
+ CINIT(FTP_SSL_CCC, LONG, 154),
+
+ CURLOPT_LASTENTRY /* the last unused */
+} CURLoption;
+
+ /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host
+ name resolves addresses using more than one IP protocol version, this
+ option might be handy to force libcurl to use a specific IP version. */
+#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP
+ versions that your system allows */
+#define CURL_IPRESOLVE_V4 1 /* resolve to ipv4 addresses */
+#define CURL_IPRESOLVE_V6 2 /* resolve to ipv6 addresses */
+
+ /* three convenient "aliases" that follow the name scheme better */
+#define CURLOPT_WRITEDATA CURLOPT_FILE
+#define CURLOPT_READDATA CURLOPT_INFILE
+#define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER
+
+#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
+ the obsolete stuff removed! */
+#else
+/* This is set if CURL_NO_OLDIES is defined at compile-time */
+#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */
+#endif
+
+
+ /* These enums are for use with the CURLOPT_HTTP_VERSION option. */
+enum {
+ CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd
+ like the library to choose the best possible
+ for us! */
+ CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */
+ CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */
+
+ CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
+};
+
+ /* These enums are for use with the CURLOPT_NETRC option. */
+enum CURL_NETRC_OPTION {
+ CURL_NETRC_IGNORED, /* The .netrc will never be read.
+ * This is the default. */
+ CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred
+ * to one in the .netrc. */
+ CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored.
+ * Unless one is set programmatically, the .netrc
+ * will be queried. */
+ CURL_NETRC_LAST
+};
+
+enum {
+ CURL_SSLVERSION_DEFAULT,
+ CURL_SSLVERSION_TLSv1,
+ CURL_SSLVERSION_SSLv2,
+ CURL_SSLVERSION_SSLv3,
+
+ CURL_SSLVERSION_LAST /* never use, keep last */
+};
+
+
+typedef enum {
+ CURL_TIMECOND_NONE,
+
+ CURL_TIMECOND_IFMODSINCE,
+ CURL_TIMECOND_IFUNMODSINCE,
+ CURL_TIMECOND_LASTMOD,
+
+ CURL_TIMECOND_LAST
+} curl_TimeCond;
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined __BEOS__ || defined __HAIKU__
+#include <support/SupportDefs.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* curl_strequal() and curl_strnequal() are subject for removal in a future
+ libcurl, see lib/README.curlx for details */
+CURL_EXTERN int (curl_strequal)(const char *s1, const char *s2);
+CURL_EXTERN int (curl_strnequal)(const char *s1, const char *s2, size_t n);
+
+/* name is uppercase CURLFORM_<name> */
+#ifdef CFINIT
+#undef CFINIT
+#endif
+
+#ifdef CURL_ISOCPP
+#define CFINIT(name) CURLFORM_ ## name
+#else
+/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */
+#define CFINIT(name) CURLFORM_/**/name
+#endif
+
+typedef enum {
+ CFINIT(NOTHING), /********* the first one is unused ************/
+
+ /* */
+ CFINIT(COPYNAME),
+ CFINIT(PTRNAME),
+ CFINIT(NAMELENGTH),
+ CFINIT(COPYCONTENTS),
+ CFINIT(PTRCONTENTS),
+ CFINIT(CONTENTSLENGTH),
+ CFINIT(FILECONTENT),
+ CFINIT(ARRAY),
+ CFINIT(OBSOLETE),
+ CFINIT(FILE),
+
+ CFINIT(BUFFER),
+ CFINIT(BUFFERPTR),
+ CFINIT(BUFFERLENGTH),
+
+ CFINIT(CONTENTTYPE),
+ CFINIT(CONTENTHEADER),
+ CFINIT(FILENAME),
+ CFINIT(END),
+ CFINIT(OBSOLETE2),
+
+ CURLFORM_LASTENTRY /* the last unusued */
+} CURLformoption;
+
+#undef CFINIT /* done */
+
+/* structure to be used as parameter for CURLFORM_ARRAY */
+struct curl_forms {
+ CURLformoption option;
+ const char *value;
+};
+
+/* use this for multipart formpost building */
+/* Returns code for curl_formadd()
+ *
+ * Returns:
+ * CURL_FORMADD_OK on success
+ * CURL_FORMADD_MEMORY if the FormInfo allocation fails
+ * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form
+ * CURL_FORMADD_NULL if a null pointer was given for a char
+ * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed
+ * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
+ * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error)
+ * CURL_FORMADD_MEMORY if a HttpPost struct cannot be allocated
+ * CURL_FORMADD_MEMORY if some allocation for string copying failed.
+ * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array
+ *
+ ***************************************************************************/
+typedef enum {
+ CURL_FORMADD_OK, /* first, no error */
+
+ CURL_FORMADD_MEMORY,
+ CURL_FORMADD_OPTION_TWICE,
+ CURL_FORMADD_NULL,
+ CURL_FORMADD_UNKNOWN_OPTION,
+ CURL_FORMADD_INCOMPLETE,
+ CURL_FORMADD_ILLEGAL_ARRAY,
+ CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */
+
+ CURL_FORMADD_LAST /* last */
+} CURLFORMcode;
+
+/*
+ * NAME curl_formadd()
+ *
+ * DESCRIPTION
+ *
+ * Pretty advanved function for building multi-part formposts. Each invoke
+ * adds one part that together construct a full post. Then use
+ * CURLOPT_HTTPPOST to send it off to libcurl.
+ */
+CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost,
+ struct curl_httppost **last_post,
+ ...);
+
+/*
+ * callback function for curl_formget()
+ * The void *arg pointer will be the one passed as second argument to curl_formget().
+ * The character buffer passed to it must not be freed.
+ * Should return the buffer length passed to it as the argument "len" on success.
+ */
+typedef size_t (*curl_formget_callback)(void *arg, const char *buf, size_t len);
+
+/*
+ * NAME curl_formget()
+ *
+ * DESCRIPTION
+ *
+ * Serialize a curl_httppost struct built with curl_formadd().
+ * Accepts a void pointer as second argument which will be passed to
+ * the curl_formget_callback function.
+ * Returns 0 on success.
+ */
+CURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg,
+ curl_formget_callback append);
+/*
+ * NAME curl_formfree()
+ *
+ * DESCRIPTION
+ *
+ * Free a multipart formpost previously built with curl_formadd().
+ */
+CURL_EXTERN void curl_formfree(struct curl_httppost *form);
+
+/*
+ * NAME curl_getenv()
+ *
+ * DESCRIPTION
+ *
+ * Returns a malloc()'ed string that MUST be curl_free()ed after usage is
+ * complete. DEPRECATED - see lib/README.curlx
+ */
+CURL_EXTERN char *curl_getenv(const char *variable);
+
+/*
+ * NAME curl_version()
+ *
+ * DESCRIPTION
+ *
+ * Returns a static ascii string of the libcurl version.
+ */
+CURL_EXTERN char *curl_version(void);
+
+/*
+ * NAME curl_easy_escape()
+ *
+ * DESCRIPTION
+ *
+ * Escapes URL strings (converts all letters consider illegal in URLs to their
+ * %XX versions). This function returns a new allocated string or NULL if an
+ * error occurred.
+ */
+CURL_EXTERN char *curl_easy_escape(CURL *handle,
+ const char *string,
+ int length);
+
+/* the previous version: */
+CURL_EXTERN char *curl_escape(const char *string,
+ int length);
+
+
+/*
+ * NAME curl_easy_unescape()
+ *
+ * DESCRIPTION
+ *
+ * Unescapes URL encoding in strings (converts all %XX codes to their 8bit
+ * versions). This function returns a new allocated string or NULL if an error
+ * occurred.
+ * Conversion Note: On non-ASCII platforms the ASCII %XX codes are
+ * converted into the host encoding.
+ */
+CURL_EXTERN char *curl_easy_unescape(CURL *handle,
+ const char *string,
+ int length,
+ int *outlength);
+
+/* the previous version */
+CURL_EXTERN char *curl_unescape(const char *string,
+ int length);
+
+/*
+ * NAME curl_free()
+ *
+ * DESCRIPTION
+ *
+ * Provided for de-allocation in the same translation unit that did the
+ * allocation. Added in libcurl 7.10
+ */
+CURL_EXTERN void curl_free(void *p);
+
+/*
+ * NAME curl_global_init()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_init() should be invoked exactly once for each application that
+ * uses libcurl
+ */
+CURL_EXTERN CURLcode curl_global_init(long flags);
+
+/*
+ * NAME curl_global_init_mem()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_init() or curl_global_init_mem() should be invoked exactly once
+ * for each application that uses libcurl. This function can be used to
+ * initialize libcurl and set user defined memory management callback
+ * functions. Users can implement memory management routines to check for
+ * memory leaks, check for mis-use of the curl library etc. User registered
+ * callback routines with be invoked by this library instead of the system
+ * memory management routines like malloc, free etc.
+ */
+CURL_EXTERN CURLcode curl_global_init_mem(long flags,
+ curl_malloc_callback m,
+ curl_free_callback f,
+ curl_realloc_callback r,
+ curl_strdup_callback s,
+ curl_calloc_callback c);
+
+/*
+ * NAME curl_global_cleanup()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_cleanup() should be invoked exactly once for each application
+ * that uses libcurl
+ */
+CURL_EXTERN void curl_global_cleanup(void);
+
+/* linked-list structure for the CURLOPT_QUOTE option (and other) */
+struct curl_slist {
+ char *data;
+ struct curl_slist *next;
+};
+
+/*
+ * NAME curl_slist_append()
+ *
+ * DESCRIPTION
+ *
+ * Appends a string to a linked list. If no list exists, it will be created
+ * first. Returns the new list, after appending.
+ */
+CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *,
+ const char *);
+
+/*
+ * NAME curl_slist_free_all()
+ *
+ * DESCRIPTION
+ *
+ * free a previously built curl_slist.
+ */
+CURL_EXTERN void curl_slist_free_all(struct curl_slist *);
+
+/*
+ * NAME curl_getdate()
+ *
+ * DESCRIPTION
+ *
+ * Returns the time, in seconds since 1 Jan 1970 of the time string given in
+ * the first argument. The time argument in the second parameter is unused
+ * and should be set to NULL.
+ */
+CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused);
+
+#define CURLINFO_STRING 0x100000
+#define CURLINFO_LONG 0x200000
+#define CURLINFO_DOUBLE 0x300000
+#define CURLINFO_SLIST 0x400000
+#define CURLINFO_MASK 0x0fffff
+#define CURLINFO_TYPEMASK 0xf00000
+
+typedef enum {
+ CURLINFO_NONE, /* first, never use this */
+ CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1,
+ CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2,
+ CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3,
+ CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4,
+ CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5,
+ CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6,
+ CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7,
+ CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8,
+ CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9,
+ CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10,
+ CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11,
+ CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12,
+ CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13,
+ CURLINFO_FILETIME = CURLINFO_LONG + 14,
+ CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15,
+ CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16,
+ CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17,
+ CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18,
+ CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19,
+ CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20,
+ CURLINFO_PRIVATE = CURLINFO_STRING + 21,
+ CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22,
+ CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23,
+ CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24,
+ CURLINFO_OS_ERRNO = CURLINFO_LONG + 25,
+ CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26,
+ CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27,
+ CURLINFO_COOKIELIST = CURLINFO_SLIST + 28,
+ CURLINFO_LASTSOCKET = CURLINFO_LONG + 29,
+ CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30,
+ /* Fill in new entries below here! */
+
+ CURLINFO_LASTONE = 30
+} CURLINFO;
+
+/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
+ CURLINFO_HTTP_CODE */
+#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE
+
+typedef enum {
+ CURLCLOSEPOLICY_NONE, /* first, never use this */
+
+ CURLCLOSEPOLICY_OLDEST,
+ CURLCLOSEPOLICY_LEAST_RECENTLY_USED,
+ CURLCLOSEPOLICY_LEAST_TRAFFIC,
+ CURLCLOSEPOLICY_SLOWEST,
+ CURLCLOSEPOLICY_CALLBACK,
+
+ CURLCLOSEPOLICY_LAST /* last, never use this */
+} curl_closepolicy;
+
+#define CURL_GLOBAL_SSL (1<<0)
+#define CURL_GLOBAL_WIN32 (1<<1)
+#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32)
+#define CURL_GLOBAL_NOTHING 0
+#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL
+
+
+/*****************************************************************************
+ * Setup defines, protos etc for the sharing stuff.
+ */
+
+/* Different data locks for a single share */
+typedef enum {
+ CURL_LOCK_DATA_NONE = 0,
+ /* CURL_LOCK_DATA_SHARE is used internaly to say that
+ * the locking is just made to change the internal state of the share
+ * itself.
+ */
+ CURL_LOCK_DATA_SHARE,
+ CURL_LOCK_DATA_COOKIE,
+ CURL_LOCK_DATA_DNS,
+ CURL_LOCK_DATA_SSL_SESSION,
+ CURL_LOCK_DATA_CONNECT,
+ CURL_LOCK_DATA_LAST
+} curl_lock_data;
+
+/* Different lock access types */
+typedef enum {
+ CURL_LOCK_ACCESS_NONE = 0, /* unspecified action */
+ CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */
+ CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */
+ CURL_LOCK_ACCESS_LAST /* never use */
+} curl_lock_access;
+
+typedef void (*curl_lock_function)(CURL *handle,
+ curl_lock_data data,
+ curl_lock_access locktype,
+ void *userptr);
+typedef void (*curl_unlock_function)(CURL *handle,
+ curl_lock_data data,
+ void *userptr);
+
+typedef void CURLSH;
+
+typedef enum {
+ CURLSHE_OK, /* all is fine */
+ CURLSHE_BAD_OPTION, /* 1 */
+ CURLSHE_IN_USE, /* 2 */
+ CURLSHE_INVALID, /* 3 */
+ CURLSHE_NOMEM, /* out of memory */
+ CURLSHE_LAST /* never use */
+} CURLSHcode;
+
+typedef enum {
+ CURLSHOPT_NONE, /* don't use */
+ CURLSHOPT_SHARE, /* specify a data type to share */
+ CURLSHOPT_UNSHARE, /* specify shich data type to stop sharing */
+ CURLSHOPT_LOCKFUNC, /* pass in a 'curl_lock_function' pointer */
+ CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */
+ CURLSHOPT_USERDATA, /* pass in a user data pointer used in the lock/unlock
+ callback functions */
+ CURLSHOPT_LAST /* never use */
+} CURLSHoption;
+
+CURL_EXTERN CURLSH *curl_share_init(void);
+CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...);
+CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *);
+
+/****************************************************************************
+ * Structures for querying information about the curl library at runtime.
+ */
+
+typedef enum {
+ CURLVERSION_FIRST,
+ CURLVERSION_SECOND,
+ CURLVERSION_THIRD,
+ CURLVERSION_FOURTH,
+ CURLVERSION_LAST /* never actually use this */
+} CURLversion;
+
+/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by
+ basicly all programs ever, that want to get version information. It is
+ meant to be a built-in version number for what kind of struct the caller
+ expects. If the struct ever changes, we redefine the NOW to another enum
+ from above. */
+#define CURLVERSION_NOW CURLVERSION_FOURTH
+
+typedef struct {
+ CURLversion age; /* age of the returned struct */
+ const char *version; /* LIBCURL_VERSION */
+ unsigned int version_num; /* LIBCURL_VERSION_NUM */
+ const char *host; /* OS/host/cpu/machine when configured */
+ int features; /* bitmask, see defines below */
+ const char *ssl_version; /* human readable string */
+ long ssl_version_num; /* not used anymore, always 0 */
+ const char *libz_version; /* human readable string */
+ /* protocols is terminated by an entry with a NULL protoname */
+ const char * const *protocols;
+
+ /* The fields below this were added in CURLVERSION_SECOND */
+ const char *ares;
+ int ares_num;
+
+ /* This field was added in CURLVERSION_THIRD */
+ const char *libidn;
+
+ /* These field were added in CURLVERSION_FOURTH */
+
+ /* Same as '_libiconv_version' if built with HAVE_ICONV */
+ int iconv_ver_num;
+
+ const char *libssh_version; /* human readable string */
+
+} curl_version_info_data;
+
+#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */
+#define CURL_VERSION_KERBEROS4 (1<<1) /* kerberos auth is supported */
+#define CURL_VERSION_SSL (1<<2) /* SSL options are present */
+#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */
+#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */
+#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth support */
+#define CURL_VERSION_DEBUG (1<<6) /* built with debug capabilities */
+#define CURL_VERSION_ASYNCHDNS (1<<7) /* asynchronous dns resolves */
+#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth */
+#define CURL_VERSION_LARGEFILE (1<<9) /* supports files bigger than 2GB */
+#define CURL_VERSION_IDN (1<<10) /* International Domain Names support */
+#define CURL_VERSION_SSPI (1<<11) /* SSPI is supported */
+#define CURL_VERSION_CONV (1<<12) /* character conversions are
+ supported */
+
+/*
+ * NAME curl_version_info()
+ *
+ * DESCRIPTION
+ *
+ * This function returns a pointer to a static copy of the version info
+ * struct. See above.
+ */
+CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion);
+
+/*
+ * NAME curl_easy_strerror()
+ *
+ * DESCRIPTION
+ *
+ * The curl_easy_strerror function may be used to turn a CURLcode value
+ * into the equivalent human readable error string. This is useful
+ * for printing meaningful error messages.
+ */
+CURL_EXTERN const char *curl_easy_strerror(CURLcode);
+
+/*
+ * NAME curl_share_strerror()
+ *
+ * DESCRIPTION
+ *
+ * The curl_share_strerror function may be used to turn a CURLSHcode value
+ * into the equivalent human readable error string. This is useful
+ * for printing meaningful error messages.
+ */
+CURL_EXTERN const char *curl_share_strerror(CURLSHcode);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* unfortunately, the easy.h and multi.h include files need options and info
+ stuff before they can be included! */
+#include "easy.h" /* nothing in curl is fun without the easy stuff */
+#include "multi.h"
+
+#endif /* __CURL_CURL_H */
diff --git a/Utilities/cmcurl/curl/curlver.h b/Utilities/cmcurl/curl/curlver.h
new file mode 100644
index 000000000..938002285
--- /dev/null
+++ b/Utilities/cmcurl/curl/curlver.h
@@ -0,0 +1,56 @@
+#ifndef __CURL_CURLVER_H
+#define __CURL_CURLVER_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/* This header file contains nothing but libcurl version info, generated by
+ a script at release-time. This was made its own header file in 7.11.2 */
+
+/* This is the version number of the libcurl package from which this header
+ file origins: */
+#define LIBCURL_VERSION "7.16.1"
+
+/* The numeric version number is also available "in parts" by using these
+ defines: */
+#define LIBCURL_VERSION_MAJOR 7
+#define LIBCURL_VERSION_MINOR 16
+#define LIBCURL_VERSION_PATCH 1
+
+/* This is the numeric version of the libcurl version number, meant for easier
+ parsing and comparions by programs. The LIBCURL_VERSION_NUM define will
+ always follow this syntax:
+
+ 0xXXYYZZ
+
+ Where XX, YY and ZZ are the main version, release and patch numbers in
+ hexadecimal (using 8 bits each). All three numbers are always represented
+ using two digits. 1.2 would appear as "0x010200" while version 9.11.7
+ appears as "0x090b07".
+
+ This 6-digit (24 bits) hexadecimal number does not show pre-release number,
+ and it is always a greater number in a more recent release. It makes
+ comparisons with greater than and less than work.
+*/
+#define LIBCURL_VERSION_NUM 0x071001
+
+#endif /* __CURL_CURLVER_H */
diff --git a/Utilities/cmcurl/curl/easy.h b/Utilities/cmcurl/curl/easy.h
new file mode 100644
index 000000000..17de21070
--- /dev/null
+++ b/Utilities/cmcurl/curl/easy.h
@@ -0,0 +1,81 @@
+#ifndef __CURL_EASY_H
+#define __CURL_EASY_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+CURL_EXTERN CURL *curl_easy_init(void);
+CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
+CURL_EXTERN CURLcode curl_easy_perform(CURL *curl);
+CURL_EXTERN void curl_easy_cleanup(CURL *curl);
+
+/*
+ * NAME curl_easy_getinfo()
+ *
+ * DESCRIPTION
+ *
+ * Request internal information from the curl session with this function. The
+ * third argument MUST be a pointer to a long, a pointer to a char * or a
+ * pointer to a double (as the documentation describes elsewhere). The data
+ * pointed to will be filled in accordingly and can be relied upon only if the
+ * function returns CURLE_OK. This function is intended to get used *AFTER* a
+ * performed transfer, all results from this function are undefined until the
+ * transfer is completed.
+ */
+CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...);
+
+
+/*
+ * NAME curl_easy_duphandle()
+ *
+ * DESCRIPTION
+ *
+ * Creates a new curl session handle with the same options set for the handle
+ * passed in. Duplicating a handle could only be a matter of cloning data and
+ * options, internal state info and things like persistant connections cannot
+ * be transfered. It is useful in multithreaded applications when you can run
+ * curl_easy_duphandle() for each new thread to avoid a series of identical
+ * curl_easy_setopt() invokes in every thread.
+ */
+CURL_EXTERN CURL* curl_easy_duphandle(CURL *curl);
+
+/*
+ * NAME curl_easy_reset()
+ *
+ * DESCRIPTION
+ *
+ * Re-initializes a CURL handle to the default values. This puts back the
+ * handle to the same state as it was in when it was just created.
+ *
+ * It does keep: live connections, the Session ID cache, the DNS cache and the
+ * cookies.
+ */
+CURL_EXTERN void curl_easy_reset(CURL *curl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Utilities/cmcurl/curl/mprintf.h b/Utilities/cmcurl/curl/mprintf.h
new file mode 100644
index 000000000..5c526882f
--- /dev/null
+++ b/Utilities/cmcurl/curl/mprintf.h
@@ -0,0 +1,80 @@
+#ifndef __CURL_MPRINTF_H
+#define __CURL_MPRINTF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include <stdarg.h>
+#include <stdio.h> /* needed for FILE */
+
+#include "curl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+CURL_EXTERN int curl_mprintf(const char *format, ...);
+CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...);
+CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...);
+CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...);
+CURL_EXTERN int curl_mvprintf(const char *format, va_list args);
+CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args);
+CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args);
+CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, va_list args);
+CURL_EXTERN char *curl_maprintf(const char *format, ...);
+CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args);
+
+#ifdef _MPRINTF_REPLACE
+# undef printf
+# undef fprintf
+# undef sprintf
+# undef vsprintf
+# undef snprintf
+# undef vprintf
+# undef vfprintf
+# undef vsnprintf
+# undef aprintf
+# undef vaprintf
+# define printf curl_mprintf
+# define fprintf curl_mfprintf
+#ifdef CURLDEBUG
+/* When built with CURLDEBUG we define away the sprintf() functions since we
+ don't want internal code to be using them */
+# define sprintf sprintf_was_used
+# define vsprintf vsprintf_was_used
+#else
+# define sprintf curl_msprintf
+# define vsprintf curl_mvsprintf
+#endif
+# define snprintf curl_msnprintf
+# define vprintf curl_mvprintf
+# define vfprintf curl_mvfprintf
+# define vsnprintf curl_mvsnprintf
+# define aprintf curl_maprintf
+# define vaprintf curl_mvaprintf
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CURL_MPRINTF_H */
diff --git a/Utilities/cmcurl/curl/multi.h b/Utilities/cmcurl/curl/multi.h
new file mode 100644
index 000000000..1b6674768
--- /dev/null
+++ b/Utilities/cmcurl/curl/multi.h
@@ -0,0 +1,327 @@
+#ifndef __CURL_MULTI_H
+#define __CURL_MULTI_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+/*
+ This is an "external" header file. Don't give away any internals here!
+
+ 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.
+
+*/
+
+/*
+ * This header file should not really need to include "curl.h" since curl.h
+ * itself includes this file and we expect user applications to do #include
+ * <curl/curl.h> without the need for especially including multi.h.
+ *
+ * For some reason we added this include here at one point, and rather than to
+ * break existing (wrongly written) libcurl applications, we leave it as-is
+ * but with this warning attached.
+ */
+#include "curl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void CURLM;
+
+typedef enum {
+ CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or
+ curl_multi_socket*() 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_BAD_SOCKET, /* the passed in socket argument did not match */
+ CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */
+ CURLM_LAST
+} CURLMcode;
+
+/* just to make code nicer when using curl_multi_socket() you can now check
+ for CURLM_CALL_MULTI_SOCKET too in the same style it works for
+ curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */
+#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM
+
+typedef enum {
+ CURLMSG_NONE, /* first, not used */
+ CURLMSG_DONE, /* This easy handle has completed. 'result' contains
+ 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.
+ */
+CURL_EXTERN 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.
+ */
+CURL_EXTERN 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.
+ */
+CURL_EXTERN 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.
+ */
+CURL_EXTERN 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.
+ */
+CURL_EXTERN 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.
+ */
+CURL_EXTERN 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 information. 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.
+ */
+CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle,
+ int *msgs_in_queue);
+
+/*
+ * Name: curl_multi_strerror()
+ *
+ * Desc: The curl_multi_strerror function may be used to turn a CURLMcode
+ * value into the equivalent human readable error string. This is
+ * useful for printing meaningful error messages.
+ *
+ * Returns: A pointer to a zero-terminated error message.
+ */
+CURL_EXTERN const char *curl_multi_strerror(CURLMcode);
+
+/*
+ * Name: curl_multi_socket() and
+ * curl_multi_socket_all()
+ *
+ * Desc: An alternative version of curl_multi_perform() that allows the
+ * application to pass in one of the file descriptors that have been
+ * detected to have "action" on them and let libcurl perform.
+ * See man page for details.
+ */
+#define CURL_POLL_NONE 0
+#define CURL_POLL_IN 1
+#define CURL_POLL_OUT 2
+#define CURL_POLL_INOUT 3
+#define CURL_POLL_REMOVE 4
+
+#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD
+
+typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */
+ curl_socket_t s, /* socket */
+ int what, /* see above */
+ void *userp, /* private callback
+ pointer */
+ void *socketp); /* private socket
+ pointer */
+/*
+ * Name: curl_multi_timer_callback
+ *
+ * Desc: Called by libcurl whenever the library detects a change in the
+ * maximum number of milliseconds the app is allowed to wait before
+ * curl_multi_socket() or curl_multi_perform() must be called
+ * (to allow libcurl's timed events to take place).
+ *
+ * Returns: The callback should return zero.
+ */
+typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */
+ long timeout_ms, /* see above */
+ void *userp); /* private callback
+ pointer */
+
+CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
+ int *running_handles);
+
+CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle,
+ int *running_handles);
+
+/*
+ * Name: curl_multi_timeout()
+ *
+ * Desc: Returns the maximum number of milliseconds the app is allowed to
+ * wait before curl_multi_socket() or curl_multi_perform() must be
+ * called (to allow libcurl's timed events to take place).
+ *
+ * Returns: CURLM error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle,
+ long *milliseconds);
+
+#undef CINIT /* re-using the same name as in curl.h */
+
+#ifdef CURL_ISOCPP
+#define CINIT(name,type,number) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + number
+#else
+/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */
+#define LONG CURLOPTTYPE_LONG
+#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT
+#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT
+#define OFF_T CURLOPTTYPE_OFF_T
+#define CINIT(name,type,number) CURLMOPT_/**/name = type + number
+#endif
+
+typedef enum {
+ /* This is the socket callback function pointer */
+ CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1),
+
+ /* This is the argument passed to the socket callback */
+ CINIT(SOCKETDATA, OBJECTPOINT, 2),
+
+ /* set to 1 to enable pipelining for this multi handle */
+ CINIT(PIPELINING, LONG, 3),
+
+ /* This is the timer callback function pointer */
+ CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4),
+
+ /* This is the argument passed to the timer callback */
+ CINIT(TIMERDATA, OBJECTPOINT, 5),
+
+ CURLMOPT_LASTENTRY /* the last unused */
+} CURLMoption;
+
+
+/*
+ * Name: curl_multi_setopt()
+ *
+ * Desc: Sets options for the multi handle.
+ *
+ * Returns: CURLM error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle,
+ CURLMoption option, ...);
+
+
+/*
+ * Name: curl_multi_assign()
+ *
+ * Desc: This function sets an association in the multi handle between the
+ * given socket and a private pointer of the application. This is
+ * (only) useful for curl_multi_socket uses.
+ *
+ * Returns: CURLM error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,
+ curl_socket_t sockfd, void *sockp);
+
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
+#endif
diff --git a/Utilities/cmcurl/curl/stdcheaders.h b/Utilities/cmcurl/curl/stdcheaders.h
new file mode 100644
index 000000000..11c1e2f6e
--- /dev/null
+++ b/Utilities/cmcurl/curl/stdcheaders.h
@@ -0,0 +1,34 @@
+#ifndef __STDC_HEADERS_H
+#define __STDC_HEADERS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include <sys/types.h>
+
+size_t fread (void *, size_t, size_t, FILE *);
+size_t fwrite (const void *, size_t, size_t, FILE *);
+
+int strcasecmp(const char *, const char *);
+int strncasecmp(const char *, const char *, size_t);
+
+#endif
diff --git a/Utilities/cmcurl/curl/types.h b/Utilities/cmcurl/curl/types.h
new file mode 100644
index 000000000..d37d6ae9e
--- /dev/null
+++ b/Utilities/cmcurl/curl/types.h
@@ -0,0 +1 @@
+/* not used */
diff --git a/Utilities/cmcurl/curlx.h b/Utilities/cmcurl/curlx.h
new file mode 100644
index 000000000..26948d305
--- /dev/null
+++ b/Utilities/cmcurl/curlx.h
@@ -0,0 +1,107 @@
+#ifndef __CURLX_H
+#define __CURLX_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ * Defines protos and includes all header files that provide the curlx_*
+ * functions. The curlx_* functions are not part of the libcurl API, but are
+ * stand-alone functions whose sources can be built and linked by apps if need
+ * be.
+ */
+
+#include <curl/mprintf.h>
+/* this is still a public header file that provides the curl_mprintf()
+ functions while they still are offered publicly. They will be made library-
+ private one day */
+
+#include "strequal.h"
+/* "strequal.h" provides the strequal protos */
+
+#include "strtoofft.h"
+/* "strtoofft.h" provides this function: curlx_strtoofft(), returns a
+ curl_off_t number from a given string.
+*/
+
+#include "timeval.h"
+/*
+ "timeval.h" sets up a 'struct timeval' even for platforms that otherwise
+ don't have one and has protos for these functions:
+
+ curlx_tvnow()
+ curlx_tvdiff()
+ curlx_tvdiff_secs()
+*/
+
+/* Now setup curlx_ * names for the functions that are to become curlx_ and
+ be removed from a future libcurl official API:
+ curlx_getenv
+ curlx_mprintf (and its variations)
+ curlx_strequal
+ curlx_strnequal
+
+*/
+
+#define curlx_getenv curl_getenv
+#define curlx_strequal curl_strequal
+#define curlx_strnequal curl_strnequal
+#define curlx_mvsnprintf curl_mvsnprintf
+#define curlx_msnprintf curl_msnprintf
+#define curlx_maprintf curl_maprintf
+#define curlx_mvaprintf curl_mvaprintf
+#define curlx_msprintf curl_msprintf
+#define curlx_mprintf curl_mprintf
+#define curlx_mfprintf curl_mfprintf
+#define curlx_mvsprintf curl_mvsprintf
+#define curlx_mvprintf curl_mvprintf
+#define curlx_mvfprintf curl_mvfprintf
+
+#ifdef ENABLE_CURLX_PRINTF
+/* If this define is set, we define all "standard" printf() functions to use
+ the curlx_* version instead. It makes the source code transparant and
+ easier to understand/patch. Undefine them first in case _MPRINTF_REPLACE
+ is set. */
+# undef printf
+# undef fprintf
+# undef sprintf
+# undef snprintf
+# undef vprintf
+# undef vfprintf
+# undef vsprintf
+# undef vsnprintf
+# undef aprintf
+# undef vaprintf
+
+# define printf curlx_mprintf
+# define fprintf curlx_mfprintf
+# define sprintf curlx_msprintf
+# define snprintf curlx_msnprintf
+# define vprintf curlx_mvprintf
+# define vfprintf curlx_mvfprintf
+# define vsprintf curlx_mvsprintf
+# define vsnprintf curlx_mvsnprintf
+# define aprintf curlx_maprintf
+# define vaprintf curlx_mvaprintf
+#endif /* ENABLE_CURLX_PRINTF */
+
+#endif /* __CURLX_H */
diff --git a/Utilities/cmcurl/dict.c b/Utilities/cmcurl/dict.c
new file mode 100644
index 000000000..d6443f4b9
--- /dev/null
+++ b/Utilities/cmcurl/dict.c
@@ -0,0 +1,280 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef CURL_DISABLE_DICT
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef WIN32
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#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"
+#include "dict.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+static char *unescape_word(struct SessionHandle *data, char *inp)
+{
+ char *newp;
+ char *dictp;
+ char *ptr;
+ int len;
+ unsigned char byte;
+ int olen=0;
+
+ newp = curl_easy_unescape(data, inp, 0, &len);
+ if(!newp)
+ return NULL;
+
+ dictp = malloc(len*2 + 1); /* add one for terminating zero */
+ if(dictp) {
+ /* According to RFC2229 section 2.2, these letters need to be escaped with
+ \[letter] */
+ for(ptr = newp;
+ (byte = (unsigned char)*ptr) != 0;
+ ptr++) {
+ if ((byte <= 32) || (byte == 127) ||
+ (byte == '\'') || (byte == '\"') || (byte == '\\')) {
+ dictp[olen++] = '\\';
+ }
+ dictp[olen++] = byte;
+ }
+ dictp[olen]=0;
+
+ free(newp);
+ }
+ return dictp;
+}
+
+CURLcode Curl_dict(struct connectdata *conn, bool *done)
+{
+ char *word;
+ char *eword;
+ char *ppath;
+ char *database = NULL;
+ char *strategy = NULL;
+ char *nthdef = NULL; /* This is not part of the protocol, but required
+ by RFC 2229 */
+ CURLcode result=CURLE_OK;
+ struct SessionHandle *data=conn->data;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+
+ char *path = data->reqdata.path;
+ curl_off_t *bytecount = &data->reqdata.keep.bytecount;
+
+ *done = TRUE; /* unconditionally */
+
+ 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 *)".";
+ }
+
+ eword = unescape_word(data, word);
+ if(!eword)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = Curl_sendf(sockfd, conn,
+ "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
+ "MATCH "
+ "%s " /* database */
+ "%s " /* strategy */
+ "%s\r\n" /* word */
+ "QUIT\r\n",
+
+ database,
+ strategy,
+ eword
+ );
+
+ free(eword);
+
+ if(result)
+ failf(data, "Failed sending DICT request");
+ else
+ result = Curl_setup_transfer(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 *)"!";
+ }
+
+ eword = unescape_word(data, word);
+ if(!eword)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = Curl_sendf(sockfd, conn,
+ "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
+ "DEFINE "
+ "%s " /* database */
+ "%s\r\n" /* word */
+ "QUIT\r\n",
+ database,
+ eword);
+
+ free(eword);
+
+ if(result)
+ failf(data, "Failed sending DICT request");
+ else
+ result = Curl_setup_transfer(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(sockfd, conn,
+ "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
+ "%s\r\n"
+ "QUIT\r\n", ppath);
+ if(result)
+ failf(data, "Failed sending DICT request");
+ else
+ result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
+ -1, NULL);
+ if(result)
+ return result;
+ }
+ }
+
+ return CURLE_OK;
+}
+#endif /*CURL_DISABLE_DICT*/
diff --git a/Utilities/cmcurl/dict.h b/Utilities/cmcurl/dict.h
new file mode 100644
index 000000000..d3da1936f
--- /dev/null
+++ b/Utilities/cmcurl/dict.h
@@ -0,0 +1,30 @@
+#ifndef __DICT_H
+#define __DICT_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#ifndef CURL_DISABLE_DICT
+CURLcode Curl_dict(struct connectdata *conn, bool *done);
+CURLcode Curl_dict_done(struct connectdata *conn);
+#endif
+#endif
diff --git a/Utilities/cmcurl/easy.c b/Utilities/cmcurl/easy.c
new file mode 100644
index 000000000..209d1c3e0
--- /dev/null
+++ b/Utilities/cmcurl/easy.c
@@ -0,0 +1,895 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <errno.h>
+
+#include "strequal.h"
+
+#ifdef WIN32
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#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 /* WIN32 ... */
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sslgen.h"
+#include "url.h"
+#include "getinfo.h"
+#include "hostip.h"
+#include "share.h"
+#include "strdup.h"
+#include "memory.h"
+#include "progress.h"
+#include "easyif.h"
+#include "sendf.h" /* for failf function prototype */
+#include <ca-bundle.h>
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
+#include <iconv.h>
+/* set default codesets for iconv */
+#ifndef CURL_ICONV_CODESET_OF_NETWORK
+#define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1"
+#endif
+#ifndef CURL_ICONV_CODESET_FOR_UTF8
+#define CURL_ICONV_CODESET_FOR_UTF8 "UTF-8"
+#endif
+#define ICONV_ERROR (size_t)-1
+#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#ifdef USE_WINSOCK
+/* win32_cleanup() is for win32 socket cleanup functionality, the opposite
+ of win32_init() */
+static void win32_cleanup(void)
+{
+ WSACleanup();
+}
+
+/* win32_init() performs win32 socket initialization to properly setup the
+ stack to allow networking */
+static CURLcode win32_init(void)
+{
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
+ Error IPV6_requires_winsock2
+#endif
+
+ wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
+
+ 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 what we need.*/
+ /* Note that if the DLL supports versions greater */
+ /* than wVersionRequested, it will still return */
+ /* wVersionRequested in wVersion. wHighVersion contains the */
+ /* highest supported version. */
+
+ if ( LOBYTE( wsaData.wVersion ) != LOBYTE(wVersionRequested) ||
+ HIBYTE( wsaData.wVersion ) != HIBYTE(wVersionRequested) ) {
+ /* Tell the user that we couldn't find a useable */
+
+ /* winsock.dll. */
+ WSACleanup();
+ return CURLE_FAILED_INIT;
+ }
+ /* The Windows Sockets DLL is acceptable. Proceed. */
+ return CURLE_OK;
+}
+
+#else
+/* These functions exist merely to prevent compiler warnings */
+static CURLcode win32_init(void) { return CURLE_OK; }
+static void win32_cleanup(void) { }
+#endif
+
+#ifdef USE_LIBIDN
+/*
+ * Initialise use of IDNA library.
+ * It falls back to ASCII if $CHARSET isn't defined. This doesn't work for
+ * idna_to_ascii_lz().
+ */
+static void idna_init (void)
+{
+#ifdef WIN32
+ char buf[60];
+ UINT cp = GetACP();
+
+ if (!getenv("CHARSET") && cp > 0) {
+ snprintf(buf, sizeof(buf), "CHARSET=cp%u", cp);
+ putenv(buf);
+ }
+#else
+ /* to do? */
+#endif
+}
+#endif /* USE_LIBIDN */
+
+/* true globals -- for curl_global_init() and curl_global_cleanup() */
+static unsigned int initialized;
+static long init_flags;
+
+/*
+ * strdup (and other memory functions) is redefined in complicated
+ * ways, but at this point it must be defined as the system-supplied strdup
+ * so the callback pointer is initialized correctly.
+ */
+#if defined(_WIN32_WCE)
+#define system_strdup _strdup
+#elif !defined(HAVE_STRDUP)
+#define system_strdup curlx_strdup
+#else
+#define system_strdup strdup
+#endif
+
+/*
+ * If a memory-using function (like curl_getenv) is used before
+ * curl_global_init() is called, we need to have these pointers set already.
+ */
+
+curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc;
+curl_free_callback Curl_cfree = (curl_free_callback)free;
+curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
+curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
+curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
+
+/**
+ * curl_global_init() globally initializes cURL given a bitwise set of the
+ * different features of what to initialize.
+ */
+CURLcode curl_global_init(long flags)
+{
+ if (initialized++)
+ return CURLE_OK;
+
+ /* Setup the default memory functions here (again) */
+ Curl_cmalloc = (curl_malloc_callback)malloc;
+ Curl_cfree = (curl_free_callback)free;
+ Curl_crealloc = (curl_realloc_callback)realloc;
+ Curl_cstrdup = (curl_strdup_callback)system_strdup;
+ Curl_ccalloc = (curl_calloc_callback)calloc;
+
+ if (flags & CURL_GLOBAL_SSL)
+ if (!Curl_ssl_init())
+ return CURLE_FAILED_INIT;
+
+ if (flags & CURL_GLOBAL_WIN32)
+ if (win32_init() != CURLE_OK)
+ return CURLE_FAILED_INIT;
+
+#ifdef _AMIGASF
+ if(!amiga_init())
+ return CURLE_FAILED_INIT;
+#endif
+
+#ifdef USE_LIBIDN
+ idna_init();
+#endif
+
+ init_flags = flags;
+
+ return CURLE_OK;
+}
+
+/*
+ * curl_global_init_mem() globally initializes cURL and also registers the
+ * user provided callback routines.
+ */
+CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
+ curl_free_callback f, curl_realloc_callback r,
+ curl_strdup_callback s, curl_calloc_callback c)
+{
+ CURLcode code = CURLE_OK;
+
+ /* Invalid input, return immediately */
+ if (!m || !f || !r || !s || !c)
+ return CURLE_FAILED_INIT;
+
+ /* Already initialized, don't do it again */
+ if ( initialized )
+ return CURLE_OK;
+
+ /* Call the actual init function first */
+ code = curl_global_init(flags);
+ if (code == CURLE_OK) {
+ Curl_cmalloc = m;
+ Curl_cfree = f;
+ Curl_cstrdup = s;
+ Curl_crealloc = r;
+ Curl_ccalloc = c;
+ }
+
+ return code;
+}
+
+/**
+ * curl_global_cleanup() globally cleanups cURL, uses the value of
+ * "init_flags" to determine what needs to be cleaned up and what doesn't.
+ */
+void curl_global_cleanup(void)
+{
+ if (!initialized)
+ return;
+
+ if (--initialized)
+ return;
+
+ Curl_global_host_cache_dtor();
+
+ if (init_flags & CURL_GLOBAL_SSL)
+ Curl_ssl_cleanup();
+
+ if (init_flags & CURL_GLOBAL_WIN32)
+ win32_cleanup();
+
+#ifdef _AMIGASF
+ amiga_cleanup();
+#endif
+
+ init_flags = 0;
+}
+
+/*
+ * curl_easy_init() is the external interface to alloc, setup and init an
+ * easy handle that is returned. If anything goes wrong, NULL is returned.
+ */
+CURL *curl_easy_init(void)
+{
+ CURLcode res;
+ struct SessionHandle *data;
+
+ /* Make sure we inited the global SSL stuff */
+ if (!initialized) {
+ res = curl_global_init(CURL_GLOBAL_DEFAULT);
+ if(res)
+ /* something in the global init failed, return nothing */
+ return NULL;
+ }
+
+ /* We use curl_open() with undefined URL so far */
+ res = Curl_open(&data);
+ if(res != CURLE_OK)
+ return NULL;
+
+ return data;
+}
+
+/*
+ * curl_easy_setopt() is the external interface for setting options on an
+ * easy handle.
+ */
+
+CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...)
+{
+ va_list arg;
+ struct SessionHandle *data = curl;
+ CURLcode ret;
+
+ if(!curl)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ va_start(arg, tag);
+
+ ret = Curl_setopt(data, tag, arg);
+
+ va_end(arg);
+ return ret;
+}
+
+#ifdef CURL_MULTIEASY
+/***************************************************************************
+ * This function is still only for testing purposes. It makes a great way
+ * to run the full test suite on the multi interface instead of the easy one.
+ ***************************************************************************
+ *
+ * The *new* curl_easy_perform() is the external interface that performs a
+ * transfer previously setup.
+ *
+ * Wrapper-function that: creates a multi handle, adds the easy handle to it,
+ * runs curl_multi_perform() until the transfer is done, then detaches the
+ * easy handle, destroys the multi handle and returns the easy handle's return
+ * code. This will make everything internally use and assume multi interface.
+ */
+CURLcode curl_easy_perform(CURL *easy)
+{
+ CURLM *multi;
+ CURLMcode mcode;
+ CURLcode code = CURLE_OK;
+ int still_running;
+ struct timeval timeout;
+ int rc;
+ CURLMsg *msg;
+ fd_set fdread;
+ fd_set fdwrite;
+ fd_set fdexcep;
+ int maxfd;
+
+ if(!easy)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ multi = curl_multi_init();
+ if(!multi)
+ return CURLE_OUT_OF_MEMORY;
+
+ mcode = curl_multi_add_handle(multi, easy);
+ if(mcode) {
+ curl_multi_cleanup(multi);
+ return CURLE_FAILED_INIT;
+ }
+
+ /* we start some action by calling perform right away */
+
+ do {
+ while(CURLM_CALL_MULTI_PERFORM ==
+ curl_multi_perform(multi, &still_running));
+
+ if(!still_running)
+ break;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+ FD_ZERO(&fdexcep);
+
+ /* timeout once per second */
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ /* get file descriptors from the transfers */
+ curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd);
+
+ rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+
+ if(rc == -1)
+ /* select error */
+ break;
+
+ /* timeout or data to send/receive => loop! */
+ } while(still_running);
+
+ msg = curl_multi_info_read(multi, &rc);
+ if(msg)
+ code = msg->data.result;
+
+ mcode = curl_multi_remove_handle(multi, easy);
+ /* what to do if it fails? */
+
+ mcode = curl_multi_cleanup(multi);
+ /* what to do if it fails? */
+
+ return code;
+}
+#else
+/*
+ * curl_easy_perform() is the external interface that performs a transfer
+ * previously setup.
+ */
+CURLcode curl_easy_perform(CURL *curl)
+{
+ struct SessionHandle *data = (struct SessionHandle *)curl;
+
+ if(!data)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if ( ! (data->share && data->share->hostcache) ) {
+
+ if (Curl_global_host_cache_use(data) &&
+ (data->dns.hostcachetype != HCACHE_GLOBAL)) {
+ if (data->dns.hostcachetype == HCACHE_PRIVATE)
+ Curl_hash_destroy(data->dns.hostcache);
+ data->dns.hostcache = Curl_global_host_cache_get();
+ data->dns.hostcachetype = HCACHE_GLOBAL;
+ }
+
+ if (!data->dns.hostcache) {
+ data->dns.hostcachetype = HCACHE_PRIVATE;
+ data->dns.hostcache = Curl_mk_dnscache();
+
+ if(!data->dns.hostcache)
+ /* While we possibly could survive and do good without a host cache,
+ the fact that creating it failed indicates that things are truly
+ screwed up and we should bail out! */
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ }
+
+ if(!data->state.connc) {
+ /* oops, no connection cache, make one up */
+ data->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, -1);
+ if(!data->state.connc)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return Curl_perform(data);
+}
+#endif
+
+/*
+ * curl_easy_cleanup() is the external interface to cleaning/freeing the given
+ * easy handle.
+ */
+void curl_easy_cleanup(CURL *curl)
+{
+ struct SessionHandle *data = (struct SessionHandle *)curl;
+
+ if(!data)
+ return;
+
+ Curl_close(data);
+}
+
+/*
+ * Store a pointed to the multi handle within the easy handle's data struct.
+ */
+void Curl_easy_addmulti(struct SessionHandle *data,
+ void *multi)
+{
+ data->multi = multi;
+}
+
+void Curl_easy_initHandleData(struct SessionHandle *data)
+{
+ memset(&data->reqdata, 0, sizeof(struct HandleData));
+
+ data->reqdata.maxdownload = -1;
+}
+
+/*
+ * curl_easy_getinfo() is an external interface that allows an app to retrieve
+ * information from a performed transfer and similar.
+ */
+CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...)
+{
+ va_list arg;
+ void *paramp;
+ struct SessionHandle *data = (struct SessionHandle *)curl;
+
+ va_start(arg, info);
+ paramp = va_arg(arg, void *);
+
+ return Curl_getinfo(data, info, paramp);
+}
+
+/*
+ * curl_easy_duphandle() is an external interface to allow duplication of a
+ * given input easy handle. The returned handle will be a new working handle
+ * with all options set exactly as the input source handle.
+ */
+CURL *curl_easy_duphandle(CURL *incurl)
+{
+ bool fail = TRUE;
+ struct SessionHandle *data=(struct SessionHandle *)incurl;
+
+ struct SessionHandle *outcurl = (struct SessionHandle *)
+ calloc(sizeof(struct SessionHandle), 1);
+
+ if(NULL == outcurl)
+ return NULL; /* failure */
+
+ do {
+
+ /*
+ * 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) {
+ break;
+ }
+ outcurl->state.headersize=HEADERSIZE;
+
+ /* copy all userdefined values */
+ outcurl->set = data->set;
+
+ if(data->state.used_interface == Curl_if_multi)
+ outcurl->state.connc = data->state.connc;
+ else
+ outcurl->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, -1);
+
+ if(!outcurl->state.connc)
+ break;
+
+ outcurl->state.lastconnect = -1;
+
+ outcurl->progress.flags = data->progress.flags;
+ outcurl->progress.callback = data->progress.callback;
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ if(data->cookies) {
+ /* If cookies are enabled in the parent handle, we enable them
+ in the clone as well! */
+ outcurl->cookies = Curl_cookie_init(data,
+ data->cookies->filename,
+ outcurl->cookies,
+ data->set.cookiesession);
+ if(!outcurl->cookies) {
+ break;
+ }
+ }
+#endif /* CURL_DISABLE_HTTP */
+
+ /* duplicate all values in 'change' */
+
+ if(data->change.url) {
+ outcurl->change.url = strdup(data->change.url);
+ if(!outcurl->change.url)
+ break;
+ outcurl->change.url_alloc = TRUE;
+ }
+
+ if(data->change.referer) {
+ outcurl->change.referer = strdup(data->change.referer);
+ if(!outcurl->change.referer)
+ break;
+ outcurl->change.referer_alloc = TRUE;
+ }
+
+#ifdef USE_ARES
+ /* If we use ares, we setup a new ares channel for the new handle */
+ if(ARES_SUCCESS != ares_init(&outcurl->state.areschannel))
+ break;
+#endif
+
+#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
+ outcurl->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
+ CURL_ICONV_CODESET_OF_NETWORK);
+ outcurl->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
+ CURL_ICONV_CODESET_OF_HOST);
+ outcurl->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
+ CURL_ICONV_CODESET_FOR_UTF8);
+#endif
+
+ Curl_easy_initHandleData(outcurl);
+
+ outcurl->magic = CURLEASY_MAGIC_NUMBER;
+
+ fail = FALSE; /* we reach this point and thus we are OK */
+
+ } while(0);
+
+ if(fail) {
+ if(outcurl) {
+ if(outcurl->state.connc->type == CONNCACHE_PRIVATE)
+ Curl_rm_connc(outcurl->state.connc);
+ if(outcurl->state.headerbuff)
+ free(outcurl->state.headerbuff);
+ if(outcurl->change.url)
+ free(outcurl->change.url);
+ if(outcurl->change.referer)
+ free(outcurl->change.referer);
+ free(outcurl); /* free the memory again */
+ outcurl = NULL;
+ }
+ }
+
+ return outcurl;
+}
+
+/*
+ * curl_easy_reset() is an external interface that allows an app to re-
+ * initialize a session handle to the default values.
+ */
+void curl_easy_reset(CURL *curl)
+{
+ struct SessionHandle *data = (struct SessionHandle *)curl;
+
+ Curl_safefree(data->reqdata.pathbuffer);
+ data->reqdata.pathbuffer=NULL;
+
+ Curl_safefree(data->reqdata.proto.generic);
+ data->reqdata.proto.generic=NULL;
+
+ /* zero out UserDefined data: */
+ memset(&data->set, 0, sizeof(struct UserDefined));
+
+ /* zero out Progress data: */
+ memset(&data->progress, 0, sizeof(struct Progress));
+
+ /* init Handle data */
+ Curl_easy_initHandleData(data);
+
+ /* The remainder of these calls have been taken from Curl_open() */
+
+ 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;
+
+ data->set.infilesize = -1; /* we don't know any size */
+ data->set.postfieldsize = -1;
+
+ 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.ftp_use_eprt = TRUE; /* FTP defaults to EPRT 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;
+
+ data->set.proxyport = 1080;
+ data->set.proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
+ data->set.httpauth = CURLAUTH_BASIC; /* defaults to basic */
+ data->set.proxyauth = CURLAUTH_BASIC; /* defaults to basic */
+
+ /*
+ * libcurl 7.10 introduced SSL verification *by default*! This needs to be
+ * switched off unless wanted.
+ */
+ data->set.ssl.verifypeer = TRUE;
+ data->set.ssl.verifyhost = 2;
+#ifdef CURL_CA_BUNDLE
+ /* This is our preferred CA cert bundle since install time */
+ data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE;
+#endif
+
+ data->set.ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth
+ type */
+}
+
+#ifdef CURL_DOES_CONVERSIONS
+/*
+ * Curl_convert_to_network() is an internal function
+ * for performing ASCII conversions on non-ASCII platforms.
+ */
+CURLcode Curl_convert_to_network(struct SessionHandle *data,
+ char *buffer, size_t length)
+{
+ CURLcode rc;
+
+ if(data->set.convtonetwork) {
+ /* use translation callback */
+ rc = data->set.convtonetwork(buffer, length);
+ if(rc != CURLE_OK) {
+ failf(data,
+ "CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %i: %s",
+ rc, curl_easy_strerror(rc));
+ }
+ return(rc);
+ } else {
+#ifdef HAVE_ICONV
+ /* do the translation ourselves */
+ char *input_ptr, *output_ptr;
+ size_t in_bytes, out_bytes, rc;
+
+ /* open an iconv conversion descriptor if necessary */
+ if(data->outbound_cd == (iconv_t)-1) {
+ data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
+ CURL_ICONV_CODESET_OF_HOST);
+ if(data->outbound_cd == (iconv_t)-1) {
+ failf(data,
+ "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
+ CURL_ICONV_CODESET_OF_NETWORK,
+ CURL_ICONV_CODESET_OF_HOST,
+ errno, strerror(errno));
+ return CURLE_CONV_FAILED;
+ }
+ }
+ /* call iconv */
+ input_ptr = output_ptr = buffer;
+ in_bytes = out_bytes = length;
+ rc = iconv(data->outbound_cd, (const char**)&input_ptr, &in_bytes,
+ &output_ptr, &out_bytes);
+ if ((rc == ICONV_ERROR) || (in_bytes != 0)) {
+ failf(data,
+ "The Curl_convert_to_network iconv call failed with errno %i: %s",
+ errno, strerror(errno));
+ return CURLE_CONV_FAILED;
+ }
+#else
+ failf(data, "CURLOPT_CONV_TO_NETWORK_FUNCTION callback required");
+ return CURLE_CONV_REQD;
+#endif /* HAVE_ICONV */
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_convert_from_network() is an internal function
+ * for performing ASCII conversions on non-ASCII platforms.
+ */
+CURLcode Curl_convert_from_network(struct SessionHandle *data,
+ char *buffer, size_t length)
+{
+ CURLcode rc;
+
+ if(data->set.convfromnetwork) {
+ /* use translation callback */
+ rc = data->set.convfromnetwork(buffer, length);
+ if(rc != CURLE_OK) {
+ failf(data,
+ "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %i: %s",
+ rc, curl_easy_strerror(rc));
+ }
+ return(rc);
+ } else {
+#ifdef HAVE_ICONV
+ /* do the translation ourselves */
+ char *input_ptr, *output_ptr;
+ size_t in_bytes, out_bytes, rc;
+
+ /* open an iconv conversion descriptor if necessary */
+ if(data->inbound_cd == (iconv_t)-1) {
+ data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
+ CURL_ICONV_CODESET_OF_NETWORK);
+ if(data->inbound_cd == (iconv_t)-1) {
+ failf(data,
+ "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
+ CURL_ICONV_CODESET_OF_HOST,
+ CURL_ICONV_CODESET_OF_NETWORK,
+ errno, strerror(errno));
+ return CURLE_CONV_FAILED;
+ }
+ }
+ /* call iconv */
+ input_ptr = output_ptr = buffer;
+ in_bytes = out_bytes = length;
+ rc = iconv(data->inbound_cd, (const char **)&input_ptr, &in_bytes,
+ &output_ptr, &out_bytes);
+ if ((rc == ICONV_ERROR) || (in_bytes != 0)) {
+ failf(data,
+ "The Curl_convert_from_network iconv call failed with errno %i: %s",
+ errno, strerror(errno));
+ return CURLE_CONV_FAILED;
+ }
+#else
+ failf(data, "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback required");
+ return CURLE_CONV_REQD;
+#endif /* HAVE_ICONV */
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_convert_from_utf8() is an internal function
+ * for performing UTF-8 conversions on non-ASCII platforms.
+ */
+CURLcode Curl_convert_from_utf8(struct SessionHandle *data,
+ char *buffer, size_t length)
+{
+ CURLcode rc;
+
+ if(data->set.convfromutf8) {
+ /* use translation callback */
+ rc = data->set.convfromutf8(buffer, length);
+ if(rc != CURLE_OK) {
+ failf(data,
+ "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %i: %s",
+ rc, curl_easy_strerror(rc));
+ }
+ return(rc);
+ } else {
+#ifdef HAVE_ICONV
+ /* do the translation ourselves */
+ char *input_ptr, *output_ptr;
+ size_t in_bytes, out_bytes, rc;
+
+ /* open an iconv conversion descriptor if necessary */
+ if(data->utf8_cd == (iconv_t)-1) {
+ data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
+ CURL_ICONV_CODESET_FOR_UTF8);
+ if(data->utf8_cd == (iconv_t)-1) {
+ failf(data,
+ "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
+ CURL_ICONV_CODESET_OF_HOST,
+ CURL_ICONV_CODESET_FOR_UTF8,
+ errno, strerror(errno));
+ return CURLE_CONV_FAILED;
+ }
+ }
+ /* call iconv */
+ input_ptr = output_ptr = buffer;
+ in_bytes = out_bytes = length;
+ rc = iconv(data->utf8_cd, (const char**)&input_ptr, &in_bytes,
+ &output_ptr, &out_bytes);
+ if ((rc == ICONV_ERROR) || (in_bytes != 0)) {
+ failf(data,
+ "The Curl_convert_from_utf8 iconv call failed with errno %i: %s",
+ errno, strerror(errno));
+ return CURLE_CONV_FAILED;
+ }
+ if (output_ptr < input_ptr) {
+ /* null terminate the now shorter output string */
+ *output_ptr = 0x00;
+ }
+#else
+ failf(data, "CURLOPT_CONV_FROM_UTF8_FUNCTION callback required");
+ return CURLE_CONV_REQD;
+#endif /* HAVE_ICONV */
+ }
+
+ return CURLE_OK;
+}
+
+#endif /* CURL_DOES_CONVERSIONS */
diff --git a/Utilities/cmcurl/easyif.h b/Utilities/cmcurl/easyif.h
new file mode 100644
index 000000000..4c0f7e795
--- /dev/null
+++ b/Utilities/cmcurl/easyif.h
@@ -0,0 +1,40 @@
+#ifndef __EASYIF_H
+#define __EASYIF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ * Prototypes for library-wide functions provided by easy.c
+ */
+void Curl_easy_addmulti(struct SessionHandle *data, void *multi);
+
+void Curl_easy_initHandleData(struct SessionHandle *data);
+
+CURLcode Curl_convert_to_network(struct SessionHandle *data,
+ char *buffer, size_t length);
+CURLcode Curl_convert_from_network(struct SessionHandle *data,
+ char *buffer, size_t length);
+CURLcode Curl_convert_from_utf8(struct SessionHandle *data,
+ char *buffer, size_t length);
+
+#endif /* __EASYIF_H */
diff --git a/Utilities/cmcurl/escape.c b/Utilities/cmcurl/escape.c
new file mode 100644
index 000000000..9552b0f31
--- /dev/null
+++ b/Utilities/cmcurl/escape.c
@@ -0,0 +1,181 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $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>
+#include "memory.h"
+/* urldata.h and easyif.h are included for Curl_convert_... prototypes */
+#include "urldata.h"
+#include "easyif.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* for ABI-compatibility with previous versions */
+char *curl_escape(const char *string, int inlength)
+{
+ return curl_easy_escape(NULL, string, inlength);
+}
+
+/* for ABI-compatibility with previous versions */
+char *curl_unescape(const char *string, int length)
+{
+ return curl_easy_unescape(NULL, string, length, NULL);
+}
+
+char *curl_easy_escape(CURL *handle, const char *string, int inlength)
+{
+ size_t alloc = (inlength?(size_t)inlength:strlen(string))+1;
+ char *ns;
+ char *testing_ptr = NULL;
+ unsigned char in;
+ size_t newlen = alloc;
+ int strindex=0;
+ size_t length;
+
+#ifndef CURL_DOES_CONVERSIONS
+ /* avoid compiler warnings */
+ (void)handle;
+#endif
+ ns = malloc(alloc);
+ if(!ns)
+ return NULL;
+
+ length = alloc-1;
+ while(length--) {
+ in = *string;
+ 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;
+ testing_ptr = realloc(ns, alloc);
+ if(!testing_ptr) {
+ free( ns );
+ return NULL;
+ }
+ else {
+ ns = testing_ptr;
+ }
+ }
+
+#ifdef CURL_DOES_CONVERSIONS
+/* escape sequences are always in ASCII so convert them on non-ASCII hosts */
+ if (!handle ||
+ (Curl_convert_to_network(handle, &in, 1) != CURLE_OK)) {
+ /* Curl_convert_to_network calls failf if unsuccessful */
+ free(ns);
+ return NULL;
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+ snprintf(&ns[strindex], 4, "%%%02X", in);
+
+ strindex+=3;
+ }
+ else {
+ /* just copy this */
+ ns[strindex++]=in;
+ }
+ string++;
+ }
+ ns[strindex]=0; /* terminate it */
+ return ns;
+}
+
+char *curl_easy_unescape(CURL *handle, const char *string, int length,
+ int *olen)
+{
+ int alloc = (length?length:(int)strlen(string))+1;
+ char *ns = malloc(alloc);
+ unsigned char in;
+ int strindex=0;
+ long hex;
+
+#ifndef CURL_DOES_CONVERSIONS
+ /* avoid compiler warnings */
+ (void)handle;
+#endif
+ if( !ns )
+ return NULL;
+
+ while(--alloc > 0) {
+ in = *string;
+ if(('%' == in) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
+ /* this is two hexadecimal digits following a '%' */
+ char hexstr[3];
+ char *ptr;
+ hexstr[0] = string[1];
+ hexstr[1] = string[2];
+ hexstr[2] = 0;
+
+ hex = strtol(hexstr, &ptr, 16);
+
+ in = (unsigned char)hex; /* this long is never bigger than 255 anyway */
+
+#ifdef CURL_DOES_CONVERSIONS
+/* escape sequences are always in ASCII so convert them on non-ASCII hosts */
+ if (!handle ||
+ (Curl_convert_from_network(handle, &in, 1) != CURLE_OK)) {
+ /* Curl_convert_from_network calls failf if unsuccessful */
+ free(ns);
+ return NULL;
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+ string+=2;
+ alloc-=2;
+ }
+
+ ns[strindex++] = in;
+ string++;
+ }
+ ns[strindex]=0; /* terminate it */
+
+ if(olen)
+ /* store output size */
+ *olen = strindex;
+ return ns;
+}
+
+/* For operating systems/environments that use different malloc/free
+ ssystems for the app and for this library, we provide a free that uses
+ the library's memory system */
+void curl_free(void *p)
+{
+ if(p)
+ free(p);
+}
diff --git a/Utilities/cmcurl/escape.h b/Utilities/cmcurl/escape.h
new file mode 100644
index 000000000..a0a0209c4
--- /dev/null
+++ b/Utilities/cmcurl/escape.h
@@ -0,0 +1,30 @@
+#ifndef __ESCAPE_H
+#define __ESCAPE_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+/* Escape and unescape URL encoding in strings. The functions return a new
+ * allocated string or NULL if an error occurred. */
+
+
+#endif
diff --git a/Utilities/cmcurl/file.c b/Utilities/cmcurl/file.c
new file mode 100644
index 000000000..b247a7736
--- /dev/null
+++ b/Utilities/cmcurl/file.c
@@ -0,0 +1,407 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef CURL_DISABLE_FILE
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef WIN32
+#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
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#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_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "progress.h"
+#include "sendf.h"
+#include "escape.h"
+#include "file.h"
+#include "speedcheck.h"
+#include "getinfo.h"
+#include "transfer.h"
+#include "url.h"
+#include "memory.h"
+#include "parsedate.h" /* for the week day and month names */
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * Curl_file_connect() gets called from Curl_protocol_connect() to allow us to
+ * do protocol-specific actions at connect-time. We emulate a
+ * connect-then-transfer protocol and "connect" to the file here
+ */
+CURLcode Curl_file_connect(struct connectdata *conn)
+{
+ char *real_path = curl_easy_unescape(conn->data, conn->data->reqdata.path, 0, NULL);
+ struct FILEPROTO *file;
+ int fd;
+#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
+ int i;
+ char *actual_path;
+#endif
+
+ if(!real_path)
+ return CURLE_OUT_OF_MEMORY;
+
+ file = (struct FILEPROTO *)calloc(sizeof(struct FILEPROTO), 1);
+ if(!file) {
+ free(real_path);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if (conn->data->reqdata.proto.file) {
+ free(conn->data->reqdata.proto.file);
+ }
+
+ conn->data->reqdata.proto.file = file;
+
+#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
+ /* If the first character is a slash, and there's
+ something that looks like a drive at the beginning of
+ the path, skip the slash. If we remove the initial
+ slash in all cases, paths without drive letters end up
+ relative to the current directory which isn't how
+ browsers work.
+
+ Some browsers accept | instead of : as the drive letter
+ separator, so we do too.
+
+ On other platforms, we need the slash to indicate an
+ absolute pathname. On Windows, absolute paths start
+ with a drive letter.
+ */
+ actual_path = real_path;
+ if ((actual_path[0] == '/') &&
+ actual_path[1] &&
+ (actual_path[2] == ':' || actual_path[2] == '|'))
+ {
+ actual_path[2] = ':';
+ actual_path++;
+ }
+
+ /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
+ for (i=0; actual_path[i] != '\0'; ++i)
+ if (actual_path[i] == '/')
+ actual_path[i] = '\\';
+
+ fd = open(actual_path, O_RDONLY | O_BINARY); /* no CR/LF translation! */
+ file->path = actual_path;
+#else
+ fd = open(real_path, O_RDONLY);
+ file->path = real_path;
+#endif
+ file->freepath = real_path; /* free this when done */
+
+ file->fd = fd;
+ if(!conn->data->set.upload && (fd == -1)) {
+ failf(conn->data, "Couldn't open file %s", conn->data->reqdata.path);
+ Curl_file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
+ return CURLE_FILE_COULDNT_READ_FILE;
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_file_done(struct connectdata *conn,
+ CURLcode status, bool premature)
+{
+ struct FILEPROTO *file = conn->data->reqdata.proto.file;
+ (void)status; /* not used */
+ (void)premature; /* not used */
+ Curl_safefree(file->freepath);
+
+ if(file->fd != -1)
+ close(file->fd);
+
+ return CURLE_OK;
+}
+
+#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
+#define DIRSEP '\\'
+#else
+#define DIRSEP '/'
+#endif
+
+static CURLcode file_upload(struct connectdata *conn)
+{
+ struct FILEPROTO *file = conn->data->reqdata.proto.file;
+ char *dir = strchr(file->path, DIRSEP);
+ FILE *fp;
+ CURLcode res=CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ char *buf = data->state.buffer;
+ size_t nread;
+ size_t nwrite;
+ curl_off_t bytecount = 0;
+ struct timeval now = Curl_tvnow();
+
+ /*
+ * Since FILE: doesn't do the full init, we need to provide some extra
+ * assignments here.
+ */
+ conn->fread = data->set.fread;
+ conn->fread_in = data->set.in;
+ conn->data->reqdata.upload_fromhere = buf;
+
+ if(!dir)
+ return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
+
+ if(!dir[1])
+ return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
+
+ fp = fopen(file->path, "wb");
+ if(!fp) {
+ failf(data, "Can't open %s for writing", file->path);
+ return CURLE_WRITE_ERROR;
+ }
+
+ if(-1 != data->set.infilesize)
+ /* known size of data to "upload" */
+ Curl_pgrsSetUploadSize(data, data->set.infilesize);
+
+ while (res == CURLE_OK) {
+ int readcount;
+ res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
+ if(res)
+ break;
+
+ if (readcount <= 0) /* fix questionable compare error. curlvms */
+ break;
+
+ nread = (size_t)readcount;
+
+ /* write the data to the target */
+ nwrite = fwrite(buf, 1, nread, fp);
+ if(nwrite != nread) {
+ res = CURLE_SEND_ERROR;
+ break;
+ }
+
+ bytecount += nread;
+
+ Curl_pgrsSetUploadCounter(data, bytecount);
+
+ if(Curl_pgrsUpdate(conn))
+ res = CURLE_ABORTED_BY_CALLBACK;
+ else
+ res = Curl_speedcheck(data, now);
+ }
+ if(!res && Curl_pgrsUpdate(conn))
+ res = CURLE_ABORTED_BY_CALLBACK;
+
+ fclose(fp);
+
+ return res;
+}
+
+/*
+ * Curl_file() is the protocol-specific function for the do-phase, separated
+ * from the connect-phase above. Other protocols merely setup the transfer in
+ * the do-phase, to have it done in the main transfer loop but since some
+ * platforms we support don't allow select()ing etc on file handles (as
+ * opposed to sockets) we instead perform the whole do-operation in this
+ * function.
+ */
+CURLcode Curl_file(struct connectdata *conn, bool *done)
+{
+ /* This implementation ignores the host name in conformance with
+ RFC 1738. Only local files (reachable via the standard file system)
+ are supported. This means that files on remotely mounted directories
+ (via NFS, Samba, NT sharing) can be accessed through a file:// URL
+ */
+ CURLcode res = CURLE_OK;
+ struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
+ Windows version to have a different struct without
+ having to redefine the simple word 'stat' */
+ curl_off_t expected_size=0;
+ bool fstated=FALSE;
+ ssize_t nread;
+ struct SessionHandle *data = conn->data;
+ char *buf = data->state.buffer;
+ curl_off_t bytecount = 0;
+ int fd;
+ struct timeval now = Curl_tvnow();
+
+ *done = TRUE; /* unconditionally */
+
+ Curl_readwrite_init(conn);
+ Curl_initinfo(data);
+ Curl_pgrsStartNow(data);
+
+ if(data->set.upload)
+ return file_upload(conn);
+
+ /* get the fd from the connection phase */
+ fd = conn->data->reqdata.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;
+ fstated = TRUE;
+ }
+
+ /* If we have selected NOBODY and HEADER, it means that we only want file
+ information. Which for FILE can't be much more than the file size and
+ date. */
+ if(conn->bits.no_body && data->set.include_header && fstated) {
+ CURLcode result;
+ snprintf(buf, sizeof(data->state.buffer),
+ "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
+ result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
+ if(result)
+ return result;
+
+ result = Curl_client_write(conn, CLIENTWRITE_BOTH,
+ (char *)"Accept-ranges: bytes\r\n", 0);
+ if(result)
+ return result;
+
+ if(fstated) {
+ struct tm *tm;
+ time_t clock = (time_t)statbuf.st_mtime;
+#ifdef HAVE_GMTIME_R
+ struct tm buffer;
+ tm = (struct tm *)gmtime_r(&clock, &buffer);
+#else
+ tm = gmtime(&clock);
+#endif
+ /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
+ snprintf(buf, BUFSIZE-1,
+ "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+ result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
+ }
+ return result;
+ }
+
+ if (data->reqdata.resume_from <= expected_size)
+ expected_size -= data->reqdata.resume_from;
+ else {
+ failf(data, "failed to resume file:// transfer");
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+
+ if (fstated && (expected_size == 0))
+ return CURLE_OK;
+
+ /* The following is a shortcut implementation of file reading
+ this is both more efficient than the former call to download() and
+ it avoids problems with select() and recv() on file descriptors
+ in Winsock */
+ if(fstated)
+ Curl_pgrsSetDownloadSize(data, expected_size);
+
+ if(data->reqdata.resume_from) {
+ if(data->reqdata.resume_from !=
+ lseek(fd, data->reqdata.resume_from, SEEK_SET))
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+
+ Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
+ while (res == CURLE_OK) {
+ nread = read(fd, buf, BUFSIZE-1);
+
+ if ( nread > 0)
+ buf[nread] = 0;
+
+ if (nread <= 0)
+ break;
+
+ bytecount += nread;
+
+ res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
+ if(res)
+ return res;
+
+ Curl_pgrsSetDownloadCounter(data, bytecount);
+
+ if(Curl_pgrsUpdate(conn))
+ res = CURLE_ABORTED_BY_CALLBACK;
+ else
+ res = Curl_speedcheck(data, now);
+ }
+ if(Curl_pgrsUpdate(conn))
+ res = CURLE_ABORTED_BY_CALLBACK;
+
+ return res;
+}
+
+#endif
diff --git a/Utilities/cmcurl/file.h b/Utilities/cmcurl/file.h
new file mode 100644
index 000000000..20a1c4c06
--- /dev/null
+++ b/Utilities/cmcurl/file.h
@@ -0,0 +1,31 @@
+#ifndef __FILE_H
+#define __FILE_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#ifndef CURL_DISABLE_FILE
+CURLcode Curl_file(struct connectdata *, bool *done);
+CURLcode Curl_file_done(struct connectdata *, CURLcode, bool premature);
+CURLcode Curl_file_connect(struct connectdata *);
+#endif
+#endif
diff --git a/Utilities/cmcurl/formdata.c b/Utilities/cmcurl/formdata.c
new file mode 100644
index 000000000..f10c6c70e
--- /dev/null
+++ b/Utilities/cmcurl/formdata.c
@@ -0,0 +1,1694 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ Debug the form generator stand-alone by compiling this source file with:
+
+ gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -DCURLDEBUG -o formdata -I../include formdata.c strequal.c memdebug.c mprintf.c strerror.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"
+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"
+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"
+Content-Type: text/plain
+...
+Content-Disposition: attachment; filename="inet_ntoa_r.h"
+Content-Type: text/plain
+...
+
+Content-Disposition: form-data; name="FILECONTENT"
+...
+
+ */
+
+#include "setup.h"
+#include <curl/curl.h>
+
+/* Length of the random boundary string. */
+#define BOUNDARY_LENGTH 40
+
+#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
+#include <libgen.h>
+#endif
+#include "urldata.h" /* for struct SessionHandle */
+#include "easyif.h" /* for Curl_convert_... prototypes */
+#include "formdata.h"
+#include "strequal.h"
+#include "memory.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#endif /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */
+
+#ifndef CURL_DISABLE_HTTP
+
+#if defined(HAVE_BASENAME) && defined(NEED_BASENAME_PROTO)
+/* This system has a basename() but no prototype for it! */
+char *basename(char *path);
+#endif
+
+static size_t readfromfile(struct Form *form, char *buffer, size_t size);
+
+/* What kind of Content-Type to use on un-specified files with unrecognized
+ extensions. */
+#define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream"
+
+#define FORM_FILE_SEPARATOR ','
+#define FORM_TYPE_SEPARATOR ';'
+
+/***************************************************************************
+ *
+ * AddHttpPost()
+ *
+ * Adds a HttpPost structure to the list, if parent_post is given becomes
+ * a subpost of parent_post instead of a direct list element.
+ *
+ * Returns newly allocated HttpPost on success and NULL if malloc failed.
+ *
+ ***************************************************************************/
+static struct curl_httppost *
+AddHttpPost(char * name, size_t namelength,
+ char * value, size_t contentslength,
+ char * buffer, size_t bufferlength,
+ char *contenttype,
+ long flags,
+ struct curl_slist* contentHeader,
+ char *showfilename,
+ struct curl_httppost *parent_post,
+ struct curl_httppost **httppost,
+ struct curl_httppost **last_post)
+{
+ struct curl_httppost *post;
+ post = (struct curl_httppost *)calloc(sizeof(struct curl_httppost), 1);
+ if(post) {
+ post->name = name;
+ post->namelength = (long)(name?(namelength?namelength:strlen(name)):0);
+ post->contents = value;
+ post->contentslength = (long)contentslength;
+ post->buffer = buffer;
+ post->bufferlength = (long)bufferlength;
+ post->contenttype = contenttype;
+ post->contentheader = contentHeader;
+ post->showfilename = showfilename;
+ post->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 const struct ContentType ctts[]={
+ {".gif", "image/gif"},
+ {".jpg", "image/jpeg"},
+ {".jpeg", "image/jpeg"},
+ {".txt", "text/plain"},
+ {".html", "text/html"}
+ };
+
+ 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;
+}
+
+/***************************************************************************
+ *
+ * memdup()
+ *
+ * Copies the 'source' data to a newly allocated buffer buffer (that is
+ * returned). Uses buffer_length if not null, else uses strlen to determine
+ * the length of the buffer to be copied
+ *
+ * Returns the new pointer or NULL on failure.
+ *
+ ***************************************************************************/
+static char *memdup(const char *src, size_t buffer_length)
+{
+ size_t length;
+ bool add = FALSE;
+ char *buffer;
+
+ if (buffer_length)
+ length = buffer_length;
+ else {
+ length = strlen(src);
+ add = TRUE;
+ }
+ buffer = (char*)malloc(length+add);
+ if (!buffer)
+ return NULL; /* fail */
+
+ memcpy(buffer, src, length);
+
+ /* if length unknown do null termination */
+ if (add)
+ buffer[length] = '\0';
+
+ return buffer;
+}
+
+/***************************************************************************
+ *
+ * FormAdd()
+ *
+ * Stores a formpost parameter and builds the appropriate linked list.
+ *
+ * Has two principal functionalities: using files and byte arrays as
+ * post parts. Byte arrays are either copied or just the pointer is stored
+ * (as the user requests) while for files only the filename and not the
+ * content is stored.
+ *
+ * While you may have only one byte array for each name, multiple filenames
+ * are allowed (and because of this feature CURLFORM_END is needed after
+ * using CURLFORM_FILE).
+ *
+ * Examples:
+ *
+ * Simple name/value pair with copied contents:
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
+ *
+ * name/value pair where only the content pointer is remembered:
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END);
+ * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used)
+ *
+ * storing a filename (CONTENTTYPE is optional!):
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
+ * CURLFORM_END);
+ *
+ * storing multiple filenames:
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END);
+ *
+ * Returns:
+ * CURL_FORMADD_OK on success
+ * CURL_FORMADD_MEMORY if the FormInfo allocation fails
+ * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form
+ * CURL_FORMADD_NULL if a null pointer was given for a char
+ * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed
+ * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
+ * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or an error)
+ * CURL_FORMADD_MEMORY if a HttpPost struct cannot be allocated
+ * CURL_FORMADD_MEMORY if some allocation for string copying failed.
+ * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array
+ *
+ ***************************************************************************/
+
+static
+CURLFORMcode FormAdd(struct curl_httppost **httppost,
+ struct curl_httppost **last_post,
+ va_list params)
+{
+ FormInfo *first_form, *current_form, *form = NULL;
+ CURLFORMcode return_value = CURL_FORMADD_OK;
+ const char *prevtype = NULL;
+ struct curl_httppost *post = NULL;
+ CURLformoption option;
+ struct curl_forms *forms = NULL;
+ char *array_value=NULL; /* value read from an array */
+
+ /* This is a state variable, that if TRUE means that we're parsing an
+ array that we got passed to us. If FALSE we're parsing the input
+ va_list arguments. */
+ bool array_state = FALSE;
+
+ /*
+ * We need to allocate the first struct to fill in.
+ */
+ first_form = (FormInfo *)calloc(sizeof(struct FormInfo), 1);
+ if(!first_form)
+ return CURL_FORMADD_MEMORY;
+
+ current_form = first_form;
+
+ /*
+ * Loop through all the options set. Break if we have an error to report.
+ */
+ while (return_value == CURL_FORMADD_OK) {
+
+ /* first see if we have more parts of the array param */
+ if ( array_state ) {
+ /* get the upcoming option from the given array */
+ option = forms->option;
+ array_value = (char *)forms->value;
+
+ forms++; /* advance this to next entry */
+ if (CURLFORM_END == option) {
+ /* end of array state */
+ array_state = FALSE;
+ continue;
+ }
+ }
+ else {
+ /* This is not array-state, get next option */
+ option = va_arg(params, CURLformoption);
+ if (CURLFORM_END == option)
+ break;
+ }
+
+ switch (option) {
+ case CURLFORM_ARRAY:
+ if(array_state)
+ /* we don't support an array from within an array */
+ return_value = CURL_FORMADD_ILLEGAL_ARRAY;
+ else {
+ forms = va_arg(params, struct curl_forms *);
+ if (forms)
+ array_state = TRUE;
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+
+ /*
+ * Set the Name property.
+ */
+ case CURLFORM_PTRNAME:
+#ifdef CURL_DOES_CONVERSIONS
+ /* treat CURLFORM_PTR like CURLFORM_COPYNAME so we'll
+ have safe memory for the eventual conversion */
+#else
+ current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
+#endif
+ case CURLFORM_COPYNAME:
+ if (current_form->name)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ char *name = array_state?
+ array_value:va_arg(params, char *);
+ if (name)
+ current_form->name = name; /* store for the moment */
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+ case CURLFORM_NAMELENGTH:
+ if (current_form->namelength)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else
+ current_form->namelength =
+ array_state?(long)array_value:(long)va_arg(params, long);
+ break;
+
+ /*
+ * Set the contents property.
+ */
+ case CURLFORM_PTRCONTENTS:
+ current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */
+ case CURLFORM_COPYCONTENTS:
+ if (current_form->value)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ char *value =
+ array_state?array_value:va_arg(params, char *);
+ if (value)
+ current_form->value = value; /* store for the moment */
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+ case CURLFORM_CONTENTSLENGTH:
+ if (current_form->contentslength)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else
+ current_form->contentslength =
+ array_state?(long)array_value:va_arg(params, long);
+ break;
+
+ /* Get contents from a given file name */
+ case CURLFORM_FILECONTENT:
+ if (current_form->flags != 0)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ char *filename = array_state?
+ array_value:va_arg(params, char *);
+ if (filename) {
+ current_form->value = strdup(filename);
+ if(!current_form->value)
+ return_value = CURL_FORMADD_MEMORY;
+ else {
+ current_form->flags |= HTTPPOST_READFILE;
+ current_form->value_alloc = TRUE;
+ }
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+
+ /* We upload a file */
+ case CURLFORM_FILE:
+ {
+ char *filename = array_state?array_value:
+ va_arg(params, char *);
+
+ if (current_form->value) {
+ if (current_form->flags & HTTPPOST_FILENAME) {
+ if (filename) {
+ if ((current_form = AddFormInfo(strdup(filename),
+ NULL, current_form)) == NULL)
+ return_value = CURL_FORMADD_MEMORY;
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ else
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ }
+ else {
+ if (filename) {
+ current_form->value = strdup(filename);
+ if(!current_form->value)
+ return_value = CURL_FORMADD_MEMORY;
+ else {
+ current_form->flags |= HTTPPOST_FILENAME;
+ current_form->value_alloc = TRUE;
+ }
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+ }
+
+ case CURLFORM_BUFFER:
+ {
+ char *filename = array_state?array_value:
+ va_arg(params, char *);
+
+ if (current_form->value) {
+ if (current_form->flags & HTTPPOST_BUFFER) {
+ if (filename) {
+ if ((current_form = AddFormInfo(strdup(filename),
+ NULL, current_form)) == NULL)
+ return_value = CURL_FORMADD_MEMORY;
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ else
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ }
+ else {
+ if (filename) {
+ current_form->value = strdup(filename);
+ if(!current_form->value)
+ return_value = CURL_FORMADD_MEMORY;
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ current_form->flags |= HTTPPOST_BUFFER;
+ }
+ break;
+ }
+
+ case CURLFORM_BUFFERPTR:
+ current_form->flags |= HTTPPOST_PTRBUFFER;
+ if (current_form->buffer)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ char *buffer =
+ array_state?array_value:va_arg(params, char *);
+ if (buffer)
+ current_form->buffer = buffer; /* store for the moment */
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+
+ case CURLFORM_BUFFERLENGTH:
+ if (current_form->bufferlength)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else
+ current_form->bufferlength =
+ array_state?(long)array_value:va_arg(params, long);
+ break;
+
+ case CURLFORM_CONTENTTYPE:
+ {
+ char *contenttype =
+ array_state?array_value:va_arg(params, char *);
+ if (current_form->contenttype) {
+ if (current_form->flags & HTTPPOST_FILENAME) {
+ if (contenttype) {
+ if ((current_form = AddFormInfo(NULL,
+ strdup(contenttype),
+ current_form)) == NULL)
+ return_value = CURL_FORMADD_MEMORY;
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ else
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ }
+ else {
+ if (contenttype) {
+ current_form->contenttype = strdup(contenttype);
+ if(!current_form->contenttype)
+ return_value = CURL_FORMADD_MEMORY;
+ else
+ current_form->contenttype_alloc = TRUE;
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+ }
+ case CURLFORM_CONTENTHEADER:
+ {
+ /* this "cast increases required alignment of target type" but
+ we consider it OK anyway */
+ struct curl_slist* list = array_state?
+ (struct curl_slist*)array_value:
+ va_arg(params, struct curl_slist*);
+
+ if( current_form->contentheader )
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else
+ current_form->contentheader = list;
+
+ break;
+ }
+ case CURLFORM_FILENAME:
+ {
+ char *filename = array_state?array_value:
+ va_arg(params, char *);
+ if( current_form->showfilename )
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ current_form->showfilename = strdup(filename);
+ if(!current_form->showfilename)
+ return_value = CURL_FORMADD_MEMORY;
+ else
+ current_form->showfilename_alloc = TRUE;
+ }
+ break;
+ }
+ default:
+ return_value = CURL_FORMADD_UNKNOWN_OPTION;
+ }
+ }
+
+ if(CURL_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->buffer) &&
+ (form->flags & HTTPPOST_BUFFER) &&
+ (form->flags & HTTPPOST_PTRBUFFER) ) ||
+
+ ( (form->flags & HTTPPOST_READFILE) &&
+ (form->flags & HTTPPOST_PTRCONTENTS) )
+ ) {
+ return_value = CURL_FORMADD_INCOMPLETE;
+ break;
+ }
+ else {
+ if ( ((form->flags & HTTPPOST_FILENAME) ||
+ (form->flags & HTTPPOST_BUFFER)) &&
+ !form->contenttype ) {
+ /* our contenttype is missing */
+ form->contenttype
+ = strdup(ContentTypeForFilename(form->value, prevtype));
+ if(!form->contenttype) {
+ return_value = CURL_FORMADD_MEMORY;
+ break;
+ }
+ form->contenttype_alloc = TRUE;
+ }
+ if ( !(form->flags & HTTPPOST_PTRNAME) &&
+ (form == first_form) ) {
+ /* copy name (without strdup; possibly contains null characters) */
+ form->name = memdup(form->name, form->namelength);
+ if (!form->name) {
+ return_value = CURL_FORMADD_MEMORY;
+ break;
+ }
+ form->name_alloc = TRUE;
+ }
+ if ( !(form->flags & HTTPPOST_FILENAME) &&
+ !(form->flags & HTTPPOST_READFILE) &&
+ !(form->flags & HTTPPOST_PTRCONTENTS) &&
+ !(form->flags & HTTPPOST_PTRBUFFER) ) {
+ /* copy value (without strdup; possibly contains null characters) */
+ form->value = memdup(form->value, form->contentslength);
+ if (!form->value) {
+ return_value = CURL_FORMADD_MEMORY;
+ break;
+ }
+ form->value_alloc = TRUE;
+ }
+ post = AddHttpPost(form->name, form->namelength,
+ form->value, form->contentslength,
+ form->buffer, form->bufferlength,
+ form->contenttype, form->flags,
+ form->contentheader, form->showfilename,
+ post, httppost,
+ last_post);
+
+ if(!post) {
+ return_value = CURL_FORMADD_MEMORY;
+ break;
+ }
+
+ if (form->contenttype)
+ prevtype = form->contenttype;
+ }
+ }
+ }
+
+ if(return_value) {
+ /* we return on error, free possibly allocated fields */
+ if(!form)
+ form = current_form;
+ if(form) {
+ if(form->name_alloc)
+ free(form->name);
+ if(form->value_alloc)
+ free(form->value);
+ if(form->contenttype_alloc)
+ free(form->contenttype);
+ if(form->showfilename_alloc)
+ free(form->showfilename);
+ }
+ }
+
+ /* 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;
+}
+
+/*
+ * curl_formadd() is a public API to add a section to the multipart formpost.
+ */
+
+CURLFORMcode curl_formadd(struct curl_httppost **httppost,
+ struct curl_httppost **last_post,
+ ...)
+{
+ va_list arg;
+ CURLFORMcode result;
+ va_start(arg, last_post);
+ result = FormAdd(httppost, last_post, arg);
+ va_end(arg);
+ return result;
+}
+
+/*
+ * AddFormData() adds a chunk of data to the FormData linked list.
+ *
+ * size is incremented by the chunk length, unless it is NULL
+ */
+static CURLcode AddFormData(struct FormData **formp,
+ enum formtype type,
+ const void *line,
+ size_t length,
+ curl_off_t *size)
+{
+ struct FormData *newform = (struct FormData *)
+ malloc(sizeof(struct FormData));
+ if (!newform)
+ return CURLE_OUT_OF_MEMORY;
+ newform->next = NULL;
+
+ /* we make it easier for plain strings: */
+ if(!length)
+ length = strlen((char *)line);
+
+ newform->line = (char *)malloc(length+1);
+ if (!newform->line) {
+ free(newform);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memcpy(newform->line, line, length);
+ newform->length = length;
+ newform->line[length]=0; /* zero terminate for easier debugging */
+ newform->type = type;
+
+ if(*formp) {
+ (*formp)->next = newform;
+ *formp = newform;
+ }
+ else
+ *formp = newform;
+
+ if (size) {
+ if((type == FORM_DATA) || (type == FORM_CONTENT))
+ *size += length;
+ else {
+ /* Since this is a file to be uploaded here, add the size of the actual
+ file */
+ if(!strequal("-", newform->line)) {
+ struct_stat file;
+ if(!stat(newform->line, &file)) {
+ *size += file.st_size;
+ }
+ }
+ }
+ }
+ return CURLE_OK;
+}
+
+/*
+ * AddFormDataf() adds printf()-style formatted data to the formdata chain.
+ */
+
+static CURLcode AddFormDataf(struct FormData **formp,
+ curl_off_t *size,
+ const char *fmt, ...)
+{
+ char s[4096];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(s, sizeof(s), fmt, ap);
+ va_end(ap);
+
+ return AddFormData(formp, FORM_DATA, s, 0, size);
+}
+
+/*
+ * Curl_formclean() is used from http.c, this cleans a built FormData linked
+ * list
+ */
+void Curl_formclean(struct FormData **form_ptr)
+{
+ struct FormData *next, *form;
+
+ form = *form_ptr;
+ if(!form)
+ return;
+
+ do {
+ next=form->next; /* the following form line */
+ free(form->line); /* free the line */
+ free(form); /* free the struct */
+
+ } while ((form = next) != NULL); /* continue */
+
+ *form_ptr = NULL;
+}
+
+#ifdef CURL_DOES_CONVERSIONS
+/*
+ * Curl_formcovert() is used from http.c, this converts any
+ form items that need to be sent in the network encoding.
+ Returns CURLE_OK on success.
+ */
+CURLcode Curl_formconvert(struct SessionHandle *data, struct FormData *form)
+{
+ struct FormData *next;
+ CURLcode rc;
+
+ if(!form)
+ return CURLE_OK;
+
+ if(!data)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ do {
+ next=form->next; /* the following form line */
+ if (form->type == FORM_DATA) {
+ rc = Curl_convert_to_network(data, form->line, form->length);
+ /* Curl_convert_to_network calls failf if unsuccessful */
+ if (rc != CURLE_OK)
+ return rc;
+ }
+ } while ((form = next) != NULL); /* continue */
+ return CURLE_OK;
+}
+#endif /* CURL_DOES_CONVERSIONS */
+
+/*
+ * curl_formget()
+ * Serialize a curl_httppost struct.
+ * Returns 0 on success.
+ */
+int curl_formget(struct curl_httppost *form, void *arg,
+ curl_formget_callback append)
+{
+ CURLcode rc;
+ curl_off_t size;
+ struct FormData *data, *ptr;
+
+ rc = Curl_getFormData(&data, form, NULL, &size);
+ if (rc != CURLE_OK)
+ return (int)rc;
+
+ for (ptr = data; ptr; ptr = ptr->next) {
+ if (ptr->type == FORM_FILE) {
+ char buffer[8192];
+ size_t read;
+ struct Form temp;
+
+ Curl_FormInit(&temp, ptr);
+
+ do {
+ read = readfromfile(&temp, buffer, sizeof(buffer));
+ if ((read == (size_t) -1) || (read != append(arg, buffer, read))) {
+ if (temp.fp) {
+ fclose(temp.fp);
+ }
+ Curl_formclean(&data);
+ return -1;
+ }
+ } while (read == sizeof(buffer));
+ } else {
+ if (ptr->length != append(arg, ptr->line, ptr->length)) {
+ Curl_formclean(&data);
+ return -1;
+ }
+ }
+ }
+ Curl_formclean(&data);
+ return 0;
+}
+
+/*
+ * curl_formfree() is an external function to free up a whole form post
+ * chain
+ */
+void curl_formfree(struct curl_httppost *form)
+{
+ struct curl_httppost *next;
+
+ if(!form)
+ /* no form to free, just get out of this */
+ return;
+
+ do {
+ next=form->next; /* the following form line */
+
+ /* recurse to sub-contents */
+ if(form->more)
+ curl_formfree(form->more);
+
+ if( !(form->flags & HTTPPOST_PTRNAME) && form->name)
+ free(form->name); /* free the name */
+ if( !(form->flags & HTTPPOST_PTRCONTENTS) && form->contents)
+ free(form->contents); /* free the contents */
+ if(form->contenttype)
+ free(form->contenttype); /* free the content type */
+ if(form->showfilename)
+ free(form->showfilename); /* free the faked file name */
+ free(form); /* free the struct */
+
+ } while ((form = next) != NULL); /* continue */
+}
+
+#ifndef HAVE_BASENAME
+/*
+ (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
+ Edition)
+
+ The basename() function shall take the pathname pointed to by path and
+ return a pointer to the final component of the pathname, deleting any
+ trailing '/' characters.
+
+ If the string pointed to by path consists entirely of the '/' character,
+ basename() shall return a pointer to the string "/". If the string pointed
+ to by path is exactly "//", it is implementation-defined whether '/' or "//"
+ is returned.
+
+ If path is a null pointer or points to an empty string, basename() shall
+ return a pointer to the string ".".
+
+ The basename() function may modify the string pointed to by path, and may
+ return a pointer to static storage that may then be overwritten by a
+ subsequent call to basename().
+
+ The basename() function need not be reentrant. A function that is not
+ required to be reentrant is not required to be thread-safe.
+
+*/
+static char *basename(char *path)
+{
+ /* Ignore all the details above for now and make a quick and simple
+ implementaion here */
+ char *s1;
+ char *s2;
+
+ s1=strrchr(path, '/');
+ s2=strrchr(path, '\\');
+
+ if(s1 && s2) {
+ path = (s1 > s2? s1 : s2)+1;
+ }
+ else if(s1)
+ path = s1 + 1;
+ else if(s2)
+ path = s2 + 1;
+
+ return path;
+}
+#endif
+
+static char *strippath(char *fullfile)
+{
+ char *filename;
+ char *base;
+ filename = strdup(fullfile); /* duplicate since basename() may ruin the
+ buffer it works on */
+ if(!filename)
+ return NULL;
+ base = strdup(basename(filename));
+
+ free(filename); /* free temporary buffer */
+
+ return base; /* returns an allocated string! */
+}
+
+/*
+ * Curl_getFormData() converts a linked list of "meta data" into a complete
+ * (possibly huge) multipart formdata. The input list is in 'post', while the
+ * output resulting linked lists gets stored in '*finalform'. *sizep will get
+ * the total size of the whole POST.
+ * A multipart/form_data content-type is built, unless a custom content-type
+ * is passed in 'custom_content_type'.
+ */
+
+CURLcode Curl_getFormData(struct FormData **finalform,
+ struct curl_httppost *post,
+ const char *custom_content_type,
+ curl_off_t *sizep)
+{
+ struct FormData *form = NULL;
+ struct FormData *firstform;
+ struct curl_httppost *file;
+ CURLcode result = CURLE_OK;
+
+ curl_off_t size=0; /* support potentially ENORMOUS formposts */
+ char *boundary;
+ char *fileboundary=NULL;
+ struct curl_slist* curList;
+
+ *finalform=NULL; /* default form is empty */
+
+ if(!post)
+ return result; /* no input => no output! */
+
+ boundary = Curl_FormBoundary();
+ if(!boundary)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Make the first line of the output */
+ result = AddFormDataf(&form, NULL,
+ "%s; boundary=%s\r\n",
+ custom_content_type?custom_content_type:
+ "Content-Type: multipart/form-data",
+ boundary);
+
+ if (result) {
+ free(boundary);
+ return result;
+ }
+ /* we DO NOT include that line in the total size of the POST, since it'll be
+ part of the header! */
+
+ firstform = form;
+
+ do {
+
+ if(size) {
+ result = AddFormDataf(&form, &size, "\r\n");
+ if (result)
+ break;
+ }
+
+ /* boundary */
+ result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
+ if (result)
+ break;
+
+ /* Maybe later this should be disabled when a custom_content_type is
+ passed, since Content-Disposition is not meaningful for all multipart
+ types.
+ */
+ result = AddFormDataf(&form, &size,
+ "Content-Disposition: form-data; name=\"");
+ if (result)
+ break;
+
+ result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
+ &size);
+ if (result)
+ break;
+
+ result = AddFormDataf(&form, &size, "\"");
+ if (result)
+ break;
+
+ if(post->more) {
+ /* If used, this is a link to more file names, we must then do
+ the magic to include several files with the same field name */
+
+ fileboundary = Curl_FormBoundary();
+
+ result = AddFormDataf(&form, &size,
+ "\r\nContent-Type: multipart/mixed,"
+ " boundary=%s\r\n",
+ fileboundary);
+ if (result)
+ break;
+ }
+
+ file = post;
+
+ do {
+
+ /* If 'showfilename' is set, that is a faked name passed on to us
+ to use to in the formpost. If that is not set, the actually used
+ local file name should be added. */
+
+ if(post->more) {
+ /* if multiple-file */
+ char *filebasename=
+ (!file->showfilename)?strippath(file->contents):NULL;
+
+ result = AddFormDataf(&form, &size,
+ "\r\n--%s\r\nContent-Disposition: "
+ "attachment; filename=\"%s\"",
+ fileboundary,
+ (file->showfilename?file->showfilename:
+ filebasename));
+ if (filebasename)
+ free(filebasename);
+ if (result)
+ break;
+ }
+ else if((post->flags & HTTPPOST_FILENAME) ||
+ (post->flags & HTTPPOST_BUFFER)) {
+
+ char *filebasename=
+ (!post->showfilename)?strippath(post->contents):NULL;
+
+ result = AddFormDataf(&form, &size,
+ "; filename=\"%s\"",
+ (post->showfilename?post->showfilename:
+ filebasename));
+ if (filebasename)
+ free(filebasename);
+
+ if (result)
+ break;
+ }
+
+ if(file->contenttype) {
+ /* we have a specified type */
+ result = AddFormDataf(&form, &size,
+ "\r\nContent-Type: %s",
+ file->contenttype);
+ if (result)
+ break;
+ }
+
+ curList = file->contentheader;
+ while( curList ) {
+ /* Process the additional headers specified for this form */
+ result = AddFormDataf( &form, &size, "\r\n%s", curList->data );
+ if (result)
+ break;
+ curList = curList->next;
+ }
+ if (result) {
+ Curl_formclean(&firstform);
+ free(boundary);
+ return result;
+ }
+
+#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 &&
+ !checkprefix("text/", file->contenttype)) {
+ /* this is not a text content, mention our binary encoding */
+ result = AddFormDataf(&form, &size,
+ "\r\nContent-Transfer-Encoding: binary");
+ if (result)
+ break;
+ }
+#endif
+
+ result = AddFormDataf(&form, &size, "\r\n\r\n");
+ if (result)
+ break;
+
+ if((post->flags & HTTPPOST_FILENAME) ||
+ (post->flags & HTTPPOST_READFILE)) {
+ /* we should include the contents from the specified file */
+ FILE *fileread;
+
+ fileread = strequal("-", file->contents)?
+ stdin:fopen(file->contents, "rb"); /* binary read for win32 */
+
+ /*
+ * VMS: This only allows for stream files on VMS. Stream files are
+ * OK, as are FIXED & VAR files WITHOUT implied CC For implied CC,
+ * every record needs to have a \n appended & 1 added to SIZE
+ */
+
+ if(fileread) {
+ if(fileread != stdin) {
+ /* close the file again */
+ fclose(fileread);
+ /* add the file name only - for later reading from this */
+ result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
+ }
+ else {
+ /* When uploading from stdin, we can't know the size of the file,
+ * thus must read the full file as before. We *could* use chunked
+ * transfer-encoding, but that only works for HTTP 1.1 and we
+ * can't be sure we work with such a server.
+ */
+ size_t nread;
+ char buffer[512];
+ while ((nread = fread(buffer, 1, sizeof(buffer), fileread)) != 0) {
+ result = AddFormData(&form, FORM_CONTENT, buffer, nread, &size);
+ if (result)
+ break;
+ }
+ }
+
+ if (result) {
+ Curl_formclean(&firstform);
+ free(boundary);
+ return result;
+ }
+
+ }
+ else {
+#ifdef _FORM_DEBUG
+ fprintf(stderr,
+ "\n==> Curl_getFormData couldn't open/read \"%s\"\n",
+ file->contents);
+#endif
+ Curl_formclean(&firstform);
+ free(boundary);
+ *finalform = NULL;
+ return CURLE_READ_ERROR;
+ }
+
+ }
+ else if (post->flags & HTTPPOST_BUFFER) {
+ /* include contents of buffer */
+ result = AddFormData(&form, FORM_CONTENT, post->buffer,
+ post->bufferlength, &size);
+ if (result)
+ break;
+ }
+
+ else {
+ /* include the contents we got */
+ result = AddFormData(&form, FORM_CONTENT, post->contents,
+ post->contentslength, &size);
+ if (result)
+ break;
+ }
+ } while ((file = file->more) != NULL); /* for each specified file for this field */
+ if (result) {
+ Curl_formclean(&firstform);
+ free(boundary);
+ return result;
+ }
+
+ if(post->more) {
+ /* this was a multiple-file inclusion, make a termination file
+ boundary: */
+ result = AddFormDataf(&form, &size,
+ "\r\n--%s--",
+ fileboundary);
+ free(fileboundary);
+ if (result)
+ break;
+ }
+
+ } while ((post = post->next) != NULL); /* for each field */
+ if (result) {
+ Curl_formclean(&firstform);
+ free(boundary);
+ return result;
+ }
+
+ /* end-boundary for everything */
+ result = AddFormDataf(&form, &size,
+ "\r\n--%s--\r\n",
+ boundary);
+ if (result) {
+ Curl_formclean(&firstform);
+ free(boundary);
+ return result;
+ }
+
+ *sizep = size;
+
+ free(boundary);
+
+ *finalform=firstform;
+
+ return result;
+}
+
+/*
+ * Curl_FormInit() inits the struct 'form' points to with the 'formdata'
+ * and resets the 'sent' counter.
+ */
+int Curl_FormInit(struct Form *form, struct FormData *formdata )
+{
+ if(!formdata)
+ return 1; /* error */
+
+ form->data = formdata;
+ form->sent = 0;
+ form->fp = NULL;
+
+ return 0;
+}
+
+static size_t readfromfile(struct Form *form, char *buffer, size_t size)
+{
+ size_t nread;
+ if(!form->fp) {
+ /* this file hasn't yet been opened */
+ form->fp = fopen(form->data->line, "rb"); /* b is for binary */
+ if(!form->fp)
+ return (size_t)-1; /* failure */
+ }
+ nread = fread(buffer, 1, size, form->fp);
+
+ if(nread != size) {
+ /* this is the last chunk from the file, move on */
+ fclose(form->fp);
+ form->fp = NULL;
+ form->data = form->data->next;
+ }
+
+ return nread;
+}
+
+/*
+ * Curl_FormReader() is the fread() emulation function that will be used to
+ * deliver the formdata to the transfer loop and then sent away to the peer.
+ */
+size_t Curl_FormReader(char *buffer,
+ size_t size,
+ size_t nitems,
+ FILE *mydata)
+{
+ struct Form *form;
+ size_t wantedsize;
+ size_t gotsize = 0;
+
+ form=(struct Form *)mydata;
+
+ wantedsize = size * nitems;
+
+ if(!form->data)
+ return 0; /* nothing, error, empty */
+
+ if(form->data->type == FORM_FILE) {
+ gotsize = readfromfile(form, buffer, wantedsize);
+
+ if(gotsize)
+ /* If positive or -1, return. If zero, continue! */
+ return gotsize;
+ }
+ do {
+
+ if( (form->data->length - form->sent ) > wantedsize - gotsize) {
+
+ memcpy(buffer + gotsize , form->data->line + form->sent,
+ wantedsize - gotsize);
+
+ form->sent += wantedsize-gotsize;
+
+ return wantedsize;
+ }
+
+ memcpy(buffer+gotsize,
+ form->data->line + form->sent,
+ (form->data->length - form->sent) );
+ gotsize += form->data->length - form->sent;
+
+ form->sent = 0;
+
+ form->data = form->data->next; /* advance */
+
+ } while(form->data && (form->data->type != FORM_FILE));
+ /* 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;
+}
+
+/*
+ * Curl_formpostheader() returns the first line of the formpost, the
+ * request-header part (which is not part of the request-body like the rest of
+ * the post).
+ */
+char *Curl_formpostheader(void *formp, size_t *len)
+{
+ char *header;
+ struct Form *form=(struct Form *)formp;
+
+ if(!form->data)
+ return 0; /* nothing, ERROR! */
+
+ header = form->data->line;
+ *len = form->data->length;
+
+ form->data = form->data->next; /* advance */
+
+ return header;
+}
+
+
+#ifdef _FORM_DEBUG
+int FormAddTest(const char * errormsg,
+ struct curl_httppost **httppost,
+ struct curl_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";
+ 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;
+ CURLcode rc;
+ size_t size;
+ size_t nread;
+ char buffer[4096];
+ struct curl_httppost *httppost=NULL;
+ struct curl_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;
+
+ rc = Curl_getFormData(&form, httppost, NULL, &size);
+ if(rc != CURLE_OK) {
+ if(rc != CURLE_READ_ERROR) {
+ const char *errortext = curl_easy_strerror(rc);
+ fprintf(stdout, "\n==> Curl_getFormData error: %s\n", errortext);
+ }
+ return 0;
+ }
+
+ Curl_FormInit(&formread, form);
+
+ do {
+ nread = Curl_FormReader(buffer, 1, sizeof(buffer),
+ (FILE *)&formread);
+
+ if(nread < 1)
+ 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 /* _FORM_DEBUG */
+
+#else /* CURL_DISABLE_HTTP */
+CURLFORMcode curl_formadd(struct curl_httppost **httppost,
+ struct curl_httppost **last_post,
+ ...)
+{
+ (void)httppost;
+ (void)last_post;
+ return CURL_FORMADD_DISABLED;
+}
+
+int curl_formget(struct curl_httppost *form, void *arg,
+ curl_formget_callback append)
+{
+ (void) form;
+ (void) arg;
+ (void) append;
+ return CURL_FORMADD_DISABLED;
+}
+
+void curl_formfree(struct curl_httppost *form)
+{
+ (void)form;
+ /* does nothing HTTP is disabled */
+}
+
+#endif /* CURL_DISABLE_HTTP */
+
+#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
+
+/*
+ * Curl_FormBoundary() creates a suitable boundary string and returns an
+ * allocated one. This is also used by SSL-code so it must be present even
+ * if HTTP is disabled!
+ */
+char *Curl_FormBoundary(void)
+{
+ char *retstring;
+ static int randomizer; /* this is just so that two boundaries within
+ the same form won't be identical */
+ size_t i;
+
+ static const char table16[]="abcdef0123456789";
+
+ retstring = (char *)malloc(BOUNDARY_LENGTH+1);
+
+ if(!retstring)
+ return NULL; /* failed */
+
+ srand((unsigned int)time(NULL)+randomizer++); /* seed */
+
+ strcpy(retstring, "----------------------------");
+
+ for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++)
+ retstring[i] = table16[rand()%16];
+
+ /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416)
+ combinations */
+ retstring[BOUNDARY_LENGTH]=0; /* zero terminate */
+
+ return retstring;
+}
+
+#endif /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */
diff --git a/Utilities/cmcurl/formdata.h b/Utilities/cmcurl/formdata.h
new file mode 100644
index 000000000..4ca0f3c5c
--- /dev/null
+++ b/Utilities/cmcurl/formdata.h
@@ -0,0 +1,97 @@
+#ifndef __FORMDATA_H
+#define __FORMDATA_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+enum formtype {
+ FORM_DATA, /* form metadata (convert to network encoding if necessary) */
+ FORM_CONTENT, /* form content (never convert) */
+ FORM_FILE /* 'line' points to a file name we should read from
+ to create the form data (never convert) */
+};
+
+/* plain and simple linked list with lines to send */
+struct FormData {
+ struct FormData *next;
+ enum formtype type;
+ char *line;
+ size_t length;
+};
+
+struct Form {
+ struct FormData *data; /* current form line to send */
+ size_t sent; /* number of bytes of the current line that has
+ already been sent in a previous invoke */
+ FILE *fp; /* file to read from */
+};
+
+/* used by FormAdd for temporary storage */
+typedef struct FormInfo {
+ char *name;
+ bool name_alloc;
+ size_t namelength;
+ char *value;
+ bool value_alloc;
+ size_t contentslength;
+ char *contenttype;
+ bool contenttype_alloc;
+ long flags;
+ char *buffer; /* pointer to existing buffer used for file upload */
+ size_t bufferlength;
+ char *showfilename; /* The file name to show. If not set, the actual
+ file name will be used */
+ bool showfilename_alloc;
+ struct curl_slist* contentheader;
+ struct FormInfo *more;
+} FormInfo;
+
+int Curl_FormInit(struct Form *form, struct FormData *formdata );
+
+CURLcode
+Curl_getFormData(struct FormData **,
+ struct curl_httppost *post,
+ const char *custom_contenttype,
+ curl_off_t *size);
+
+/* fread() emulation */
+size_t Curl_FormReader(char *buffer,
+ size_t size,
+ size_t nitems,
+ FILE *mydata);
+
+/*
+ * Curl_formpostheader() returns the first line of the formpost, the
+ * request-header part (which is not part of the request-body like the rest of
+ * the post).
+ */
+char *Curl_formpostheader(void *formp, size_t *len);
+
+char *Curl_FormBoundary(void);
+
+void Curl_formclean(struct FormData **);
+
+CURLcode Curl_formconvert(struct SessionHandle *, struct FormData *);
+
+#endif
+
diff --git a/Utilities/cmcurl/ftp.c b/Utilities/cmcurl/ftp.c
new file mode 100644
index 000000000..883988938
--- /dev/null
+++ b/Utilities/cmcurl/ftp.c
@@ -0,0 +1,3865 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef CURL_DISABLE_FTP
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef WIN32
+
+#else /* probably 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
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#endif
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "easyif.h" /* for Curl_convert_... prototypes */
+
+#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 HAVE_KRB4
+#include "krb4.h"
+#endif
+
+#include "strtoofft.h"
+#include "strequal.h"
+#include "sslgen.h"
+#include "connect.h"
+#include "strerror.h"
+#include "memory.h"
+#include "inet_ntop.h"
+#include "select.h"
+#include "parsedate.h" /* for the week day and month names */
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "multiif.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 CURLDEBUG
+#include "memdebug.h"
+#endif
+
+#ifdef HAVE_NI_WITHSCOPEID
+#define NIFLAGS NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID
+#else
+#define NIFLAGS NI_NUMERICHOST | NI_NUMERICSERV
+#endif
+
+/* Local API functions */
+static CURLcode ftp_sendquote(struct connectdata *conn,
+ struct curl_slist *quote);
+static CURLcode ftp_quit(struct connectdata *conn);
+static CURLcode ftp_parse_url_path(struct connectdata *conn);
+static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
+static void ftp_pasv_verbose(struct connectdata *conn,
+ Curl_addrinfo *ai,
+ char *newhost, /* ascii version */
+ int port);
+static CURLcode ftp_state_post_rest(struct connectdata *conn);
+static CURLcode ftp_state_post_cwd(struct connectdata *conn);
+static CURLcode ftp_state_quote(struct connectdata *conn,
+ bool init, ftpstate instate);
+static CURLcode ftp_nb_type(struct connectdata *conn,
+ bool ascii, ftpstate state);
+static int ftp_need_type(struct connectdata *conn,
+ bool ascii);
+
+/* easy-to-use macro: */
+#define FTPSENDF(x,y,z) if ((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
+ return result
+#define NBFTPSENDF(x,y,z) if ((result = Curl_nbftpsendf(x,y,z)) != CURLE_OK) \
+ return result
+
+static void freedirs(struct connectdata *conn)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct FTP *ftp = conn->data->reqdata.proto.ftp;
+
+ int i;
+ if(ftpc->dirs) {
+ for (i=0; i < ftpc->dirdepth; i++){
+ if(ftpc->dirs[i]) {
+ free(ftpc->dirs[i]);
+ ftpc->dirs[i]=NULL;
+ }
+ }
+ free(ftpc->dirs);
+ ftpc->dirs = NULL;
+ }
+ if(ftp->file) {
+ free(ftp->file);
+ ftp->file = NULL;
+ }
+}
+
+/* Returns non-zero if the given string contains CR (\r) or LF (\n),
+ which are not allowed within RFC 959 <string>.
+ Note: The input string is in the client's encoding which might
+ not be ASCII, so escape sequences \r & \n must be used instead
+ of hex values 0x0d & 0x0a.
+*/
+static bool isBadFtpString(const char *string)
+{
+ return (bool)((NULL != strchr(string, '\r')) || (NULL != strchr(string, '\n')));
+}
+
+/***********************************************************************
+ *
+ * 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 connectdata *conn)
+{
+ int timeout_ms;
+ struct SessionHandle *data = conn->data;
+ curl_socket_t sock = conn->sock[SECONDARYSOCKET];
+ struct timeval now = Curl_tvnow();
+ long timespent = Curl_tvdiff(Curl_tvnow(), now)/1000;
+ long timeout = data->set.connecttimeout?data->set.connecttimeout:
+ (data->set.timeout?data->set.timeout: 0);
+
+ if(timeout) {
+ timeout -= timespent;
+ if(timeout<=0) {
+ failf(data, "Timed out before server could connect to us");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+
+ /* We allow the server 60 seconds to connect to us, or a custom timeout.
+ Note the typecast here. */
+ timeout_ms = (timeout?(int)timeout:60) * 1000;
+
+ switch (Curl_select(sock, CURL_SOCKET_BAD, timeout_ms)) {
+ 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 */
+ {
+ curl_socket_t s = CURL_SOCKET_BAD;
+#ifdef ENABLE_IPV6
+ struct Curl_sockaddr_storage add;
+#else
+ struct sockaddr_in add;
+#endif
+ socklen_t size = (socklen_t) sizeof(add);
+
+ if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
+ size = sizeof(add);
+
+ s=accept(sock, (struct sockaddr *) &add, &size);
+ }
+ sclose(sock); /* close the first socket */
+
+ if (CURL_SOCKET_BAD == s) {
+ /* DIE! */
+ failf(data, "Error accept()ing server connect");
+ return CURLE_FTP_PORT_FAILED;
+ }
+ infof(data, "Connection accepted from server\n");
+
+ conn->sock[SECONDARYSOCKET] = s;
+ Curl_nonblock(s, TRUE); /* enable non-blocking */
+ }
+ break;
+ }
+
+ return CURLE_OK;
+}
+
+/* initialize stuff to prepare for reading a fresh new response */
+static void ftp_respinit(struct connectdata *conn)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ ftpc->nread_resp = 0;
+ ftpc->linestart_resp = conn->data->state.buffer;
+}
+
+/* macro to check for the last line in an FTP server response */
+#define lastline(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
+ ISDIGIT(line[2]) && (' ' == line[3]))
+
+static CURLcode ftp_readresp(curl_socket_t sockfd,
+ struct connectdata *conn,
+ int *ftpcode, /* return the ftp-code if done */
+ size_t *size) /* size of the response */
+{
+ int perline; /* count bytes per line */
+ bool keepon=TRUE;
+ ssize_t gotbytes;
+ char *ptr;
+ struct SessionHandle *data = conn->data;
+ char *buf = data->state.buffer;
+ CURLcode result = CURLE_OK;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ int code = 0;
+
+ if (ftpcode)
+ *ftpcode = 0; /* 0 for errors or not done */
+
+ ptr=buf + ftpc->nread_resp;
+
+ perline= (int)(ptr-ftpc->linestart_resp); /* number of bytes in the current
+ line, so far */
+ keepon=TRUE;
+
+ while((ftpc->nread_resp<BUFSIZE) && (keepon && !result)) {
+
+ if(ftpc->cache) {
+ /* we had data in the "cache", copy that instead of doing an actual
+ * read
+ *
+ * ftp->cache_size is cast to int here. This should be safe,
+ * because it would have been populated with something of size
+ * int to begin with, even though its datatype may be larger
+ * than an int.
+ */
+ memcpy(ptr, ftpc->cache, (int)ftpc->cache_size);
+ gotbytes = (int)ftpc->cache_size;
+ free(ftpc->cache); /* free the cache */
+ ftpc->cache = NULL; /* clear the pointer */
+ ftpc->cache_size = 0; /* zero the size just in case */
+ }
+ else {
+ int res = Curl_read(conn, sockfd, ptr, BUFSIZE-ftpc->nread_resp,
+ &gotbytes);
+ if(res < 0)
+ /* EWOULDBLOCK */
+ return CURLE_OK; /* return */
+
+#ifdef CURL_DOES_CONVERSIONS
+ if((res == CURLE_OK) && (gotbytes > 0)) {
+ /* convert from the network encoding */
+ result = res = Curl_convert_from_network(data, ptr, gotbytes);
+ /* Curl_convert_from_network calls failf if unsuccessful */
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+ if(CURLE_OK != res)
+ keepon = FALSE;
+ }
+
+ if(!keepon)
+ ;
+ else if(gotbytes <= 0) {
+ keepon = FALSE;
+ result = CURLE_RECV_ERROR;
+ failf(data, "FTP response reading failed");
+ }
+ else {
+ /* we got a whole chunk of data, which can be anything from one
+ * byte to a set of lines and possible just a piece of the last
+ * line */
+ int i;
+
+ conn->headerbytecount += gotbytes;
+
+ ftpc->nread_resp += 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 */
+
+ /* output debug output if that is requested */
+ if(data->set.verbose)
+ Curl_debug(data, CURLINFO_HEADER_IN,
+ ftpc->linestart_resp, (size_t)perline, conn);
+
+ /*
+ * We pass all response-lines to the callback function registered
+ * for "headers". The response lines can be seen as a kind of
+ * headers.
+ */
+ result = Curl_client_write(conn, CLIENTWRITE_HEADER,
+ ftpc->linestart_resp, perline);
+ if(result)
+ return result;
+
+ if(perline>3 && lastline(ftpc->linestart_resp)) {
+ /* 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=ftpc->linestart_resp, n=0; meow<ptr; meow++, n++)
+ buf[n] = *meow;
+ *meow=0; /* zero terminate */
+ keepon=FALSE;
+ ftpc->linestart_resp = ptr+1; /* advance pointer */
+ i++; /* skip this before getting out */
+
+ *size = ftpc->nread_resp; /* size of the response */
+ ftpc->nread_resp = 0; /* restart */
+ break;
+ }
+ perline=0; /* line starts over here */
+ ftpc->linestart_resp = ptr+1;
+ }
+ }
+ if(!keepon && (i != gotbytes)) {
+ /* We found the end of the response lines, but we didn't parse the
+ full chunk of data we have read from the server. We therefore need
+ to store the rest of the data to be checked on the next invoke as
+ it may actually contain another end of response already! */
+ ftpc->cache_size = gotbytes - i;
+ ftpc->cache = (char *)malloc((int)ftpc->cache_size);
+ if(ftpc->cache)
+ memcpy(ftpc->cache, ftpc->linestart_resp, (int)ftpc->cache_size);
+ else
+ return CURLE_OUT_OF_MEMORY; /**BANG**/
+ }
+ } /* there was data */
+
+ } /* while there's buffer left and loop is requested */
+
+ if(!result)
+ code = atoi(buf);
+
+#ifdef HAVE_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
+
+ *ftpcode=code; /* return the initial number like this */
+
+
+ /* store the latest code for later retrieval */
+ conn->data->info.httpcode=code;
+
+ return result;
+}
+
+/* --- 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.
+ */
+
+CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
+ struct connectdata *conn,
+ int *ftpcode) /* return the ftp-code */
+{
+ /*
+ * We cannot read just one byte per read() and then go back to select() as
+ * the OpenSSL read() doesn't grok that properly.
+ *
+ * Alas, read as much as possible, split up into lines, use the ending
+ * line in a response or continue reading. */
+
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ int perline; /* count bytes per line */
+ bool keepon=TRUE;
+ ssize_t gotbytes;
+ char *ptr;
+ long timeout; /* timeout in seconds */
+ int interval_ms;
+ struct SessionHandle *data = conn->data;
+ char *line_start;
+ int code=0; /* default ftp "error code" to return */
+ char *buf = data->state.buffer;
+ CURLcode result = CURLE_OK;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct timeval now = Curl_tvnow();
+
+ if (ftpcode)
+ *ftpcode = 0; /* 0 for errors */
+
+ ptr=buf;
+ line_start = buf;
+
+ *nreadp=0;
+ perline=0;
+ keepon=TRUE;
+
+ while((*nreadp<BUFSIZE) && (keepon && !result)) {
+ /* check and reset timeout value every lap */
+ if(data->set.ftp_response_timeout )
+ /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
+ remaining time. Also, use "now" as opposed to "conn->now"
+ because ftp_response_timeout is only supposed to govern
+ the response for any given ftp response, not for the time
+ from connect to the given ftp response. */
+ timeout = data->set.ftp_response_timeout - /* timeout time */
+ Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
+ else 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 */
+ else
+ /* Even without a requested timeout, we only wait response_time
+ seconds for the full response to arrive before we bail out */
+ timeout = ftpc->response_time -
+ Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
+
+ if(timeout <=0 ) {
+ failf(data, "FTP response timeout");
+ return CURLE_OPERATION_TIMEDOUT; /* already too little time */
+ }
+
+ if(!ftpc->cache) {
+ interval_ms = 1 * 1000; /* use 1 second timeout intervals */
+
+ switch (Curl_select(sockfd, CURL_SOCKET_BAD, interval_ms)) {
+ case -1: /* select() error, stop reading */
+ result = CURLE_RECV_ERROR;
+ failf(data, "FTP response aborted due to select() error: %d",
+ Curl_sockerrno());
+ break;
+ case 0: /* timeout */
+ if(Curl_pgrsUpdate(conn))
+ return CURLE_ABORTED_BY_CALLBACK;
+ continue; /* just continue in our loop for the timeout duration */
+
+ default:
+ break;
+ }
+ }
+ if(CURLE_OK == result) {
+ /*
+ * 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(ftpc->cache) {
+ /* we had data in the "cache", copy that instead of doing an actual
+ * read
+ *
+ * Dave Meyer, December 2003:
+ * ftp->cache_size is cast to int here. This should be safe,
+ * because it would have been populated with something of size
+ * int to begin with, even though its datatype may be larger
+ * than an int.
+ */
+ memcpy(ptr, ftpc->cache, (int)ftpc->cache_size);
+ gotbytes = (int)ftpc->cache_size;
+ free(ftpc->cache); /* free the cache */
+ ftpc->cache = NULL; /* clear the pointer */
+ ftpc->cache_size = 0; /* zero the size just in case */
+ }
+ else {
+ int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes);
+ if(res < 0)
+ /* EWOULDBLOCK */
+ continue; /* go looping again */
+
+#ifdef CURL_DOES_CONVERSIONS
+ if((res == CURLE_OK) && (gotbytes > 0)) {
+ /* convert from the network encoding */
+ result = res = Curl_convert_from_network(data, ptr, gotbytes);
+ /* Curl_convert_from_network calls failf if unsuccessful */
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+ if(CURLE_OK != res)
+ keepon = FALSE;
+ }
+
+ if(!keepon)
+ ;
+ else if(gotbytes <= 0) {
+ keepon = FALSE;
+ result = CURLE_RECV_ERROR;
+ failf(data, "FTP response reading failed");
+ }
+ else {
+ /* we got a whole chunk of data, which can be anything from one
+ * byte to a set of lines and possible just a piece of the last
+ * line */
+ int i;
+
+ conn->headerbytecount += gotbytes;
+
+ *nreadp += 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 */
+
+ /* output debug output if that is requested */
+ if(data->set.verbose)
+ Curl_debug(data, CURLINFO_HEADER_IN,
+ line_start, (size_t)perline, conn);
+
+ /*
+ * We pass all response-lines to the callback function registered
+ * for "headers". The response lines can be seen as a kind of
+ * headers.
+ */
+ result = Curl_client_write(conn, CLIENTWRITE_HEADER,
+ line_start, perline);
+ if(result)
+ return result;
+
+ 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. */
+ ftpc->cache_size = gotbytes - i;
+ ftpc->cache = (char *)malloc((int)ftpc->cache_size);
+ if(ftpc->cache)
+ memcpy(ftpc->cache, line_start, (int)ftpc->cache_size);
+ else
+ return CURLE_OUT_OF_MEMORY; /**BANG**/
+ }
+ } /* there was data */
+ } /* if(no error) */
+ } /* while there's buffer left and loop is requested */
+
+ if(!result)
+ code = atoi(buf);
+
+#ifdef HAVE_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(ftpcode)
+ *ftpcode=code; /* return the initial number like this */
+
+ /* store the latest code for later retrieval */
+ conn->data->info.httpcode=code;
+
+ return result;
+}
+
+/* This is the ONLY way to change FTP state! */
+static void state(struct connectdata *conn,
+ ftpstate state)
+{
+#ifdef CURLDEBUG
+ /* for debug purposes */
+ const char *names[]={
+ "STOP",
+ "WAIT220",
+ "AUTH",
+ "USER",
+ "PASS",
+ "ACCT",
+ "PBSZ",
+ "PROT",
+ "CCC",
+ "PWD",
+ "QUOTE",
+ "RETR_PREQUOTE",
+ "STOR_PREQUOTE",
+ "POSTQUOTE",
+ "CWD",
+ "MKD",
+ "MDTM",
+ "TYPE",
+ "LIST_TYPE",
+ "RETR_TYPE",
+ "STOR_TYPE",
+ "SIZE",
+ "RETR_SIZE",
+ "STOR_SIZE",
+ "REST",
+ "RETR_REST",
+ "PORT",
+ "PASV",
+ "LIST",
+ "RETR",
+ "STOR",
+ "QUIT"
+ };
+#endif
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+#ifdef CURLDEBUG
+ if(ftpc->state != state)
+ infof(conn->data, "FTP %p state change from %s to %s\n",
+ ftpc, names[ftpc->state], names[state]);
+#endif
+ ftpc->state = state;
+}
+
+static CURLcode ftp_state_user(struct connectdata *conn)
+{
+ CURLcode result;
+ struct FTP *ftp = conn->data->reqdata.proto.ftp;
+ /* send USER */
+ NBFTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
+
+ state(conn, FTP_USER);
+ conn->data->state.ftp_trying_alternative = FALSE;
+
+ return CURLE_OK;
+}
+
+static CURLcode ftp_state_pwd(struct connectdata *conn)
+{
+ CURLcode result;
+
+ /* send PWD to discover our entry point */
+ NBFTPSENDF(conn, "PWD", NULL);
+ state(conn, FTP_PWD);
+
+ return CURLE_OK;
+}
+
+/* For the FTP "protocol connect" and "doing" phases only */
+int Curl_ftp_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ if(!numsocks)
+ return GETSOCK_BLANK;
+
+ socks[0] = conn->sock[FIRSTSOCKET];
+
+ if(ftpc->sendleft) {
+ /* write mode */
+ return GETSOCK_WRITESOCK(0);
+ }
+
+ /* read mode */
+ return GETSOCK_READSOCK(0);
+}
+
+/* This is called after the FTP_QUOTE state is passed.
+
+ ftp_state_cwd() sends the range of PWD commands to the server to change to
+ the correct directory. It may also need to send MKD commands to create
+ missing ones, if that option is enabled.
+*/
+static CURLcode ftp_state_cwd(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ if(ftpc->cwddone)
+ /* already done and fine */
+ result = ftp_state_post_cwd(conn);
+ else {
+ ftpc->count2 = 0;
+ if (conn->bits.reuse && ftpc->entrypath) {
+ /* This is a re-used connection. Since we change directory to where the
+ transfer is taking place, we must first get back to the original dir
+ where we ended up after login: */
+ ftpc->count1 = 0; /* we count this as the first path, then we add one
+ for all upcoming ones in the ftp->dirs[] array */
+ NBFTPSENDF(conn, "CWD %s", ftpc->entrypath);
+ state(conn, FTP_CWD);
+ }
+ else {
+ if(ftpc->dirdepth) {
+ ftpc->count1 = 1;
+ /* issue the first CWD, the rest is sent when the CWD responses are
+ received... */
+ NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
+ state(conn, FTP_CWD);
+ }
+ else {
+ /* No CWD necessary */
+ result = ftp_state_post_cwd(conn);
+ }
+ }
+ }
+ return result;
+}
+
+typedef enum {
+ EPRT,
+ PORT,
+ DONE
+} ftpport;
+
+static CURLcode ftp_state_use_port(struct connectdata *conn,
+ ftpport fcmd) /* start with this */
+
+{
+ CURLcode result = CURLE_OK;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct SessionHandle *data=conn->data;
+ curl_socket_t portsock= CURL_SOCKET_BAD;
+ char myhost[256] = "";
+
+#ifdef ENABLE_IPV6
+ /******************************************************************
+ * IPv6-specific section
+ */
+ struct Curl_sockaddr_storage ss;
+ struct addrinfo *res, *ai;
+ socklen_t sslen;
+ char hbuf[NI_MAXHOST];
+ struct sockaddr *sa=(struct sockaddr *)&ss;
+ char tmp[1024];
+ const char *mode[] = { "EPRT", "PORT", NULL };
+ int rc;
+ int error;
+ char *host=NULL;
+ struct Curl_dns_entry *h=NULL;
+ unsigned short port = 0;
+
+ /* Step 1, figure out what address that is requested */
+
+ if(data->set.ftpport && (strlen(data->set.ftpport) > 1)) {
+ /* attempt to get the address of the given interface name */
+ if(!Curl_if2ip(data->set.ftpport, hbuf, sizeof(hbuf)))
+ /* not an interface, use the given string as host name instead */
+ host = data->set.ftpport;
+ else
+ host = hbuf; /* use the hbuf for host name */
+ } /* data->set.ftpport */
+
+ if(!host) {
+ /* not an interface and not a host name, get default by extracting
+ the IP from the control connection */
+
+ sslen = sizeof(ss);
+ if (getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen)) {
+ failf(data, "getsockname() failed: %s",
+ Curl_strerror(conn, Curl_sockerrno()) );
+ return CURLE_FTP_PORT_FAILED;
+ }
+
+ if (sslen > (socklen_t)sizeof(ss))
+ sslen = sizeof(ss);
+ rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL,
+ 0, NIFLAGS);
+ if(rc) {
+ failf(data, "getnameinfo() returned %d \n", rc);
+ return CURLE_FTP_PORT_FAILED;
+ }
+ host = hbuf; /* use this host name */
+ }
+
+ rc = Curl_resolv(conn, host, 0, &h);
+ if(rc == CURLRESOLV_PENDING)
+ rc = Curl_wait_for_resolv(conn, &h);
+ if(h) {
+ res = h->addr;
+ /* when we return from this function, we can forget about this entry
+ to we can unlock it now already */
+ Curl_resolv_unlock(data, h);
+ } /* (h) */
+ else
+ res = NULL; /* failure! */
+
+
+ /* step 2, create a socket for the requested address */
+
+ portsock = CURL_SOCKET_BAD;
+ error = 0;
+ for (ai = res; ai; ai = ai->ai_next) {
+ /*
+ * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
+ */
+ if (ai->ai_socktype == 0)
+ ai->ai_socktype = conn->socktype;
+
+ portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (portsock == CURL_SOCKET_BAD) {
+ error = Curl_sockerrno();
+ continue;
+ }
+ break;
+ }
+ if(!ai) {
+ failf(data, "socket failure: %s", Curl_strerror(conn, error));
+ return CURLE_FTP_PORT_FAILED;
+ }
+
+ /* step 3, bind to a suitable local address */
+
+ /* Try binding the given address. */
+ if (bind(portsock, ai->ai_addr, ai->ai_addrlen)) {
+
+ /* It failed. Bind the address used for the control connection instead */
+ sslen = sizeof(ss);
+ if (getsockname(conn->sock[FIRSTSOCKET],
+ (struct sockaddr *)sa, &sslen)) {
+ failf(data, "getsockname() failed: %s",
+ Curl_strerror(conn, Curl_sockerrno()) );
+ sclose(portsock);
+ return CURLE_FTP_PORT_FAILED;
+ }
+
+ /* set port number to zero to make bind() pick "any" */
+ if(((struct sockaddr *)sa)->sa_family == AF_INET)
+ ((struct sockaddr_in *)sa)->sin_port=0;
+ else
+ ((struct sockaddr_in6 *)sa)->sin6_port =0;
+
+ if (sslen > (socklen_t)sizeof(ss))
+ sslen = sizeof(ss);
+
+ if(bind(portsock, (struct sockaddr *)sa, sslen)) {
+ failf(data, "bind failed: %s", Curl_strerror(conn, Curl_sockerrno()));
+ sclose(portsock);
+ return CURLE_FTP_PORT_FAILED;
+ }
+ }
+
+ /* get the name again after the bind() so that we can extract the
+ port number it uses now */
+ sslen = sizeof(ss);
+ if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
+ failf(data, "getsockname() failed: %s",
+ Curl_strerror(conn, Curl_sockerrno()) );
+ sclose(portsock);
+ return CURLE_FTP_PORT_FAILED;
+ }
+
+ /* step 4, listen on the socket */
+
+ if (listen(portsock, 1)) {
+ failf(data, "socket failure: %s", Curl_strerror(conn, Curl_sockerrno()));
+ sclose(portsock);
+ return CURLE_FTP_PORT_FAILED;
+ }
+
+ /* step 5, send the proper FTP command */
+
+ /* get a plain printable version of the numerical address to work with
+ below */
+ Curl_printable_address(ai, myhost, sizeof(myhost));
+
+#ifdef PF_INET6
+ if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
+ /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
+ request and enable EPRT again! */
+ conn->bits.ftp_use_eprt = TRUE;
+#endif
+
+ for (; fcmd != DONE; fcmd++) {
+
+ if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
+ /* if disabled, goto next */
+ continue;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+ break;
+ case AF_INET6:
+ port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
+ break;
+ default:
+ break;
+ }
+
+ if (EPRT == fcmd) {
+ /*
+ * Two fine examples from RFC2428;
+ *
+ * EPRT |1|132.235.1.2|6275|
+ *
+ * EPRT |2|1080::8:800:200C:417A|5282|
+ */
+
+ result = Curl_nbftpsendf(conn, "%s |%d|%s|%d|", mode[fcmd],
+ ai->ai_family == AF_INET?1:2,
+ myhost, port);
+ if(result)
+ return result;
+ break;
+ }
+ else if (PORT == fcmd) {
+ char *source = myhost;
+ char *dest = tmp;
+
+ if ((PORT == fcmd) && ai->ai_family != AF_INET)
+ continue;
+
+ /* translate x.x.x.x to x,x,x,x */
+ while(source && *source) {
+ if(*source == '.')
+ *dest=',';
+ else
+ *dest = *source;
+ dest++;
+ source++;
+ }
+ *dest = 0;
+ snprintf(dest, 20, ",%d,%d", port>>8, port&0xff);
+
+ result = Curl_nbftpsendf(conn, "%s %s", mode[fcmd], tmp);
+ if(result)
+ return result;
+ break;
+ }
+ }
+
+ /* store which command was sent */
+ ftpc->count1 = fcmd;
+
+ /* we set the secondary socket variable to this for now, it is only so that
+ the cleanup function will close it in case we fail before the true
+ secondary stuff is made */
+ if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
+ sclose(conn->sock[SECONDARYSOCKET]);
+ conn->sock[SECONDARYSOCKET] = portsock;
+
+#else
+ /******************************************************************
+ * IPv4-specific section
+ */
+ struct sockaddr_in sa;
+ unsigned short porttouse;
+ bool sa_filled_in = FALSE;
+ Curl_addrinfo *addr = NULL;
+ unsigned short ip[4];
+ bool freeaddr = TRUE;
+ socklen_t sslen = sizeof(sa);
+
+ (void)fcmd; /* not used in the IPv4 code */
+ if(data->set.ftpport) {
+ in_addr_t in;
+
+ /* First check if the given name is an IP address */
+ in=inet_addr(data->set.ftpport);
+
+ if(in != CURL_INADDR_NONE)
+ /* this is an IPv4 address */
+ addr = Curl_ip2addr(in, data->set.ftpport, 0);
+ else {
+ if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
+ /* The interface to IP conversion provided a dotted address */
+ in=inet_addr(myhost);
+ addr = Curl_ip2addr(in, myhost, 0);
+ }
+ else if(strlen(data->set.ftpport)> 1) {
+ /* might be a host name! */
+ struct Curl_dns_entry *h=NULL;
+ int rc = Curl_resolv(conn, data->set.ftpport, 0, &h);
+ if(rc == CURLRESOLV_PENDING)
+ /* BLOCKING */
+ rc = Curl_wait_for_resolv(conn, &h);
+ if(h) {
+ addr = h->addr;
+ /* when we return from this function, we can forget about this entry
+ so we can unlock it now already */
+ Curl_resolv_unlock(data, h);
+
+ freeaddr = FALSE; /* make sure we don't free 'addr' in this function
+ since it points to a DNS cache entry! */
+ } /* (h) */
+ else {
+ infof(data, "Failed to resolve host name %s\n", data->set.ftpport);
+ }
+ } /* strlen */
+ } /* CURL_INADDR_NONE */
+ } /* data->set.ftpport */
+
+ if(!addr) {
+ /* pick a suitable default here */
+
+ if (getsockname(conn->sock[FIRSTSOCKET],
+ (struct sockaddr *)&sa, &sslen)) {
+ failf(data, "getsockname() failed: %s",
+ Curl_strerror(conn, Curl_sockerrno()) );
+ return CURLE_FTP_PORT_FAILED;
+ }
+ if (sslen > (socklen_t)sizeof(sa))
+ sslen = sizeof(sa);
+
+ sa_filled_in = TRUE; /* the sa struct is filled in */
+ }
+
+ if (addr || sa_filled_in) {
+ portsock = socket(AF_INET, SOCK_STREAM, 0);
+ if(CURL_SOCKET_BAD != portsock) {
+
+ /* we set the secondary socket variable to this for now, it
+ is only so that the cleanup function will close it in case
+ we fail before the true secondary stuff is made */
+ if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
+ sclose(conn->sock[SECONDARYSOCKET]);
+ conn->sock[SECONDARYSOCKET] = portsock;
+
+ if(!sa_filled_in) {
+ memcpy(&sa, addr->ai_addr, sslen);
+ sa.sin_addr.s_addr = INADDR_ANY;
+ }
+
+ sa.sin_port = 0;
+ sslen = sizeof(sa);
+
+ if(bind(portsock, (struct sockaddr *)&sa, sslen) == 0) {
+ /* we succeeded to bind */
+ struct sockaddr_in add;
+ socklen_t socksize = sizeof(add);
+
+ if(getsockname(portsock, (struct sockaddr *) &add,
+ &socksize)) {
+ failf(data, "getsockname() failed: %s",
+ Curl_strerror(conn, Curl_sockerrno()) );
+ return CURLE_FTP_PORT_FAILED;
+ }
+ porttouse = ntohs(add.sin_port);
+
+ if ( listen(portsock, 1) < 0 ) {
+ failf(data, "listen(2) failed on socket");
+ return CURLE_FTP_PORT_FAILED;
+ }
+ }
+ else {
+ failf(data, "bind(2) failed on socket");
+ return CURLE_FTP_PORT_FAILED;
+ }
+ }
+ else {
+ failf(data, "socket(2) failed (%s)");
+ return CURLE_FTP_PORT_FAILED;
+ }
+ }
+ else {
+ failf(data, "couldn't find IP address to use");
+ return CURLE_FTP_PORT_FAILED;
+ }
+
+ if(sa_filled_in)
+ Curl_inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
+ myhost, sizeof(myhost));
+ else
+ Curl_printable_address(addr, myhost, sizeof(myhost));
+
+ if(4 == sscanf(myhost, "%hu.%hu.%hu.%hu",
+ &ip[0], &ip[1], &ip[2], &ip[3])) {
+
+ infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
+ ip[0], ip[1], ip[2], ip[3], porttouse);
+
+ result=Curl_nbftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
+ ip[0], ip[1], ip[2], ip[3],
+ porttouse >> 8, porttouse & 255);
+ if(result)
+ return result;
+ }
+ else
+ return CURLE_FTP_PORT_FAILED;
+
+ if(freeaddr)
+ Curl_freeaddrinfo(addr);
+
+ ftpc->count1 = PORT;
+
+#endif /* end of ipv4-specific code */
+
+ /* this tcpconnect assignment below is a hackish work-around to make the
+ multi interface with active FTP work - as it will not wait for a
+ (passive) connect in Curl_is_connected().
+
+ The *proper* fix is to make sure that the active connection from the
+ server is done in a non-blocking way. Currently, it is still BLOCKING.
+ */
+ conn->bits.tcpconnect = TRUE;
+
+ state(conn, FTP_PORT);
+ return result;
+}
+
+static CURLcode ftp_state_use_pasv(struct connectdata *conn)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ CURLcode result = CURLE_OK;
+ /*
+ Here's the excecutive summary on what to do:
+
+ PASV is RFC959, expect:
+ 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
+
+ LPSV is RFC1639, expect:
+ 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
+
+ EPSV is RFC2428, expect:
+ 229 Entering Extended Passive Mode (|||port|)
+
+ */
+
+ const char *mode[] = { "EPSV", "PASV", NULL };
+ int modeoff;
+
+#ifdef PF_INET6
+ if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
+ /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
+ request and enable EPSV again! */
+ conn->bits.ftp_use_epsv = TRUE;
+#endif
+
+ modeoff = conn->bits.ftp_use_epsv?0:1;
+
+ result = Curl_nbftpsendf(conn, "%s", mode[modeoff]);
+ if(result)
+ return result;
+
+ ftpc->count1 = modeoff;
+ state(conn, FTP_PASV);
+ infof(conn->data, "Connect data stream passively\n");
+
+ return result;
+}
+
+/* REST is the last command in the chain of commands when a "head"-like
+ request is made. Thus, if an actual transfer is to be made this is where
+ we take off for real. */
+static CURLcode ftp_state_post_rest(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = conn->data->reqdata.proto.ftp;
+ struct SessionHandle *data = conn->data;
+
+ if(ftp->no_transfer || conn->bits.no_body) {
+ /* doesn't transfer any data */
+ ftp->no_transfer = TRUE;
+
+ /* still possibly do PRE QUOTE jobs */
+ state(conn, FTP_RETR_PREQUOTE);
+ result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
+ }
+ else if(data->set.ftp_use_port) {
+ /* We have chosen to use the PORT (or similar) command */
+ result = ftp_state_use_port(conn, EPRT);
+ }
+ else {
+ /* We have chosen (this is default) to use the PASV (or similar) command */
+ result = ftp_state_use_pasv(conn);
+ }
+ return result;
+}
+
+static CURLcode ftp_state_post_size(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = conn->data->reqdata.proto.ftp;
+
+ if(ftp->no_transfer) {
+ /* if a "head"-like request is being made */
+
+ /* Determine if server can respond to REST command and therefore
+ whether it supports range */
+ NBFTPSENDF(conn, "REST %d", 0);
+
+ state(conn, FTP_REST);
+ }
+ else
+ result = ftp_state_post_rest(conn);
+
+ return result;
+}
+
+static CURLcode ftp_state_post_type(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = conn->data->reqdata.proto.ftp;
+
+ if(ftp->no_transfer) {
+ /* if a "head"-like request is being made */
+
+ /* we know ftp->file is a valid pointer to a file name */
+ NBFTPSENDF(conn, "SIZE %s", ftp->file);
+
+ state(conn, FTP_SIZE);
+ }
+ else
+ result = ftp_state_post_size(conn);
+
+ return result;
+}
+
+static CURLcode ftp_state_post_listtype(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+
+ /* If this output is to be machine-parsed, the NLST command might be better
+ to use, since the LIST command output is not specified or standard in any
+ way. It has turned out that the NLST list output is not the same on all
+ servers either... */
+
+ NBFTPSENDF(conn, "%s",
+ data->set.customrequest?data->set.customrequest:
+ (data->set.ftp_list_only?"NLST":"LIST"));
+
+ state(conn, FTP_LIST);
+
+ return result;
+}
+
+static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+
+ /* We've sent the TYPE, now we must send the list of prequote strings */
+
+ result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
+
+ return result;
+}
+
+static CURLcode ftp_state_post_stortype(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+
+ /* We've sent the TYPE, now we must send the list of prequote strings */
+
+ result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
+
+ return result;
+}
+
+static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = conn->data->reqdata.proto.ftp;
+ struct SessionHandle *data = conn->data;
+
+ /* 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(conn->bits.no_body && data->set.include_header && ftp->file &&
+ ftp_need_type(conn, data->set.prefer_ascii)) {
+ /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
+ may not support it! It is however the only way we have to get a file's
+ size! */
+
+ ftp->no_transfer = TRUE; /* this means no actual transfer will be made */
+
+ /* Some servers return different sizes for different modes, and thus we
+ must set the proper type before we check the size */
+ result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
+ if (result)
+ return result;
+ }
+ else
+ result = ftp_state_post_type(conn);
+
+ return result;
+}
+
+/* This is called after the CWD commands have been done in the beginning of
+ the DO phase */
+static CURLcode ftp_state_post_cwd(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = conn->data->reqdata.proto.ftp;
+ struct SessionHandle *data = conn->data;
+
+ /* Requested time of file or time-depended transfer? */
+ if((data->set.get_filetime || data->set.timecondition) && ftp->file) {
+
+ /* we have requested to get the modified-time of the file, this is a white
+ spot as the MDTM is not mentioned in RFC959 */
+ NBFTPSENDF(conn, "MDTM %s", ftp->file);
+
+ state(conn, FTP_MDTM);
+ }
+ else
+ result = ftp_state_post_mdtm(conn);
+
+ return result;
+}
+
+
+/* This is called after the TYPE and possible quote commands have been sent */
+static CURLcode ftp_state_ul_setup(struct connectdata *conn,
+ bool sizechecked)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = conn->data->reqdata.proto.ftp;
+ struct SessionHandle *data = conn->data;
+ curl_off_t passed=0;
+
+ if((data->reqdata.resume_from && !sizechecked) ||
+ ((data->reqdata.resume_from > 0) && sizechecked)) {
+ /* we're about to continue the uploading of a file */
+ /* 1. get already existing file's size. We use the SIZE command for this
+ which may not exist in the server! The SIZE command is not in
+ RFC959. */
+
+ /* 2. This used to set REST. But since we can do append, we
+ don't another ftp command. We just skip the source file
+ offset and then we APPEND the rest on the file instead */
+
+ /* 3. pass file-size number of bytes in the source file */
+ /* 4. lower the infilesize counter */
+ /* => transfer as usual */
+
+ if(data->reqdata.resume_from < 0 ) {
+ /* Got no given size to start from, figure it out */
+ NBFTPSENDF(conn, "SIZE %s", ftp->file);
+ state(conn, FTP_STOR_SIZE);
+ return result;
+ }
+
+ /* enable append */
+ data->set.ftp_append = TRUE;
+
+ /* Let's read off the proper amount of bytes from the input. If we knew it
+ was a proper file we could've just fseek()ed but we only have a stream
+ here */
+
+ /* TODO: allow the ioctlfunction to provide a fast forward function that
+ can be used here and use this method only as a fallback! */
+ do {
+ curl_off_t readthisamountnow = (data->reqdata.resume_from - passed);
+ curl_off_t actuallyread;
+
+ if(readthisamountnow > BUFSIZE)
+ readthisamountnow = BUFSIZE;
+
+ actuallyread = (curl_off_t)
+ conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
+ conn->fread_in);
+
+ passed += actuallyread;
+ if(actuallyread != readthisamountnow) {
+ failf(data, "Could only read %" FORMAT_OFF_T
+ " bytes from the input", passed);
+ return CURLE_FTP_COULDNT_USE_REST;
+ }
+ } while(passed != data->reqdata.resume_from);
+
+ /* now, decrease the size of the read */
+ if(data->set.infilesize>0) {
+ data->set.infilesize -= data->reqdata.resume_from;
+
+ if(data->set.infilesize <= 0) {
+ infof(data, "File already completely uploaded\n");
+
+ /* no data to transfer */
+ result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+
+ /* Set no_transfer so that we won't get any error in
+ * Curl_ftp_done() because we didn't transfer anything! */
+ ftp->no_transfer = TRUE;
+
+ state(conn, FTP_STOP);
+ return CURLE_OK;
+ }
+ }
+ /* we've passed, proceed as normal */
+ } /* resume_from */
+
+ NBFTPSENDF(conn, data->set.ftp_append?"APPE %s":"STOR %s",
+ ftp->file);
+
+ state(conn, FTP_STOR);
+
+ return result;
+}
+
+static CURLcode ftp_state_quote(struct connectdata *conn,
+ bool init,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ struct FTP *ftp = data->reqdata.proto.ftp;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ bool quote=FALSE;
+ struct curl_slist *item;
+
+ switch(instate) {
+ case FTP_QUOTE:
+ default:
+ item = data->set.quote;
+ break;
+ case FTP_RETR_PREQUOTE:
+ case FTP_STOR_PREQUOTE:
+ item = data->set.prequote;
+ break;
+ case FTP_POSTQUOTE:
+ item = data->set.postquote;
+ break;
+ }
+
+ if(init)
+ ftpc->count1 = 0;
+ else
+ ftpc->count1++;
+
+ if(item) {
+ int i = 0;
+
+ /* Skip count1 items in the linked list */
+ while((i< ftpc->count1) && item) {
+ item = item->next;
+ i++;
+ }
+ if(item) {
+ NBFTPSENDF(conn, "%s", item->data);
+ state(conn, instate);
+ quote = TRUE;
+ }
+ }
+
+ if(!quote) {
+ /* No more quote to send, continue to ... */
+ switch(instate) {
+ case FTP_QUOTE:
+ default:
+ result = ftp_state_cwd(conn);
+ break;
+ case FTP_RETR_PREQUOTE:
+ if (ftp->no_transfer)
+ state(conn, FTP_STOP);
+ else {
+ NBFTPSENDF(conn, "SIZE %s", ftp->file);
+ state(conn, FTP_RETR_SIZE);
+ }
+ break;
+ case FTP_STOR_PREQUOTE:
+ result = ftp_state_ul_setup(conn, FALSE);
+ break;
+ case FTP_POSTQUOTE:
+ break;
+ }
+ }
+
+ return result;
+}
+
+static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
+ int ftpcode)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ CURLcode result;
+ struct SessionHandle *data=conn->data;
+ Curl_addrinfo *conninfo;
+ struct Curl_dns_entry *addr=NULL;
+ int rc;
+ unsigned short connectport; /* the local port connect() should use! */
+ unsigned short newport=0; /* remote port */
+ bool connected;
+
+ /* newhost must be able to hold a full IP-style address in ASCII, which
+ in the IPv6 case means 5*8-1 = 39 letters */
+#define NEWHOST_BUFSIZE 48
+ char newhost[NEWHOST_BUFSIZE];
+ char *str=&data->state.buffer[4]; /* start on the first letter */
+
+ if((ftpc->count1 == 0) &&
+ (ftpcode == 229)) {
+ /* positive EPSV response */
+ char *ptr = strchr(str, '(');
+ if(ptr) {
+ unsigned int num;
+ char separator[4];
+ ptr++;
+ if(5 == sscanf(ptr, "%c%c%c%u%c",
+ &separator[0],
+ &separator[1],
+ &separator[2],
+ &num,
+ &separator[3])) {
+ const char sep1 = separator[0];
+ int i;
+
+ /* The four separators should be identical, or else this is an oddly
+ formatted reply and we bail out immediately. */
+ for(i=1; i<4; i++) {
+ if(separator[i] != sep1) {
+ ptr=NULL; /* set to NULL to signal error */
+ break;
+ }
+ }
+ if(ptr) {
+ newport = num;
+
+ if (conn->bits.tunnel_proxy)
+ /* proxy tunnel -> use other host info because ip_addr_str is the
+ proxy address not the ftp host */
+ snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
+ else
+ /* use the same IP we are already connected to */
+ snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
+ }
+ }
+ else
+ ptr=NULL;
+ }
+ if(!ptr) {
+ failf(data, "Weirdly formatted EPSV reply");
+ return CURLE_FTP_WEIRD_PASV_REPLY;
+ }
+ }
+ else if((ftpc->count1 == 1) &&
+ (ftpcode == 227)) {
+ /* positive PASV response */
+ int ip[4];
+ int port[2];
+
+ /*
+ * Scan for a sequence of six comma-separated numbers and use them as
+ * IP+port indicators.
+ *
+ * Found reply-strings include:
+ * "227 Entering Passive Mode (127,0,0,1,4,51)"
+ * "227 Data transfer will passively listen to 127,0,0,1,4,51"
+ * "227 Entering passive mode. 127,0,0,1,4,51"
+ */
+ while(*str) {
+ if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
+ &ip[0], &ip[1], &ip[2], &ip[3],
+ &port[0], &port[1]))
+ break;
+ str++;
+ }
+
+ if(!*str) {
+ failf(data, "Couldn't interpret the 227-response");
+ return CURLE_FTP_WEIRD_227_FORMAT;
+ }
+
+ /* we got OK from server */
+ if(data->set.ftp_skip_ip) {
+ /* told to ignore the remotely given IP but instead use the one we used
+ for the control connection */
+ infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
+ ip[0], ip[1], ip[2], ip[3],
+ conn->ip_addr_str);
+ if (conn->bits.tunnel_proxy)
+ /* proxy tunnel -> use other host info because ip_addr_str is the
+ proxy address not the ftp host */
+ snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
+ else
+ snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
+ }
+ else
+ snprintf(newhost, sizeof(newhost),
+ "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+ newport = (port[0]<<8) + port[1];
+ }
+ else if(ftpc->count1 == 0) {
+ /* EPSV failed, move on to PASV */
+
+ /* disable it for next transfer */
+ conn->bits.ftp_use_epsv = FALSE;
+ infof(data, "disabling EPSV usage\n");
+
+ NBFTPSENDF(conn, "PASV", NULL);
+ ftpc->count1++;
+ /* remain in the FTP_PASV state */
+ return result;
+ }
+ else {
+ failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
+ return CURLE_FTP_WEIRD_PASV_REPLY;
+ }
+
+ if(data->set.proxy && *data->set.proxy) {
+ /*
+ * This is a tunnel through a http proxy and we need to connect to the
+ * proxy again here.
+ *
+ * We don't want to rely on a former host lookup that might've expired
+ * now, instead we remake the lookup here and now!
+ */
+ rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
+ if(rc == CURLRESOLV_PENDING)
+ /* BLOCKING */
+ rc = Curl_wait_for_resolv(conn, &addr);
+
+ connectport =
+ (unsigned short)conn->port; /* we connect to the proxy's port */
+
+ }
+ else {
+ /* normal, direct, ftp connection */
+ rc = Curl_resolv(conn, newhost, newport, &addr);
+ if(rc == CURLRESOLV_PENDING)
+ /* BLOCKING */
+ rc = Curl_wait_for_resolv(conn, &addr);
+
+ if(!addr) {
+ failf(data, "Can't resolve new host %s:%d", newhost, newport);
+ return CURLE_FTP_CANT_GET_HOST;
+ }
+ connectport = newport; /* we connect to the remote port */
+ }
+
+ result = Curl_connecthost(conn,
+ addr,
+ &conn->sock[SECONDARYSOCKET],
+ &conninfo,
+ &connected);
+
+ Curl_resolv_unlock(data, addr); /* we're done using this address */
+
+ if (result && ftpc->count1 == 0 && ftpcode == 229) {
+ infof(data, "got positive EPSV response, but can't connect. "
+ "Disabling EPSV\n");
+ /* disable it for next transfer */
+ conn->bits.ftp_use_epsv = FALSE;
+ data->state.errorbuf = FALSE; /* allow error message to get rewritten */
+ NBFTPSENDF(conn, "PASV", NULL);
+ ftpc->count1++;
+ /* remain in the FTP_PASV state */
+ return result;
+ }
+
+ if(result)
+ return result;
+
+ conn->bits.tcpconnect = connected; /* simply TRUE or FALSE */
+
+ /*
+ * When this is used from the multi interface, this might've returned with
+ * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
+ * connect to connect and we should not be "hanging" here waiting.
+ */
+
+ if(data->set.verbose)
+ /* this just dumps information about this second connection */
+ ftp_pasv_verbose(conn, conninfo, newhost, connectport);
+
+#ifndef CURL_DISABLE_HTTP
+ if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
+ /* FIX: this MUST wait for a proper connect first if 'connected' is
+ * FALSE */
+
+ /* BLOCKING */
+ /* We want "seamless" FTP operations through HTTP proxy tunnel */
+
+ /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
+ * conn->proto.http; we want FTP through HTTP and we have to change the
+ * member temporarily for connecting to the HTTP proxy. After
+ * Curl_proxyCONNECT we have to set back the member to the original struct
+ * FTP pointer
+ */
+ struct HTTP http_proxy;
+ struct FTP *ftp_save = data->reqdata.proto.ftp;
+ memset(&http_proxy, 0, sizeof(http_proxy));
+ data->reqdata.proto.http = &http_proxy;
+
+ result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
+
+ data->reqdata.proto.ftp = ftp_save;
+
+ if(CURLE_OK != result)
+ return result;
+ }
+#endif /* CURL_DISABLE_HTTP */
+
+ state(conn, FTP_STOP); /* this phase is completed */
+
+ return result;
+}
+
+static CURLcode ftp_state_port_resp(struct connectdata *conn,
+ int ftpcode)
+{
+ struct SessionHandle *data = conn->data;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ ftpport fcmd = (ftpport)ftpc->count1;
+ CURLcode result = CURLE_OK;
+
+ if(ftpcode != 200) {
+ /* the command failed */
+
+ if (EPRT == fcmd) {
+ infof(data, "disabling EPRT usage\n");
+ conn->bits.ftp_use_eprt = FALSE;
+ }
+ fcmd++;
+
+ if(fcmd == DONE) {
+ failf(data, "Failed to do PORT");
+ result = CURLE_FTP_PORT_FAILED;
+ }
+ else
+ /* try next */
+ result = ftp_state_use_port(conn, fcmd);
+ }
+ else {
+ infof(data, "Connect data stream actively\n");
+ state(conn, FTP_STOP); /* end of DO phase */
+ }
+
+ return result;
+}
+
+static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
+ int ftpcode)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data=conn->data;
+ struct FTP *ftp = data->reqdata.proto.ftp;
+
+ switch(ftpcode) {
+ case 213:
+ {
+ /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
+ last .sss part is optional and means fractions of a second */
+ int year, month, day, hour, minute, second;
+ char *buf = data->state.buffer;
+ if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
+ &year, &month, &day, &hour, &minute, &second)) {
+ /* we have a time, reformat it */
+ time_t secs=time(NULL);
+ /* using the good old yacc/bison yuck */
+ snprintf(buf, sizeof(conn->data->state.buffer),
+ "%04d%02d%02d %02d:%02d:%02d GMT",
+ year, month, day, hour, minute, second);
+ /* now, convert this into a time() value: */
+ data->info.filetime = (long)curl_getdate(buf, &secs);
+ }
+
+ /* If we asked for a time of the file and we actually got one as well,
+ we "emulate" a HTTP-style header in our output. */
+
+ if(conn->bits.no_body &&
+ data->set.include_header &&
+ ftp->file &&
+ data->set.get_filetime &&
+ (data->info.filetime>=0) ) {
+ struct tm *tm;
+ time_t clock = (time_t)data->info.filetime;
+#ifdef HAVE_GMTIME_R
+ struct tm buffer;
+ tm = (struct tm *)gmtime_r(&clock, &buffer);
+#else
+ tm = gmtime(&clock);
+#endif
+ /* format: "Tue, 15 Nov 1994 12:45:26" */
+ snprintf(buf, BUFSIZE-1,
+ "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+ result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
+ if(result)
+ return result;
+ } /* end of a ridiculous amount of conditionals */
+ }
+ break;
+ default:
+ infof(data, "unsupported MDTM reply format\n");
+ break;
+ case 550: /* "No such file or directory" */
+ failf(data, "Given file does not exist");
+ result = CURLE_FTP_COULDNT_RETR_FILE;
+ break;
+ }
+
+ if(data->set.timecondition) {
+ if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
+ switch(data->set.timecondition) {
+ case CURL_TIMECOND_IFMODSINCE:
+ default:
+ if(data->info.filetime <= data->set.timevalue) {
+ infof(data, "The requested document is not new enough\n");
+ ftp->no_transfer = TRUE; /* mark this to not transfer data */
+ state(conn, FTP_STOP);
+ return CURLE_OK;
+ }
+ break;
+ case CURL_TIMECOND_IFUNMODSINCE:
+ if(data->info.filetime > data->set.timevalue) {
+ infof(data, "The requested document is not old enough\n");
+ ftp->no_transfer = TRUE; /* mark this to not transfer data */
+ state(conn, FTP_STOP);
+ return CURLE_OK;
+ }
+ break;
+ } /* switch */
+ }
+ else {
+ infof(data, "Skipping time comparison\n");
+ }
+ }
+
+ if(!result)
+ result = ftp_state_post_mdtm(conn);
+
+ return result;
+}
+
+static CURLcode ftp_state_type_resp(struct connectdata *conn,
+ int ftpcode,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data=conn->data;
+
+ if(ftpcode/100 != 2) {
+ /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
+ successful 'TYPE I'. While that is not as RFC959 says, it is still a
+ positive response code and we allow that. */
+ failf(data, "Couldn't set desired mode");
+ return CURLE_FTP_COULDNT_SET_BINARY; /* FIX */
+ }
+ if(ftpcode != 200)
+ infof(data, "Got a %03d response code instead of the assumed 200\n",
+ ftpcode);
+
+ if(instate == FTP_TYPE)
+ result = ftp_state_post_type(conn);
+ else if(instate == FTP_LIST_TYPE)
+ result = ftp_state_post_listtype(conn);
+ else if(instate == FTP_RETR_TYPE)
+ result = ftp_state_post_retrtype(conn);
+ else if(instate == FTP_STOR_TYPE)
+ result = ftp_state_post_stortype(conn);
+
+ return result;
+}
+
+static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
+ curl_off_t filesize)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data=conn->data;
+ struct FTP *ftp = data->reqdata.proto.ftp;
+
+ if (data->set.max_filesize && (filesize > data->set.max_filesize)) {
+ failf(data, "Maximum file size exceeded");
+ return CURLE_FILESIZE_EXCEEDED;
+ }
+ ftp->downloadsize = filesize;
+
+ if(data->reqdata.resume_from) {
+ /* We always (attempt to) get the size of downloads, so it is done before
+ this even when not doing resumes. */
+ if(filesize == -1) {
+ infof(data, "ftp server doesn't support SIZE\n");
+ /* We couldn't get the size and therefore we can't know if there really
+ is a part of the file left to get, although the server will just
+ close the connection when we start the connection so it won't cause
+ us any harm, just not make us exit as nicely. */
+ }
+ else {
+ /* We got a file size report, so we check that there actually is a
+ part of the file left to get, or else we go home. */
+ if(data->reqdata.resume_from< 0) {
+ /* We're supposed to download the last abs(from) bytes */
+ if(filesize < -data->reqdata.resume_from) {
+ failf(data, "Offset (%" FORMAT_OFF_T
+ ") was beyond file size (%" FORMAT_OFF_T ")",
+ data->reqdata.resume_from, filesize);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ /* convert to size to download */
+ ftp->downloadsize = -data->reqdata.resume_from;
+ /* download from where? */
+ data->reqdata.resume_from = filesize - ftp->downloadsize;
+ }
+ else {
+ if(filesize < data->reqdata.resume_from) {
+ failf(data, "Offset (%" FORMAT_OFF_T
+ ") was beyond file size (%" FORMAT_OFF_T ")",
+ data->reqdata.resume_from, filesize);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ /* Now store the number of bytes we are expected to download */
+ ftp->downloadsize = filesize-data->reqdata.resume_from;
+ }
+ }
+
+ if(ftp->downloadsize == 0) {
+ /* no data to transfer */
+ result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ infof(data, "File already completely downloaded\n");
+
+ /* Set no_transfer so that we won't get any error in Curl_ftp_done()
+ * because we didn't transfer the any file */
+ ftp->no_transfer = TRUE;
+ state(conn, FTP_STOP);
+ return CURLE_OK;
+ }
+
+ /* Set resume file transfer offset */
+ infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
+ "\n", data->reqdata.resume_from);
+
+ NBFTPSENDF(conn, "REST %" FORMAT_OFF_T, data->reqdata.resume_from);
+
+ state(conn, FTP_RETR_REST);
+
+ }
+ else {
+ /* no resume */
+ NBFTPSENDF(conn, "RETR %s", ftp->file);
+ state(conn, FTP_RETR);
+ }
+
+ return result;
+}
+
+static CURLcode ftp_state_size_resp(struct connectdata *conn,
+ int ftpcode,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data=conn->data;
+ curl_off_t filesize;
+ char *buf = data->state.buffer;
+
+ /* get the size from the ascii string: */
+ filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
+
+ if(instate == FTP_SIZE) {
+ if(-1 != filesize) {
+ snprintf(buf, sizeof(data->state.buffer),
+ "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
+ result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
+ if(result)
+ return result;
+ }
+ result = ftp_state_post_size(conn);
+ }
+ else if(instate == FTP_RETR_SIZE)
+ result = ftp_state_post_retr_size(conn, filesize);
+ else if(instate == FTP_STOR_SIZE) {
+ data->reqdata.resume_from = filesize;
+ result = ftp_state_ul_setup(conn, TRUE);
+ }
+
+ return result;
+}
+
+static CURLcode ftp_state_rest_resp(struct connectdata *conn,
+ int ftpcode,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = conn->data->reqdata.proto.ftp;
+
+ switch(instate) {
+ case FTP_REST:
+ default:
+ if (ftpcode == 350) {
+ result = Curl_client_write(conn, CLIENTWRITE_BOTH,
+ (char *)"Accept-ranges: bytes\r\n", 0);
+ if(result)
+ return result;
+ }
+
+ result = ftp_state_post_rest(conn);
+ break;
+
+ case FTP_RETR_REST:
+ if (ftpcode != 350) {
+ failf(conn->data, "Couldn't use REST");
+ result = CURLE_FTP_COULDNT_USE_REST;
+ }
+ else {
+ NBFTPSENDF(conn, "RETR %s", ftp->file);
+ state(conn, FTP_RETR);
+ }
+ break;
+ }
+
+ return result;
+}
+
+static CURLcode ftp_state_stor_resp(struct connectdata *conn,
+ int ftpcode)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ struct FTP *ftp = data->reqdata.proto.ftp;
+
+ if(ftpcode>=400) {
+ failf(data, "Failed FTP upload: %0d", ftpcode);
+ /* oops, we never close the sockets! */
+ return CURLE_FTP_COULDNT_STOR_FILE;
+ }
+
+ if(data->set.ftp_use_port) {
+ /* BLOCKING */
+ /* PORT means we are now awaiting the server to connect to us. */
+ result = AllowServerConnect(conn);
+ if( result )
+ return result;
+ }
+
+ if(conn->ssl[SECONDARYSOCKET].use) {
+ /* since we only have a plaintext TCP connection here, we must now
+ do the TLS stuff */
+ infof(data, "Doing the SSL/TLS handshake on the data stream\n");
+ /* BLOCKING */
+ result = Curl_ssl_connect(conn, SECONDARYSOCKET);
+ if(result)
+ return result;
+ }
+
+ *(ftp->bytecountp)=0;
+
+ /* When we know we're uploading a specified file, we can get the file
+ size prior to the actual upload. */
+
+ Curl_pgrsSetUploadSize(data, data->set.infilesize);
+
+ result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
+ SECONDARYSOCKET, ftp->bytecountp);
+ state(conn, FTP_STOP);
+
+ return result;
+}
+
+/* for LIST and RETR responses */
+static CURLcode ftp_state_get_resp(struct connectdata *conn,
+ int ftpcode,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ struct FTP *ftp = data->reqdata.proto.ftp;
+ char *buf = data->state.buffer;
+
+ if((ftpcode == 150) || (ftpcode == 125)) {
+
+ /*
+ A;
+ 150 Opening BINARY mode data connection for /etc/passwd (2241
+ bytes). (ok, the file is being 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. */
+
+ curl_off_t size=-1; /* default unknown size */
+
+
+ /*
+ * It appears that there are FTP-servers that return size 0 for files when
+ * SIZE is used on the file while being in BINARY mode. To work around
+ * that (stupid) behavior, we attempt to parse the RETR response even if
+ * the SIZE returned size zero.
+ *
+ * Debugging help from Salvatore Sorrentino on February 26, 2003.
+ */
+
+ if((instate != FTP_LIST) &&
+ !data->set.prefer_ascii &&
+ (ftp->downloadsize < 1)) {
+ /*
+ * It seems directory listings either don't show the size or very
+ * often uses size 0 anyway. ASCII transfers may very well turn out
+ * that the 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--) {
+ long in=(long)(bytes-buf);
+ /* this is a hint there is size information in there! ;-) */
+ while(--in) {
+ /* scan for the left parenthesis and break there */
+ if('(' == *bytes)
+ break;
+ /* skip only digits */
+ if(!ISDIGIT(*bytes)) {
+ bytes=NULL;
+ break;
+ }
+ /* one more estep backwards */
+ bytes--;
+ }
+ /* if we have nothing but digits: */
+ if(bytes++) {
+ /* get the number! */
+ size = curlx_strtoofft(bytes, NULL, 0);
+ }
+ }
+ }
+ else if(ftp->downloadsize > -1)
+ size = ftp->downloadsize;
+
+ if(data->set.ftp_use_port) {
+ /* BLOCKING */
+ result = AllowServerConnect(conn);
+ if( result )
+ return result;
+ }
+
+ if(conn->ssl[SECONDARYSOCKET].use) {
+ /* since we only have a plaintext TCP connection here, we must now
+ do the TLS stuff */
+ infof(data, "Doing the SSL/TLS handshake on the data stream\n");
+ result = Curl_ssl_connect(conn, SECONDARYSOCKET);
+ if(result)
+ return result;
+ }
+
+ if(size > data->reqdata.maxdownload && data->reqdata.maxdownload > 0)
+ size = data->reqdata.size = data->reqdata.maxdownload;
+
+ infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->reqdata.maxdownload);
+
+ if(instate != FTP_LIST)
+ infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
+
+ /* FTP download: */
+ result=Curl_setup_transfer(conn, SECONDARYSOCKET, size, FALSE,
+ ftp->bytecountp,
+ -1, NULL); /* no upload here */
+ if(result)
+ return result;
+
+ state(conn, FTP_STOP);
+ }
+ else {
+ if((instate == FTP_LIST) && (ftpcode == 450)) {
+ /* simply no matching files in the dir listing */
+ ftp->no_transfer = TRUE; /* don't download anything */
+ state(conn, FTP_STOP); /* this phase is over */
+ }
+ else {
+ failf(data, "RETR response: %03d", ftpcode);
+ return CURLE_FTP_COULDNT_RETR_FILE;
+ }
+ }
+
+ return result;
+}
+
+/* after USER, PASS and ACCT */
+static CURLcode ftp_state_loggedin(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+
+#ifdef HAVE_KRB4
+ if(conn->data->set.krb4) {
+ /* We are logged in, asked to use Kerberos. Set the requested
+ * protection level
+ */
+ if(conn->sec_complete)
+ /* BLOCKING */
+ 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(conn->passwd && *conn->passwd) {
+ /* BLOCKING */
+ result = Curl_krb_kauth(conn);
+ if(result)
+ return result;
+ }
+ }
+#endif
+ if(conn->ssl[FIRSTSOCKET].use) {
+ /* PBSZ = PROTECTION BUFFER SIZE.
+
+ The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
+
+ Specifically, the PROT command MUST be preceded by a PBSZ
+ command and a PBSZ command MUST be preceded by a successful
+ security data exchange (the TLS negotiation in this case)
+
+ ... (and on page 8):
+
+ Thus the PBSZ command must still be issued, but must have a
+ parameter of '0' to indicate that no buffering is taking place
+ and the data connection should not be encapsulated.
+ */
+ NBFTPSENDF(conn, "PBSZ %d", 0);
+ state(conn, FTP_PBSZ);
+ }
+ else {
+ result = ftp_state_pwd(conn);
+ }
+ return result;
+}
+
+/* for USER and PASS responses */
+static CURLcode ftp_state_user_resp(struct connectdata *conn,
+ int ftpcode,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ struct FTP *ftp = data->reqdata.proto.ftp;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ (void)instate; /* no use for this yet */
+
+ if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
+ /* 331 Password required for ...
+ (the server requires to send the user's password too) */
+ NBFTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
+ state(conn, FTP_PASS);
+ }
+ else if(ftpcode/100 == 2) {
+ /* 230 User ... logged in.
+ (the user logged in with or without password) */
+ result = ftp_state_loggedin(conn);
+ }
+ else if(ftpcode == 332) {
+ if(data->set.ftp_account) {
+ NBFTPSENDF(conn, "ACCT %s", data->set.ftp_account);
+ state(conn, FTP_ACCT);
+ }
+ else {
+ failf(data, "ACCT requested but none available");
+ result = CURLE_LOGIN_DENIED;
+ }
+ }
+ else {
+ /* All other response codes, like:
+
+ 530 User ... access denied
+ (the server denies to log the specified user) */
+
+ if (conn->data->set.ftp_alternative_to_user &&
+ !conn->data->state.ftp_trying_alternative) {
+ /* Ok, USER failed. Let's try the supplied command. */
+ NBFTPSENDF(conn, "%s", conn->data->set.ftp_alternative_to_user);
+ conn->data->state.ftp_trying_alternative = TRUE;
+ state(conn, FTP_USER);
+ result = CURLE_OK;
+ }
+ else {
+ failf(data, "Access denied: %03d", ftpcode);
+ result = CURLE_LOGIN_DENIED;
+ }
+ }
+ return result;
+}
+
+/* for ACCT response */
+static CURLcode ftp_state_acct_resp(struct connectdata *conn,
+ int ftpcode)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ if(ftpcode != 230) {
+ failf(data, "ACCT rejected by server: %03d", ftpcode);
+ result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
+ }
+ else
+ result = ftp_state_loggedin(conn);
+
+ return result;
+}
+
+
+static CURLcode ftp_statemach_act(struct connectdata *conn)
+{
+ CURLcode result;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ struct SessionHandle *data=conn->data;
+ int ftpcode;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ static const char * const ftpauth[] = {
+ "SSL", "TLS"
+ };
+ size_t nread = 0;
+
+ if(ftpc->sendleft) {
+ /* we have a piece of a command still left to send */
+ ssize_t written;
+ result = Curl_write(conn, sock, ftpc->sendthis + ftpc->sendsize -
+ ftpc->sendleft, ftpc->sendleft, &written);
+ if(result)
+ return result;
+
+ if(written != (ssize_t)ftpc->sendleft) {
+ /* only a fraction was sent */
+ ftpc->sendleft -= written;
+ }
+ else {
+ free(ftpc->sendthis);
+ ftpc->sendthis=NULL;
+ ftpc->sendleft = ftpc->sendsize = 0;
+ ftpc->response = Curl_tvnow();
+ }
+ return CURLE_OK;
+ }
+
+ /* we read a piece of response */
+ result = ftp_readresp(sock, conn, &ftpcode, &nread);
+ if(result)
+ return result;
+
+ if(ftpcode) {
+ /* we have now received a full FTP server response */
+ switch(ftpc->state) {
+ case FTP_WAIT220:
+ if(ftpcode != 220) {
+ failf(data, "This doesn't seem like a nice ftp-server response");
+ return CURLE_FTP_WEIRD_SERVER_REPLY;
+ }
+
+ /* We have received a 220 response fine, now we proceed. */
+#ifdef HAVE_KRB4
+ if(data->set.krb4) {
+ /* If not anonymous login, try a secure login. Note that this
+ procedure is still BLOCKING. */
+
+ Curl_sec_request_prot(conn, "private");
+ /* We set private first as default, in case the line below fails to
+ set a valid level */
+ Curl_sec_request_prot(conn, data->set.krb4_level);
+
+ if(Curl_sec_login(conn) != 0)
+ infof(data, "Logging in with password in cleartext!\n");
+ else
+ infof(data, "Authentication successful\n");
+ }
+#endif
+
+ if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
+ /* We don't have a SSL/TLS connection yet, but FTPS is
+ requested. Try a FTPS connection now */
+
+ ftpc->count3=0;
+ switch(data->set.ftpsslauth) {
+ case CURLFTPAUTH_DEFAULT:
+ case CURLFTPAUTH_SSL:
+ ftpc->count2 = 1; /* add one to get next */
+ ftpc->count1 = 0;
+ break;
+ case CURLFTPAUTH_TLS:
+ ftpc->count2 = -1; /* subtract one to get next */
+ ftpc->count1 = 1;
+ break;
+ default:
+ failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d\n",
+ data->set.ftpsslauth);
+ return CURLE_FAILED_INIT; /* we don't know what to do */
+ }
+ NBFTPSENDF(conn, "AUTH %s", ftpauth[ftpc->count1]);
+ state(conn, FTP_AUTH);
+ }
+ else {
+ result = ftp_state_user(conn);
+ if(result)
+ return result;
+ }
+
+ break;
+
+ case FTP_AUTH:
+ /* we have gotten the response to a previous AUTH command */
+
+ /* RFC2228 (page 5) says:
+ *
+ * If the server is willing to accept the named security mechanism,
+ * and does not require any security data, it must respond with
+ * reply code 234/334.
+ */
+
+ if((ftpcode == 234) || (ftpcode == 334)) {
+ /* Curl_ssl_connect is BLOCKING */
+ result = Curl_ssl_connect(conn, FIRSTSOCKET);
+ if(CURLE_OK == result) {
+ conn->protocol |= PROT_FTPS;
+ conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
+ result = ftp_state_user(conn);
+ }
+ }
+ else if(ftpc->count3 < 1) {
+ ftpc->count3++;
+ ftpc->count1 += ftpc->count2; /* get next attempt */
+ result = Curl_nbftpsendf(conn, "AUTH %s", ftpauth[ftpc->count1]);
+ /* remain in this same state */
+ }
+ else {
+ if(data->set.ftp_ssl > CURLFTPSSL_TRY)
+ /* we failed and CURLFTPSSL_CONTROL or CURLFTPSSL_ALL is set */
+ result = CURLE_FTP_SSL_FAILED;
+ else
+ /* ignore the failure and continue */
+ result = ftp_state_user(conn);
+ }
+
+ if(result)
+ return result;
+ break;
+
+ case FTP_USER:
+ case FTP_PASS:
+ result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
+ break;
+
+ case FTP_ACCT:
+ result = ftp_state_acct_resp(conn, ftpcode);
+ break;
+
+ case FTP_PBSZ:
+ /* FIX: check response code */
+
+ /* For TLS, the data connection can have one of two security levels.
+
+ 1) Clear (requested by 'PROT C')
+
+ 2)Private (requested by 'PROT P')
+ */
+ if(!conn->ssl[SECONDARYSOCKET].use) {
+ NBFTPSENDF(conn, "PROT %c",
+ data->set.ftp_ssl == CURLFTPSSL_CONTROL ? 'C' : 'P');
+ state(conn, FTP_PROT);
+ }
+ else {
+ result = ftp_state_pwd(conn);
+ if(result)
+ return result;
+ }
+
+ break;
+
+ case FTP_PROT:
+ if(ftpcode/100 == 2)
+ /* We have enabled SSL for the data connection! */
+ conn->ssl[SECONDARYSOCKET].use =
+ (bool)(data->set.ftp_ssl != CURLFTPSSL_CONTROL);
+ /* FTP servers typically responds with 500 if they decide to reject
+ our 'P' request */
+ else if(data->set.ftp_ssl> CURLFTPSSL_CONTROL)
+ /* we failed and bails out */
+ return CURLE_FTP_SSL_FAILED;
+
+ if(data->set.ftp_use_ccc) {
+ /* CCC - Clear Command Channel
+ */
+ NBFTPSENDF(conn, "CCC", NULL);
+ state(conn, FTP_CCC);
+ }
+ else {
+ result = ftp_state_pwd(conn);
+ if(result)
+ return result;
+ }
+ break;
+
+ case FTP_CCC:
+ if (ftpcode < 500) {
+ /* First shut down the SSL layer (note: this call will block) */
+ result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
+
+ if(result) {
+ failf(conn->data, "Failed to clear the command channel (CCC)");
+ return result;
+ }
+ }
+
+ /* Then continue as normal */
+ result = ftp_state_pwd(conn);
+ if(result)
+ return result;
+ break;
+
+ case FTP_PWD:
+ if(ftpcode == 257) {
+ char *dir = (char *)malloc(nread+1);
+ char *store=dir;
+ char *ptr=&data->state.buffer[4]; /* start on the first letter */
+
+ if(!dir)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Reply format is like
+ 257<space>"<directory-name>"<space><commentary> and the RFC959
+ says
+
+ The directory name can contain any character; embedded
+ double-quotes should be escaped by double-quotes (the
+ "quote-doubling" convention).
+ */
+ if('\"' == *ptr) {
+ /* it started good */
+ ptr++;
+ 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++;
+ }
+ ftpc->entrypath =dir; /* remember this */
+ infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+ /* also save it where getinfo can access it: */
+ data->state.most_recent_ftp_entrypath = ftpc->entrypath;
+ }
+ else {
+ /* couldn't get the path */
+ free(dir);
+ infof(data, "Failed to figure out path\n");
+ }
+ }
+ state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
+ DEBUGF(infof(data, "protocol connect phase DONE\n"));
+ break;
+
+ case FTP_QUOTE:
+ case FTP_POSTQUOTE:
+ case FTP_RETR_PREQUOTE:
+ case FTP_STOR_PREQUOTE:
+ if(ftpcode >= 400) {
+ failf(conn->data, "QUOT command failed with %03d", ftpcode);
+ return CURLE_FTP_QUOTE_ERROR;
+ }
+ result = ftp_state_quote(conn, FALSE, ftpc->state);
+ if(result)
+ return result;
+
+ break;
+
+ case FTP_CWD:
+ if(ftpcode/100 != 2) {
+ /* failure to CWD there */
+ if(conn->data->set.ftp_create_missing_dirs &&
+ ftpc->count1 && !ftpc->count2) {
+ /* try making it */
+ ftpc->count2++; /* counter to prevent CWD-MKD loops */
+ NBFTPSENDF(conn, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
+ state(conn, FTP_MKD);
+ }
+ else {
+ /* return failure */
+ failf(data, "Server denied you to change to the given directory");
+ ftpc->cwdfail = TRUE; /* don't remember this path as we failed
+ to enter it */
+ return CURLE_FTP_ACCESS_DENIED;
+ }
+ }
+ else {
+ /* success */
+ ftpc->count2=0;
+ if(++ftpc->count1 <= ftpc->dirdepth) {
+ /* send next CWD */
+ NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
+ }
+ else {
+ result = ftp_state_post_cwd(conn);
+ if(result)
+ return result;
+ }
+ }
+ break;
+
+ case FTP_MKD:
+ if(ftpcode/100 != 2) {
+ /* failure to MKD the dir */
+ failf(data, "Failed to MKD dir: %03d", ftpcode);
+ return CURLE_FTP_ACCESS_DENIED;
+ }
+ state(conn, FTP_CWD);
+ /* send CWD */
+ NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
+ break;
+
+ case FTP_MDTM:
+ result = ftp_state_mdtm_resp(conn, ftpcode);
+ break;
+
+ case FTP_TYPE:
+ case FTP_LIST_TYPE:
+ case FTP_RETR_TYPE:
+ case FTP_STOR_TYPE:
+ result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
+ break;
+
+ case FTP_SIZE:
+ case FTP_RETR_SIZE:
+ case FTP_STOR_SIZE:
+ result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
+ break;
+
+ case FTP_REST:
+ case FTP_RETR_REST:
+ result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
+ break;
+
+ case FTP_PASV:
+ result = ftp_state_pasv_resp(conn, ftpcode);
+ break;
+
+ case FTP_PORT:
+ result = ftp_state_port_resp(conn, ftpcode);
+ break;
+
+ case FTP_LIST:
+ case FTP_RETR:
+ result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
+ break;
+
+ case FTP_STOR:
+ result = ftp_state_stor_resp(conn, ftpcode);
+ break;
+
+ case FTP_QUIT:
+ /* fallthrough, just stop! */
+ default:
+ /* internal error */
+ state(conn, FTP_STOP);
+ break;
+ }
+ } /* if(ftpcode) */
+
+ return result;
+}
+
+/* Returns timeout in ms. 0 or negative number means the timeout has already
+ triggered */
+static long ftp_state_timeout(struct connectdata *conn)
+{
+ struct SessionHandle *data=conn->data;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ long timeout_ms=360000; /* in milliseconds */
+
+ if(data->set.ftp_response_timeout )
+ /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine remaining
+ time. Also, use ftp->response because FTP_RESPONSE_TIMEOUT is supposed
+ to govern the response for any given ftp response, not for the time
+ from connect to the given ftp response. */
+ timeout_ms = data->set.ftp_response_timeout*1000 - /* timeout time */
+ Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */
+ else if(data->set.timeout)
+ /* if timeout is requested, find out how much remaining time we have */
+ timeout_ms = data->set.timeout*1000 - /* timeout time */
+ Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
+ else
+ /* Without a requested timeout, we only wait 'response_time' seconds for
+ the full response to arrive before we bail out */
+ timeout_ms = ftpc->response_time*1000 -
+ Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */
+
+ return timeout_ms;
+}
+
+
+/* called repeatedly until done from multi.c */
+CURLcode Curl_ftp_multi_statemach(struct connectdata *conn,
+ bool *done)
+{
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int rc;
+ struct SessionHandle *data=conn->data;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ CURLcode result = CURLE_OK;
+ long timeout_ms = ftp_state_timeout(conn);
+
+ *done = FALSE; /* default to not done yet */
+
+ if(timeout_ms <= 0) {
+ failf(data, "FTP response timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ rc = Curl_select(ftpc->sendleft?CURL_SOCKET_BAD:sock, /* reading */
+ ftpc->sendleft?sock:CURL_SOCKET_BAD, /* writing */
+ 0);
+
+ if(rc == -1) {
+ failf(data, "select error");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else if(rc != 0) {
+ result = ftp_statemach_act(conn);
+ *done = (bool)(ftpc->state == FTP_STOP);
+ }
+ /* if rc == 0, then select() timed out */
+
+ return result;
+}
+
+static CURLcode ftp_easy_statemach(struct connectdata *conn)
+{
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int rc;
+ struct SessionHandle *data=conn->data;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ CURLcode result = CURLE_OK;
+
+ while(ftpc->state != FTP_STOP) {
+ long timeout_ms = ftp_state_timeout(conn);
+
+ if(timeout_ms <=0 ) {
+ failf(data, "FTP response timeout");
+ return CURLE_OPERATION_TIMEDOUT; /* already too little time */
+ }
+
+ rc = Curl_select(ftpc->sendleft?CURL_SOCKET_BAD:sock, /* reading */
+ ftpc->sendleft?sock:CURL_SOCKET_BAD, /* writing */
+ (int)timeout_ms);
+
+ if(rc == -1) {
+ failf(data, "select error");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else if(rc == 0) {
+ result = CURLE_OPERATION_TIMEDOUT;
+ break;
+ }
+ else {
+ result = ftp_statemach_act(conn);
+ if(result)
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Allocate and initialize the struct FTP for the current SessionHandle. If
+ * need be.
+ */
+static CURLcode ftp_init(struct connectdata *conn)
+{
+ struct SessionHandle *data = conn->data;
+ struct FTP *ftp;
+ if(data->reqdata.proto.ftp)
+ return CURLE_OK;
+
+ ftp = (struct FTP *)calloc(sizeof(struct FTP), 1);
+ if(!ftp)
+ return CURLE_OUT_OF_MEMORY;
+
+ data->reqdata.proto.ftp = ftp;
+
+ /* get some initial data into the ftp struct */
+ ftp->bytecountp = &data->reqdata.keep.bytecount;
+
+ /* no need to duplicate them, this connectdata struct won't change */
+ ftp->user = conn->user;
+ ftp->passwd = conn->passwd;
+ if (isBadFtpString(ftp->user) || isBadFtpString(ftp->passwd))
+ return CURLE_URL_MALFORMAT;
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_ftp_connect() should do everything that is to be considered a part of
+ * the connection phase.
+ *
+ * The variable 'done' points to will be TRUE if the protocol-layer connect
+ * phase is done when this function returns, or FALSE is not. When called as
+ * a part of the easy interface, it will always be TRUE.
+ */
+CURLcode Curl_ftp_connect(struct connectdata *conn,
+ bool *done) /* see description above */
+{
+ CURLcode result;
+#ifndef CURL_DISABLE_HTTP
+ /* for FTP over HTTP proxy */
+ struct HTTP http_proxy;
+ struct FTP *ftp_save;
+#endif /* CURL_DISABLE_HTTP */
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct SessionHandle *data=conn->data;
+
+ *done = FALSE; /* default to not done yet */
+
+ if (data->reqdata.proto.ftp) {
+ Curl_ftp_disconnect(conn);
+ free(data->reqdata.proto.ftp);
+ data->reqdata.proto.ftp = NULL;
+ }
+
+ result = ftp_init(conn);
+ if(result)
+ return result;
+
+ /* We always support persistant connections on ftp */
+ conn->bits.close = FALSE;
+
+ ftpc->response_time = 3600; /* set default response time-out */
+
+#ifndef CURL_DISABLE_HTTP
+ if (conn->bits.tunnel_proxy && conn->bits.httpproxy) {
+ /* BLOCKING */
+ /* We want "seamless" FTP operations through HTTP proxy tunnel */
+
+ /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
+ * conn->proto.http; we want FTP through HTTP and we have to change the
+ * member temporarily for connecting to the HTTP proxy. After
+ * Curl_proxyCONNECT we have to set back the member to the original struct
+ * FTP pointer
+ */
+ ftp_save = data->reqdata.proto.ftp;
+ memset(&http_proxy, 0, sizeof(http_proxy));
+ data->reqdata.proto.http = &http_proxy;
+
+ result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
+ conn->host.name, conn->remote_port);
+
+ data->reqdata.proto.ftp = ftp_save;
+
+ if(CURLE_OK != result)
+ return result;
+ }
+#endif /* CURL_DISABLE_HTTP */
+
+ if(conn->protocol & PROT_FTPS) {
+ /* BLOCKING */
+ /* FTPS is simply ftp with SSL for the control channel */
+ /* now, perform the SSL initialization for this socket */
+ result = Curl_ssl_connect(conn, FIRSTSOCKET);
+ if(result)
+ return result;
+ }
+
+ /* When we connect, we start in the state where we await the 220
+ response */
+ ftp_respinit(conn); /* init the response reader stuff */
+ state(conn, FTP_WAIT220);
+ ftpc->response = Curl_tvnow(); /* start response time-out now! */
+
+ if(data->state.used_interface == Curl_if_multi)
+ result = Curl_ftp_multi_statemach(conn, done);
+ else {
+ result = ftp_easy_statemach(conn);
+ if(!result)
+ *done = TRUE;
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * 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, CURLcode status, bool premature)
+{
+ struct SessionHandle *data = conn->data;
+ struct FTP *ftp = data->reqdata.proto.ftp;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ ssize_t nread;
+ int ftpcode;
+ CURLcode result=CURLE_OK;
+ bool was_ctl_valid = ftpc->ctl_valid;
+ size_t flen;
+ size_t dlen;
+ char *path;
+ char *path_to_use = data->reqdata.path;
+ struct Curl_transfer_keeper *k = &data->reqdata.keep;
+
+ if(!ftp)
+ /* When the easy handle is removed from the multi while libcurl is still
+ * trying to resolve the host name, it seems that the ftp struct is not
+ * yet initialized, but the removal action calls Curl_done() which calls
+ * this function. So we simply return success if no ftp pointer is set.
+ */
+ return CURLE_OK;
+
+ switch(status) {
+ case CURLE_BAD_DOWNLOAD_RESUME:
+ case CURLE_FTP_WEIRD_PASV_REPLY:
+ case CURLE_FTP_PORT_FAILED:
+ case CURLE_FTP_COULDNT_SET_BINARY:
+ case CURLE_FTP_COULDNT_RETR_FILE:
+ case CURLE_FTP_COULDNT_STOR_FILE:
+ case CURLE_FTP_ACCESS_DENIED:
+ /* the connection stays alive fine even though this happened */
+ /* fall-through */
+ case CURLE_OK: /* doesn't affect the control connection's status */
+ if (!premature) {
+ ftpc->ctl_valid = was_ctl_valid;
+ break;
+ }
+ /* until we cope better with prematurely ended requests, let them
+ * fallback as if in complete failure */
+ default: /* by default, an error means the control connection is
+ wedged and should not be used anymore */
+ ftpc->ctl_valid = FALSE;
+ ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
+ current path, as this connection is going */
+ conn->bits.close = TRUE; /* marked for closure */
+ break;
+ }
+
+ /* now store a copy of the directory we are in */
+ if(ftpc->prevpath)
+ free(ftpc->prevpath);
+
+ /* get the "raw" path */
+ path = curl_easy_unescape(data, path_to_use, 0, NULL);
+ if(!path)
+ return CURLE_OUT_OF_MEMORY;
+
+ flen = ftp->file?strlen(ftp->file):0; /* file is "raw" already */
+ dlen = strlen(path)-flen;
+ if(dlen && !ftpc->cwdfail) {
+ ftpc->prevpath = path;
+ if(flen)
+ /* if 'path' is not the whole string */
+ ftpc->prevpath[dlen]=0; /* terminate */
+ infof(data, "Remembering we are in dir %s\n", ftpc->prevpath);
+ }
+ else {
+ ftpc->prevpath = NULL; /* no path */
+ free(path);
+ }
+ /* free the dir tree and file parts */
+ freedirs(conn);
+
+#ifdef HAVE_KRB4
+ Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
+#endif
+
+ /* shut down the socket to inform the server we're done */
+
+#ifdef _WIN32_WCE
+ shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
+#endif
+
+ sclose(conn->sock[SECONDARYSOCKET]);
+
+ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+
+ if(!ftp->no_transfer && !status && !premature) {
+ /*
+ * Let's see what the server says about the transfer we just performed,
+ * but lower the timeout as sometimes this connection has died while the
+ * data has been transfered. This happens when doing through NATs etc that
+ * abandon old silent connections.
+ */
+ long old_time = ftpc->response_time;
+
+ ftpc->response_time = 60; /* give it only a minute for now */
+
+ result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
+
+ ftpc->response_time = old_time; /* set this back to previous value */
+
+ if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
+ failf(data, "control connection looks dead");
+ ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+ return result;
+ }
+
+ if(result)
+ return result;
+
+ if(!ftpc->dont_check) {
+ /* 226 Transfer complete, 250 Requested file action okay, completed. */
+ if((ftpcode != 226) && (ftpcode != 250)) {
+ failf(data, "server did not report OK, got %d", ftpcode);
+ result = CURLE_PARTIAL_FILE;
+ }
+ }
+ }
+
+ if(result || premature)
+ /* the response code from the transfer showed an error already so no
+ use checking further */
+ ;
+ else if(data->set.upload) {
+ if((-1 != data->set.infilesize) &&
+ (data->set.infilesize != *ftp->bytecountp) &&
+ !data->set.crlf &&
+ !ftp->no_transfer) {
+ failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
+ " out of %" FORMAT_OFF_T " bytes)",
+ *ftp->bytecountp, data->set.infilesize);
+ result = CURLE_PARTIAL_FILE;
+ }
+ }
+ else {
+ if((-1 != k->size) && (k->size != *ftp->bytecountp) &&
+#ifdef CURL_DO_LINEEND_CONV
+ /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
+ * we'll check to see if the discrepancy can be explained by the number
+ * of CRLFs we've changed to LFs.
+ */
+ ((k->size + data->state.crlf_conversions) != *ftp->bytecountp) &&
+#endif /* CURL_DO_LINEEND_CONV */
+ (k->maxdownload != *ftp->bytecountp)) {
+ failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
+ *ftp->bytecountp);
+ result = CURLE_PARTIAL_FILE;
+ }
+ else if(!ftpc->dont_check &&
+ !*ftp->bytecountp &&
+ (k->size>0)) {
+ failf(data, "No data was received!");
+ result = CURLE_FTP_COULDNT_RETR_FILE;
+ }
+ }
+
+ /* clear these for next connection */
+ ftp->no_transfer = FALSE;
+ ftpc->dont_check = FALSE;
+
+ /* Send any post-transfer QUOTE strings? */
+ if(!status && !result && !premature && data->set.postquote)
+ result = ftp_sendquote(conn, data->set.postquote);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_sendquote()
+ *
+ * Where a 'quote' means a list of custom commands to send to the server.
+ * The quote list is passed as an argument.
+ */
+
+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);
+
+ result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
+ if (result)
+ return result;
+
+ 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_need_type()
+ *
+ * Returns TRUE if we in the current situation should send TYPE
+ */
+static int ftp_need_type(struct connectdata *conn,
+ bool ascii_wanted)
+{
+ return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
+}
+
+/***********************************************************************
+ *
+ * ftp_nb_type()
+ *
+ * Set TYPE. We only deal with ASCII or BINARY so this function
+ * sets one of them.
+ * If the transfer type is not sent, simulate on OK response in newstate
+ */
+static CURLcode ftp_nb_type(struct connectdata *conn,
+ bool ascii, ftpstate newstate)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ CURLcode result;
+ int want = ascii?'A':'I';
+
+ if (ftpc->transfertype == want) {
+ state(conn, newstate);
+ return ftp_state_type_resp(conn, 200, newstate);
+ }
+
+ NBFTPSENDF(conn, "TYPE %c", want);
+ state(conn, newstate);
+
+ /* keep track of our current transfer type */
+ ftpc->transfertype = want;
+ return CURLE_OK;
+}
+
+/***************************************************************************
+ *
+ * ftp_pasv_verbose()
+ *
+ * This function only outputs some informationals about this second connection
+ * when we've issued a PASV command before and thus we have connected to a
+ * possibly new IP address.
+ *
+ */
+static void
+ftp_pasv_verbose(struct connectdata *conn,
+ Curl_addrinfo *ai,
+ char *newhost, /* ascii version */
+ int port)
+{
+ char buf[256];
+ Curl_printable_address(ai, buf, sizeof(buf));
+ infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
+}
+
+/*
+ Check if this is a range download, and if so, set the internal variables
+ properly.
+ */
+
+static CURLcode ftp_range(struct connectdata *conn)
+{
+ curl_off_t from, to;
+ curl_off_t totalsize=-1;
+ char *ptr;
+ char *ptr2;
+ struct SessionHandle *data = conn->data;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ if(data->reqdata.use_range && data->reqdata.range) {
+ from=curlx_strtoofft(data->reqdata.range, &ptr, 0);
+ while(ptr && *ptr && (ISSPACE(*ptr) || (*ptr=='-')))
+ ptr++;
+ to=curlx_strtoofft(ptr, &ptr2, 0);
+ if(ptr == ptr2) {
+ /* we didn't get any digit */
+ to=-1;
+ }
+ if((-1 == to) && (from>=0)) {
+ /* X - */
+ data->reqdata.resume_from = from;
+ DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
+ from));
+ }
+ else if(from < 0) {
+ /* -Y */
+ totalsize = -from;
+ data->reqdata.maxdownload = -from;
+ data->reqdata.resume_from = from;
+ DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
+ totalsize));
+ }
+ else {
+ /* X-Y */
+ totalsize = to-from;
+ data->reqdata.maxdownload = totalsize+1; /* include last byte */
+ data->reqdata.resume_from = from;
+ DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
+ " getting %" FORMAT_OFF_T " bytes\n",
+ from, data->reqdata.maxdownload));
+ }
+ DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
+ " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
+ from, to, data->reqdata.maxdownload));
+ ftpc->dont_check = TRUE; /* dont check for successful transfer */
+ }
+ return CURLE_OK;
+}
+
+
+/*
+ * Curl_ftp_nextconnect()
+ *
+ * This function shall be called when the second FTP (data) connection is
+ * connected.
+ */
+
+CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
+{
+ struct SessionHandle *data=conn->data;
+ CURLcode result = CURLE_OK;
+
+ /* the ftp struct is inited in Curl_ftp_connect() */
+ struct FTP *ftp = data->reqdata.proto.ftp;
+
+ DEBUGF(infof(data, "DO-MORE phase starts\n"));
+
+ if(!ftp->no_transfer && !conn->bits.no_body) {
+ /* a transfer is about to take place */
+
+ if(data->set.upload) {
+ result = ftp_nb_type(conn, data->set.prefer_ascii,
+ FTP_STOR_TYPE);
+ if (result)
+ return result;
+ }
+ else {
+ /* download */
+ ftp->downloadsize = -1; /* unknown as of yet */
+
+ result = ftp_range(conn);
+ if(result)
+ ;
+ else if((data->set.ftp_list_only) || !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. */
+ result = ftp_nb_type(conn, 1, FTP_LIST_TYPE);
+ if (result)
+ return result;
+ }
+ else {
+ result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
+ if (result)
+ return result;
+ }
+ }
+ result = ftp_easy_statemach(conn);
+ }
+
+ if(ftp->no_transfer)
+ /* no data to transfer. FIX: it feels like a kludge to have this here
+ too! */
+ result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+
+ /* end of transfer */
+ DEBUGF(infof(data, "DO-MORE phase ends with %d\n", result));
+
+ return result;
+}
+
+
+
+/***********************************************************************
+ *
+ * ftp_perform()
+ *
+ * This is the actual DO function for FTP. Get a file/directory according to
+ * the options previously setup.
+ */
+
+static
+CURLcode ftp_perform(struct connectdata *conn,
+ bool *connected, /* connect status after PASV / PORT */
+ bool *dophase_done)
+{
+ /* this is FTP and no proxy */
+ CURLcode result=CURLE_OK;
+
+ DEBUGF(infof(conn->data, "DO phase starts\n"));
+
+ *dophase_done = FALSE; /* not done yet */
+
+ /* start the first command in the DO phase */
+ result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
+ if(result)
+ return result;
+
+ /* run the state-machine */
+ if(conn->data->state.used_interface == Curl_if_multi)
+ result = Curl_ftp_multi_statemach(conn, dophase_done);
+ else {
+ result = ftp_easy_statemach(conn);
+ *dophase_done = TRUE; /* with the easy interface we are done here */
+ }
+ *connected = conn->bits.tcpconnect;
+
+ if(*dophase_done) {
+ DEBUGF(infof(conn->data, "DO phase is complete\n"));
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * 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, bool *done)
+{
+ CURLcode retcode = CURLE_OK;
+
+ *done = FALSE; /* default to false */
+
+ /*
+ Since connections can be re-used between SessionHandles, this might be a
+ connection already existing but on a fresh SessionHandle struct so we must
+ make sure we have a good 'struct FTP' to play with. For new connections,
+ the struct FTP is allocated and setup in the Curl_ftp_connect() function.
+ */
+ retcode = ftp_init(conn);
+ if(retcode)
+ return retcode;
+
+ retcode = ftp_parse_url_path(conn);
+ if (retcode)
+ return retcode;
+
+ retcode = ftp_regular_transfer(conn, done);
+
+ return retcode;
+}
+
+/***********************************************************************
+ *
+ * Curl_(nb)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!
+ *
+ * The "nb" version is made to Never Block.
+ */
+CURLcode Curl_nbftpsendf(struct connectdata *conn,
+ const char *fmt, ...)
+{
+ ssize_t bytes_written;
+ char s[256];
+ size_t write_len;
+ char *sptr=s;
+ CURLcode res = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(s, 250, fmt, ap);
+ va_end(ap);
+
+ strcat(s, "\r\n"); /* append a trailing CRLF */
+
+ bytes_written=0;
+ write_len = strlen(s);
+
+ ftp_respinit(conn);
+
+#ifdef CURL_DOES_CONVERSIONS
+ res = Curl_convert_to_network(data, s, write_len);
+ /* Curl_convert_to_network calls failf if unsuccessful */
+ if(res != CURLE_OK) {
+ return res;
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+ res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
+ &bytes_written);
+
+ if(CURLE_OK != res)
+ return res;
+
+ if(conn->data->set.verbose)
+ Curl_debug(conn->data, CURLINFO_HEADER_OUT,
+ sptr, (size_t)bytes_written, conn);
+
+ if(bytes_written != (ssize_t)write_len) {
+ /* the whole chunk was not sent, store the rest of the data */
+ write_len -= bytes_written;
+ sptr += bytes_written;
+ ftpc->sendthis = malloc(write_len);
+ if(ftpc->sendthis) {
+ memcpy(ftpc->sendthis, sptr, write_len);
+ ftpc->sendsize = ftpc->sendleft = write_len;
+ }
+ else {
+ failf(data, "out of memory");
+ res = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else
+ ftpc->response = Curl_tvnow();
+
+ return res;
+}
+
+CURLcode Curl_ftpsendf(struct connectdata *conn,
+ const char *fmt, ...)
+{
+ ssize_t bytes_written;
+ char s[256];
+ size_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);
+
+ strcat(s, "\r\n"); /* append a trailing CRLF */
+
+ bytes_written=0;
+ write_len = strlen(s);
+
+#ifdef CURL_DOES_CONVERSIONS
+ res = Curl_convert_to_network(conn->data, s, write_len);
+ /* Curl_convert_to_network calls failf if unsuccessful */
+ if(res != CURLE_OK) {
+ return(res);
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+ while(1) {
+ res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
+ &bytes_written);
+
+ if(CURLE_OK != res)
+ break;
+
+ if(conn->data->set.verbose)
+ Curl_debug(conn->data, CURLINFO_HEADER_OUT,
+ sptr, (size_t)bytes_written, conn);
+
+ if(bytes_written != (ssize_t)write_len) {
+ write_len -= bytes_written;
+ sptr += bytes_written;
+ }
+ else
+ break;
+ }
+
+ return res;
+}
+
+/***********************************************************************
+ *
+ * ftp_quit()
+ *
+ * This should be called before calling sclose() on an ftp control connection
+ * (not data connections). We should then wait for the response from the
+ * server before returning. The calling code should then try to close the
+ * connection.
+ *
+ */
+static CURLcode ftp_quit(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+
+ if(conn->proto.ftpc.ctl_valid) {
+ NBFTPSENDF(conn, "QUIT", NULL);
+ state(conn, FTP_QUIT);
+
+ result = ftp_easy_statemach(conn);
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * Curl_ftp_disconnect()
+ *
+ * Disconnect from an FTP server. Cleanup protocol-specific per-connection
+ * resources. BLOCKING.
+ */
+CURLcode Curl_ftp_disconnect(struct connectdata *conn)
+{
+ struct ftp_conn *ftpc= &conn->proto.ftpc;
+
+ /* We cannot send quit unconditionally. If this connection is stale or
+ bad in any way, sending quit and waiting around here will make the
+ disconnect wait in vain and cause more problems than we need to.
+
+ ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
+ will try to send the QUIT command, otherwise it will just return.
+ */
+
+ /* The FTP session may or may not have been allocated/setup at this point! */
+ if(conn->data->reqdata.proto.ftp) {
+ (void)ftp_quit(conn); /* ignore errors on the QUIT */
+
+ if(ftpc->entrypath) {
+ struct SessionHandle *data = conn->data;
+ data->state.most_recent_ftp_entrypath = NULL;
+ free(ftpc->entrypath);
+ ftpc->entrypath = NULL;
+ }
+ if(ftpc->cache) {
+ free(ftpc->cache);
+ ftpc->cache = NULL;
+ }
+ freedirs(conn);
+ if(ftpc->prevpath) {
+ free(ftpc->prevpath);
+ ftpc->prevpath = NULL;
+ }
+ }
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * ftp_parse_url_path()
+ *
+ * Parse the URL path into separate path components.
+ *
+ */
+static
+CURLcode ftp_parse_url_path(struct connectdata *conn)
+{
+ CURLcode retcode = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ /* the ftp struct is already inited in ftp_connect() */
+ struct FTP *ftp = data->reqdata.proto.ftp;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ size_t dlen;
+ char *slash_pos; /* position of the first '/' char in curpos */
+ char *path_to_use = data->reqdata.path;
+ char *cur_pos;
+
+ cur_pos = path_to_use; /* current position in path. point at the begin
+ of next path component */
+
+ ftpc->ctl_valid = FALSE;
+ ftpc->cwdfail = FALSE;
+
+ switch(data->set.ftp_filemethod) {
+ case FTPFILE_NOCWD:
+ /* fastest, but less standard-compliant */
+ ftp->file = data->reqdata.path; /* this is a full file path */
+ break;
+
+ case FTPFILE_SINGLECWD:
+ /* get the last slash */
+ slash_pos=strrchr(cur_pos, '/');
+ if(slash_pos || !cur_pos || !*cur_pos) {
+ ftpc->dirdepth = 1; /* we consider it to be a single dir */
+ ftpc->dirs = (char **)calloc(1, sizeof(ftpc->dirs[0]));
+ if(!ftpc->dirs)
+ return CURLE_OUT_OF_MEMORY;
+
+ ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
+ slash_pos?(int)(slash_pos-cur_pos):1,
+ NULL);
+ if(!ftpc->dirs[0]) {
+ free(ftpc->dirs);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ ftp->file = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
+ }
+ else
+ ftp->file = cur_pos; /* this is a file name only */
+ break;
+
+ default: /* allow pretty much anything */
+ case FTPFILE_MULTICWD:
+ ftpc->dirdepth = 0;
+ ftpc->diralloc = 5; /* default dir depth to allocate */
+ ftpc->dirs = (char **)calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
+ if(!ftpc->dirs)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* parse the URL path into separate path components */
+ while ((slash_pos = strchr(cur_pos, '/')) != NULL) {
+ /* 1 or 0 to indicate absolute directory */
+ bool absolute_dir = (bool)((cur_pos - data->reqdata.path > 0) &&
+ (ftpc->dirdepth == 0));
+
+ /* seek out the next path component */
+ if (slash_pos-cur_pos) {
+ /* we skip empty path components, like "x//y" since the FTP command
+ CWD requires a parameter and a non-existant parameter a) doesn't
+ work on many servers and b) has no effect on the others. */
+ int len = (int)(slash_pos - cur_pos + absolute_dir);
+ ftpc->dirs[ftpc->dirdepth] = curl_easy_unescape(conn->data,
+ cur_pos - absolute_dir,
+ len, NULL);
+ if (!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
+ failf(data, "no memory");
+ freedirs(conn);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ if (isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
+ freedirs(conn);
+ return CURLE_URL_MALFORMAT;
+ }
+ }
+ else {
+ cur_pos = slash_pos + 1; /* jump to the rest of the string */
+ continue;
+ }
+
+ if(!retcode) {
+ cur_pos = slash_pos + 1; /* jump to the rest of the string */
+ if(++ftpc->dirdepth >= ftpc->diralloc) {
+ /* enlarge array */
+ char *bigger;
+ ftpc->diralloc *= 2; /* double the size each time */
+ bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
+ if(!bigger) {
+ ftpc->dirdepth--;
+ freedirs(conn);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ ftpc->dirs = (char **)bigger;
+ }
+ }
+ }
+
+ ftp->file = cur_pos; /* the rest is the file name */
+ }
+
+ if(*ftp->file) {
+ ftp->file = curl_easy_unescape(conn->data, ftp->file, 0, NULL);
+ if(NULL == ftp->file) {
+ freedirs(conn);
+ failf(data, "no memory");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ if (isBadFtpString(ftp->file)) {
+ freedirs(conn);
+ return CURLE_URL_MALFORMAT;
+ }
+ }
+ else
+ ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
+ pointer */
+
+ if(data->set.upload && !ftp->file &&
+ (!ftp->no_transfer || conn->bits.no_body)) {
+ /* We need a file name when uploading. Return error! */
+ failf(data, "Uploading to a URL without a file name!");
+ return CURLE_URL_MALFORMAT;
+ }
+
+ ftpc->cwddone = FALSE; /* default to not done */
+
+ if(ftpc->prevpath) {
+ /* prevpath is "raw" so we convert the input path before we compare the
+ strings */
+ char *path = curl_easy_unescape(conn->data, data->reqdata.path, 0, NULL);
+ if(!path)
+ return CURLE_OUT_OF_MEMORY;
+
+ dlen = strlen(path) - (ftp->file?strlen(ftp->file):0);
+ if((dlen == strlen(ftpc->prevpath)) &&
+ curl_strnequal(path, ftpc->prevpath, dlen)) {
+ infof(data, "Request has same path as previous transfer\n");
+ ftpc->cwddone = TRUE;
+ }
+ free(path);
+ }
+
+ return retcode;
+}
+
+/* call this when the DO phase has completed */
+static CURLcode ftp_dophase_done(struct connectdata *conn,
+ bool connected)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = conn->data->reqdata.proto.ftp;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ if(connected)
+ result = Curl_ftp_nextconnect(conn);
+
+ if(result && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
+ /* Failure detected, close the second socket if it was created already */
+ sclose(conn->sock[SECONDARYSOCKET]);
+ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+ return result;
+ }
+
+ if(ftp->no_transfer)
+ /* no data to transfer */
+ result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ else if(!connected)
+ /* since we didn't connect now, we want do_more to get called */
+ conn->bits.do_more = TRUE;
+
+ ftpc->ctl_valid = TRUE; /* seems good */
+
+ return result;
+}
+
+/* called from multi.c while DOing */
+CURLcode Curl_ftp_doing(struct connectdata *conn,
+ bool *dophase_done)
+{
+ CURLcode result;
+ result = Curl_ftp_multi_statemach(conn, dophase_done);
+
+ if(*dophase_done) {
+ result = ftp_dophase_done(conn, FALSE /* not connected */);
+
+ DEBUGF(infof(conn->data, "DO phase is complete\n"));
+ }
+ return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_regular_transfer()
+ *
+ * The input argument is already checked for validity.
+ *
+ * Performs all commands done before a regular transfer between a local and a
+ * remote host.
+ *
+ * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
+ * Curl_ftp_done() function without finding any major problem.
+ */
+static
+CURLcode ftp_regular_transfer(struct connectdata *conn,
+ bool *dophase_done)
+{
+ CURLcode result=CURLE_OK;
+ bool connected=0;
+ struct SessionHandle *data = conn->data;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ data->reqdata.size = -1; /* make sure this is unknown at this point */
+
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+ Curl_pgrsSetUploadSize(data, 0);
+ Curl_pgrsSetDownloadSize(data, 0);
+
+ ftpc->ctl_valid = TRUE; /* starts good */
+
+ result = ftp_perform(conn,
+ &connected, /* have we connected after PASV/PORT */
+ dophase_done); /* all commands in the DO-phase done? */
+
+ if(CURLE_OK == result) {
+
+ if(!*dophase_done)
+ /* the DO phase has not completed yet */
+ return CURLE_OK;
+
+ result = ftp_dophase_done(conn, connected);
+ if(result)
+ return result;
+ }
+ else
+ freedirs(conn);
+
+ return result;
+}
+
+#endif /* CURL_DISABLE_FTP */
diff --git a/Utilities/cmcurl/ftp.h b/Utilities/cmcurl/ftp.h
new file mode 100644
index 000000000..b64e70506
--- /dev/null
+++ b/Utilities/cmcurl/ftp.h
@@ -0,0 +1,43 @@
+#ifndef __FTP_H
+#define __FTP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#ifndef CURL_DISABLE_FTP
+CURLcode Curl_ftp(struct connectdata *conn, bool *done);
+CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode, bool premature);
+CURLcode Curl_ftp_connect(struct connectdata *conn, bool *done);
+CURLcode Curl_ftp_disconnect(struct connectdata *conn);
+CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...);
+CURLcode Curl_nbftpsendf(struct connectdata *, const char *fmt, ...);
+CURLcode Curl_GetFTPResponse(ssize_t *nread, struct connectdata *conn,
+ int *ftpcode);
+CURLcode Curl_ftp_nextconnect(struct connectdata *conn);
+CURLcode Curl_ftp_multi_statemach(struct connectdata *conn, bool *done);
+int Curl_ftp_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks);
+CURLcode Curl_ftp_doing(struct connectdata *conn,
+ bool *dophase_done);
+#endif /* CURL_DISABLE_FTP */
+#endif /* __FTP_H */
diff --git a/Utilities/cmcurl/getenv.c b/Utilities/cmcurl/getenv.c
new file mode 100644
index 000000000..4f955f893
--- /dev/null
+++ b/Utilities/cmcurl/getenv.c
@@ -0,0 +1,69 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef VMS
+#include <unixlib.h>
+#endif
+
+#include <curl/curl.h>
+#include "memory.h"
+
+#include "memdebug.h"
+
+static
+char *GetEnv(const char *variable)
+{
+#ifdef _WIN32_WCE
+ return NULL;
+#else
+#ifdef WIN32
+ char env[MAX_PATH]; /* MAX_PATH is from windef.h */
+ char *temp = getenv(variable);
+ env[0] = '\0';
+ if (temp != NULL)
+ 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;
+#endif
+}
+
+char *curl_getenv(const char *v)
+{
+ return GetEnv(v);
+}
diff --git a/Utilities/cmcurl/getinfo.c b/Utilities/cmcurl/getinfo.c
new file mode 100644
index 000000000..5cf3bcacd
--- /dev/null
+++ b/Utilities/cmcurl/getinfo.c
@@ -0,0 +1,234 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "getinfo.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "memory.h"
+#include "sslgen.h"
+
+/* Make this the last #include */
+#include "memdebug.h"
+
+/*
+ * This is supposed to be called in the beginning of a perform() session
+ * and should reset all session-info variables
+ */
+CURLcode Curl_initinfo(struct SessionHandle *data)
+{
+ struct Progress *pro = &data->progress;
+ struct PureInfo *info =&data->info;
+
+ pro->t_nslookup = 0;
+ pro->t_connect = 0;
+ pro->t_pretransfer = 0;
+ pro->t_starttransfer = 0;
+ pro->timespent = 0;
+ pro->t_redirect = 0;
+
+ info->httpcode = 0;
+ info->httpversion=0;
+ info->filetime=-1; /* -1 is an illegal time and thus means unknown */
+
+ if (info->contenttype)
+ free(info->contenttype);
+ info->contenttype = NULL;
+
+ info->header_size = 0;
+ info->request_size = 0;
+ info->numconnects = 0;
+ return CURLE_OK;
+}
+
+CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...)
+{
+ va_list arg;
+ long *param_longp=NULL;
+ double *param_doublep=NULL;
+ char **param_charp=NULL;
+ struct curl_slist **param_slistp=NULL;
+ char buf;
+
+ if(!data)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ 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;
+ case CURLINFO_SLIST:
+ param_slistp = va_arg(arg, struct curl_slist **);
+ if(NULL == param_slistp)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ break;
+ }
+
+ switch(info) {
+ case CURLINFO_EFFECTIVE_URL:
+ *param_charp = data->change.url?data->change.url:(char *)"";
+ break;
+ case CURLINFO_RESPONSE_CODE:
+ *param_longp = data->info.httpcode;
+ break;
+ case CURLINFO_HTTP_CONNECTCODE:
+ *param_longp = data->info.httpproxycode;
+ break;
+ case CURLINFO_FILETIME:
+ *param_longp = data->info.filetime;
+ break;
+ case CURLINFO_HEADER_SIZE:
+ *param_longp = data->info.header_size;
+ break;
+ case CURLINFO_REQUEST_SIZE:
+ *param_longp = data->info.request_size;
+ break;
+ case CURLINFO_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 = (double)data->progress.uploaded;
+ break;
+ case CURLINFO_SIZE_DOWNLOAD:
+ *param_doublep = (double)data->progress.downloaded;
+ break;
+ case CURLINFO_SPEED_DOWNLOAD:
+ *param_doublep = (double)data->progress.dlspeed;
+ break;
+ case CURLINFO_SPEED_UPLOAD:
+ *param_doublep = (double)data->progress.ulspeed;
+ break;
+ case CURLINFO_SSL_VERIFYRESULT:
+ *param_longp = data->set.ssl.certverifyresult;
+ break;
+ case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
+ *param_doublep = (double)data->progress.size_dl;
+ break;
+ case CURLINFO_CONTENT_LENGTH_UPLOAD:
+ *param_doublep = (double)data->progress.size_ul;
+ break;
+ case CURLINFO_REDIRECT_TIME:
+ *param_doublep = data->progress.t_redirect;
+ break;
+ case CURLINFO_REDIRECT_COUNT:
+ *param_longp = data->set.followlocation;
+ break;
+ case CURLINFO_CONTENT_TYPE:
+ *param_charp = data->info.contenttype;
+ break;
+ case CURLINFO_PRIVATE:
+ *param_charp = data->set.private_data;
+ break;
+ case CURLINFO_HTTPAUTH_AVAIL:
+ *param_longp = data->info.httpauthavail;
+ break;
+ case CURLINFO_PROXYAUTH_AVAIL:
+ *param_longp = data->info.proxyauthavail;
+ break;
+ case CURLINFO_OS_ERRNO:
+ *param_longp = data->state.os_errno;
+ break;
+ case CURLINFO_NUM_CONNECTS:
+ *param_longp = data->info.numconnects;
+ break;
+ case CURLINFO_SSL_ENGINES:
+ *param_slistp = Curl_ssl_engines_list(data);
+ break;
+ case CURLINFO_COOKIELIST:
+ *param_slistp = Curl_cookie_list(data);
+ break;
+ case CURLINFO_FTP_ENTRY_PATH:
+ /* Return the entrypath string from the most recent connection.
+ This pointer was copied from the connectdata structure by FTP.
+ The actual string may be free()ed by subsequent libcurl calls so
+ it must be copied to a safer area before the next libcurl call.
+ Callers must never free it themselves. */
+ *param_charp = data->state.most_recent_ftp_entrypath;
+ break;
+ case CURLINFO_LASTSOCKET:
+ if((data->state.lastconnect != -1) &&
+ (data->state.connc->connects[data->state.lastconnect] != NULL)) {
+ struct connectdata *c = data->state.connc->connects
+ [data->state.lastconnect];
+ *param_longp = c->sock[FIRSTSOCKET];
+ /* we have a socket connected, let's determine if the server shut down */
+ /* determine if ssl */
+ if(c->ssl[FIRSTSOCKET].use) {
+ /* use the SSL context */
+ if (!Curl_ssl_check_cxn(c))
+ *param_longp = -1; /* FIN received */
+ }
+/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
+#ifdef MSG_PEEK
+ else {
+ /* use the socket */
+ if(recv((RECV_TYPE_ARG1)c->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
+ (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
+ *param_longp = -1; /* FIN received */
+ }
+ }
+#endif
+ }
+ else
+ *param_longp = -1;
+ break;
+ default:
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ return CURLE_OK;
+}
diff --git a/Utilities/cmcurl/getinfo.h b/Utilities/cmcurl/getinfo.h
new file mode 100644
index 000000000..2fe1b5c36
--- /dev/null
+++ b/Utilities/cmcurl/getinfo.h
@@ -0,0 +1,28 @@
+#ifndef __GETINFO_H
+#define __GETINFO_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...);
+CURLcode Curl_initinfo(struct SessionHandle *data);
+
+#endif
diff --git a/Utilities/cmcurl/gtls.c b/Utilities/cmcurl/gtls.c
new file mode 100644
index 000000000..250ecada4
--- /dev/null
+++ b/Utilities/cmcurl/gtls.c
@@ -0,0 +1,640 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code
+ * but sslgen.c should ever call or use these functions.
+ *
+ * Note: don't use the GnuTLS' *_t variable type names in this source code,
+ * since they were not present in 1.0.X.
+ */
+
+#include "setup.h"
+#ifdef USE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "gtls.h"
+#include "sslgen.h"
+#include "parsedate.h"
+#include "connect.h" /* for the connect timeout */
+#include "select.h"
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+#include "memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* Enable GnuTLS debugging by defining GTLSDEBUG */
+/*#define GTLSDEBUG */
+
+#ifdef GTLSDEBUG
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "|<%d>| %s", level, str);
+}
+#endif
+
+/*
+ * Custom push and pull callback functions used by GNU TLS to read and write
+ * to the socket. These functions are simple wrappers to send() and recv()
+ * (although here using the sread/swrite macros as defined by setup_once.h).
+ * We use custom functions rather than the GNU TLS defaults because it allows
+ * us to get specific about the fourth "flags" argument, and to use arbitrary
+ * private data with gnutls_transport_set_ptr if we wish.
+ */
+static ssize_t Curl_gtls_push(void *s, const void *buf, size_t len)
+{
+ return swrite(s, buf, len);
+}
+
+static ssize_t Curl_gtls_pull(void *s, void *buf, size_t len)
+{
+ return sread(s, buf, len);
+}
+
+/* Global GnuTLS init, called from Curl_ssl_init() */
+int Curl_gtls_init(void)
+{
+ gnutls_global_init();
+#ifdef GTLSDEBUG
+ gnutls_global_set_log_function(tls_log_func);
+ gnutls_global_set_log_level(2);
+#endif
+ return 1;
+}
+
+int Curl_gtls_cleanup(void)
+{
+ gnutls_global_deinit();
+ return 1;
+}
+
+static void showtime(struct SessionHandle *data,
+ const char *text,
+ time_t stamp)
+{
+ struct tm *tm;
+#ifdef HAVE_GMTIME_R
+ struct tm buffer;
+ tm = (struct tm *)gmtime_r(&stamp, &buffer);
+#else
+ tm = gmtime(&stamp);
+#endif
+ snprintf(data->state.buffer,
+ BUFSIZE,
+ "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n",
+ text,
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+ infof(data, "%s", data->state.buffer);
+}
+
+/* this function does a BLOCKING SSL/TLS (re-)handshake */
+static CURLcode handshake(struct connectdata *conn,
+ gnutls_session session,
+ int sockindex,
+ bool duringconnect)
+{
+ struct SessionHandle *data = conn->data;
+ int rc;
+
+ do {
+ rc = gnutls_handshake(session);
+
+ if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
+ long timeout_ms = DEFAULT_CONNECT_TIMEOUT;
+ long has_passed;
+
+ if(duringconnect && data->set.connecttimeout)
+ timeout_ms = data->set.connecttimeout*1000;
+
+ if(data->set.timeout) {
+ /* get the strictest timeout of the ones converted to milliseconds */
+ if((data->set.timeout*1000) < timeout_ms)
+ timeout_ms = data->set.timeout*1000;
+ }
+
+ /* Evaluate in milliseconds how much time that has passed */
+ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
+
+ /* subtract the passed time */
+ timeout_ms -= 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;
+ }
+
+ rc = Curl_select(conn->sock[sockindex],
+ conn->sock[sockindex], (int)timeout_ms);
+ if(rc > 0)
+ /* reabable or writable, go loop*/
+ continue;
+ else if(0 == rc) {
+ /* timeout */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ else {
+ /* anything that gets here is fatally bad */
+ failf(data, "select on SSL socket, errno: %d", Curl_sockerrno());
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else
+ break;
+ } while(1);
+
+ if (rc < 0) {
+ failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ return CURLE_OK;
+}
+
+static gnutls_x509_crt_fmt do_file_type(const char *type)
+{
+ if(!type || !type[0])
+ return GNUTLS_X509_FMT_PEM;
+ if(curl_strequal(type, "PEM"))
+ return GNUTLS_X509_FMT_PEM;
+ if(curl_strequal(type, "DER"))
+ return GNUTLS_X509_FMT_DER;
+ return -1;
+}
+
+
+/*
+ * This function is called after the TCP connect has completed. Setup the TLS
+ * layer and do all necessary magic.
+ */
+CURLcode
+Curl_gtls_connect(struct connectdata *conn,
+ int sockindex)
+
+{
+ const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
+ struct SessionHandle *data = conn->data;
+ gnutls_session session;
+ int rc;
+ unsigned int cert_list_size;
+ const gnutls_datum *chainp;
+ unsigned int verify_status;
+ gnutls_x509_crt x509_cert;
+ char certbuf[256]; /* big enough? */
+ size_t size;
+ unsigned int algo;
+ unsigned int bits;
+ time_t clock;
+ const char *ptr;
+ void *ssl_sessionid;
+ size_t ssl_idsize;
+
+ /* GnuTLS only supports TLSv1 (and SSLv3?) */
+ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
+ failf(data, "GnuTLS does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* allocate a cred struct */
+ rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred);
+ if(rc < 0) {
+ failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(data->set.ssl.CAfile) {
+ /* set the trusted CA cert bundle file */
+ gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+
+ rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred,
+ data->set.ssl.CAfile,
+ GNUTLS_X509_FMT_PEM);
+ if(rc < 0) {
+ infof(data, "error reading ca cert file %s (%s)\n",
+ data->set.ssl.CAfile, gnutls_strerror(rc));
+ if (data->set.ssl.verifypeer)
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else
+ infof(data, "found %d certificates in %s\n",
+ rc, data->set.ssl.CAfile);
+ }
+
+ /* Initialize TLS session as a client */
+ rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT);
+ if(rc) {
+ failf(data, "gnutls_init() failed: %d", rc);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* convenient assign */
+ session = conn->ssl[sockindex].session;
+
+ /* Use default priorities */
+ rc = gnutls_set_default_priority(session);
+ if(rc < 0)
+ return CURLE_SSL_CONNECT_ERROR;
+
+ /* Sets the priority on the certificate types supported by gnutls. Priority
+ is higher for types specified before others. After specifying the types
+ you want, you must append a 0. */
+ rc = gnutls_certificate_type_set_priority(session, cert_type_priority);
+ if(rc < 0)
+ return CURLE_SSL_CONNECT_ERROR;
+
+ if(data->set.cert) {
+ if( gnutls_certificate_set_x509_key_file(
+ conn->ssl[sockindex].cred, data->set.cert,
+ data->set.key != 0 ? data->set.key : data->set.cert,
+ do_file_type(data->set.cert_type) ) ) {
+ failf(data, "error reading X.509 key or certificate file");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ /* put the credentials to the current session */
+ rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ conn->ssl[sockindex].cred);
+
+ /* set the connection handle (file descriptor for the socket) */
+ gnutls_transport_set_ptr(session,
+ (gnutls_transport_ptr)conn->sock[sockindex]);
+
+ /* register callback functions to send and receive data. */
+ gnutls_transport_set_push_function(session, Curl_gtls_push);
+ gnutls_transport_set_pull_function(session, Curl_gtls_pull);
+
+ /* lowat must be set to zero when using custom push and pull functions. */
+ gnutls_transport_set_lowat(session, 0);
+
+ /* This might be a reconnect, so we check for a session ID in the cache
+ to speed up things */
+
+ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) {
+ /* we got a session id, use it! */
+ gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);
+
+ /* Informational message */
+ infof (data, "SSL re-using session ID\n");
+ }
+
+ rc = handshake(conn, session, sockindex, TRUE);
+ if(rc)
+ /* handshake() sets its own error message with failf() */
+ return rc;
+
+ /* This function will return the peer's raw certificate (chain) as sent by
+ the peer. These certificates are in raw format (DER encoded for
+ X.509). In case of a X.509 then a certificate list may be present. The
+ first certificate in the list is the peer's certificate, following the
+ issuer's certificate, then the issuer's issuer etc. */
+
+ chainp = gnutls_certificate_get_peers(session, &cert_list_size);
+ if(!chainp) {
+ if(data->set.ssl.verifyhost) {
+ failf(data, "failed to get server cert");
+ return CURLE_SSL_PEER_CERTIFICATE;
+ }
+ infof(data, "\t common name: WARNING couldn't obtain\n");
+ }
+
+ /* This function will try to verify the peer's certificate and return its
+ status (trusted, invalid etc.). The value of status should be one or more
+ of the gnutls_certificate_status_t enumerated elements bitwise or'd. To
+ avoid denial of service attacks some default upper limits regarding the
+ certificate key size and chain size are set. To override them use
+ gnutls_certificate_set_verify_limits(). */
+
+ rc = gnutls_certificate_verify_peers2(session, &verify_status);
+ if (rc < 0) {
+ failf(data, "server cert verify failed: %d", rc);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* verify_status is a bitmask of gnutls_certificate_status bits */
+ if(verify_status & GNUTLS_CERT_INVALID) {
+ if (data->set.ssl.verifypeer) {
+ failf(data, "server certificate verification failed. CAfile: %s",
+ data->set.ssl.CAfile?data->set.ssl.CAfile:"none");
+ return CURLE_SSL_CACERT;
+ }
+ else
+ infof(data, "\t server certificate verification FAILED\n");
+ }
+ else
+ infof(data, "\t server certificate verification OK\n");
+
+ /* initialize an X.509 certificate structure. */
+ gnutls_x509_crt_init(&x509_cert);
+
+ /* convert the given DER or PEM encoded Certificate to the native
+ gnutls_x509_crt_t format */
+ gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
+
+ size=sizeof(certbuf);
+ rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
+ 0, /* the first and only one */
+ FALSE,
+ certbuf,
+ &size);
+ if(rc) {
+ infof(data, "error fetching CN from cert:%s\n",
+ gnutls_strerror(rc));
+ }
+
+ /* This function will check if the given certificate's subject matches the
+ given hostname. This is a basic implementation of the matching described
+ in RFC2818 (HTTPS), which takes into account wildcards, and the subject
+ alternative name PKIX extension. Returns non zero on success, and zero on
+ failure. */
+ rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name);
+
+ if(!rc) {
+ if (data->set.ssl.verifyhost > 1) {
+ failf(data, "SSL: certificate subject name (%s) does not match "
+ "target host name '%s'", certbuf, conn->host.dispname);
+ gnutls_x509_crt_deinit(x509_cert);
+ return CURLE_SSL_PEER_CERTIFICATE;
+ }
+ else
+ infof(data, "\t common name: %s (does not match '%s')\n",
+ certbuf, conn->host.dispname);
+ }
+ else
+ infof(data, "\t common name: %s (matched)\n", certbuf);
+
+ /* Show:
+
+ - ciphers used
+ - subject
+ - start date
+ - expire date
+ - common name
+ - issuer
+
+ */
+
+ /* public key algorithm's parameters */
+ algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
+ infof(data, "\t certificate public key: %s\n",
+ gnutls_pk_algorithm_get_name(algo));
+
+ /* version of the X.509 certificate. */
+ infof(data, "\t certificate version: #%d\n",
+ gnutls_x509_crt_get_version(x509_cert));
+
+
+ size = sizeof(certbuf);
+ gnutls_x509_crt_get_dn(x509_cert, certbuf, &size);
+ infof(data, "\t subject: %s\n", certbuf);
+
+ clock = gnutls_x509_crt_get_activation_time(x509_cert);
+ showtime(data, "start date", clock);
+
+ clock = gnutls_x509_crt_get_expiration_time(x509_cert);
+ showtime(data, "expire date", clock);
+
+ size = sizeof(certbuf);
+ gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size);
+ infof(data, "\t issuer: %s\n", certbuf);
+
+ gnutls_x509_crt_deinit(x509_cert);
+
+ /* compression algorithm (if any) */
+ ptr = gnutls_compression_get_name(gnutls_compression_get(session));
+ /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
+ infof(data, "\t compression: %s\n", ptr);
+
+ /* the name of the cipher used. ie 3DES. */
+ ptr = gnutls_cipher_get_name(gnutls_cipher_get(session));
+ infof(data, "\t cipher: %s\n", ptr);
+
+ /* the MAC algorithms name. ie SHA1 */
+ ptr = gnutls_mac_get_name(gnutls_mac_get(session));
+ infof(data, "\t MAC: %s\n", ptr);
+
+ if(!ssl_sessionid) {
+ /* this session was not previously in the cache, add it now */
+
+ /* get the session ID data size */
+ gnutls_session_get_data(session, NULL, &ssl_idsize);
+ ssl_sessionid = malloc(ssl_idsize); /* get a buffer for it */
+
+ if(ssl_sessionid) {
+ /* extract session ID to the allocated buffer */
+ gnutls_session_get_data(session, ssl_sessionid, &ssl_idsize);
+
+ /* store this session id */
+ return Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_idsize);
+ }
+ }
+
+ return CURLE_OK;
+}
+
+
+/* return number of sent (non-SSL) bytes */
+ssize_t Curl_gtls_send(struct connectdata *conn,
+ int sockindex,
+ void *mem,
+ size_t len)
+{
+ ssize_t rc = gnutls_record_send(conn->ssl[sockindex].session, mem, len);
+
+ if(rc < 0 ) {
+ if(rc == GNUTLS_E_AGAIN)
+ return 0; /* EWOULDBLOCK equivalent */
+ rc = -1; /* generic error code for send failure */
+ }
+
+ return rc;
+}
+
+void Curl_gtls_close_all(struct SessionHandle *data)
+{
+ /* FIX: make the OpenSSL code more generic and use parts of it here */
+ (void)data;
+}
+
+static void close_one(struct connectdata *conn,
+ int index)
+{
+ if(conn->ssl[index].session) {
+ gnutls_bye(conn->ssl[index].session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(conn->ssl[index].session);
+ }
+ gnutls_certificate_free_credentials(conn->ssl[index].cred);
+}
+
+void Curl_gtls_close(struct connectdata *conn)
+{
+ if(conn->ssl[0].use)
+ close_one(conn, 0);
+ if(conn->ssl[1].use)
+ close_one(conn, 1);
+}
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+int Curl_gtls_shutdown(struct connectdata *conn, int sockindex)
+{
+ int result;
+ int retval = 0;
+ struct SessionHandle *data = conn->data;
+ int done = 0;
+ ssize_t nread;
+ char buf[120];
+
+ /* This has only been tested on the proftpd server, and the mod_tls code
+ sends a close notify alert without waiting for a close notify alert in
+ response. Thus we wait for a close notify alert from the server, but
+ we do not send one. Let's hope other servers do the same... */
+
+ if(conn->ssl[sockindex].session) {
+ while(!done) {
+ int what = Curl_select(conn->sock[sockindex],
+ CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
+ if(what > 0) {
+ /* Something to read, let's do it and hope that it is the close
+ notify alert from the server */
+ result = gnutls_record_recv(conn->ssl[sockindex].session,
+ buf, sizeof(buf));
+ switch(result) {
+ case 0:
+ /* This is the expected response. There was no data but only
+ the close notify alert */
+ done = 1;
+ break;
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_INTERRUPTED:
+ infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n");
+ break;
+ default:
+ retval = -1;
+ done = 1;
+ break;
+ }
+ }
+ else if(0 == what) {
+ /* timeout */
+ failf(data, "SSL shutdown timeout");
+ done = 1;
+ break;
+ }
+ else {
+ /* anything that gets here is fatally bad */
+ failf(data, "select on SSL socket, errno: %d", Curl_sockerrno());
+ retval = -1;
+ done = 1;
+ }
+ }
+ gnutls_deinit(conn->ssl[sockindex].session);
+ }
+ gnutls_certificate_free_credentials(conn->ssl[sockindex].cred);
+
+ conn->ssl[sockindex].session = NULL;
+ conn->ssl[sockindex].use = FALSE;
+
+ return retval;
+}
+
+/*
+ * If the read would block we return -1 and set 'wouldblock' to TRUE.
+ * Otherwise we return the amount of data read. Other errors should return -1
+ * and set 'wouldblock' to FALSE.
+ */
+ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */
+ int num, /* socketindex */
+ char *buf, /* store read data here */
+ size_t buffersize, /* max amount to read */
+ bool *wouldblock)
+{
+ ssize_t ret;
+
+ ret = gnutls_record_recv(conn->ssl[num].session, buf, buffersize);
+ if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
+ *wouldblock = TRUE;
+ return -1;
+ }
+
+ if(ret == GNUTLS_E_REHANDSHAKE) {
+ /* BLOCKING call, this is bad but a work-around for now. Fixing this "the
+ proper way" takes a whole lot of work. */
+ CURLcode rc = handshake(conn, conn->ssl[num].session, num, FALSE);
+ if(rc)
+ /* handshake() writes error message on its own */
+ return rc;
+ *wouldblock = TRUE; /* then return as if this was a wouldblock */
+ return -1;
+ }
+
+ *wouldblock = FALSE;
+ if (!ret) {
+ failf(conn->data, "Peer closed the TLS connection");
+ return -1;
+ }
+
+ if (ret < 0) {
+ failf(conn->data, "GnuTLS recv error (%d): %s",
+ (int)ret, gnutls_strerror(ret));
+ return -1;
+ }
+
+ return ret;
+}
+
+void Curl_gtls_session_free(void *ptr)
+{
+ free(ptr);
+}
+
+size_t Curl_gtls_version(char *buffer, size_t size)
+{
+ return snprintf(buffer, size, " GnuTLS/%s", gnutls_check_version(NULL));
+}
+
+#endif /* USE_GNUTLS */
diff --git a/Utilities/cmcurl/gtls.h b/Utilities/cmcurl/gtls.h
new file mode 100644
index 000000000..bff3f8693
--- /dev/null
+++ b/Utilities/cmcurl/gtls.h
@@ -0,0 +1,46 @@
+#ifndef __GTLS_H
+#define __GTLS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+int Curl_gtls_init(void);
+int Curl_gtls_cleanup(void);
+CURLcode Curl_gtls_connect(struct connectdata *conn, int sockindex);
+
+/* tell GnuTLS to close down all open information regarding connections (and
+ thus session ID caching etc) */
+void Curl_gtls_close_all(struct SessionHandle *data);
+void Curl_gtls_close(struct connectdata *conn); /* close a SSL connection */
+
+/* return number of sent (non-SSL) bytes */
+ssize_t Curl_gtls_send(struct connectdata *conn, int sockindex,
+ void *mem, size_t len);
+ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */
+ int num, /* socketindex */
+ char *buf, /* store read data here */
+ size_t buffersize, /* max amount to read */
+ bool *wouldblock);
+void Curl_gtls_session_free(void *ptr);
+size_t Curl_gtls_version(char *buffer, size_t size);
+int Curl_gtls_shutdown(struct connectdata *conn, int sockindex);
+
+#endif
diff --git a/Utilities/cmcurl/hash.c b/Utilities/cmcurl/hash.c
new file mode 100644
index 000000000..e00462778
--- /dev/null
+++ b/Utilities/cmcurl/hash.c
@@ -0,0 +1,315 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "hash.h"
+#include "llist.h"
+#include "memory.h"
+
+/* this must be the last include file */
+#include "memdebug.h"
+
+static unsigned long
+hash_str(const char *key, size_t key_length)
+{
+ char *end = (char *) key + key_length;
+ unsigned long h = 5381;
+
+ while (key < end) {
+ h += h << 5;
+ h ^= (unsigned long) *key++;
+ }
+
+ return h;
+}
+
+static void
+hash_element_dtor(void *user, void *element)
+{
+ struct curl_hash *h = (struct curl_hash *) user;
+ struct curl_hash_element *e = (struct curl_hash_element *) element;
+
+ if (e->key)
+ free(e->key);
+
+ h->dtor(e->ptr);
+
+ free(e);
+}
+
+/* return 1 on error, 0 is fine */
+int
+Curl_hash_init(struct curl_hash *h, int slots, curl_hash_dtor dtor)
+{
+ int i;
+
+ h->dtor = dtor;
+ h->size = 0;
+ h->slots = slots;
+
+ h->table = (struct curl_llist **) malloc(slots * sizeof(struct curl_llist *));
+ if(h->table) {
+ for (i = 0; i < slots; ++i) {
+ h->table[i] = Curl_llist_alloc((curl_llist_dtor) hash_element_dtor);
+ if(!h->table[i]) {
+ while(i--)
+ Curl_llist_destroy(h->table[i], NULL);
+ free(h->table);
+ return 1; /* failure */
+ }
+ }
+ return 0; /* fine */
+ }
+ else
+ return 1; /* failure */
+}
+
+struct curl_hash *
+Curl_hash_alloc(int slots, curl_hash_dtor dtor)
+{
+ struct curl_hash *h;
+
+ h = (struct curl_hash *) malloc(sizeof(struct curl_hash));
+ if (h) {
+ if(Curl_hash_init(h, slots, dtor)) {
+ /* failure */
+ free(h);
+ h = NULL;
+ }
+ }
+
+ return h;
+}
+
+static int
+hash_key_compare(char *key1, size_t key1_len, char *key2, size_t key2_len)
+{
+ if (key1_len == key2_len &&
+ *key1 == *key2 &&
+ memcmp(key1, key2, key1_len) == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct curl_hash_element *
+mk_hash_element(char *key, size_t key_len, const void *p)
+{
+ struct curl_hash_element *he =
+ (struct curl_hash_element *) malloc(sizeof(struct curl_hash_element));
+
+ if(he) {
+ char *dup = malloc(key_len);
+ if(dup) {
+ /* copy the key */
+ memcpy(dup, key, key_len);
+
+ he->key = dup;
+ he->key_len = key_len;
+ he->ptr = (void *) p;
+ }
+ else {
+ /* failed to duplicate the key, free memory and fail */
+ free(he);
+ he = NULL;
+ }
+ }
+ return he;
+}
+
+#define find_slot(__h, __k, __k_len) (hash_str(__k, __k_len) % (__h)->slots)
+
+#define FETCH_LIST(x,y,z) x->table[find_slot(x, y, z)]
+
+/* Return the data in the hash. If there already was a match in the hash,
+ that data is returned. */
+void *
+Curl_hash_add(struct curl_hash *h, char *key, size_t key_len, void *p)
+{
+ struct curl_hash_element *he;
+ struct curl_llist_element *le;
+ struct curl_llist *l = FETCH_LIST(h, key, key_len);
+
+ for (le = l->head; le; le = le->next) {
+ he = (struct curl_hash_element *) le->ptr;
+ if (hash_key_compare(he->key, he->key_len, key, key_len)) {
+ h->dtor(p); /* remove the NEW entry */
+ return he->ptr; /* return the EXISTING entry */
+ }
+ }
+
+ he = mk_hash_element(key, key_len, p);
+ if (he) {
+ if(Curl_llist_insert_next(l, l->tail, he)) {
+ ++h->size;
+ return p; /* return the new entry */
+ }
+ /*
+ * Couldn't insert it, destroy the 'he' element and the key again. We
+ * don't call hash_element_dtor() since that would also call the
+ * "destructor" for the actual data 'p'. When we fail, we shall not touch
+ * that data.
+ */
+ free(he->key);
+ free(he);
+ }
+
+ return NULL; /* failure */
+}
+
+/* remove the identified hash entry, returns non-zero on failure */
+int Curl_hash_delete(struct curl_hash *h, char *key, size_t key_len)
+{
+ struct curl_llist_element *le;
+ struct curl_hash_element *he;
+ struct curl_llist *l = FETCH_LIST(h, key, key_len);
+
+ for (le = l->head; le; le = le->next) {
+ he = le->ptr;
+ if (hash_key_compare(he->key, he->key_len, key, key_len)) {
+ Curl_llist_remove(l, le, (void *) h);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void *
+Curl_hash_pick(struct curl_hash *h, char *key, size_t key_len)
+{
+ struct curl_llist_element *le;
+ struct curl_hash_element *he;
+ struct curl_llist *l = FETCH_LIST(h, key, key_len);
+
+ for (le = l->head; le; le = le->next) {
+ he = le->ptr;
+ if (hash_key_compare(he->key, he->key_len, key, key_len)) {
+ return he->ptr;
+ }
+ }
+
+ return NULL;
+}
+
+#if defined(CURLDEBUG) && defined(AGGRESIVE_TEST)
+void
+Curl_hash_apply(curl_hash *h, void *user,
+ void (*cb)(void *user, void *ptr))
+{
+ struct curl_llist_element *le;
+ int i;
+
+ for (i = 0; i < h->slots; ++i) {
+ for (le = (h->table[i])->head;
+ le;
+ le = le->next) {
+ curl_hash_element *el = le->ptr;
+ cb(user, el->ptr);
+ }
+ }
+}
+#endif
+
+void
+Curl_hash_clean(struct curl_hash *h)
+{
+ int i;
+
+ for (i = 0; i < h->slots; ++i) {
+ Curl_llist_destroy(h->table[i], (void *) h);
+ }
+
+ free(h->table);
+}
+
+void
+Curl_hash_clean_with_criterium(struct curl_hash *h, void *user,
+ int (*comp)(void *, void *))
+{
+ struct curl_llist_element *le;
+ struct curl_llist_element *lnext;
+ struct curl_llist *list;
+ int i;
+
+ for (i = 0; i < h->slots; ++i) {
+ list = h->table[i];
+ le = list->head; /* get first list entry */
+ while(le) {
+ struct curl_hash_element *he = le->ptr;
+ lnext = le->next;
+ /* ask the callback function if we shall remove this entry or not */
+ if (comp(user, he->ptr)) {
+ Curl_llist_remove(list, le, (void *) h);
+ --h->size; /* one less entry in the hash now */
+ }
+ le = lnext;
+ }
+ }
+}
+
+void
+Curl_hash_destroy(struct curl_hash *h)
+{
+ if (!h)
+ return;
+
+ Curl_hash_clean(h);
+ free(h);
+}
+
+#if 0 /* useful function for debugging hashes and their contents */
+void Curl_hash_print(struct curl_hash *h,
+ void (*func)(void *))
+{
+ int i;
+ struct curl_llist_element *le;
+ struct curl_llist *list;
+ struct curl_hash_element *he;
+ if (!h)
+ return;
+
+ fprintf(stderr, "=Hash dump=\n");
+
+ for (i = 0; i < h->slots; i++) {
+ list = h->table[i];
+ le = list->head; /* get first list entry */
+ if(le) {
+ fprintf(stderr, "index %d:", i);
+ while(le) {
+ he = le->ptr;
+ if(func)
+ func(he->ptr);
+ else
+ fprintf(stderr, " [%p]", he->ptr);
+ le = le->next;
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+}
+#endif
diff --git a/Utilities/cmcurl/hash.h b/Utilities/cmcurl/hash.h
new file mode 100644
index 000000000..ceebb5234
--- /dev/null
+++ b/Utilities/cmcurl/hash.h
@@ -0,0 +1,61 @@
+#ifndef __HASH_H
+#define __HASH_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <stddef.h>
+
+#include "llist.h"
+
+typedef void (*curl_hash_dtor)(void *);
+
+struct curl_hash {
+ struct curl_llist **table;
+ curl_hash_dtor dtor;
+ int slots;
+ size_t size;
+};
+
+struct curl_hash_element {
+ void *ptr;
+ char *key;
+ size_t key_len;
+};
+
+
+int Curl_hash_init(struct curl_hash *, int, curl_hash_dtor);
+struct curl_hash *Curl_hash_alloc(int, curl_hash_dtor);
+void *Curl_hash_add(struct curl_hash *, char *, size_t, void *);
+int Curl_hash_delete(struct curl_hash *h, char *key, size_t key_len);
+void *Curl_hash_pick(struct curl_hash *, char *, size_t);
+void Curl_hash_apply(struct curl_hash *h, void *user,
+ void (*cb)(void *user, void *ptr));
+int Curl_hash_count(struct curl_hash *h);
+void Curl_hash_clean(struct curl_hash *h);
+void Curl_hash_clean_with_criterium(struct curl_hash *h, void *user,
+ int (*comp)(void *, void *));
+void Curl_hash_destroy(struct curl_hash *h);
+
+#endif
diff --git a/Utilities/cmcurl/hostares.c b/Utilities/cmcurl/hostares.c
new file mode 100644
index 000000000..1db0f4320
--- /dev/null
+++ b/Utilities/cmcurl/hostares.c
@@ -0,0 +1,307 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+
+#ifdef NEED_MALLOC_H
+#include <malloc.h>
+#endif
+#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 HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+#include "multiif.h"
+#include "connect.h" /* for the Curl_sockerrno() proto */
+
+#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
+
+#include "memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/***********************************************************************
+ * Only for ares-enabled builds
+ **********************************************************************/
+
+#ifdef CURLRES_ARES
+
+/*
+ * Curl_resolv_fdset() is called when someone from the outside world (using
+ * curl_multi_fdset()) wants to get our fd_set setup and we're talking with
+ * ares. The caller must make sure that this function is only called when we
+ * have a working ares channel.
+ *
+ * Returns: CURLE_OK always!
+ */
+
+int Curl_resolv_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks)
+
+{
+ struct timeval maxtime;
+ struct timeval timeout;
+ int max = ares_getsock(conn->data->state.areschannel,
+ (int *)socks, numsocks);
+
+
+ maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
+ maxtime.tv_usec = 0;
+
+ ares_timeout(conn->data->state.areschannel, &maxtime, &timeout);
+
+ Curl_expire(conn->data,
+ (timeout.tv_sec * 1000) + (timeout.tv_usec/1000) );
+
+ return max;
+}
+
+/*
+ * Curl_is_resolved() is called repeatedly to check if a previous name resolve
+ * request has completed. It should also make sure to time-out if the
+ * operation seems to take too long.
+ *
+ * Returns normal CURLcode errors.
+ */
+CURLcode Curl_is_resolved(struct connectdata *conn,
+ struct Curl_dns_entry **dns)
+{
+ fd_set read_fds, write_fds;
+ struct timeval tv={0,0};
+ struct SessionHandle *data = conn->data;
+ int nfds;
+
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+
+ nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
+
+ (void)select(nfds, &read_fds, &write_fds, NULL,
+ (struct timeval *)&tv);
+
+ /* Call ares_process() unconditonally here, even if we simply timed out
+ above, as otherwise the ares name resolve won't timeout! */
+ ares_process(data->state.areschannel, &read_fds, &write_fds);
+
+ *dns = NULL;
+
+ if(conn->async.done) {
+ /* we're done, kill the ares handle */
+ if(!conn->async.dns) {
+ failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
+ ares_strerror(conn->async.status));
+ return CURLE_COULDNT_RESOLVE_HOST;
+ }
+ *dns = conn->async.dns;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_wait_for_resolv() waits for a resolve to finish. This function should
+ * be avoided since using this risk getting the multi interface to "hang".
+ *
+ * If 'entry' is non-NULL, make it point to the resolved dns entry
+ *
+ * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
+ * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
+ */
+CURLcode Curl_wait_for_resolv(struct connectdata *conn,
+ struct Curl_dns_entry **entry)
+{
+ CURLcode rc=CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ long timeout = CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */
+
+ /* now, see if there's a connect timeout or a regular timeout to
+ use instead of the default one */
+ if(conn->data->set.connecttimeout)
+ timeout = conn->data->set.connecttimeout;
+ else if(conn->data->set.timeout)
+ timeout = conn->data->set.timeout;
+
+ /* We convert the number of seconds into number of milliseconds here: */
+ if(timeout < 2147483)
+ /* maximum amount of seconds that can be multiplied with 1000 and
+ still fit within 31 bits */
+ timeout *= 1000;
+ else
+ timeout = 0x7fffffff; /* ridiculous amount of time anyway */
+
+ /* Wait for the name resolve query to complete. */
+ while (1) {
+ int nfds=0;
+ fd_set read_fds, write_fds;
+ struct timeval *tvp, tv, store;
+ int count;
+ struct timeval now = Curl_tvnow();
+ long timediff;
+
+ store.tv_sec = (int)timeout/1000;
+ store.tv_usec = (timeout%1000)*1000;
+
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+ nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
+ if (nfds == 0)
+ /* no file descriptors means we're done waiting */
+ break;
+ tvp = ares_timeout(data->state.areschannel, &store, &tv);
+ count = select(nfds, &read_fds, &write_fds, NULL, tvp);
+ if (count < 0 && Curl_sockerrno() != EINVAL)
+ break;
+
+ ares_process(data->state.areschannel, &read_fds, &write_fds);
+
+ timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */
+ timeout -= timediff?timediff:1; /* always deduct at least 1 */
+ if (timeout < 0) {
+ /* our timeout, so we cancel the ares operation */
+ ares_cancel(data->state.areschannel);
+ break;
+ }
+ }
+
+ /* Operation complete, if the lookup was successful we now have the entry
+ in the cache. */
+
+ if(entry)
+ *entry = conn->async.dns;
+
+ if(!conn->async.dns) {
+ /* a name was not resolved */
+ if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
+ failf(data, "Resolving host timed out: %s", conn->host.dispname);
+ rc = CURLE_OPERATION_TIMEDOUT;
+ }
+ else if(conn->async.done) {
+ failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
+ ares_strerror(conn->async.status));
+ rc = CURLE_COULDNT_RESOLVE_HOST;
+ }
+ else
+ rc = CURLE_OPERATION_TIMEDOUT;
+
+ /* close the connection, since we can't return failure here without
+ cleaning up this connection properly */
+ conn->bits.close = TRUE;
+ }
+
+ return rc;
+}
+
+/*
+ * Curl_getaddrinfo() - when using ares
+ *
+ * Returns name information about the given hostname and port number. If
+ * successful, the 'hostent' is returned and the forth argument will point to
+ * memory we need to free after use. That memory *MUST* be freed with
+ * Curl_freeaddrinfo(), nothing else.
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ char *bufp;
+ struct SessionHandle *data = conn->data;
+ in_addr_t in = inet_addr(hostname);
+
+ *waitp = FALSE;
+
+ if (in != CURL_INADDR_NONE) {
+ /* This is a dotted IP address 123.123.123.123-style */
+ return Curl_ip2addr(in, hostname, port);
+ }
+
+ bufp = strdup(hostname);
+
+ if(bufp) {
+ Curl_safefree(conn->async.hostname);
+ conn->async.hostname = bufp;
+ conn->async.port = port;
+ conn->async.done = FALSE; /* not done */
+ conn->async.status = 0; /* clear */
+ conn->async.dns = NULL; /* clear */
+
+ /* areschannel is already setup in the Curl_open() function */
+ ares_gethostbyname(data->state.areschannel, hostname, PF_INET,
+ (ares_host_callback)Curl_addrinfo4_callback, conn);
+
+ *waitp = TRUE; /* please wait for the response */
+ }
+ return NULL; /* no struct yet */
+}
+#endif /* CURLRES_ARES */
diff --git a/Utilities/cmcurl/hostasyn.c b/Utilities/cmcurl/hostasyn.c
new file mode 100644
index 000000000..3df147910
--- /dev/null
+++ b/Utilities/cmcurl/hostasyn.c
@@ -0,0 +1,174 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+
+#ifdef NEED_MALLOC_H
+#include <malloc.h>
+#endif
+#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 HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.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
+
+#include "memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/***********************************************************************
+ * Only for builds using asynchronous name resolves
+ **********************************************************************/
+#ifdef CURLRES_ASYNCH
+/*
+ * addrinfo_callback() gets called by ares, gethostbyname_thread() or
+ * getaddrinfo_thread() when we got the name resolved (or not!).
+ *
+ * If the status argument is CURL_ASYNC_SUCCESS, we might need to copy the
+ * address field since it might be freed when this function returns. This
+ * operation stores the resolved data in the DNS cache.
+ *
+ * NOTE: for IPv6 operations, Curl_addrinfo_copy() returns the same
+ * pointer it is given as argument!
+ *
+ * The storage operation locks and unlocks the DNS cache.
+ */
+static CURLcode addrinfo_callback(void *arg, /* "struct connectdata *" */
+ int status,
+ void *addr)
+{
+ struct connectdata *conn = (struct connectdata *)arg;
+ struct Curl_dns_entry *dns = NULL;
+ CURLcode rc = CURLE_OK;
+
+ conn->async.status = status;
+
+ if(CURL_ASYNC_SUCCESS == status) {
+
+ /*
+ * IPv4/ares: Curl_addrinfo_copy() copies the address and returns an
+ * allocated version.
+ *
+ * IPv6: Curl_addrinfo_copy() returns the input pointer!
+ */
+ Curl_addrinfo *ai = Curl_addrinfo_copy(addr, conn->async.port);
+ if(ai) {
+ struct SessionHandle *data = conn->data;
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ dns = Curl_cache_addr(data, ai,
+ conn->async.hostname,
+ conn->async.port);
+ if(!dns) {
+ /* failed to store, cleanup and return error */
+ Curl_freeaddrinfo(ai);
+ rc = CURLE_OUT_OF_MEMORY;
+ }
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ }
+ else
+ rc = CURLE_OUT_OF_MEMORY;
+ }
+
+ conn->async.dns = dns;
+
+ /* Set async.done TRUE last in this function since it may be used multi-
+ threaded and once this is TRUE the other thread may read fields from the
+ async struct */
+ conn->async.done = TRUE;
+
+ /* ipv4: The input hostent struct will be freed by ares when we return from
+ this function */
+ return rc;
+}
+
+CURLcode Curl_addrinfo4_callback(void *arg, /* "struct connectdata *" */
+ int status,
+ struct hostent *hostent)
+{
+ return addrinfo_callback(arg, status, hostent);
+}
+
+#ifdef CURLRES_IPV6
+CURLcode Curl_addrinfo6_callback(void *arg, /* "struct connectdata *" */
+ int status,
+ struct addrinfo *ai)
+{
+ /* NOTE: for CURLRES_ARES, the 'ai' argument is really a
+ * 'struct hostent' pointer.
+ */
+ return addrinfo_callback(arg, status, ai);
+}
+#endif
+
+#endif /* CURLRES_ASYNC */
diff --git a/Utilities/cmcurl/hostip.c b/Utilities/cmcurl/hostip.c
new file mode 100644
index 000000000..fd555ef9d
--- /dev/null
+++ b/Utilities/cmcurl/hostip.c
@@ -0,0 +1,636 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+
+#ifdef NEED_MALLOC_H
+#include <malloc.h>
+#endif
+#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 HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+#include "inet_ntop.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
+
+#include "memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * hostip.c explained
+ * ==================
+ *
+ * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
+ * source file are these:
+ *
+ * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
+ * that. The host may not be able to resolve IPv6, but we don't really have to
+ * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
+ * defined.
+ *
+ * CURLRES_ARES - is defined if libcurl is built to use c-ares for
+ * asynchronous name resolves. This can be Windows or *nix.
+ *
+ * CURLRES_THREADED - is defined if libcurl is built to run under (native)
+ * Windows, and then the name resolve will be done in a new thread, and the
+ * supported API will be the same as for ares-builds.
+ *
+ * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
+ * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
+ * defined.
+ *
+ * The host*.c sources files are split up like this:
+ *
+ * hostip.c - method-independent resolver functions and utility functions
+ * hostasyn.c - functions for asynchronous name resolves
+ * hostsyn.c - functions for synchronous name resolves
+ * hostares.c - functions for ares-using name resolves
+ * hostthre.c - functions for threaded name resolves
+ * hostip4.c - ipv4-specific functions
+ * hostip6.c - ipv6-specific functions
+ *
+ * The hostip.h is the united header file for all this. It defines the
+ * CURLRES_* defines based on the config*.h and setup.h defines.
+ */
+
+/* These two symbols are for the global DNS cache */
+static struct curl_hash hostname_cache;
+static int host_cache_initialized;
+
+static void freednsentry(void *freethis);
+
+/*
+ * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
+ * Global DNS cache is general badness. Do not use. This will be removed in
+ * a future version. Use the share interface instead!
+ */
+void Curl_global_host_cache_init(void)
+{
+ if (!host_cache_initialized) {
+ Curl_hash_init(&hostname_cache, 7, freednsentry);
+ host_cache_initialized = 1;
+ }
+}
+
+/*
+ * Return a pointer to the global cache
+ */
+struct curl_hash *Curl_global_host_cache_get(void)
+{
+ return &hostname_cache;
+}
+
+/*
+ * Destroy and cleanup the global DNS cache
+ */
+void Curl_global_host_cache_dtor(void)
+{
+ if (host_cache_initialized) {
+ Curl_hash_clean(&hostname_cache);
+ host_cache_initialized = 0;
+ }
+}
+
+/*
+ * Return # of adresses in a Curl_addrinfo struct
+ */
+int Curl_num_addresses(const Curl_addrinfo *addr)
+{
+ int i;
+ for (i = 0; addr; addr = addr->ai_next, i++)
+ ; /* empty loop */
+ return i;
+}
+
+/*
+ * Curl_printable_address() returns a printable version of the 1st address
+ * given in the 'ip' argument. The result will be stored in the buf that is
+ * bufsize bytes big.
+ *
+ * If the conversion fails, it returns NULL.
+ */
+const char *Curl_printable_address(const Curl_addrinfo *ip,
+ char *buf, size_t bufsize)
+{
+ const void *ip4 = &((const struct sockaddr_in*)ip->ai_addr)->sin_addr;
+ int af = ip->ai_family;
+#ifdef CURLRES_IPV6
+ const void *ip6 = &((const struct sockaddr_in6*)ip->ai_addr)->sin6_addr;
+#else
+ const void *ip6 = NULL;
+#endif
+
+ return Curl_inet_ntop(af, af == AF_INET ? ip4 : ip6, buf, bufsize);
+}
+
+/*
+ * Return a hostcache id string for the providing host + port, to be used by
+ * the DNS caching.
+ */
+static char *
+create_hostcache_id(const char *server, int port)
+{
+ /* create and return the new allocated entry */
+ return aprintf("%s:%d", server, port);
+}
+
+struct hostcache_prune_data {
+ int cache_timeout;
+ time_t now;
+};
+
+/*
+ * This function is set as a callback to be called for every entry in the DNS
+ * cache when we want to prune old unused entries.
+ *
+ * Returning non-zero means remove the entry, return 0 to keep it in the
+ * cache.
+ */
+static int
+hostcache_timestamp_remove(void *datap, void *hc)
+{
+ struct hostcache_prune_data *data =
+ (struct hostcache_prune_data *) datap;
+ struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
+
+ if ((data->now - c->timestamp < data->cache_timeout) ||
+ c->inuse) {
+ /* please don't remove */
+ return 0;
+ }
+
+ /* fine, remove */
+ return 1;
+}
+
+/*
+ * Prune the DNS cache. This assumes that a lock has already been taken.
+ */
+static void
+hostcache_prune(struct curl_hash *hostcache, int cache_timeout, time_t now)
+{
+ struct hostcache_prune_data user;
+
+ user.cache_timeout = cache_timeout;
+ user.now = now;
+
+ Curl_hash_clean_with_criterium(hostcache,
+ (void *) &user,
+ hostcache_timestamp_remove);
+}
+
+/*
+ * Library-wide function for pruning the DNS cache. This function takes and
+ * returns the appropriate locks.
+ */
+void Curl_hostcache_prune(struct SessionHandle *data)
+{
+ time_t now;
+
+ if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
+ /* cache forever means never prune, and NULL hostcache means
+ we can't do it */
+ return;
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ time(&now);
+
+ /* Remove outdated and unused entries from the hostcache */
+ hostcache_prune(data->dns.hostcache,
+ data->set.dns_cache_timeout,
+ now);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+}
+
+static int
+remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
+{
+ struct hostcache_prune_data user;
+
+ if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
+ /* cache forever means never prune, and NULL hostcache means
+ we can't do it */
+ return 0;
+
+ time(&user.now);
+ user.cache_timeout = data->set.dns_cache_timeout;
+
+ if ( !hostcache_timestamp_remove(&user,dns) )
+ return 0;
+
+ /* ok, we do need to clear the cache. although we need to remove just a
+ single entry we clean the entire hash, as no explicit delete function
+ is provided */
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ Curl_hash_clean_with_criterium(data->dns.hostcache,
+ (void *) &user,
+ hostcache_timestamp_remove);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ return 1;
+}
+
+
+#ifdef HAVE_SIGSETJMP
+/* Beware this is a global and unique instance. This is used to store the
+ return address that we can jump back to from inside a signal handler. This
+ is not thread-safe stuff. */
+sigjmp_buf curl_jmpenv;
+#endif
+
+
+/*
+ * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
+ *
+ * When calling Curl_resolv() has resulted in a response with a returned
+ * address, we call this function to store the information in the dns
+ * cache etc
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
+ */
+struct Curl_dns_entry *
+Curl_cache_addr(struct SessionHandle *data,
+ Curl_addrinfo *addr,
+ const char *hostname,
+ int port)
+{
+ char *entry_id;
+ size_t entry_len;
+ struct Curl_dns_entry *dns;
+ struct Curl_dns_entry *dns2;
+ time_t now;
+
+ /* Create an entry id, based upon the hostname and port */
+ entry_id = create_hostcache_id(hostname, port);
+ /* If we can't create the entry id, fail */
+ if (!entry_id)
+ return NULL;
+ entry_len = strlen(entry_id);
+
+ /* Create a new cache entry */
+ dns = (struct Curl_dns_entry *) calloc(sizeof(struct Curl_dns_entry), 1);
+ if (!dns) {
+ free(entry_id);
+ return NULL;
+ }
+
+ dns->inuse = 0; /* init to not used */
+ dns->addr = addr; /* this is the address(es) */
+
+ /* Store the resolved data in our DNS cache. This function may return a
+ pointer to an existing struct already present in the hash, and it may
+ return the same argument we pass in. Make no assumptions. */
+ dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
+ (void *)dns);
+ if(!dns2) {
+ /* Major badness, run away. */
+ free(dns);
+ free(entry_id);
+ return NULL;
+ }
+ time(&now);
+ dns = dns2;
+
+ dns->timestamp = now; /* used now */
+ dns->inuse++; /* mark entry as in-use */
+
+ /* free the allocated entry_id again */
+ free(entry_id);
+
+ return dns;
+}
+
+/*
+ * Curl_resolv() is the main name resolve function within libcurl. It resolves
+ * a name and returns a pointer to the entry in the 'entry' argument (if one
+ * is provided). This function might return immediately if we're using asynch
+ * resolves. See the return codes.
+ *
+ * The cache entry we return will get its 'inuse' counter increased when this
+ * function is used. You MUST call Curl_resolv_unlock() later (when you're
+ * done using this struct) to decrease the counter again.
+ *
+ * Return codes:
+ *
+ * CURLRESOLV_ERROR (-1) = error, no pointer
+ * CURLRESOLV_RESOLVED (0) = OK, pointer provided
+ * CURLRESOLV_PENDING (1) = waiting for response, no pointer
+ */
+
+int Curl_resolv(struct connectdata *conn,
+ const char *hostname,
+ int port,
+ struct Curl_dns_entry **entry)
+{
+ char *entry_id = NULL;
+ struct Curl_dns_entry *dns = NULL;
+ size_t entry_len;
+ int wait;
+ struct SessionHandle *data = conn->data;
+ CURLcode result;
+ int rc;
+ *entry = NULL;
+
+#ifdef HAVE_SIGSETJMP
+ /* this allows us to time-out from the name resolver, as the timeout
+ will generate a signal and we will siglongjmp() from that here */
+ if(!data->set.no_signal) {
+ if (sigsetjmp(curl_jmpenv, 1)) {
+ /* this is coming from a siglongjmp() */
+ failf(data, "name lookup timed out");
+ return CURLRESOLV_ERROR;
+ }
+ }
+#endif
+
+ /* Create an entry id, based upon the hostname and port */
+ entry_id = create_hostcache_id(hostname, port);
+ /* If we can't create the entry id, fail */
+ if (!entry_id)
+ return CURLRESOLV_ERROR;
+
+ entry_len = strlen(entry_id);
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ /* See if its already in our dns cache */
+ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ /* free the allocated entry_id again */
+ free(entry_id);
+
+ /* See whether the returned entry is stale. Deliberately done after the
+ locked block */
+ if ( remove_entry_if_stale(data,dns) )
+ dns = NULL; /* the memory deallocation is being handled by the hash */
+
+ rc = CURLRESOLV_ERROR; /* default to failure */
+
+ if (!dns) {
+ /* The entry was not in the cache. Resolve it to IP address */
+
+ Curl_addrinfo *addr;
+
+ /* Check what IP specifics the app has requested and if we can provide it.
+ * If not, bail out. */
+ if(!Curl_ipvalid(data))
+ return CURLRESOLV_ERROR;
+
+ /* If Curl_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
+ value indicating that we need to wait for the response to the resolve
+ call */
+ addr = Curl_getaddrinfo(conn, hostname, port, &wait);
+
+ if (!addr) {
+ if(wait) {
+ /* the response to our resolve call will come asynchronously at
+ a later time, good or bad */
+ /* First, check that we haven't received the info by now */
+ result = Curl_is_resolved(conn, &dns);
+ if(result) /* error detected */
+ return CURLRESOLV_ERROR;
+ if(dns)
+ rc = CURLRESOLV_RESOLVED; /* pointer provided */
+ else
+ rc = CURLRESOLV_PENDING; /* no info yet */
+ }
+ }
+ else {
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ /* we got a response, store it in the cache */
+ dns = Curl_cache_addr(data, addr, hostname, port);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ if(!dns)
+ /* returned failure, bail out nicely */
+ Curl_freeaddrinfo(addr);
+ else
+ rc = CURLRESOLV_RESOLVED;
+ }
+ }
+ else {
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+ dns->inuse++; /* we use it! */
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ rc = CURLRESOLV_RESOLVED;
+ }
+
+ *entry = dns;
+
+ return rc;
+}
+
+/*
+ * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
+ * made, the struct may be destroyed due to pruning. It is important that only
+ * one unlock is made for each Curl_resolv() call.
+ */
+void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
+{
+ curlassert(dns && (dns->inuse>0));
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ dns->inuse--;
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+}
+
+/*
+ * File-internal: free a cache dns entry.
+ */
+static void freednsentry(void *freethis)
+{
+ struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
+
+ Curl_freeaddrinfo(p->addr);
+
+ free(p);
+}
+
+/*
+ * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
+ */
+struct curl_hash *Curl_mk_dnscache(void)
+{
+ return Curl_hash_alloc(7, freednsentry);
+}
+
+#ifdef CURLRES_ADDRINFO_COPY
+
+/* align on even 64bit boundaries */
+#define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
+
+/*
+ * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
+ * returns a pointer to the malloc()ed copy. You need to call free() on the
+ * returned buffer when you're done with it.
+ */
+Curl_addrinfo *Curl_addrinfo_copy(const void *org, int port)
+{
+ const struct hostent *orig = org;
+
+ return Curl_he2ai(orig, port);
+}
+#endif /* CURLRES_ADDRINFO_COPY */
+
+/***********************************************************************
+ * Only for plain-ipv4 and c-ares builds
+ **********************************************************************/
+
+#if defined(CURLRES_IPV4) || defined(CURLRES_ARES)
+/*
+ * This is a function for freeing name information in a protocol independent
+ * way.
+ */
+void Curl_freeaddrinfo(Curl_addrinfo *ai)
+{
+ Curl_addrinfo *next;
+
+ /* walk over the list and free all entries */
+ while(ai) {
+ next = ai->ai_next;
+ free(ai);
+ ai = next;
+ }
+}
+
+struct namebuf {
+ struct hostent hostentry;
+ char *h_addr_list[2];
+ struct in_addr addrentry;
+ char h_name[16]; /* 123.123.123.123 = 15 letters is maximum */
+};
+
+/*
+ * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter
+ * together with a pointer to the string version of the address, and it
+ * returns a Curl_addrinfo chain filled in correctly with information for this
+ * address/host.
+ *
+ * The input parameters ARE NOT checked for validity but they are expected
+ * to have been checked already when this is called.
+ */
+Curl_addrinfo *Curl_ip2addr(in_addr_t num, const char *hostname, int port)
+{
+ Curl_addrinfo *ai;
+ struct hostent *h;
+ struct in_addr *addrentry;
+ struct namebuf buffer;
+ struct namebuf *buf = &buffer;
+
+ h = &buf->hostentry;
+ h->h_addr_list = &buf->h_addr_list[0];
+ addrentry = &buf->addrentry;
+#ifdef _CRAYC
+ /* On UNICOS, s_addr is a bit field and for some reason assigning to it
+ * doesn't work. There must be a better fix than this ugly hack.
+ */
+ memcpy(addrentry, &num, SIZEOF_in_addr);
+#else
+ addrentry->s_addr = num;
+#endif
+ 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 = &buf->h_name[0];
+ h->h_aliases = NULL;
+
+ /* Now store the dotted version of the address */
+ snprintf((char *)h->h_name, 16, "%s", hostname);
+
+ ai = Curl_he2ai(h, port);
+
+ return ai;
+}
+#endif
+
+
diff --git a/Utilities/cmcurl/hostip.h b/Utilities/cmcurl/hostip.h
new file mode 100644
index 000000000..e6d63ca71
--- /dev/null
+++ b/Utilities/cmcurl/hostip.h
@@ -0,0 +1,271 @@
+#ifndef __HOSTIP_H
+#define __HOSTIP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+#include "hash.h"
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t uint32_t
+#endif
+
+/*
+ * Setup comfortable CURLRES_* defines to use in the host*.c sources.
+ */
+
+#ifdef USE_ARES
+#define CURLRES_ASYNCH
+#define CURLRES_ARES
+#endif
+
+#ifdef USE_THREADING_GETHOSTBYNAME
+#define CURLRES_ASYNCH
+#define CURLRES_THREADED
+#endif
+
+#ifdef USE_THREADING_GETADDRINFO
+#define CURLRES_ASYNCH
+#define CURLRES_THREADED
+#endif
+
+#ifdef ENABLE_IPV6
+#define CURLRES_IPV6
+#else
+#define CURLRES_IPV4
+#endif
+
+#if defined(CURLRES_IPV4) || defined(CURLRES_ARES)
+#if !defined(HAVE_GETHOSTBYNAME_R) || defined(CURLRES_ASYNCH)
+/* If built for ipv4 and missing gethostbyname_r(), or if using async name
+ resolve, we need the Curl_addrinfo_copy() function (which itself needs the
+ Curl_he2ai() function)) */
+#define CURLRES_ADDRINFO_COPY
+#endif
+#endif /* IPv4/ares-only */
+
+#ifndef CURLRES_ASYNCH
+#define CURLRES_SYNCH
+#endif
+
+#ifndef USE_LIBIDN
+#define CURLRES_IDN
+#endif
+
+/* 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 edition, p. 304: 8192 bytes!
+ */
+#define CURL_HOSTENT_SIZE 9000
+
+#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this
+ many seconds for a name resolve */
+
+#ifdef CURLRES_ARES
+#define CURL_ASYNC_SUCCESS ARES_SUCCESS
+#else
+#define CURL_ASYNC_SUCCESS CURLE_OK
+#define ares_cancel(x) do {} while(0)
+#define ares_destroy(x) do {} while(0)
+#endif
+
+/*
+ * Curl_addrinfo MUST be used for all name resolved info.
+ */
+#ifdef CURLRES_IPV6
+typedef struct addrinfo Curl_addrinfo;
+#else
+/* OK, so some ipv4-only include tree probably have the addrinfo struct, but
+ to work even on those that don't, we provide our own look-alike! */
+struct Curl_addrinfo {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ socklen_t ai_addrlen; /* Follow rfc3493 struct addrinfo */
+ char *ai_canonname;
+ struct sockaddr *ai_addr;
+ struct Curl_addrinfo *ai_next;
+};
+typedef struct Curl_addrinfo Curl_addrinfo;
+#endif
+
+struct addrinfo;
+struct hostent;
+struct SessionHandle;
+struct connectdata;
+
+void Curl_global_host_cache_init(void);
+void Curl_global_host_cache_dtor(void);
+struct curl_hash *Curl_global_host_cache_get(void);
+
+#define Curl_global_host_cache_use(__p) ((__p)->set.global_dns_cache)
+
+struct Curl_dns_entry {
+ Curl_addrinfo *addr;
+ time_t timestamp;
+ long inuse; /* use-counter, make very sure you decrease this
+ when you're done using the address you received */
+};
+
+/*
+ * Curl_resolv() returns an entry with the info for the specified host
+ * and port.
+ *
+ * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
+ * use, or we'll leak memory!
+ */
+/* return codes */
+#define CURLRESOLV_ERROR -1
+#define CURLRESOLV_RESOLVED 0
+#define CURLRESOLV_PENDING 1
+int Curl_resolv(struct connectdata *conn, const char *hostname,
+ int port, struct Curl_dns_entry **dnsentry);
+
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct SessionHandle *data);
+
+/*
+ * Curl_getaddrinfo() is the generic low-level name resolve API within this
+ * source file. There are several versions of this function - for different
+ * name resolve layers (selected at build-time). They all take this same set
+ * of arguments
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ const char *hostname,
+ int port,
+ int *waitp);
+
+CURLcode Curl_is_resolved(struct connectdata *conn,
+ struct Curl_dns_entry **dns);
+CURLcode Curl_wait_for_resolv(struct connectdata *conn,
+ struct Curl_dns_entry **dnsentry);
+
+/* Curl_resolv_getsock() is a generic function that exists in multiple
+ versions depending on what name resolve technology we've built to use. The
+ function is called from the multi_getsock() function. 'sock' is a pointer
+ to an array to hold the file descriptors, with 'numsock' being the size of
+ that array (in number of entries). This function is supposed to return
+ bitmask indicating what file descriptors (referring to array indexes in the
+ 'sock' array) to wait for, read/write. */
+int Curl_resolv_getsock(struct connectdata *conn, curl_socket_t *sock,
+ int numsocks);
+
+/* unlock a previously resolved dns entry */
+void Curl_resolv_unlock(struct SessionHandle *data,
+ struct Curl_dns_entry *dns);
+
+/* for debugging purposes only: */
+void Curl_scan_cache_used(void *user, void *ptr);
+
+/* free name info */
+void Curl_freeaddrinfo(Curl_addrinfo *freeaddr);
+
+/* make a new dns cache and return the handle */
+struct curl_hash *Curl_mk_dnscache(void);
+
+/* prune old entries from the DNS cache */
+void Curl_hostcache_prune(struct SessionHandle *data);
+
+/* Return # of adresses in a Curl_addrinfo struct */
+int Curl_num_addresses (const Curl_addrinfo *addr);
+
+#ifdef CURLDEBUG
+void curl_dofreeaddrinfo(struct addrinfo *freethis,
+ int line, const char *source);
+int curl_dogetaddrinfo(const char *hostname, const char *service,
+ struct addrinfo *hints,
+ struct addrinfo **result,
+ int line, const char *source);
+#ifdef HAVE_GETNAMEINFO
+int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa,
+ GETNAMEINFO_TYPE_ARG2 salen,
+ char *host, GETNAMEINFO_TYPE_ARG46 hostlen,
+ char *serv, GETNAMEINFO_TYPE_ARG46 servlen,
+ GETNAMEINFO_TYPE_ARG7 flags,
+ int line, const char *source);
+#endif
+#endif
+
+/* This is the callback function that is used when we build with asynch
+ resolve, ipv4 */
+CURLcode Curl_addrinfo4_callback(void *arg,
+ int status,
+ struct hostent *hostent);
+/* This is the callback function that is used when we build with asynch
+ resolve, ipv6 */
+CURLcode Curl_addrinfo6_callback(void *arg,
+ int status,
+ struct addrinfo *ai);
+
+
+/* [ipv4/ares only] Creates a Curl_addrinfo struct from a numerical-only IP
+ address */
+Curl_addrinfo *Curl_ip2addr(in_addr_t num, const char *hostname, int port);
+
+/* [ipv4/ares only] Curl_he2ai() converts a struct hostent to a Curl_addrinfo chain
+ and returns it */
+Curl_addrinfo *Curl_he2ai(const struct hostent *, int port);
+
+/* Clone a Curl_addrinfo struct, works protocol independently */
+Curl_addrinfo *Curl_addrinfo_copy(const void *orig, int port);
+
+/*
+ * Curl_printable_address() returns a printable version of the 1st address
+ * given in the 'ip' argument. The result will be stored in the buf that is
+ * bufsize bytes big.
+ */
+const char *Curl_printable_address(const Curl_addrinfo *ip,
+ char *buf, size_t bufsize);
+
+/*
+ * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
+ */
+struct Curl_dns_entry *
+Curl_cache_addr(struct SessionHandle *data, Curl_addrinfo *addr,
+ const char *hostname, int port);
+
+/*
+ * Curl_destroy_thread_data() cleans up async resolver data.
+ * Complementary of ares_destroy.
+ */
+struct Curl_async; /* forward-declaration */
+void Curl_destroy_thread_data(struct Curl_async *async);
+
+#ifndef INADDR_NONE
+#define CURL_INADDR_NONE (in_addr_t) ~0
+#else
+#define CURL_INADDR_NONE INADDR_NONE
+#endif
+
+
+
+
+#endif
diff --git a/Utilities/cmcurl/hostip4.c b/Utilities/cmcurl/hostip4.c
new file mode 100644
index 000000000..877baa2ca
--- /dev/null
+++ b/Utilities/cmcurl/hostip4.c
@@ -0,0 +1,389 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <errno.h>
+
+#ifdef NEED_MALLOC_H
+#include <malloc.h>
+#endif
+#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 HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+#include "inet_pton.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
+
+#include "memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/***********************************************************************
+ * Only for plain-ipv4 builds
+ **********************************************************************/
+#ifdef CURLRES_IPV4 /* plain ipv4 code coming up */
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct SessionHandle *data)
+{
+ if(data->set.ip_version == CURL_IPRESOLVE_V6)
+ /* an ipv6 address was requested and we can't get/use one */
+ return FALSE;
+
+ return TRUE; /* OK, proceed */
+}
+
+#ifdef CURLRES_SYNCH /* the functions below are for synchronous resolves */
+
+/*
+ * Curl_getaddrinfo() - the ipv4 synchronous version.
+ *
+ * The original code to this function was from the Dancer source code, written
+ * by Bjorn Reese, it has since been patched and modified considerably.
+ *
+ * gethostbyname_r() is the thread-safe version of the gethostbyname()
+ * function. When we build for plain IPv4, we attempt to use this
+ * function. There are _three_ different gethostbyname_r() versions, and we
+ * detect which one this platform supports in the configure script and set up
+ * the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or
+ * HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME
+ * has the corresponding rules. This is primarily on *nix. Note that some unix
+ * flavours have thread-safe versions of the plain gethostbyname() etc.
+ *
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ Curl_addrinfo *ai = NULL;
+ struct hostent *h = NULL;
+ in_addr_t in;
+ struct SessionHandle *data = conn->data;
+ struct hostent *buf = NULL;
+
+ (void)port; /* unused in IPv4 code */
+
+ *waitp = 0; /* don't wait, we act synchronously */
+
+ if(1 == Curl_inet_pton(AF_INET, hostname, &in))
+ /* This is a dotted IP address 123.123.123.123-style */
+ return Curl_ip2addr(in, hostname, port);
+
+#if defined(HAVE_GETHOSTBYNAME_R)
+ /*
+ * gethostbyname_r() is the preferred resolve function for many platforms.
+ * Since there are three different versions of it, the following code is
+ * somewhat #ifdef-ridden.
+ */
+ else {
+ int h_errnop;
+ int res=ERANGE;
+
+ buf = (struct hostent *)calloc(CURL_HOSTENT_SIZE, 1);
+ if(!buf)
+ return NULL; /* major failure */
+ /*
+ * The clearing of the buffer is a workaround for a gethostbyname_r bug in
+ * qnx nto and it is also _required_ for some of these functions on some
+ * platforms.
+ */
+
+#ifdef HAVE_GETHOSTBYNAME_R_5
+ /* Solaris, IRIX and more */
+ (void)res; /* prevent compiler warning */
+ h = gethostbyname_r(hostname,
+ (struct hostent *)buf,
+ (char *)buf + sizeof(struct hostent),
+ CURL_HOSTENT_SIZE - sizeof(struct hostent),
+ &h_errnop);
+
+ /* If the buffer is too small, it returns NULL and sets errno to
+ * ERANGE. The errno is thread safe if this is compiled with
+ * -D_REENTRANT as then the 'errno' variable is a macro defined to get
+ * used properly for threads.
+ */
+
+ if(h) {
+ ;
+ }
+ else
+#endif /* HAVE_GETHOSTBYNAME_R_5 */
+#ifdef HAVE_GETHOSTBYNAME_R_6
+ /* Linux */
+
+ res=gethostbyname_r(hostname,
+ (struct hostent *)buf,
+ (char *)buf + sizeof(struct hostent),
+ CURL_HOSTENT_SIZE - sizeof(struct hostent),
+ &h, /* DIFFERENCE */
+ &h_errnop);
+ /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a
+ * sudden this function returns EAGAIN if the given buffer size is too
+ * small. Previous versions are known to return ERANGE for the same
+ * problem.
+ *
+ * This wouldn't be such a big problem if older versions wouldn't
+ * sometimes return EAGAIN on a common failure case. Alas, we can't
+ * assume that EAGAIN *or* ERANGE means ERANGE for any given version of
+ * glibc.
+ *
+ * For now, we do that and thus we may call the function repeatedly and
+ * fail for older glibc versions that return EAGAIN, until we run out of
+ * buffer size (step_size grows beyond CURL_HOSTENT_SIZE).
+ *
+ * If anyone has a better fix, please tell us!
+ *
+ * -------------------------------------------------------------------
+ *
+ * On October 23rd 2003, Dan C dug up more details on the mysteries of
+ * gethostbyname_r() in glibc:
+ *
+ * In glibc 2.2.5 the interface is different (this has also been
+ * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't
+ * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32
+ * (shipped/upgraded by Redhat 7.2) don't show this behavior!
+ *
+ * In this "buggy" version, the return code is -1 on error and 'errno'
+ * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a
+ * thread-safe variable.
+ */
+
+ if(!h) /* failure */
+#endif/* HAVE_GETHOSTBYNAME_R_6 */
+#ifdef HAVE_GETHOSTBYNAME_R_3
+ /* AIX, Digital Unix/Tru64, HPUX 10, more? */
+
+ /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of
+ * the plain fact that it does not return unique full buffers on each
+ * call, but instead several of the pointers in the hostent structs will
+ * point to the same actual data! This have the unfortunate down-side that
+ * our caching system breaks down horribly. Luckily for us though, AIX 4.3
+ * and more recent versions have a "completely thread-safe"[*] libc where
+ * all the data is stored in thread-specific memory areas making calls to
+ * the plain old gethostbyname() work fine even for multi-threaded
+ * programs.
+ *
+ * This AIX 4.3 or later detection is all made in the configure script.
+ *
+ * Troels Walsted Hansen helped us work this out on March 3rd, 2003.
+ *
+ * [*] = much later we've found out that it isn't at all "completely
+ * thread-safe", but at least the gethostbyname() function is.
+ */
+
+ if(CURL_HOSTENT_SIZE >=
+ (sizeof(struct hostent)+sizeof(struct hostent_data))) {
+
+ /* August 22nd, 2000: Albert Chin-A-Young brought an updated version
+ * that should work! September 20: Richard Prescott worked on the buffer
+ * size dilemma.
+ */
+
+ res = gethostbyname_r(hostname,
+ (struct hostent *)buf,
+ (struct hostent_data *)((char *)buf +
+ sizeof(struct hostent)));
+ h_errnop= errno; /* we don't deal with this, but set it anyway */
+ }
+ else
+ res = -1; /* failure, too smallish buffer size */
+
+ if(!res) { /* success */
+
+ h = buf; /* result expected in h */
+
+ /* This is the worst kind of the different gethostbyname_r() interfaces.
+ * Since we don't know how big buffer this particular lookup required,
+ * we can't realloc down the huge alloc without doing closer analysis of
+ * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every
+ * name lookup. Fixing this would require an extra malloc() and then
+ * calling Curl_addrinfo_copy() that subsequent realloc()s down the new
+ * memory area to the actually used amount.
+ */
+ }
+ else
+#endif /* HAVE_GETHOSTBYNAME_R_3 */
+ {
+ infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
+ h = NULL; /* set return code to NULL */
+ free(buf);
+ }
+#else /* HAVE_GETHOSTBYNAME_R */
+ /*
+ * Here is code for platforms that don't have gethostbyname_r() or for
+ * which the gethostbyname() is the preferred() function.
+ */
+ else {
+ h = gethostbyname(hostname);
+ if (!h)
+ infof(data, "gethostbyname(2) failed for %s\n", hostname);
+#endif /*HAVE_GETHOSTBYNAME_R */
+ }
+
+ if(h) {
+ ai = Curl_he2ai(h, port);
+
+ if (buf) /* used a *_r() function */
+ free(buf);
+ }
+
+ return ai;
+}
+
+#endif /* CURLRES_SYNCH */
+#endif /* CURLRES_IPV4 */
+
+/*
+ * Curl_he2ai() translates from a hostent struct to a Curl_addrinfo struct.
+ * The Curl_addrinfo is meant to work like the addrinfo struct does for IPv6
+ * stacks, but for all hosts and environments.
+ *
+ * Curl_addrinfo defined in "lib/hostip.h"
+ *
+ * struct Curl_addrinfo {
+ * int ai_flags;
+ * int ai_family;
+ * int ai_socktype;
+ * int ai_protocol;
+ * socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo *
+ * char *ai_canonname;
+ * struct sockaddr *ai_addr;
+ * struct Curl_addrinfo *ai_next;
+ * };
+ *
+ * hostent defined in <netdb.h>
+ *
+ * struct hostent {
+ * char *h_name;
+ * char **h_aliases;
+ * int h_addrtype;
+ * int h_length;
+ * char **h_addr_list;
+ * };
+ *
+ * for backward compatibility:
+ *
+ * #define h_addr h_addr_list[0]
+ */
+
+Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port)
+{
+ Curl_addrinfo *ai;
+ Curl_addrinfo *prevai = NULL;
+ Curl_addrinfo *firstai = NULL;
+ struct sockaddr_in *addr;
+ int i;
+ struct in_addr *curr;
+
+ if(!he)
+ /* no input == no output! */
+ return NULL;
+
+ for(i=0; (curr = (struct in_addr *)he->h_addr_list[i]) != NULL; i++) {
+
+ ai = calloc(1, sizeof(Curl_addrinfo) + sizeof(struct sockaddr_in));
+
+ if(!ai)
+ break;
+
+ if(!firstai)
+ /* store the pointer we want to return from this function */
+ firstai = ai;
+
+ if(prevai)
+ /* make the previous entry point to this */
+ prevai->ai_next = ai;
+
+ ai->ai_family = AF_INET; /* we only support this */
+
+ /* we return all names as STREAM, so when using this address for TFTP
+ the type must be ignored and conn->socktype be used instead! */
+ ai->ai_socktype = SOCK_STREAM;
+
+ ai->ai_addrlen = sizeof(struct sockaddr_in);
+ /* make the ai_addr point to the address immediately following this struct
+ and use that area to store the address */
+ ai->ai_addr = (struct sockaddr *) ((char*)ai + sizeof(Curl_addrinfo));
+
+ /* leave the rest of the struct filled with zero */
+
+ addr = (struct sockaddr_in *)ai->ai_addr; /* storage area for this info */
+
+ memcpy((char *)&(addr->sin_addr), curr, sizeof(struct in_addr));
+ addr->sin_family = he->h_addrtype;
+ addr->sin_port = htons((unsigned short)port);
+
+ prevai = ai;
+ }
+ return firstai;
+}
+
diff --git a/Utilities/cmcurl/hostip6.c b/Utilities/cmcurl/hostip6.c
new file mode 100644
index 000000000..c8bbdb5e9
--- /dev/null
+++ b/Utilities/cmcurl/hostip6.c
@@ -0,0 +1,306 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+
+#ifdef NEED_MALLOC_H
+#include <malloc.h>
+#endif
+#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 HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+#include "inet_pton.h"
+#include "connect.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
+
+#include "memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/***********************************************************************
+ * Only for ipv6-enabled builds
+ **********************************************************************/
+#ifdef CURLRES_IPV6
+#ifndef CURLRES_ARES
+/*
+ * This is a wrapper function for freeing name information in a protocol
+ * independent way. This takes care of using the appropriate underlaying
+ * function.
+ */
+void Curl_freeaddrinfo(Curl_addrinfo *p)
+{
+ freeaddrinfo(p);
+}
+
+#ifdef CURLRES_ASYNCH
+/*
+ * Curl_addrinfo_copy() is used by the asynch callback to copy a given
+ * address. But this is an ipv6 build and then we don't copy the address, we
+ * just return the same pointer!
+ */
+Curl_addrinfo *Curl_addrinfo_copy(const void *orig, int port)
+{
+ (void) port;
+ return (Curl_addrinfo*)orig;
+}
+#endif /* CURLRES_ASYNCH */
+#endif /* CURLRES_ARES */
+
+#ifdef CURLDEBUG
+/* These 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 structs I didn't wanna include in memdebug.c
+ */
+int curl_dogetaddrinfo(const char *hostname, const 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;
+}
+
+/*
+ * For CURLRES_ARS, this should be written using ares_gethostbyaddr()
+ * (ignoring the fact c-ares doesn't return 'serv').
+ */
+#ifdef HAVE_GETNAMEINFO
+int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa,
+ GETNAMEINFO_TYPE_ARG2 salen,
+ char *host, GETNAMEINFO_TYPE_ARG46 hostlen,
+ char *serv, GETNAMEINFO_TYPE_ARG46 servlen,
+ GETNAMEINFO_TYPE_ARG7 flags,
+ int line, const char *source)
+{
+ int res = (getnameinfo)(sa, salen,
+ host, hostlen,
+ serv, servlen,
+ flags);
+ if(0 == res) {
+ /* success */
+ if(logfile)
+ fprintf(logfile, "GETNAME %s:%d getnameinfo()\n",
+ source, line);
+ }
+ else {
+ if(logfile)
+ fprintf(logfile, "GETNAME %s:%d getnameinfo() failed = %d\n",
+ source, line, res);
+ }
+ return res;
+}
+#endif
+
+void curl_dofreeaddrinfo(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 /* CURLDEBUG */
+
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct SessionHandle *data)
+{
+ if(data->set.ip_version == CURL_IPRESOLVE_V6) {
+ /* see if we have an IPv6 stack */
+ curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (s == CURL_SOCKET_BAD)
+ /* an ipv6 address was requested and we can't get/use one */
+ return FALSE;
+ sclose(s);
+ }
+ return TRUE;
+}
+
+#if !defined(USE_THREADING_GETADDRINFO) && !defined(CURLRES_ARES)
+
+#ifdef DEBUG_ADDRINFO
+static void dump_addrinfo(struct connectdata *conn, const struct addrinfo *ai)
+{
+ printf("dump_addrinfo:\n");
+ for ( ; ai; ai = ai->ai_next) {
+ char buf[INET6_ADDRSTRLEN];
+
+ printf(" fam %2d, CNAME %s, ",
+ ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>");
+ if (Curl_printable_address(ai, buf, sizeof(buf)))
+ printf("%s\n", buf);
+ else
+ printf("failed; %s\n", Curl_strerror(conn, Curl_sockerrno()));
+ }
+}
+#else
+#define dump_addrinfo(x,y)
+#endif
+
+/*
+ * Curl_getaddrinfo() when built ipv6-enabled (non-threading and
+ * non-ares version).
+ *
+ * Returns name information about the given hostname and port number. If
+ * successful, the 'addrinfo' is returned and the forth argument will point to
+ * memory we need to free after use. That memory *MUST* be freed with
+ * Curl_freeaddrinfo(), nothing else.
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ struct addrinfo hints, *res;
+ int error;
+ char sbuf[NI_MAXSERV];
+ char *sbufptr = NULL;
+ char addrbuf[128];
+ curl_socket_t s;
+ int pf;
+ struct SessionHandle *data = conn->data;
+
+ *waitp=0; /* don't wait, we have the response now */
+
+ /* see if we have an IPv6 stack */
+ s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (s == CURL_SOCKET_BAD) {
+ /* Some non-IPv6 stacks have been found to make very slow name resolves
+ * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
+ * the stack seems to be a non-ipv6 one. */
+
+ pf = PF_INET;
+ }
+ else {
+ /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
+ * possible checks. And close the socket again.
+ */
+ sclose(s);
+
+ /*
+ * Check if a more limited name resolve has been requested.
+ */
+ switch(data->set.ip_version) {
+ case CURL_IPRESOLVE_V4:
+ pf = PF_INET;
+ break;
+ case CURL_IPRESOLVE_V6:
+ pf = PF_INET6;
+ break;
+ default:
+ pf = PF_UNSPEC;
+ break;
+ }
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = pf;
+ hints.ai_socktype = conn->socktype;
+
+ if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) ||
+ (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) {
+ /* the given address is numerical only, prevent a reverse lookup */
+ hints.ai_flags = AI_NUMERICHOST;
+ }
+#if 0 /* removed nov 8 2005 before 7.15.1 */
+ else
+ hints.ai_flags = AI_CANONNAME;
+#endif
+
+ if(port) {
+ snprintf(sbuf, sizeof(sbuf), "%d", port);
+ sbufptr=sbuf;
+ }
+ error = getaddrinfo(hostname, sbufptr, &hints, &res);
+ if (error) {
+ infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
+ return NULL;
+ }
+
+ dump_addrinfo(conn, res);
+
+ return res;
+}
+#endif /* !USE_THREADING_GETADDRINFO && !CURLRES_ARES */
+#endif /* ipv6 */
+
diff --git a/Utilities/cmcurl/hostsyn.c b/Utilities/cmcurl/hostsyn.c
new file mode 100644
index 000000000..2f816b858
--- /dev/null
+++ b/Utilities/cmcurl/hostsyn.c
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+
+#ifdef NEED_MALLOC_H
+#include <malloc.h>
+#endif
+#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 HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.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
+
+#include "memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/***********************************************************************
+ * Only for builds using synchronous name resolves
+ **********************************************************************/
+#ifdef CURLRES_SYNCH
+
+/*
+ * Curl_wait_for_resolv() for synch-builds. Curl_resolv() can never return
+ * wait==TRUE, so this function will never be called. If it still gets called,
+ * we return failure at once.
+ *
+ * We provide this function only to allow multi.c to remain unaware if we are
+ * doing asynch resolves or not.
+ */
+CURLcode Curl_wait_for_resolv(struct connectdata *conn,
+ struct Curl_dns_entry **entry)
+{
+ (void)conn;
+ *entry=NULL;
+ return CURLE_COULDNT_RESOLVE_HOST;
+}
+
+/*
+ * This function will never be called when synch-built. If it still gets
+ * called, we return failure at once.
+ *
+ * We provide this function only to allow multi.c to remain unaware if we are
+ * doing asynch resolves or not.
+ */
+CURLcode Curl_is_resolved(struct connectdata *conn,
+ struct Curl_dns_entry **dns)
+{
+ (void)conn;
+ *dns = NULL;
+
+ return CURLE_COULDNT_RESOLVE_HOST;
+}
+
+/*
+ * We just return OK, this function is never actually used for synch builds.
+ * It is present here to keep #ifdefs out from multi.c
+ */
+
+int Curl_resolv_getsock(struct connectdata *conn,
+ curl_socket_t *sock,
+ int numsocks)
+{
+ (void)conn;
+ (void)sock;
+ (void)numsocks;
+
+ return 0; /* no bits since we don't use any socks */
+}
+
+#endif /* truly sync */
diff --git a/Utilities/cmcurl/hostthre.c b/Utilities/cmcurl/hostthre.c
new file mode 100644
index 000000000..12e31ea34
--- /dev/null
+++ b/Utilities/cmcurl/hostthre.c
@@ -0,0 +1,840 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <errno.h>
+
+#ifdef NEED_MALLOC_H
+#include <malloc.h>
+#endif
+#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 HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+#include "multiif.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "inet_ntop.h"
+
+#include "memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#if defined(_MSC_VER) && defined(CURL_NO__BEGINTHREADEX)
+#pragma message ("No _beginthreadex() available in this RTL")
+#endif
+
+/***********************************************************************
+ * Only for Windows threaded name resolves builds
+ **********************************************************************/
+#ifdef CURLRES_THREADED
+
+/* This function is used to init a threaded resolve */
+static bool init_resolve_thread(struct connectdata *conn,
+ const char *hostname, int port,
+ const Curl_addrinfo *hints);
+
+#ifdef CURLRES_IPV4
+ #define THREAD_FUNC gethostbyname_thread
+ #define THREAD_NAME "gethostbyname_thread"
+#else
+ #define THREAD_FUNC getaddrinfo_thread
+ #define THREAD_NAME "getaddrinfo_thread"
+#endif
+
+#if defined(DEBUG_THREADING_GETHOSTBYNAME) || \
+ defined(DEBUG_THREADING_GETADDRINFO)
+/* If this is defined, provide tracing */
+#define TRACE(args) \
+ do { trace_it("%u: ", __LINE__); trace_it args; } while (0)
+
+static void trace_it (const char *fmt, ...)
+{
+ static int do_trace = -1;
+ va_list args;
+
+ if (do_trace == -1) {
+ const char *env = getenv("CURL_TRACE");
+ do_trace = (env && atoi(env) > 0);
+ }
+ if (!do_trace)
+ return;
+ va_start (args, fmt);
+ vfprintf (stderr, fmt, args);
+ fflush (stderr);
+ va_end (args);
+}
+#else
+#define TRACE(x)
+#endif
+
+#ifdef DEBUG_THREADING_GETADDRINFO
+static void dump_addrinfo (struct connectdata *conn, const struct addrinfo *ai)
+{
+ TRACE(("dump_addrinfo:\n"));
+ for ( ; ai; ai = ai->ai_next) {
+ char buf [INET6_ADDRSTRLEN];
+
+ trace_it(" fam %2d, CNAME %s, ",
+ ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>");
+ if (Curl_printable_address(ai, buf, sizeof(buf)))
+ trace_it("%s\n", buf);
+ else
+ trace_it("failed; %s\n", Curl_strerror(conn,WSAGetLastError()));
+ }
+}
+#endif
+
+struct thread_data {
+ HANDLE thread_hnd;
+ unsigned thread_id;
+ DWORD thread_status;
+ curl_socket_t dummy_sock; /* dummy for Curl_resolv_fdset() */
+ HANDLE mutex_waiting; /* marks that we are still waiting for a resolve */
+ HANDLE event_resolved; /* marks that the thread obtained the information */
+ HANDLE event_thread_started; /* marks that the thread has initialized and
+ started */
+ HANDLE mutex_terminate; /* serializes access to flag_terminate */
+ HANDLE event_terminate; /* flag for thread to terminate instead of calling
+ callbacks */
+#ifdef CURLRES_IPV6
+ struct addrinfo hints;
+#endif
+};
+
+/* Data for synchronization between resolver thread and its parent */
+struct thread_sync_data {
+ HANDLE mutex_waiting; /* thread_data.mutex_waiting duplicate */
+ HANDLE mutex_terminate; /* thread_data.mutex_terminate duplicate */
+ HANDLE event_terminate; /* thread_data.event_terminate duplicate */
+ char * hostname; /* hostname to resolve, Curl_async.hostname
+ duplicate */
+};
+
+/* Destroy resolver thread synchronization data */
+static
+void destroy_thread_sync_data(struct thread_sync_data * tsd)
+{
+ if (tsd->hostname) {
+ free(tsd->hostname);
+ tsd->hostname = NULL;
+ }
+ if (tsd->event_terminate) {
+ CloseHandle(tsd->event_terminate);
+ tsd->event_terminate = NULL;
+ }
+ if (tsd->mutex_terminate) {
+ CloseHandle(tsd->mutex_terminate);
+ tsd->mutex_terminate = NULL;
+ }
+ if (tsd->mutex_waiting) {
+ CloseHandle(tsd->mutex_waiting);
+ tsd->mutex_waiting = NULL;
+ }
+}
+
+/* Initialize resolver thread synchronization data */
+static
+BOOL init_thread_sync_data(struct thread_data * td,
+ char * hostname,
+ struct thread_sync_data * tsd)
+{
+ HANDLE curr_proc = GetCurrentProcess();
+
+ memset(tsd, 0, sizeof(*tsd));
+ if (!DuplicateHandle(curr_proc, td->mutex_waiting,
+ curr_proc, &tsd->mutex_waiting, 0, FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ /* failed to duplicate the mutex, no point in continuing */
+ destroy_thread_sync_data(tsd);
+ return FALSE;
+ }
+ if (!DuplicateHandle(curr_proc, td->mutex_terminate,
+ curr_proc, &tsd->mutex_terminate, 0, FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ /* failed to duplicate the mutex, no point in continuing */
+ destroy_thread_sync_data(tsd);
+ return FALSE;
+ }
+ if (!DuplicateHandle(curr_proc, td->event_terminate,
+ curr_proc, &tsd->event_terminate, 0, FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ /* failed to duplicate the event, no point in continuing */
+ destroy_thread_sync_data(tsd);
+ return FALSE;
+ }
+ /* Copying hostname string because original can be destroyed by parent
+ * thread during gethostbyname execution.
+ */
+ tsd->hostname = strdup(hostname);
+ if (!tsd->hostname) {
+ /* Memory allocation failed */
+ destroy_thread_sync_data(tsd);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* acquire resolver thread synchronization */
+static
+BOOL acquire_thread_sync(struct thread_sync_data * tsd)
+{
+ /* is the thread initiator still waiting for us ? */
+ if (WaitForSingleObject(tsd->mutex_waiting, 0) == WAIT_TIMEOUT) {
+ /* yes, it is */
+
+ /* Waiting access to event_terminate */
+ if (WaitForSingleObject(tsd->mutex_terminate, INFINITE) != WAIT_OBJECT_0) {
+ /* Something went wrong - now just ignoring */
+ }
+ else {
+ if (WaitForSingleObject(tsd->event_terminate, 0) != WAIT_TIMEOUT) {
+ /* Parent thread signaled us to terminate.
+ * This means that all data in conn->async is now destroyed
+ * and we cannot use it.
+ */
+ }
+ else {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/* release resolver thread synchronization */
+static
+void release_thread_sync(struct thread_sync_data * tsd)
+{
+ ReleaseMutex(tsd->mutex_terminate);
+}
+
+#if defined(CURLRES_IPV4)
+/*
+ * gethostbyname_thread() resolves a name, calls the Curl_addrinfo4_callback
+ * and then exits.
+ *
+ * For builds without ARES/ENABLE_IPV6, create a resolver thread and wait on
+ * it.
+ */
+static unsigned __stdcall gethostbyname_thread (void *arg)
+{
+ struct connectdata *conn = (struct connectdata*) arg;
+ struct thread_data *td = (struct thread_data*) conn->async.os_specific;
+ struct hostent *he;
+ int rc = 0;
+
+ /* Duplicate the passed mutex and event handles.
+ * This allows us to use it even after the container gets destroyed
+ * due to a resolver timeout.
+ */
+ struct thread_sync_data tsd = { 0,0,0,NULL };
+ if (!init_thread_sync_data(td, conn->async.hostname, &tsd)) {
+ /* thread synchronization data initialization failed */
+ return (unsigned)-1;
+ }
+
+ WSASetLastError (conn->async.status = NO_DATA); /* pending status */
+
+ /* Signaling that we have initialized all copies of data and handles we
+ need */
+ SetEvent(td->event_thread_started);
+
+ he = gethostbyname (tsd.hostname);
+
+ /* is parent thread waiting for us and are we able to access conn members? */
+ if (acquire_thread_sync(&tsd)) {
+ /* Mark that we have obtained the information, and that we are calling
+ * back with it. */
+ SetEvent(td->event_resolved);
+ if (he) {
+ rc = Curl_addrinfo4_callback(conn, CURL_ASYNC_SUCCESS, he);
+ }
+ else {
+ rc = Curl_addrinfo4_callback(conn, (int)WSAGetLastError(), NULL);
+ }
+ TRACE(("Winsock-error %d, addr %s\n", conn->async.status,
+ he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown"));
+ release_thread_sync(&tsd);
+ }
+
+ /* clean up */
+ destroy_thread_sync_data(&tsd);
+
+ return (rc);
+ /* An implicit _endthreadex() here */
+}
+
+#elif defined(CURLRES_IPV6)
+
+/*
+ * getaddrinfo_thread() resolves a name, calls Curl_addrinfo6_callback and then
+ * exits.
+ *
+ * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
+ * and wait on it.
+ */
+static unsigned __stdcall getaddrinfo_thread (void *arg)
+{
+ struct connectdata *conn = (struct connectdata*) arg;
+ struct thread_data *td = (struct thread_data*) conn->async.os_specific;
+ struct addrinfo *res;
+ char service [NI_MAXSERV];
+ int rc;
+ struct addrinfo hints = td->hints;
+
+ /* Duplicate the passed mutex handle.
+ * This allows us to use it even after the container gets destroyed
+ * due to a resolver timeout.
+ */
+ struct thread_sync_data tsd = { 0,0,0,NULL };
+ if (!init_thread_sync_data(td, conn->async.hostname, &tsd)) {
+ /* thread synchronization data initialization failed */
+ return -1;
+ }
+
+ itoa(conn->async.port, service, 10);
+
+ WSASetLastError(conn->async.status = NO_DATA); /* pending status */
+
+ /* Signaling that we have initialized all copies of data and handles we
+ need */
+ SetEvent(td->event_thread_started);
+
+ rc = getaddrinfo(tsd.hostname, service, &hints, &res);
+
+ /* is parent thread waiting for us and are we able to access conn members? */
+ if (acquire_thread_sync(&tsd)) {
+ /* Mark that we have obtained the information, and that we are calling
+ back with it. */
+ SetEvent(td->event_resolved);
+
+ if (rc == 0) {
+#ifdef DEBUG_THREADING_GETADDRINFO
+ dump_addrinfo (conn, res);
+#endif
+ rc = Curl_addrinfo6_callback(conn, CURL_ASYNC_SUCCESS, res);
+ }
+ else {
+ rc = Curl_addrinfo6_callback(conn, (int)WSAGetLastError(), NULL);
+ TRACE(("Winsock-error %d, no address\n", conn->async.status));
+ }
+ release_thread_sync(&tsd);
+ }
+
+ /* clean up */
+ destroy_thread_sync_data(&tsd);
+
+ return (rc);
+ /* An implicit _endthreadex() here */
+}
+#endif
+
+/*
+ * Curl_destroy_thread_data() cleans up async resolver data and thread handle.
+ * Complementary of ares_destroy.
+ */
+void Curl_destroy_thread_data (struct Curl_async *async)
+{
+ if (async->hostname)
+ free(async->hostname);
+
+ if (async->os_specific) {
+ struct thread_data *td = (struct thread_data*) async->os_specific;
+ curl_socket_t sock = td->dummy_sock;
+
+ if (td->mutex_terminate && td->event_terminate) {
+ /* Signaling resolver thread to terminate */
+ if (WaitForSingleObject(td->mutex_terminate, INFINITE) == WAIT_OBJECT_0) {
+ SetEvent(td->event_terminate);
+ ReleaseMutex(td->mutex_terminate);
+ }
+ else {
+ /* Something went wrong - just ignoring it */
+ }
+ }
+
+ if (td->mutex_terminate)
+ CloseHandle(td->mutex_terminate);
+ if (td->event_terminate)
+ CloseHandle(td->event_terminate);
+ if (td->event_thread_started)
+ CloseHandle(td->event_thread_started);
+
+ if (sock != CURL_SOCKET_BAD)
+ sclose(sock);
+
+ /* destroy the synchronization objects */
+ if (td->mutex_waiting)
+ CloseHandle(td->mutex_waiting);
+ td->mutex_waiting = NULL;
+ if (td->event_resolved)
+ CloseHandle(td->event_resolved);
+
+ if (td->thread_hnd)
+ CloseHandle(td->thread_hnd);
+
+ free(async->os_specific);
+ }
+ async->hostname = NULL;
+ async->os_specific = NULL;
+}
+
+/*
+ * init_resolve_thread() starts a new thread that performs the actual
+ * resolve. This function returns before the resolve is done.
+ *
+ * Returns FALSE in case of failure, otherwise TRUE.
+ */
+static bool init_resolve_thread (struct connectdata *conn,
+ const char *hostname, int port,
+ const Curl_addrinfo *hints)
+{
+ struct thread_data *td = calloc(sizeof(*td), 1);
+ HANDLE thread_and_event[2] = {0};
+
+ if (!td) {
+ SetLastError(ENOMEM);
+ return FALSE;
+ }
+
+ Curl_safefree(conn->async.hostname);
+ conn->async.hostname = strdup(hostname);
+ if (!conn->async.hostname) {
+ free(td);
+ SetLastError(ENOMEM);
+ return FALSE;
+ }
+
+ conn->async.port = port;
+ conn->async.done = FALSE;
+ conn->async.status = 0;
+ conn->async.dns = NULL;
+ conn->async.os_specific = (void*) td;
+ td->dummy_sock = CURL_SOCKET_BAD;
+
+ /* Create the mutex used to inform the resolver thread that we're
+ * still waiting, and take initial ownership.
+ */
+ td->mutex_waiting = CreateMutex(NULL, TRUE, NULL);
+ if (td->mutex_waiting == NULL) {
+ Curl_destroy_thread_data(&conn->async);
+ SetLastError(EAGAIN);
+ return FALSE;
+ }
+
+ /* Create the event that the thread uses to inform us that it's
+ * done resolving. Do not signal it.
+ */
+ td->event_resolved = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (td->event_resolved == NULL) {
+ Curl_destroy_thread_data(&conn->async);
+ SetLastError(EAGAIN);
+ return FALSE;
+ }
+ /* Create the mutex used to serialize access to event_terminated
+ * between us and resolver thread.
+ */
+ td->mutex_terminate = CreateMutex(NULL, FALSE, NULL);
+ if (td->mutex_terminate == NULL) {
+ Curl_destroy_thread_data(&conn->async);
+ SetLastError(EAGAIN);
+ return FALSE;
+ }
+ /* Create the event used to signal thread that it should terminate.
+ */
+ td->event_terminate = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (td->event_terminate == NULL) {
+ Curl_destroy_thread_data(&conn->async);
+ SetLastError(EAGAIN);
+ return FALSE;
+ }
+ /* Create the event used by thread to inform it has initialized its own data.
+ */
+ td->event_thread_started = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (td->event_thread_started == NULL) {
+ Curl_destroy_thread_data(&conn->async);
+ SetLastError(EAGAIN);
+ return FALSE;
+ }
+
+#ifdef _WIN32_WCE
+ td->thread_hnd = (HANDLE) CreateThread(NULL, 0,
+ (LPTHREAD_START_ROUTINE) THREAD_FUNC,
+ conn, 0, &td->thread_id);
+#else
+ td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC,
+ conn, 0, &td->thread_id);
+#endif
+
+#ifdef CURLRES_IPV6
+ curlassert(hints);
+ td->hints = *hints;
+#else
+ (void) hints;
+#endif
+
+ if (!td->thread_hnd) {
+#ifdef _WIN32_WCE
+ TRACE(("CreateThread() failed; %s\n", Curl_strerror(conn,GetLastError())));
+#else
+ SetLastError(errno);
+ TRACE(("_beginthreadex() failed; %s\n", Curl_strerror(conn,errno)));
+#endif
+ Curl_destroy_thread_data(&conn->async);
+ return FALSE;
+ }
+ /* Waiting until the thread will initialize its data or it will exit due errors.
+ */
+ thread_and_event[0] = td->thread_hnd;
+ thread_and_event[1] = td->event_thread_started;
+ if (WaitForMultipleObjects(sizeof(thread_and_event) /
+ sizeof(thread_and_event[0]),
+ (const HANDLE*)thread_and_event, FALSE,
+ INFINITE) == WAIT_FAILED) {
+ /* The resolver thread has been created,
+ * most probably it works now - ignoring this "minor" error
+ */
+ }
+ /* This socket is only to keep Curl_resolv_fdset() and select() happy;
+ * should never become signalled for read/write since it's unbound but
+ * Windows needs atleast 1 socket in select().
+ */
+ td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ return TRUE;
+}
+
+
+/*
+ * Curl_wait_for_resolv() waits for a resolve to finish. This function should
+ * be avoided since using this risk getting the multi interface to "hang".
+ *
+ * If 'entry' is non-NULL, make it point to the resolved dns entry
+ *
+ * This is the version for resolves-in-a-thread.
+ */
+CURLcode Curl_wait_for_resolv(struct connectdata *conn,
+ struct Curl_dns_entry **entry)
+{
+ struct thread_data *td = (struct thread_data*) conn->async.os_specific;
+ struct SessionHandle *data = conn->data;
+ long timeout;
+ DWORD status, ticks;
+ CURLcode rc;
+
+ curlassert (conn && td);
+
+ /* now, see if there's a connect timeout or a regular timeout to
+ use instead of the default one */
+ timeout =
+ conn->data->set.connecttimeout ? conn->data->set.connecttimeout :
+ conn->data->set.timeout ? conn->data->set.timeout :
+ CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */
+ ticks = GetTickCount();
+
+ /* wait for the thread to resolve the name */
+ status = WaitForSingleObject(td->event_resolved, 1000UL*timeout);
+
+ /* mark that we are now done waiting */
+ ReleaseMutex(td->mutex_waiting);
+
+ /* close our handle to the mutex, no point in hanging on to it */
+ CloseHandle(td->mutex_waiting);
+ td->mutex_waiting = NULL;
+
+ /* close the event handle, it's useless now */
+ CloseHandle(td->event_resolved);
+ td->event_resolved = NULL;
+
+ /* has the resolver thread succeeded in resolving our query ? */
+ if (status == WAIT_OBJECT_0) {
+ /* wait for the thread to exit, it's in the callback sequence */
+ if (WaitForSingleObject(td->thread_hnd, 5000) == WAIT_TIMEOUT) {
+ TerminateThread(td->thread_hnd, 0);
+ conn->async.done = TRUE;
+ td->thread_status = (DWORD)-1;
+ TRACE(("%s() thread stuck?!, ", THREAD_NAME));
+ }
+ else {
+ /* Thread finished before timeout; propagate Winsock error to this
+ * thread. 'conn->async.done = TRUE' is set in
+ * Curl_addrinfo4/6_callback().
+ */
+ WSASetLastError(conn->async.status);
+ GetExitCodeThread(td->thread_hnd, &td->thread_status);
+ TRACE(("%s() status %lu, thread retval %lu, ",
+ THREAD_NAME, status, td->thread_status));
+ }
+ }
+ else {
+ conn->async.done = TRUE;
+ td->thread_status = (DWORD)-1;
+ TRACE(("%s() timeout, ", THREAD_NAME));
+ }
+
+ TRACE(("elapsed %lu ms\n", GetTickCount()-ticks));
+
+ if(entry)
+ *entry = conn->async.dns;
+
+ rc = CURLE_OK;
+
+ if (!conn->async.dns) {
+ /* a name was not resolved */
+ if (td->thread_status == CURLE_OUT_OF_MEMORY) {
+ rc = CURLE_OUT_OF_MEMORY;
+ failf(data, "Could not resolve host: %s", curl_easy_strerror(rc));
+ }
+ else if(conn->async.done) {
+ if(conn->bits.httpproxy) {
+ failf(data, "Could not resolve proxy: %s; %s",
+ conn->proxy.dispname, Curl_strerror(conn, conn->async.status));
+ rc = CURLE_COULDNT_RESOLVE_PROXY;
+ }
+ else {
+ failf(data, "Could not resolve host: %s; %s",
+ conn->host.name, Curl_strerror(conn, conn->async.status));
+ rc = CURLE_COULDNT_RESOLVE_HOST;
+ }
+ }
+ else if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) {
+ failf(data, "Resolving host timed out: %s", conn->host.name);
+ rc = CURLE_OPERATION_TIMEDOUT;
+ }
+ else
+ rc = CURLE_OPERATION_TIMEDOUT;
+ }
+
+ Curl_destroy_thread_data(&conn->async);
+
+ if(!conn->async.dns)
+ conn->bits.close = TRUE;
+
+ return (rc);
+}
+
+/*
+ * Curl_is_resolved() is called repeatedly to check if a previous name resolve
+ * request has completed. It should also make sure to time-out if the
+ * operation seems to take too long.
+ */
+CURLcode Curl_is_resolved(struct connectdata *conn,
+ struct Curl_dns_entry **entry)
+{
+ *entry = NULL;
+
+ if (conn->async.done) {
+ /* we're done */
+ Curl_destroy_thread_data(&conn->async);
+ if (!conn->async.dns) {
+ TRACE(("Curl_is_resolved(): CURLE_COULDNT_RESOLVE_HOST\n"));
+ return CURLE_COULDNT_RESOLVE_HOST;
+ }
+ *entry = conn->async.dns;
+ TRACE(("resolved okay, dns %p\n", *entry));
+ }
+ return CURLE_OK;
+}
+
+int Curl_resolv_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks)
+{
+ const struct thread_data *td =
+ (const struct thread_data *) conn->async.os_specific;
+
+ if (td && td->dummy_sock != CURL_SOCKET_BAD) {
+ if(numsocks) {
+ /* return one socket waiting for writable, even though this is just
+ a dummy */
+ socks[0] = td->dummy_sock;
+ return GETSOCK_WRITESOCK(0);
+ }
+ }
+ return 0;
+}
+
+#ifdef CURLRES_IPV4
+/*
+ * Curl_getaddrinfo() - for Windows threading without ENABLE_IPV6.
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ struct hostent *h = NULL;
+ struct SessionHandle *data = conn->data;
+ in_addr_t in;
+
+ *waitp = 0; /* don't wait, we act synchronously */
+
+ in = inet_addr(hostname);
+ if (in != CURL_INADDR_NONE)
+ /* This is a dotted IP address 123.123.123.123-style */
+ return Curl_ip2addr(in, hostname, port);
+
+ /* fire up a new resolver thread! */
+ if (init_resolve_thread(conn, hostname, port, NULL)) {
+ *waitp = TRUE; /* please wait for the response */
+ return NULL;
+ }
+
+ /* fall-back to blocking version */
+ infof(data, "init_resolve_thread() failed for %s; %s\n",
+ hostname, Curl_strerror(conn,GetLastError()));
+
+ h = gethostbyname(hostname);
+ if (!h) {
+ infof(data, "gethostbyname(2) failed for %s:%d; %s\n",
+ hostname, port, Curl_strerror(conn,WSAGetLastError()));
+ return NULL;
+ }
+ return Curl_he2ai(h, port);
+}
+#endif /* CURLRES_IPV4 */
+
+#ifdef CURLRES_IPV6
+/*
+ * Curl_getaddrinfo() - for Windows threading IPv6 enabled
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ struct addrinfo hints, *res;
+ int error;
+ char sbuf[NI_MAXSERV];
+ curl_socket_t s;
+ int pf;
+ struct SessionHandle *data = conn->data;
+
+ *waitp = FALSE; /* default to synch response */
+
+ /* see if we have an IPv6 stack */
+ s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (s == CURL_SOCKET_BAD) {
+ /* Some non-IPv6 stacks have been found to make very slow name resolves
+ * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
+ * the stack seems to be a non-ipv6 one. */
+
+ pf = PF_INET;
+ }
+ else {
+ /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
+ * possible checks. And close the socket again.
+ */
+ sclose(s);
+
+ /*
+ * Check if a more limited name resolve has been requested.
+ */
+ switch(data->set.ip_version) {
+ case CURL_IPRESOLVE_V4:
+ pf = PF_INET;
+ break;
+ case CURL_IPRESOLVE_V6:
+ pf = PF_INET6;
+ break;
+ default:
+ pf = PF_UNSPEC;
+ break;
+ }
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = pf;
+ hints.ai_socktype = conn->socktype;
+#if 0 /* removed nov 8 2005 before 7.15.1 */
+ hints.ai_flags = AI_CANONNAME;
+#endif
+ itoa(port, sbuf, 10);
+
+ /* fire up a new resolver thread! */
+ if (init_resolve_thread(conn, hostname, port, &hints)) {
+ *waitp = TRUE; /* please wait for the response */
+ return NULL;
+ }
+
+ /* fall-back to blocking version */
+ infof(data, "init_resolve_thread() failed for %s; %s\n",
+ hostname, Curl_strerror(conn,GetLastError()));
+
+ error = getaddrinfo(hostname, sbuf, &hints, &res);
+ if (error) {
+ infof(data, "getaddrinfo() failed for %s:%d; %s\n",
+ hostname, port, Curl_strerror(conn,WSAGetLastError()));
+ return NULL;
+ }
+ return res;
+}
+#endif /* CURLRES_IPV6 */
+#endif /* CURLRES_THREADED */
diff --git a/Utilities/cmcurl/http.c b/Utilities/cmcurl/http.c
new file mode 100644
index 000000000..c07053bdd
--- /dev/null
+++ b/Utilities/cmcurl/http.c
@@ -0,0 +1,2422 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef CURL_DISABLE_HTTP
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef WIN32
+#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
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#endif
+#endif
+
+#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
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "easyif.h" /* for Curl_convert_... prototypes */
+#include "formdata.h"
+#include "progress.h"
+#include "base64.h"
+#include "cookie.h"
+#include "strequal.h"
+#include "sslgen.h"
+#include "http_digest.h"
+#include "http_ntlm.h"
+#include "http_negotiate.h"
+#include "url.h"
+#include "share.h"
+#include "hostip.h"
+#include "http.h"
+#include "memory.h"
+#include "select.h"
+#include "parsedate.h" /* for the week day and month names */
+#include "strtoofft.h"
+#include "multiif.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * checkheaders() checks the linked list of custom HTTP headers for a
+ * particular header (prefix).
+ *
+ * Returns a pointer to the first matching header or NULL if none matched.
+ */
+static char *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 head->data;
+ }
+ return NULL;
+}
+
+/*
+ * Curl_output_basic() sets up an Authorization: header (or the proxy version)
+ * for HTTP Basic authentication.
+ *
+ * Returns CURLcode.
+ */
+static CURLcode Curl_output_basic(struct connectdata *conn, bool proxy)
+{
+ char *authorization;
+ struct SessionHandle *data=conn->data;
+ char **userp;
+ char *user;
+ char *pwd;
+
+ if(proxy) {
+ userp = &conn->allocptr.proxyuserpwd;
+ user = conn->proxyuser;
+ pwd = conn->proxypasswd;
+ }
+ else {
+ userp = &conn->allocptr.userpwd;
+ user = conn->user;
+ pwd = conn->passwd;
+ }
+
+ snprintf(data->state.buffer, sizeof(data->state.buffer), "%s:%s", user, pwd);
+ if(Curl_base64_encode(data, data->state.buffer,
+ strlen(data->state.buffer),
+ &authorization) > 0) {
+ if(*userp)
+ free(*userp);
+ *userp = aprintf( "%sAuthorization: Basic %s\r\n",
+ proxy?"Proxy-":"",
+ authorization);
+ free(authorization);
+ }
+ else
+ return CURLE_OUT_OF_MEMORY;
+ return CURLE_OK;
+}
+
+/* pickoneauth() selects the most favourable authentication method from the
+ * ones available and the ones we want.
+ *
+ * return TRUE if one was picked
+ */
+static bool pickoneauth(struct auth *pick)
+{
+ bool picked;
+ /* only deal with authentication we want */
+ long avail = pick->avail & pick->want;
+ picked = TRUE;
+
+ /* The order of these checks is highly relevant, as this will be the order
+ of preference in case of the existance of multiple accepted types. */
+ if(avail & CURLAUTH_GSSNEGOTIATE)
+ pick->picked = CURLAUTH_GSSNEGOTIATE;
+ else if(avail & CURLAUTH_DIGEST)
+ pick->picked = CURLAUTH_DIGEST;
+ else if(avail & CURLAUTH_NTLM)
+ pick->picked = CURLAUTH_NTLM;
+ else if(avail & CURLAUTH_BASIC)
+ pick->picked = CURLAUTH_BASIC;
+ else {
+ pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */
+ picked = FALSE;
+ }
+ pick->avail = CURLAUTH_NONE; /* clear it here */
+
+ return picked;
+}
+
+/*
+ * perhapsrewind()
+ *
+ * If we are doing POST or PUT {
+ * If we have more data to send {
+ * If we are doing NTLM {
+ * Keep sending since we must not disconnect
+ * }
+ * else {
+ * If there is more than just a little data left to send, close
+ * the current connection by force.
+ * }
+ * }
+ * If we have sent any data {
+ * If we don't have track of all the data {
+ * call app to tell it to rewind
+ * }
+ * else {
+ * rewind internally so that the operation can restart fine
+ * }
+ * }
+ * }
+ */
+static CURLcode perhapsrewind(struct connectdata *conn)
+{
+ struct SessionHandle *data = conn->data;
+ struct HTTP *http = data->reqdata.proto.http;
+ struct Curl_transfer_keeper *k = &data->reqdata.keep;
+ curl_off_t bytessent;
+ curl_off_t expectsend = -1; /* default is unknown */
+
+ if(!http)
+ /* If this is still NULL, we have not reach very far and we can
+ safely skip this rewinding stuff */
+ return CURLE_OK;
+
+ bytessent = http->writebytecount;
+
+ if(conn->bits.authneg)
+ /* This is a state where we are known to be negotiating and we don't send
+ any data then. */
+ expectsend = 0;
+ else {
+ /* figure out how much data we are expected to send */
+ switch(data->set.httpreq) {
+ case HTTPREQ_POST:
+ if(data->set.postfieldsize != -1)
+ expectsend = data->set.postfieldsize;
+ break;
+ case HTTPREQ_PUT:
+ if(data->set.infilesize != -1)
+ expectsend = data->set.infilesize;
+ break;
+ case HTTPREQ_POST_FORM:
+ expectsend = http->postsize;
+ break;
+ default:
+ break;
+ }
+ }
+
+ conn->bits.rewindaftersend = FALSE; /* default */
+
+ if((expectsend == -1) || (expectsend > bytessent)) {
+ /* There is still data left to send */
+ if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
+ (data->state.authhost.picked == CURLAUTH_NTLM)) {
+ if(((expectsend - bytessent) < 2000) ||
+ (conn->ntlm.state != NTLMSTATE_NONE)) {
+ /* The NTLM-negotiation has started *OR* there is just a little (<2K)
+ data left to send, keep on sending. */
+
+ /* rewind data when completely done sending! */
+ if(!conn->bits.authneg)
+ conn->bits.rewindaftersend = TRUE;
+
+ return CURLE_OK;
+ }
+ if(conn->bits.close)
+ /* this is already marked to get closed */
+ return CURLE_OK;
+
+ infof(data, "NTLM send, close instead of sending %" FORMAT_OFF_T
+ " bytes\n", (curl_off_t)(expectsend - bytessent));
+ }
+
+ /* This is not NTLM or NTLM with many bytes left to send: close
+ */
+ conn->bits.close = TRUE;
+ k->size = 0; /* don't download any more than 0 bytes */
+ }
+
+ if(bytessent)
+ return Curl_readrewind(conn);
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_http_auth_act() gets called when a all HTTP headers have been received
+ * and it checks what authentication methods that are available and decides
+ * which one (if any) to use. It will set 'newurl' if an auth metod was
+ * picked.
+ */
+
+CURLcode Curl_http_auth_act(struct connectdata *conn)
+{
+ struct SessionHandle *data = conn->data;
+ bool pickhost = FALSE;
+ bool pickproxy = FALSE;
+ CURLcode code = CURLE_OK;
+
+ if(100 == data->reqdata.keep.httpcode)
+ /* this is a transient response code, ignore */
+ return CURLE_OK;
+
+ if(data->state.authproblem)
+ return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK;
+
+ if(conn->bits.user_passwd &&
+ ((data->reqdata.keep.httpcode == 401) ||
+ (conn->bits.authneg && data->reqdata.keep.httpcode < 300))) {
+ pickhost = pickoneauth(&data->state.authhost);
+ if(!pickhost)
+ data->state.authproblem = TRUE;
+ }
+ if(conn->bits.proxy_user_passwd &&
+ ((data->reqdata.keep.httpcode == 407) ||
+ (conn->bits.authneg && data->reqdata.keep.httpcode < 300))) {
+ pickproxy = pickoneauth(&data->state.authproxy);
+ if(!pickproxy)
+ data->state.authproblem = TRUE;
+ }
+
+ if(pickhost || pickproxy) {
+ data->reqdata.newurl = strdup(data->change.url); /* clone URL */
+
+ if((data->set.httpreq != HTTPREQ_GET) &&
+ (data->set.httpreq != HTTPREQ_HEAD) &&
+ !conn->bits.rewindaftersend) {
+ code = perhapsrewind(conn);
+ if(code)
+ return code;
+ }
+ }
+
+ else if((data->reqdata.keep.httpcode < 300) &&
+ (!data->state.authhost.done) &&
+ conn->bits.authneg) {
+ /* no (known) authentication available,
+ authentication is not "done" yet and
+ no authentication seems to be required and
+ we didn't try HEAD or GET */
+ if((data->set.httpreq != HTTPREQ_GET) &&
+ (data->set.httpreq != HTTPREQ_HEAD)) {
+ data->reqdata.newurl = strdup(data->change.url); /* clone URL */
+ data->state.authhost.done = TRUE;
+ }
+ }
+ if (Curl_http_should_fail(conn)) {
+ failf (data, "The requested URL returned error: %d",
+ data->reqdata.keep.httpcode);
+ code = CURLE_HTTP_RETURNED_ERROR;
+ }
+
+ return code;
+}
+
+/**
+ * Curl_http_output_auth() setups the authentication headers for the
+ * host/proxy and the correct authentication
+ * method. conn->data->state.authdone is set to TRUE when authentication is
+ * done.
+ *
+ * @param conn all information about the current connection
+ * @param request pointer to the request keyword
+ * @param path pointer to the requested path
+ * @param proxytunnel boolean if this is the request setting up a "proxy
+ * tunnel"
+ *
+ * @returns CURLcode
+ */
+static CURLcode
+Curl_http_output_auth(struct connectdata *conn,
+ char *request,
+ char *path,
+ bool proxytunnel) /* TRUE if this is the request setting
+ up the proxy tunnel */
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ char *auth=NULL;
+ struct auth *authhost;
+ struct auth *authproxy;
+
+ curlassert(data);
+
+ authhost = &data->state.authhost;
+ authproxy = &data->state.authproxy;
+
+ if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
+ conn->bits.user_passwd)
+ /* continue please */ ;
+ else {
+ authhost->done = TRUE;
+ authproxy->done = TRUE;
+ return CURLE_OK; /* no authentication with no user or password */
+ }
+
+ if(authhost->want && !authhost->picked)
+ /* The app has selected one or more methods, but none has been picked
+ so far by a server round-trip. Then we set the picked one to the
+ want one, and if this is one single bit it'll be used instantly. */
+ authhost->picked = authhost->want;
+
+ if(authproxy->want && !authproxy->picked)
+ /* The app has selected one or more methods, but none has been picked so
+ far by a proxy round-trip. Then we set the picked one to the want one,
+ and if this is one single bit it'll be used instantly. */
+ authproxy->picked = authproxy->want;
+
+ /* Send proxy authentication header if needed */
+ if (conn->bits.httpproxy &&
+ (conn->bits.tunnel_proxy == proxytunnel)) {
+#ifdef USE_NTLM
+ if(authproxy->picked == CURLAUTH_NTLM) {
+ auth=(char *)"NTLM";
+ result = Curl_output_ntlm(conn, TRUE);
+ if(result)
+ return result;
+ }
+ else
+#endif
+ if(authproxy->picked == CURLAUTH_BASIC) {
+ /* Basic */
+ if(conn->bits.proxy_user_passwd &&
+ !checkheaders(data, "Proxy-authorization:")) {
+ auth=(char *)"Basic";
+ result = Curl_output_basic(conn, TRUE);
+ if(result)
+ return result;
+ }
+ /* NOTE: Curl_output_basic() should set 'done' TRUE, as the other auth
+ functions work that way */
+ authproxy->done = TRUE;
+ }
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ else if(authproxy->picked == CURLAUTH_DIGEST) {
+ auth=(char *)"Digest";
+ result = Curl_output_digest(conn,
+ TRUE, /* proxy */
+ (unsigned char *)request,
+ (unsigned char *)path);
+ if(result)
+ return result;
+ }
+#endif
+ if(auth) {
+ infof(data, "Proxy auth using %s with user '%s'\n",
+ auth, conn->proxyuser?conn->proxyuser:"");
+ authproxy->multi = (bool)(!authproxy->done);
+ }
+ else
+ authproxy->multi = FALSE;
+ }
+ else
+ /* we have no proxy so let's pretend we're done authenticating
+ with it */
+ authproxy->done = TRUE;
+
+ /* To prevent the user+password to get sent to other than the original
+ host due to a location-follow, we do some weirdo checks here */
+ if(!data->state.this_is_a_follow ||
+ conn->bits.netrc ||
+ !data->state.first_host ||
+ curl_strequal(data->state.first_host, conn->host.name) ||
+ data->set.http_disable_hostname_check_before_authentication) {
+
+ /* Send web authentication header if needed */
+ {
+ auth = NULL;
+#ifdef HAVE_GSSAPI
+ if((authhost->picked == CURLAUTH_GSSNEGOTIATE) &&
+ data->state.negotiate.context &&
+ !GSS_ERROR(data->state.negotiate.status)) {
+ auth=(char *)"GSS-Negotiate";
+ result = Curl_output_negotiate(conn);
+ if (result)
+ return result;
+ authhost->done = TRUE;
+ }
+ else
+#endif
+#ifdef USE_NTLM
+ if(authhost->picked == CURLAUTH_NTLM) {
+ auth=(char *)"NTLM";
+ result = Curl_output_ntlm(conn, FALSE);
+ if(result)
+ return result;
+ }
+ else
+#endif
+ {
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ if(authhost->picked == CURLAUTH_DIGEST) {
+ auth=(char *)"Digest";
+ result = Curl_output_digest(conn,
+ FALSE, /* not a proxy */
+ (unsigned char *)request,
+ (unsigned char *)path);
+ if(result)
+ return result;
+ } else
+#endif
+ if(authhost->picked == CURLAUTH_BASIC) {
+ if(conn->bits.user_passwd &&
+ !checkheaders(data, "Authorization:")) {
+ auth=(char *)"Basic";
+ result = Curl_output_basic(conn, FALSE);
+ if(result)
+ return result;
+ }
+ /* basic is always ready */
+ authhost->done = TRUE;
+ }
+ }
+ if(auth) {
+ infof(data, "Server auth using %s with user '%s'\n",
+ auth, conn->user);
+
+ authhost->multi = (bool)(!authhost->done);
+ }
+ else
+ authhost->multi = FALSE;
+ }
+ }
+ else
+ authhost->done = TRUE;
+
+ return result;
+}
+
+
+/*
+ * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
+ * headers. They are dealt with both in the transfer.c main loop and in the
+ * proxy CONNECT loop.
+ */
+
+CURLcode Curl_http_input_auth(struct connectdata *conn,
+ int httpcode,
+ char *header) /* the first non-space */
+{
+ /*
+ * This resource requires authentication
+ */
+ struct SessionHandle *data = conn->data;
+
+ long *availp;
+ char *start;
+ struct auth *authp;
+
+ if (httpcode == 407) {
+ start = header+strlen("Proxy-authenticate:");
+ availp = &data->info.proxyauthavail;
+ authp = &data->state.authproxy;
+ }
+ else {
+ start = header+strlen("WWW-Authenticate:");
+ availp = &data->info.httpauthavail;
+ authp = &data->state.authhost;
+ }
+
+ /* pass all white spaces */
+ while(*start && ISSPACE(*start))
+ start++;
+
+ /*
+ * Here we check if we want the specific single authentication (using ==) and
+ * if we do, we initiate usage of it.
+ *
+ * If the provided authentication is wanted as one out of several accepted
+ * types (using &), we OR this authentication type to the authavail
+ * variable.
+ */
+
+#ifdef HAVE_GSSAPI
+ if (checkprefix("GSS-Negotiate", start) ||
+ checkprefix("Negotiate", start)) {
+ *availp |= CURLAUTH_GSSNEGOTIATE;
+ authp->avail |= CURLAUTH_GSSNEGOTIATE;
+ if(authp->picked == CURLAUTH_GSSNEGOTIATE) {
+ /* if exactly this is wanted, go */
+ int neg = Curl_input_negotiate(conn, start);
+ if (neg == 0) {
+ data->reqdata.newurl = strdup(data->change.url);
+ data->state.authproblem = (data->reqdata.newurl == NULL);
+ }
+ else {
+ infof(data, "Authentication problem. Ignoring this.\n");
+ data->state.authproblem = TRUE;
+ }
+ }
+ }
+ else
+#endif
+#ifdef USE_NTLM
+ /* NTLM support requires the SSL crypto libs */
+ if(checkprefix("NTLM", start)) {
+ *availp |= CURLAUTH_NTLM;
+ authp->avail |= CURLAUTH_NTLM;
+ if(authp->picked == CURLAUTH_NTLM) {
+ /* NTLM authentication is picked and activated */
+ CURLntlm ntlm =
+ Curl_input_ntlm(conn, (bool)(httpcode == 407), start);
+
+ if(CURLNTLM_BAD != ntlm)
+ data->state.authproblem = FALSE;
+ else {
+ infof(data, "Authentication problem. Ignoring this.\n");
+ data->state.authproblem = TRUE;
+ }
+ }
+ }
+ else
+#endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ if(checkprefix("Digest", start)) {
+ if((authp->avail & CURLAUTH_DIGEST) != 0) {
+ infof(data, "Ignoring duplicate digest auth header.\n");
+ }
+ else {
+ CURLdigest dig;
+ *availp |= CURLAUTH_DIGEST;
+ authp->avail |= CURLAUTH_DIGEST;
+
+ /* We call this function on input Digest headers even if Digest
+ * authentication isn't activated yet, as we need to store the
+ * incoming data from this header in case we are gonna use Digest. */
+ dig = Curl_input_digest(conn, (bool)(httpcode == 407), start);
+
+ if(CURLDIGEST_FINE != dig) {
+ infof(data, "Authentication problem. Ignoring this.\n");
+ data->state.authproblem = TRUE;
+ }
+ }
+ }
+ else
+#endif
+ if(checkprefix("Basic", start)) {
+ *availp |= CURLAUTH_BASIC;
+ authp->avail |= CURLAUTH_BASIC;
+ if(authp->picked == CURLAUTH_BASIC) {
+ /* We asked for Basic authentication but got a 40X back
+ anyway, which basicly means our name+password isn't
+ valid. */
+ authp->avail = CURLAUTH_NONE;
+ infof(data, "Authentication problem. Ignoring this.\n");
+ data->state.authproblem = TRUE;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+/**
+ * Curl_http_should_fail() determines whether an HTTP response has gotten us
+ * into an error state or not.
+ *
+ * @param conn all information about the current connection
+ *
+ * @retval 0 communications should continue
+ *
+ * @retval 1 communications should not continue
+ */
+int Curl_http_should_fail(struct connectdata *conn)
+{
+ struct SessionHandle *data;
+ struct Curl_transfer_keeper *k;
+
+ curlassert(conn);
+ data = conn->data;
+ curlassert(data);
+
+ /*
+ ** For readability
+ */
+ k = &data->reqdata.keep;
+
+ /*
+ ** If we haven't been asked to fail on error,
+ ** don't fail.
+ */
+ if (!data->set.http_fail_on_error)
+ return 0;
+
+ /*
+ ** Any code < 400 is never terminal.
+ */
+ if (k->httpcode < 400)
+ return 0;
+
+ if (data->reqdata.resume_from &&
+ (data->set.httpreq==HTTPREQ_GET) &&
+ (k->httpcode == 416)) {
+ /* "Requested Range Not Satisfiable", just proceed and
+ pretend this is no error */
+ return 0;
+ }
+
+ /*
+ ** Any code >= 400 that's not 401 or 407 is always
+ ** a terminal error
+ */
+ if ((k->httpcode != 401) &&
+ (k->httpcode != 407))
+ return 1;
+
+ /*
+ ** All we have left to deal with is 401 and 407
+ */
+ curlassert((k->httpcode == 401) || (k->httpcode == 407));
+
+ /*
+ ** Examine the current authentication state to see if this
+ ** is an error. The idea is for this function to get
+ ** called after processing all the headers in a response
+ ** message. So, if we've been to asked to authenticate a
+ ** particular stage, and we've done it, we're OK. But, if
+ ** we're already completely authenticated, it's not OK to
+ ** get another 401 or 407.
+ **
+ ** It is possible for authentication to go stale such that
+ ** the client needs to reauthenticate. Once that info is
+ ** available, use it here.
+ */
+#if 0 /* set to 1 when debugging this functionality */
+ infof(data,"%s: authstage = %d\n",__FUNCTION__,data->state.authstage);
+ infof(data,"%s: authwant = 0x%08x\n",__FUNCTION__,data->state.authwant);
+ infof(data,"%s: authavail = 0x%08x\n",__FUNCTION__,data->state.authavail);
+ infof(data,"%s: httpcode = %d\n",__FUNCTION__,k->httpcode);
+ infof(data,"%s: authdone = %d\n",__FUNCTION__,data->state.authdone);
+ infof(data,"%s: newurl = %s\n",__FUNCTION__,data->reqdata.newurl ? data->reqdata.newurl : "(null)");
+ infof(data,"%s: authproblem = %d\n",__FUNCTION__,data->state.authproblem);
+#endif
+
+ /*
+ ** Either we're not authenticating, or we're supposed to
+ ** be authenticating something else. This is an error.
+ */
+ if((k->httpcode == 401) && !conn->bits.user_passwd)
+ return TRUE;
+ if((k->httpcode == 407) && !conn->bits.proxy_user_passwd)
+ return TRUE;
+
+ return data->state.authproblem;
+}
+
+/*
+ * readmoredata() is a "fread() emulation" to provide POST and/or request
+ * data. It is used when a huge POST is to be made and the entire chunk wasn't
+ * sent in the first send(). This function will then be called from the
+ * transfer.c loop when more data is to be sent to the peer.
+ *
+ * Returns the amount of bytes it filled the buffer with.
+ */
+static size_t readmoredata(char *buffer,
+ size_t size,
+ size_t nitems,
+ void *userp)
+{
+ struct connectdata *conn = (struct connectdata *)userp;
+ struct HTTP *http = conn->data->reqdata.proto.http;
+ size_t fullsize = size * nitems;
+
+ if(0 == http->postsize)
+ /* nothing to return */
+ return 0;
+
+ /* make sure that a HTTP request is never sent away chunked! */
+ conn->bits.forbidchunk = (bool)(http->sending == HTTPSEND_REQUEST);
+
+ if(http->postsize <= (curl_off_t)fullsize) {
+ memcpy(buffer, http->postdata, (size_t)http->postsize);
+ fullsize = (size_t)http->postsize;
+
+ if(http->backup.postsize) {
+ /* move backup data into focus and continue on that */
+ http->postdata = http->backup.postdata;
+ http->postsize = http->backup.postsize;
+ conn->fread = http->backup.fread;
+ conn->fread_in = http->backup.fread_in;
+
+ http->sending++; /* move one step up */
+
+ http->backup.postsize=0;
+ }
+ else
+ http->postsize = 0;
+
+ return fullsize;
+ }
+
+ memcpy(buffer, http->postdata, fullsize);
+ http->postdata += fullsize;
+ http->postsize -= fullsize;
+
+ return fullsize;
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * 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.
+ */
+
+struct send_buffer {
+ char *buffer;
+ size_t size_max;
+ size_t size_used;
+};
+typedef struct send_buffer send_buffer;
+
+static CURLcode add_custom_headers(struct connectdata *conn,
+ send_buffer *req_buffer);
+static CURLcode
+ add_buffer(send_buffer *in, const void *inptr, size_t size);
+
+/*
+ * add_buffer_init() sets up and 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 header buffer and frees all associated memory.
+ * Body data may be appended to the header data if desired.
+ *
+ * Returns CURLcode
+ */
+static
+CURLcode add_buffer_send(send_buffer *in,
+ struct connectdata *conn,
+ long *bytes_written, /* add the number of sent
+ bytes to this counter */
+ size_t included_body_bytes, /* how much of the buffer
+ contains body data (for log tracing) */
+ int socketindex)
+
+{
+ ssize_t amount;
+ CURLcode res;
+ char *ptr;
+ size_t size;
+ struct HTTP *http = conn->data->reqdata.proto.http;
+ size_t sendsize;
+ curl_socket_t sockfd;
+
+ curlassert(socketindex <= SECONDARYSOCKET);
+
+ sockfd = conn->sock[socketindex];
+
+ /* The looping below is required since we use non-blocking sockets, but due
+ to the circumstances we will just loop and try again and again etc */
+
+ ptr = in->buffer;
+ size = in->size_used;
+
+#ifdef CURL_DOES_CONVERSIONS
+ if(size - included_body_bytes > 0) {
+ res = Curl_convert_to_network(conn->data, ptr, size - included_body_bytes);
+ /* Curl_convert_to_network calls failf if unsuccessful */
+ if(res != CURLE_OK) {
+ /* conversion failed, free memory and return to the caller */
+ if(in->buffer)
+ free(in->buffer);
+ free(in);
+ return res;
+ }
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+ if(conn->protocol & PROT_HTTPS) {
+ /* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk
+ when we speak HTTPS, as if only a fraction of it is sent now, this data
+ needs to fit into the normal read-callback buffer later on and that
+ buffer is using this size.
+ */
+
+ sendsize= (size > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:size;
+
+ /* OpenSSL is very picky and we must send the SAME buffer pointer to the
+ library when we attempt to re-send this buffer. Sending the same data
+ is not enough, we must use the exact same address. For this reason, we
+ must copy the data to the uploadbuffer first, since that is the buffer
+ we will be using if this send is retried later.
+ */
+ memcpy(conn->data->state.uploadbuffer, ptr, sendsize);
+ ptr = conn->data->state.uploadbuffer;
+ }
+ else
+ sendsize = size;
+
+ res = Curl_write(conn, sockfd, ptr, sendsize, &amount);
+
+ if(CURLE_OK == res) {
+
+ if(conn->data->set.verbose) {
+ /* this data _may_ contain binary stuff */
+ Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr,
+ (size_t)(amount-included_body_bytes), conn);
+ if (included_body_bytes)
+ Curl_debug(conn->data, CURLINFO_DATA_OUT,
+ ptr+amount-included_body_bytes,
+ (size_t)included_body_bytes, conn);
+ }
+
+ *bytes_written += amount;
+
+ if(http) {
+ if((size_t)amount != size) {
+ /* The whole request could not be sent in one system call. We must
+ queue it up and send it later when we get the chance. We must not
+ loop here and wait until it might work again. */
+
+ size -= amount;
+
+ ptr = in->buffer + amount;
+
+ /* backup the currently set pointers */
+ http->backup.fread = conn->fread;
+ http->backup.fread_in = conn->fread_in;
+ http->backup.postdata = http->postdata;
+ http->backup.postsize = http->postsize;
+
+ /* set the new pointers for the request-sending */
+ conn->fread = (curl_read_callback)readmoredata;
+ conn->fread_in = (void *)conn;
+ http->postdata = ptr;
+ http->postsize = (curl_off_t)size;
+
+ http->send_buffer = in;
+ http->sending = HTTPSEND_REQUEST;
+
+ return CURLE_OK;
+ }
+ http->sending = HTTPSEND_BODY;
+ /* the full buffer was sent, clean up and return */
+ }
+ else {
+ if((size_t)amount != size)
+ /* We have no continue-send mechanism now, fail. This can only happen
+ when this function is used from the CONNECT sending function. We
+ currently (stupidly) assume that the whole request is always sent
+ away in the first single chunk.
+
+ This needs FIXing.
+ */
+ return CURLE_SEND_ERROR;
+ else
+ conn->writechannel_inuse = FALSE;
+ }
+ }
+ if(in->buffer)
+ free(in->buffer);
+ free(in);
+
+ return res;
+}
+
+
+/*
+ * add_bufferf() add the formatted input to the buffer.
+ */
+static
+CURLcode add_bufferf(send_buffer *in, const char *fmt, ...)
+{
+ char *s;
+ va_list ap;
+ va_start(ap, fmt);
+ s = vaprintf(fmt, ap); /* this allocs a new string to append */
+ va_end(ap);
+
+ if(s) {
+ CURLcode result = add_buffer(in, s, strlen(s));
+ free(s);
+ if(CURLE_OK == result)
+ return CURLE_OK;
+ }
+ /* If we failed, we cleanup the whole buffer and return error */
+ if(in->buffer)
+ free(in->buffer);
+ free(in);
+ return CURLE_OUT_OF_MEMORY;
+}
+
+/*
+ * add_buffer() appends a memory chunk to the existing buffer
+ */
+static
+CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size)
+{
+ char *new_rb;
+ size_t 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 */
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Curl_compareheader()
+ *
+ * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
+ * Pass headers WITH the colon.
+ */
+bool
+Curl_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(*start))
+ start++;
+
+ /* find the end of the header line */
+ end = strchr(start, '\r'); /* lines end with CRLF */
+ if(!end) {
+ /* in case there's a non-standard compliant line here */
+ end = strchr(start, '\n');
+
+ if(!end)
+ /* hm, there's no line ending here, use the zero byte! */
+ end = strchr(start, '\0');
+ }
+
+ len = end-start; /* length of the content part of the input line */
+ clen = strlen(content); /* length of the word to find */
+
+ /* find the content string in the rest of the line */
+ for(;len>=clen;len--, start++) {
+ if(strnequal(start, content, clen))
+ return TRUE; /* match! */
+ }
+
+ return FALSE; /* no match */
+}
+
+/*
+ * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
+ * function will issue the necessary commands to get a seamless tunnel through
+ * this proxy. After that, the socket can be used just as a normal socket.
+ *
+ * This badly needs to be rewritten. CONNECT should be sent and dealt with
+ * like any ordinary HTTP request, and not specially crafted like this. This
+ * function only remains here like this for now since the rewrite is a bit too
+ * much work to do at the moment.
+ *
+ * This function is BLOCKING which is nasty for all multi interface using apps.
+ */
+
+CURLcode Curl_proxyCONNECT(struct connectdata *conn,
+ int sockindex,
+ char *hostname,
+ int remote_port)
+{
+ int subversion=0;
+ struct SessionHandle *data=conn->data;
+ struct Curl_transfer_keeper *k = &data->reqdata.keep;
+ CURLcode result;
+ int res;
+ size_t nread; /* total size read */
+ int perline; /* count bytes per line */
+ int keepon=TRUE;
+ ssize_t gotbytes;
+ char *ptr;
+ long timeout =
+ data->set.timeout?data->set.timeout:3600; /* in seconds */
+ char *line_start;
+ char *host_port;
+ curl_socket_t tunnelsocket = conn->sock[sockindex];
+ send_buffer *req_buffer;
+ curl_off_t cl=0;
+ bool closeConnection = FALSE;
+
+#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);
+ conn->bits.proxy_connect_closed = FALSE;
+
+ do {
+ if(data->reqdata.newurl) {
+ /* This only happens if we've looped here due to authentication reasons,
+ and we don't really use the newly cloned URL here then. Just free()
+ it. */
+ free(data->reqdata.newurl);
+ data->reqdata.newurl = NULL;
+ }
+
+ /* initialize a dynamic send-buffer */
+ req_buffer = add_buffer_init();
+
+ if(!req_buffer)
+ return CURLE_OUT_OF_MEMORY;
+
+ host_port = aprintf("%s:%d", hostname, remote_port);
+ if(!host_port)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Setup the proxy-authorization header, if any */
+ result = Curl_http_output_auth(conn, (char *)"CONNECT", host_port, TRUE);
+
+ if(CURLE_OK == result) {
+ char *host=(char *)"";
+ const char *proxyconn="";
+ const char *useragent="";
+
+ if(!checkheaders(data, "Host:")) {
+ host = aprintf("Host: %s\r\n", host_port);
+ if(!host)
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ if(!checkheaders(data, "Proxy-Connection:"))
+ proxyconn = "Proxy-Connection: Keep-Alive\r\n";
+
+ if(!checkheaders(data, "User-Agent:") && data->set.useragent)
+ useragent = conn->allocptr.uagent;
+
+ if(CURLE_OK == result) {
+ /* Send the connect request to the proxy */
+ /* BLOCKING */
+ result =
+ add_bufferf(req_buffer,
+ "CONNECT %s:%d HTTP/1.0\r\n"
+ "%s" /* Host: */
+ "%s" /* Proxy-Authorization */
+ "%s" /* User-Agent */
+ "%s", /* Proxy-Connection */
+ hostname, remote_port,
+ host,
+ conn->allocptr.proxyuserpwd?
+ conn->allocptr.proxyuserpwd:"",
+ useragent,
+ proxyconn);
+
+ if(CURLE_OK == result)
+ result = add_custom_headers(conn, req_buffer);
+
+ if(host && *host)
+ free(host);
+
+ if(CURLE_OK == result)
+ /* CRLF terminate the request */
+ result = add_bufferf(req_buffer, "\r\n");
+
+ if(CURLE_OK == result)
+ /* Now send off the request */
+ result = add_buffer_send(req_buffer, conn,
+ &data->info.request_size, 0, sockindex);
+ }
+ if(result)
+ failf(data, "Failed sending CONNECT to proxy");
+ }
+ free(host_port);
+ if(result)
+ return result;
+
+ ptr=data->state.buffer;
+ line_start = ptr;
+
+ nread=0;
+ perline=0;
+ keepon=TRUE;
+
+ while((nread<BUFSIZE) && (keepon && !error)) {
+
+ /* if timeout is requested, find out how much remaining time we have */
+ long check = timeout - /* timeout time */
+ Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
+ if(check <=0 ) {
+ failf(data, "Proxy CONNECT aborted due to timeout");
+ error = SELECT_TIMEOUT; /* already too little time */
+ break;
+ }
+
+ /* timeout each second and check the timeout */
+ switch (Curl_select(tunnelsocket, CURL_SOCKET_BAD, 1000)) {
+ case -1: /* select() error, stop reading */
+ error = SELECT_ERROR;
+ failf(data, "Proxy CONNECT aborted due to select() error");
+ break;
+ case 0: /* timeout */
+ break;
+ default:
+ 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, "Proxy CONNECT aborted");
+ }
+ else {
+ /*
+ * We got a whole chunk of data, which can be anything from one byte
+ * to a set of lines and possibly just a piece of the last line.
+ */
+ int i;
+
+ nread += gotbytes;
+
+ if(keepon > TRUE) {
+ /* This means we are currently ignoring a response-body, so we
+ simply count down our counter and make sure to break out of the
+ loop when we're done! */
+ cl -= gotbytes;
+ if(cl<=0) {
+ keepon = FALSE;
+ break;
+ }
+ }
+ else
+ for(i = 0; i < gotbytes; ptr++, i++) {
+ perline++; /* amount of bytes in this line so far */
+ if(*ptr=='\n') {
+ char letter;
+ int writetype;
+
+ /* output debug if that is requested */
+ if(data->set.verbose)
+ Curl_debug(data, CURLINFO_HEADER_IN,
+ line_start, (size_t)perline, conn);
+
+ /* send the header to the callback */
+ writetype = CLIENTWRITE_HEADER;
+ if(data->set.include_header)
+ writetype |= CLIENTWRITE_BODY;
+
+ result = Curl_client_write(conn, writetype, line_start, perline);
+ if(result)
+ return result;
+
+ /* Newlines are CRLF, so the CR is ignored as the line isn't
+ really terminated until the LF comes. Treat a following CR
+ as end-of-headers as well.*/
+
+ if(('\r' == line_start[0]) ||
+ ('\n' == line_start[0])) {
+ /* end of response-headers from the proxy */
+ if(cl && (407 == k->httpcode) && !data->state.authproblem) {
+ /* If we get a 407 response code with content length when we
+ * have no auth problem, we must ignore the whole
+ * response-body */
+ keepon = 2;
+ infof(data, "Ignore %" FORMAT_OFF_T
+ " bytes of response-body\n", cl);
+ cl -= (gotbytes - i);/* remove the remaining chunk of what
+ we already read */
+ if(cl<=0)
+ /* if the whole thing was already read, we are done! */
+ keepon=FALSE;
+ }
+ else
+ keepon = FALSE;
+ break; /* breaks out of for-loop, not switch() */
+ }
+
+ /* keep a backup of the position we are about to blank */
+ letter = line_start[perline];
+ line_start[perline]=0; /* zero terminate the buffer */
+ if((checkprefix("WWW-Authenticate:", line_start) &&
+ (401 == k->httpcode)) ||
+ (checkprefix("Proxy-authenticate:", line_start) &&
+ (407 == k->httpcode))) {
+ result = Curl_http_input_auth(conn, k->httpcode, line_start);
+ if(result)
+ return result;
+ }
+ else if(checkprefix("Content-Length:", line_start)) {
+ cl = curlx_strtoofft(line_start + strlen("Content-Length:"),
+ NULL, 10);
+ }
+ else if(Curl_compareheader(line_start,
+ "Connection:", "close"))
+ closeConnection = TRUE;
+ else if(2 == sscanf(line_start, "HTTP/1.%d %d",
+ &subversion,
+ &k->httpcode)) {
+ /* store the HTTP code from the proxy */
+ data->info.httpproxycode = k->httpcode;
+ }
+ /* put back the letter we blanked out before */
+ line_start[perline]= letter;
+
+ perline=0; /* line starts over here */
+ line_start = ptr+1; /* this skips the zero byte we wrote */
+ }
+ }
+ }
+ break;
+ } /* switch */
+ } /* while there's buffer left and loop is requested */
+
+ if(error)
+ return CURLE_RECV_ERROR;
+
+ if(data->info.httpproxycode != 200)
+ /* Deal with the possibly already received authenticate
+ headers. 'newurl' is set to a new URL if we must loop. */
+ Curl_http_auth_act(conn);
+
+ if (closeConnection && data->reqdata.newurl) {
+ /* Connection closed by server. Don't use it anymore */
+ sclose(conn->sock[sockindex]);
+ conn->sock[sockindex] = CURL_SOCKET_BAD;
+ break;
+ }
+ } while(data->reqdata.newurl);
+
+ if(200 != k->httpcode) {
+ failf(data, "Received HTTP code %d from proxy after CONNECT",
+ k->httpcode);
+
+ if (closeConnection && data->reqdata.newurl)
+ conn->bits.proxy_connect_closed = TRUE;
+
+ return CURLE_RECV_ERROR;
+ }
+
+ /* If a proxy-authorization header was used for the proxy, then we should
+ make sure that it isn't accidentally used for the document request
+ after we've connected. So let's free and clear it here. */
+ Curl_safefree(conn->allocptr.proxyuserpwd);
+ conn->allocptr.proxyuserpwd = NULL;
+
+ data->state.authproxy.done = TRUE;
+
+ infof (data, "Proxy replied OK to CONNECT request\n");
+ return CURLE_OK;
+}
+
+/*
+ * Curl_http_connect() performs HTTP stuff to do at connect-time, called from
+ * the generic Curl_connect().
+ */
+CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
+{
+ struct SessionHandle *data;
+ CURLcode result;
+
+ data=conn->data;
+
+ /* 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 to the host we want to talk to. Only
+ * after the connect has occurred, can we start talking SSL
+ */
+
+ if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
+
+ /* either SSL over proxy, or explicitly asked for */
+ result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
+ conn->host.name,
+ conn->remote_port);
+ if(CURLE_OK != result)
+ return result;
+ }
+
+ if(!data->state.this_is_a_follow) {
+ /* this is not a followed location, get the original host name */
+ if (data->state.first_host)
+ /* Free to avoid leaking memory on multiple requests*/
+ free(data->state.first_host);
+
+ data->state.first_host = strdup(conn->host.name);
+ if(!data->state.first_host)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(conn->protocol & PROT_HTTPS) {
+ /* perform SSL initialization */
+ if(data->state.used_interface == Curl_if_multi) {
+ result = Curl_https_connecting(conn, done);
+ if(result)
+ return result;
+ }
+ else {
+ /* BLOCKING */
+ result = Curl_ssl_connect(conn, FIRSTSOCKET);
+ if(result)
+ return result;
+ *done = TRUE;
+ }
+ }
+ else {
+ *done = TRUE;
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_https_connecting(struct connectdata *conn, bool *done)
+{
+ CURLcode result;
+ curlassert(conn->protocol & PROT_HTTPS);
+
+ /* perform SSL initialization for this socket */
+ result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
+ if(result)
+ return result;
+
+ return CURLE_OK;
+}
+
+#ifdef USE_SSLEAY
+/* This function is OpenSSL-specific. It should be made to query the generic
+ SSL layer instead. */
+int Curl_https_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks)
+{
+ if (conn->protocol & PROT_HTTPS) {
+ struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
+
+ if(!numsocks)
+ return GETSOCK_BLANK;
+
+ if (connssl->connecting_state == ssl_connect_2_writing) {
+ /* write mode */
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_WRITESOCK(0);
+ }
+ else if (connssl->connecting_state == ssl_connect_2_reading) {
+ /* read mode */
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_READSOCK(0);
+ }
+ }
+ return CURLE_OK;
+}
+#else
+#ifdef USE_GNUTLS
+int Curl_https_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks)
+{
+ (void)conn;
+ (void)socks;
+ (void)numsocks;
+ return GETSOCK_BLANK;
+}
+#endif
+#endif
+
+/*
+ * Curl_http_done() gets called from Curl_done() after a single HTTP request
+ * has been performed.
+ */
+
+CURLcode Curl_http_done(struct connectdata *conn,
+ CURLcode status, bool premature)
+{
+ struct SessionHandle *data = conn->data;
+ struct HTTP *http =data->reqdata.proto.http;
+ struct Curl_transfer_keeper *k = &data->reqdata.keep;
+ (void)premature; /* not used */
+
+ /* set the proper values (possibly modified on POST) */
+ conn->fread = data->set.fread; /* restore */
+ conn->fread_in = data->set.in; /* restore */
+
+ if (http == NULL)
+ return CURLE_OK;
+
+ if(http->send_buffer) {
+ send_buffer *buff = http->send_buffer;
+
+ free(buff->buffer);
+ free(buff);
+ http->send_buffer = NULL; /* clear the pointer */
+ }
+
+ if(HTTPREQ_POST_FORM == data->set.httpreq) {
+ k->bytecount = http->readbytecount + http->writebytecount;
+
+ Curl_formclean(&http->sendit); /* Now free that whole lot */
+ if(http->form.fp) {
+ /* a file being uploaded was left opened, close it! */
+ fclose(http->form.fp);
+ http->form.fp = NULL;
+ }
+ }
+ else if(HTTPREQ_PUT == data->set.httpreq)
+ k->bytecount = http->readbytecount + http->writebytecount;
+
+ if (status != CURLE_OK)
+ return (status);
+
+ if(!conn->bits.retry &&
+ ((http->readbytecount +
+ conn->headerbytecount -
+ conn->deductheadercount)) <= 0) {
+ /* If this connection isn't simply closed to be retried, AND nothing was
+ read from the HTTP server (that counts), this can't be right so we
+ return an error here */
+ failf(data, "Empty reply from server");
+ return CURLE_GOT_NOTHING;
+ }
+
+ return CURLE_OK;
+}
+
+/* check and possibly add an Expect: header */
+static CURLcode expect100(struct SessionHandle *data,
+ send_buffer *req_buffer)
+{
+ CURLcode result = CURLE_OK;
+ data->state.expect100header = FALSE; /* default to false unless it is set
+ to TRUE below */
+ if((data->set.httpversion != CURL_HTTP_VERSION_1_0) &&
+ !checkheaders(data, "Expect:")) {
+ /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
+ 100-continue to the headers which actually speeds up post
+ operations (as there is one packet coming back from the web
+ server) */
+ result = add_bufferf(req_buffer,
+ "Expect: 100-continue\r\n");
+ if(result == CURLE_OK)
+ data->state.expect100header = TRUE;
+ }
+ return result;
+}
+
+static CURLcode add_custom_headers(struct connectdata *conn,
+ send_buffer *req_buffer)
+{
+ CURLcode result = CURLE_OK;
+ char *ptr;
+ struct curl_slist *headers=conn->data->set.headers;
+
+ while(headers) {
+ ptr = strchr(headers->data, ':');
+ if(ptr) {
+ /* we require a colon for this to be a true header */
+
+ ptr++; /* pass the colon */
+ while(*ptr && ISSPACE(*ptr))
+ ptr++;
+
+ if(*ptr) {
+ /* only send this if the contents was non-blank */
+
+ if(conn->allocptr.host &&
+ /* a Host: header was sent already, don't pass on any custom Host:
+ header as that will produce *two* in the same request! */
+ curl_strnequal("Host:", headers->data, 5))
+ ;
+ else if(conn->data->set.httpreq == HTTPREQ_POST_FORM &&
+ /* this header (extended by formdata.c) is sent later */
+ curl_strnequal("Content-Type:", headers->data,
+ strlen("Content-Type:")))
+ ;
+ else {
+ result = add_bufferf(req_buffer, "%s\r\n", headers->data);
+ if(result)
+ return result;
+ }
+ }
+ }
+ headers = headers->next;
+ }
+ return result;
+}
+
+/*
+ * Curl_http() gets called from the generic Curl_do() function when a HTTP
+ * request is to be performed. This creates and sends a properly constructed
+ * HTTP request.
+ */
+CURLcode Curl_http(struct connectdata *conn, bool *done)
+{
+ struct SessionHandle *data=conn->data;
+ char *buf = data->state.buffer; /* this is a short cut to the buffer */
+ CURLcode result=CURLE_OK;
+ struct HTTP *http;
+ char *ppath = data->reqdata.path;
+ char *host = conn->host.name;
+ const char *te = ""; /* transfer-encoding */
+ char *ptr;
+ char *request;
+ Curl_HttpReq httpreq = data->set.httpreq;
+ char *addcookies = NULL;
+ curl_off_t included_body = 0;
+
+ /* Always consider the DO phase done after this function call, even if there
+ may be parts of the request that is not yet sent, since we can deal with
+ the rest of the request in the PERFORM phase. */
+ *done = TRUE;
+
+ if(!data->reqdata.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));
+ data->reqdata.proto.http = http;
+ }
+ else
+ http = data->reqdata.proto.http;
+
+ /* We default to persistent connections */
+ conn->bits.close = FALSE;
+
+ if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
+ data->set.upload) {
+ httpreq = HTTPREQ_PUT;
+ }
+
+ /* Now set the 'request' pointer to the proper request string */
+ if(data->set.customrequest)
+ request = data->set.customrequest;
+ else {
+ if(conn->bits.no_body)
+ request = (char *)"HEAD";
+ else {
+ curlassert((httpreq > HTTPREQ_NONE) && (httpreq < HTTPREQ_LAST));
+ switch(httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ request = (char *)"POST";
+ break;
+ case HTTPREQ_PUT:
+ request = (char *)"PUT";
+ break;
+ default: /* this should never happen */
+ case HTTPREQ_GET:
+ request = (char *)"GET";
+ break;
+ case HTTPREQ_HEAD:
+ request = (char *)"HEAD";
+ break;
+ }
+ }
+ }
+
+ /* The User-Agent string might have been allocated 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;
+ }
+
+ /* setup the authentication headers */
+ result = Curl_http_output_auth(conn, request, ppath, FALSE);
+ if(result)
+ return result;
+
+ if((data->state.authhost.multi || data->state.authproxy.multi) &&
+ (httpreq != HTTPREQ_GET) &&
+ (httpreq != HTTPREQ_HEAD)) {
+ /* Auth is required and we are not authenticated yet. Make a PUT or POST
+ with content-length zero as a "probe". */
+ conn->bits.authneg = TRUE;
+ }
+ else
+ conn->bits.authneg = FALSE;
+
+ Curl_safefree(conn->allocptr.ref);
+ if(data->change.referer && !checkheaders(data, "Referer:"))
+ conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
+ else
+ conn->allocptr.ref = NULL;
+
+ if(data->set.cookie && !checkheaders(data, "Cookie:"))
+ addcookies = data->set.cookie;
+
+ if(!checkheaders(data, "Accept-Encoding:") &&
+ data->set.encoding) {
+ Curl_safefree(conn->allocptr.accept_encoding);
+ conn->allocptr.accept_encoding =
+ aprintf("Accept-Encoding: %s\r\n", data->set.encoding);
+ if(!conn->allocptr.accept_encoding)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ ptr = checkheaders(data, "Transfer-Encoding:");
+ if(ptr) {
+ /* Some kind of TE is requested, check if 'chunked' is chosen */
+ conn->bits.upload_chunky =
+ Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
+ }
+ else {
+ if (httpreq == HTTPREQ_GET)
+ conn->bits.upload_chunky = FALSE;
+ if(conn->bits.upload_chunky)
+ te = "Transfer-Encoding: chunked\r\n";
+ }
+
+ Curl_safefree(conn->allocptr.host);
+
+ ptr = checkheaders(data, "Host:");
+ if(ptr && (!data->state.this_is_a_follow ||
+ curl_strequal(data->state.first_host, conn->host.name))) {
+#if !defined(CURL_DISABLE_COOKIES)
+ /* If we have a given custom Host: header, we extract the host name in
+ order to possibly use it for cookie reasons later on. We only allow the
+ custom Host: header if this is NOT a redirect, as setting Host: in the
+ redirected request is being out on thin ice. Except if the host name
+ is the same as the first one! */
+ char *start = ptr+strlen("Host:");
+ while(*start && ISSPACE(*start ))
+ start++;
+ ptr = start; /* start host-scanning here */
+
+ /* scan through the string to find the end (space or colon) */
+ while(*ptr && !ISSPACE(*ptr) && !(':'==*ptr))
+ ptr++;
+
+ if(ptr != start) {
+ size_t len=ptr-start;
+ Curl_safefree(conn->allocptr.cookiehost);
+ conn->allocptr.cookiehost = malloc(len+1);
+ if(!conn->allocptr.cookiehost)
+ return CURLE_OUT_OF_MEMORY;
+ memcpy(conn->allocptr.cookiehost, start, len);
+ conn->allocptr.cookiehost[len]=0;
+ }
+#endif
+
+ conn->allocptr.host = NULL;
+ }
+ else {
+ /* When building Host: headers, we must put the host name within
+ [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
+
+ if(((conn->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%s%s\r\n",
+ conn->bits.ipv6_ip?"[":"",
+ host,
+ conn->bits.ipv6_ip?"]":"");
+ else
+ conn->allocptr.host = aprintf("Host: %s%s%s:%d\r\n",
+ conn->bits.ipv6_ip?"[":"",
+ host,
+ conn->bits.ipv6_ip?"]":"",
+ conn->remote_port);
+
+ if(!conn->allocptr.host)
+ /* without Host: we can't make a nice request */
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if (conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ /* Using a proxy but does not tunnel through it */
+
+ /* The path sent to the proxy is in fact the entire URL. But if the remote
+ host is a IDN-name, we must make sure that the request we produce only
+ uses the encoded host name! */
+ if(conn->host.dispname != conn->host.name) {
+ char *url = data->change.url;
+ ptr = strstr(url, conn->host.dispname);
+ if(ptr) {
+ /* This is where the display name starts in the URL, now replace this
+ part with the encoded name. TODO: This method of replacing the host
+ name is rather crude as I believe there's a slight risk that the
+ user has entered a user name or password that contain the host name
+ string. */
+ size_t currlen = strlen(conn->host.dispname);
+ size_t newlen = strlen(conn->host.name);
+ size_t urllen = strlen(url);
+
+ char *newurl;
+
+ newurl = malloc(urllen + newlen - currlen + 1);
+ if(newurl) {
+ /* copy the part before the host name */
+ memcpy(newurl, url, ptr - url);
+ /* append the new host name instead of the old */
+ memcpy(newurl + (ptr - url), conn->host.name, newlen);
+ /* append the piece after the host name */
+ memcpy(newurl + newlen + (ptr - url),
+ ptr + currlen, /* copy the trailing zero byte too */
+ urllen - (ptr-url) - currlen + 1);
+ if(data->change.url_alloc)
+ free(data->change.url);
+ data->change.url = newurl;
+ data->change.url_alloc = TRUE;
+ }
+ else
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ ppath = data->change.url;
+ }
+ if(HTTPREQ_POST_FORM == 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 */
+ result = Curl_getFormData(&http->sendit, data->set.httppost,
+ checkheaders(data, "Content-Type:"),
+ &http->postsize);
+ if(CURLE_OK != result) {
+ /* Curl_getFormData() doesn't use failf() */
+ failf(data, "failed creating formpost data");
+ return result;
+ }
+ }
+
+
+ http->p_pragma =
+ (!checkheaders(data, "Pragma:") &&
+ (conn->bits.httpproxy && !conn->bits.tunnel_proxy) )?
+ "Pragma: no-cache\r\n":NULL;
+
+ if(!checkheaders(data, "Accept:"))
+ http->p_accept = "Accept: */*\r\n";
+
+ if(( (HTTPREQ_POST == httpreq) ||
+ (HTTPREQ_POST_FORM == httpreq) ||
+ (HTTPREQ_PUT == httpreq) ) &&
+ data->reqdata.resume_from) {
+ /**********************************************************************
+ * Resuming upload in HTTP means that we PUT or POST and that we have
+ * got a resume_from value set. The resume value has already created
+ * a Range: header that will be passed along. We need to "fast forward"
+ * the file the given number of bytes and decrease the assume upload
+ * file size before we continue this venture in the dark lands of HTTP.
+ *********************************************************************/
+
+ if(data->reqdata.resume_from < 0 ) {
+ /*
+ * This is meant to get the size of the present remote-file by itself.
+ * We don't support this now. Bail out!
+ */
+ data->reqdata.resume_from = 0;
+ }
+
+ if(data->reqdata.resume_from) {
+ /* do we still game? */
+ curl_off_t 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 {
+ size_t readthisamountnow = (size_t)(data->reqdata.resume_from - passed);
+ size_t actuallyread;
+
+ if(readthisamountnow > BUFSIZE)
+ readthisamountnow = BUFSIZE;
+
+ actuallyread =
+ data->set.fread(data->state.buffer, 1, (size_t)readthisamountnow,
+ data->set.in);
+
+ passed += actuallyread;
+ if(actuallyread != readthisamountnow) {
+ failf(data, "Could only read %" FORMAT_OFF_T
+ " bytes from the input",
+ passed);
+ return CURLE_READ_ERROR;
+ }
+ } while(passed != data->reqdata.resume_from); /* loop until done */
+
+ /* now, decrease the size of the read */
+ if(data->set.infilesize>0) {
+ data->set.infilesize -= data->reqdata.resume_from;
+
+ if(data->set.infilesize <= 0) {
+ failf(data, "File already completely uploaded");
+ return CURLE_PARTIAL_FILE;
+ }
+ }
+ /* we've passed, proceed as normal */
+ }
+ }
+ if(data->reqdata.use_range) {
+ /*
+ * A range is selected. We use different headers whether we're downloading
+ * or uploading and we always let customized headers override our internal
+ * ones if any such are specified.
+ */
+ if((httpreq == HTTPREQ_GET) &&
+ !checkheaders(data, "Range:")) {
+ /* if a line like this was already allocated, free the previous one */
+ if(conn->allocptr.rangeline)
+ free(conn->allocptr.rangeline);
+ conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", data->reqdata.range);
+ }
+ else if((httpreq != HTTPREQ_GET) &&
+ !checkheaders(data, "Content-Range:")) {
+
+ if(data->reqdata.resume_from) {
+ /* This is because "resume" was selected */
+ curl_off_t total_expected_size=
+ data->reqdata.resume_from + data->set.infilesize;
+ conn->allocptr.rangeline =
+ aprintf("Content-Range: bytes %s%" FORMAT_OFF_T
+ "/%" FORMAT_OFF_T "\r\n",
+ data->reqdata.range, total_expected_size-1,
+ total_expected_size);
+ }
+ else {
+ /* Range was selected and then we just pass the incoming range and
+ append total size */
+ conn->allocptr.rangeline =
+ aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n",
+ data->reqdata.range, data->set.infilesize);
+ }
+ }
+ }
+
+ {
+ /* 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;
+ curl_off_t postsize; /* off_t type to be able to hold a large file size */
+
+ /* initialize a dynamic send-buffer */
+ req_buffer = add_buffer_init();
+
+ if(!req_buffer)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* add the main request stuff */
+ result =
+ add_bufferf(req_buffer,
+ "%s " /* GET/HEAD/POST/PUT */
+ "%s HTTP/%s\r\n" /* path + HTTP version */
+ "%s" /* proxyuserpwd */
+ "%s" /* userpwd */
+ "%s" /* range */
+ "%s" /* user agent */
+ "%s" /* host */
+ "%s" /* pragma */
+ "%s" /* accept */
+ "%s" /* accept-encoding */
+ "%s" /* referer */
+ "%s" /* Proxy-Connection */
+ "%s",/* transfer-encoding */
+
+ request,
+ ppath,
+ httpstring,
+ conn->allocptr.proxyuserpwd?
+ conn->allocptr.proxyuserpwd:"",
+ conn->allocptr.userpwd?conn->allocptr.userpwd:"",
+ (data->reqdata.use_range && conn->allocptr.rangeline)?
+ conn->allocptr.rangeline:"",
+ (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)?
+ conn->allocptr.uagent:"",
+ (conn->allocptr.host?conn->allocptr.host:""), /* Host: host */
+ http->p_pragma?http->p_pragma:"",
+ http->p_accept?http->p_accept:"",
+ (data->set.encoding && *data->set.encoding && conn->allocptr.accept_encoding)?
+ conn->allocptr.accept_encoding:"",
+ (data->change.referer && conn->allocptr.ref)?conn->allocptr.ref:"" /* Referer: <data> */,
+ (conn->bits.httpproxy &&
+ !conn->bits.tunnel_proxy &&
+ !checkheaders(data, "Proxy-Connection:"))?
+ "Proxy-Connection: Keep-Alive\r\n":"",
+ te
+ );
+
+ if(result)
+ return result;
+
+#if !defined(CURL_DISABLE_COOKIES)
+ if(data->cookies || addcookies) {
+ struct Cookie *co=NULL; /* no cookies from start */
+ int count=0;
+
+ if(data->cookies) {
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+ co = Curl_cookie_getlist(data->cookies,
+ conn->allocptr.cookiehost?
+ conn->allocptr.cookiehost:host, data->reqdata.path,
+ (bool)(conn->protocol&PROT_HTTPS?TRUE:FALSE));
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+ }
+ if(co) {
+ struct Cookie *store=co;
+ /* now loop through all cookies that matched */
+ while(co) {
+ if(co->value) {
+ if(0 == count) {
+ result = add_bufferf(req_buffer, "Cookie: ");
+ if(result)
+ break;
+ }
+ result = add_bufferf(req_buffer,
+ "%s%s=%s", count?"; ":"",
+ co->name, co->value);
+ if(result)
+ break;
+ count++;
+ }
+ co = co->next; /* next cookie please */
+ }
+ Curl_cookie_freelist(store); /* free the cookie list */
+ }
+ if(addcookies && (CURLE_OK == result)) {
+ if(!count)
+ result = add_bufferf(req_buffer, "Cookie: ");
+ if(CURLE_OK == result) {
+ result = add_bufferf(req_buffer, "%s%s",
+ count?"; ":"",
+ addcookies);
+ count++;
+ }
+ }
+ if(count && (CURLE_OK == result))
+ result = add_buffer(req_buffer, "\r\n", 2);
+
+ if(result)
+ return result;
+ }
+#endif
+
+ if(data->set.timecondition) {
+ struct tm *tm;
+
+ /* 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;
+ tm = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
+#else
+ tm = gmtime(&data->set.timevalue);
+#endif
+
+ /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
+ snprintf(buf, BUFSIZE-1,
+ "%s, %02d %s %4d %02d:%02d:%02d GMT",
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+
+ switch(data->set.timecondition) {
+ case CURL_TIMECOND_IFMODSINCE:
+ default:
+ result = add_bufferf(req_buffer,
+ "If-Modified-Since: %s\r\n", buf);
+ break;
+ case CURL_TIMECOND_IFUNMODSINCE:
+ result = add_bufferf(req_buffer,
+ "If-Unmodified-Since: %s\r\n", buf);
+ break;
+ case CURL_TIMECOND_LASTMOD:
+ result = add_bufferf(req_buffer,
+ "Last-Modified: %s\r\n", buf);
+ break;
+ }
+ if(result)
+ return result;
+ }
+
+ result = add_custom_headers(conn, req_buffer);
+ if(result)
+ return result;
+
+ http->postdata = NULL; /* nothing to post at this point */
+ Curl_pgrsSetUploadSize(data, 0); /* upload size is 0 atm */
+
+ /* If 'authdone' is FALSE, we must not set the write socket index to the
+ Curl_transfer() call below, as we're not ready to actually upload any
+ data yet. */
+
+ switch(httpreq) {
+
+ case HTTPREQ_POST_FORM:
+ if(!http->sendit || conn->bits.authneg) {
+ /* nothing to post! */
+ result = add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
+ if(result)
+ return result;
+
+ result = add_buffer_send(req_buffer, conn,
+ &data->info.request_size, 0, FIRSTSOCKET);
+ if(result)
+ failf(data, "Failed sending POST request");
+ else
+ /* setup variables for the upcoming transfer */
+ result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
+ &http->readbytecount,
+ -1, NULL);
+ break;
+ }
+
+ if(Curl_FormInit(&http->form, http->sendit)) {
+ failf(data, "Internal HTTP POST error!");
+ return CURLE_HTTP_POST_ERROR;
+ }
+
+ /* set the read function to read from the generated form data */
+ conn->fread = (curl_read_callback)Curl_FormReader;
+ conn->fread_in = &http->form;
+
+ http->sending = HTTPSEND_BODY;
+
+ if(!conn->bits.upload_chunky) {
+ /* only add Content-Length if not uploading chunked */
+ result = add_bufferf(req_buffer,
+ "Content-Length: %" FORMAT_OFF_T "\r\n",
+ http->postsize);
+ if(result)
+ return result;
+ }
+
+ result = expect100(data, req_buffer);
+ if(result)
+ return result;
+
+ {
+
+ /* Get Content-Type: line from Curl_formpostheader.
+ */
+ char *contentType;
+ size_t linelength=0;
+ contentType = Curl_formpostheader((void *)&http->form,
+ &linelength);
+ if(!contentType) {
+ failf(data, "Could not get Content-Type header line!");
+ return CURLE_HTTP_POST_ERROR;
+ }
+
+ result = add_buffer(req_buffer, contentType, linelength);
+ if(result)
+ return result;
+ }
+
+ /* make the request end in a true CRLF */
+ result = add_buffer(req_buffer, "\r\n", 2);
+ if(result)
+ return result;
+
+ /* set upload size to the progress meter */
+ Curl_pgrsSetUploadSize(data, http->postsize);
+
+ /* fire away the whole request to the server */
+ result = add_buffer_send(req_buffer, conn,
+ &data->info.request_size, 0, FIRSTSOCKET);
+ if(result)
+ failf(data, "Failed sending POST request");
+ else
+ /* setup variables for the upcoming transfer */
+ result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
+ &http->readbytecount,
+ FIRSTSOCKET,
+ &http->writebytecount);
+
+ if(result) {
+ Curl_formclean(&http->sendit); /* free that whole lot */
+ return result;
+ }
+#ifdef CURL_DOES_CONVERSIONS
+/* time to convert the form data... */
+ result = Curl_formconvert(data, http->sendit);
+ if(result) {
+ Curl_formclean(&http->sendit); /* free that whole lot */
+ return result;
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+ break;
+
+ case HTTPREQ_PUT: /* Let's PUT the data to the server! */
+
+ if(conn->bits.authneg)
+ postsize = 0;
+ else
+ postsize = data->set.infilesize;
+
+ if((postsize != -1) && !conn->bits.upload_chunky) {
+ /* only add Content-Length if not uploading chunked */
+ result = add_bufferf(req_buffer,
+ "Content-Length: %" FORMAT_OFF_T "\r\n",
+ postsize );
+ if(result)
+ return result;
+ }
+
+ result = expect100(data, req_buffer);
+ if(result)
+ return result;
+
+ result = add_buffer(req_buffer, "\r\n", 2); /* end of headers */
+ if(result)
+ return result;
+
+ /* set the upload size to the progress meter */
+ Curl_pgrsSetUploadSize(data, postsize);
+
+ /* this sends the buffer and frees all the buffer resources */
+ result = add_buffer_send(req_buffer, conn,
+ &data->info.request_size, 0, FIRSTSOCKET);
+ if(result)
+ failf(data, "Failed sending PUT request");
+ else
+ /* prepare for transfer */
+ result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
+ &http->readbytecount,
+ postsize?FIRSTSOCKET:-1,
+ postsize?&http->writebytecount:NULL);
+ if(result)
+ return result;
+ break;
+
+ case HTTPREQ_POST:
+ /* this is the simple POST, using x-www-form-urlencoded style */
+
+ if(conn->bits.authneg)
+ postsize = 0;
+ else
+ /* figure out the size of the postfields */
+ postsize = (data->set.postfieldsize != -1)?
+ data->set.postfieldsize:
+ (data->set.postfields?(curl_off_t)strlen(data->set.postfields):0);
+
+ if(!conn->bits.upload_chunky) {
+ /* We only set Content-Length and allow a custom Content-Length if
+ we don't upload data chunked, as RFC2616 forbids us to set both
+ kinds of headers (Transfer-Encoding: chunked and Content-Length) */
+
+ if(!checkheaders(data, "Content-Length:")) {
+ /* we allow replacing this header, although it isn't very wise to
+ actually set your own */
+ result = add_bufferf(req_buffer,
+ "Content-Length: %" FORMAT_OFF_T"\r\n",
+ postsize);
+ if(result)
+ return result;
+ }
+ }
+
+ if(!checkheaders(data, "Content-Type:")) {
+ result = add_bufferf(req_buffer,
+ "Content-Type: application/x-www-form-urlencoded\r\n");
+ if(result)
+ return result;
+ }
+
+ if(data->set.postfields) {
+
+ /* for really small posts we don't use Expect: headers at all, and for
+ the somewhat bigger ones we allow the app to disable it */
+ if(postsize > TINY_INITIAL_POST_SIZE) {
+ result = expect100(data, req_buffer);
+ if(result)
+ return result;
+ }
+ else
+ data->state.expect100header = FALSE;
+
+ if(!data->state.expect100header &&
+ (postsize < MAX_INITIAL_POST_SIZE)) {
+ /* if we don't use expect:-100 AND
+ postsize is less than MAX_INITIAL_POST_SIZE
+
+ then append the post data to the HTTP request header. This limit
+ is no magic limit but only set to prevent really huge POSTs to
+ get the data duplicated with malloc() and family. */
+
+ result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+ if(result)
+ return result;
+
+ if(!conn->bits.upload_chunky) {
+ /* We're not sending it 'chunked', append it to the request
+ already now to reduce the number if send() calls */
+ result = add_buffer(req_buffer, data->set.postfields,
+ (size_t)postsize);
+ included_body = postsize;
+ }
+ else {
+ /* Append the POST data chunky-style */
+ result = add_bufferf(req_buffer, "%x\r\n", (int)postsize);
+ if(CURLE_OK == result)
+ result = add_buffer(req_buffer, data->set.postfields,
+ (size_t)postsize);
+ if(CURLE_OK == result)
+ result = add_buffer(req_buffer,
+ "\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
+ /* CR LF 0 CR LF CR LF */
+ included_body = postsize + 7;
+ }
+ if(result)
+ return result;
+ }
+ else {
+ /* A huge POST coming up, do data separate from the request */
+ http->postsize = postsize;
+ http->postdata = data->set.postfields;
+
+ http->sending = HTTPSEND_BODY;
+
+ conn->fread = (curl_read_callback)readmoredata;
+ conn->fread_in = (void *)conn;
+
+ /* set the upload size to the progress meter */
+ Curl_pgrsSetUploadSize(data, http->postsize);
+
+ add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+ }
+ }
+ else {
+ add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+
+ if(data->set.postfieldsize) {
+ /* set the upload size to the progress meter */
+ Curl_pgrsSetUploadSize(data, postsize?postsize:-1);
+
+ /* set the pointer to mark that we will send the post body using
+ the read callback */
+ http->postdata = (char *)&http->postdata;
+ }
+ }
+ /* issue the request */
+ result = add_buffer_send(req_buffer, conn, &data->info.request_size,
+ (size_t)included_body, FIRSTSOCKET);
+
+ if(result)
+ failf(data, "Failed sending HTTP POST request");
+ else
+ result =
+ Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
+ &http->readbytecount,
+ http->postdata?FIRSTSOCKET:-1,
+ http->postdata?&http->writebytecount:NULL);
+ break;
+
+ default:
+ add_buffer(req_buffer, "\r\n", 2);
+
+ /* issue the request */
+ result = add_buffer_send(req_buffer, conn,
+ &data->info.request_size, 0, FIRSTSOCKET);
+
+ if(result)
+ failf(data, "Failed sending HTTP request");
+ else
+ /* HTTP GET/HEAD download: */
+ result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
+ &http->readbytecount,
+ http->postdata?FIRSTSOCKET:-1,
+ http->postdata?&http->writebytecount:NULL);
+ }
+ if(result)
+ return result;
+ }
+
+ return CURLE_OK;
+}
+#endif
diff --git a/Utilities/cmcurl/http.h b/Utilities/cmcurl/http.h
new file mode 100644
index 000000000..0f4b58f72
--- /dev/null
+++ b/Utilities/cmcurl/http.h
@@ -0,0 +1,85 @@
+#ifndef __HTTP_H
+#define __HTTP_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#ifndef CURL_DISABLE_HTTP
+bool Curl_compareheader(char *headerline, /* line to check */
+ const char *header, /* header keyword _with_ colon */
+ const char *content); /* content string to find */
+
+/* ftp can use this as well */
+CURLcode Curl_proxyCONNECT(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, bool *done);
+CURLcode Curl_http_done(struct connectdata *, CURLcode, bool premature);
+CURLcode Curl_http_connect(struct connectdata *conn, bool *done);
+CURLcode Curl_https_connecting(struct connectdata *conn, bool *done);
+int Curl_https_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks);
+
+/* 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);
+
+/* These functions are in http.c */
+void Curl_http_auth_stage(struct SessionHandle *data, int stage);
+CURLcode Curl_http_input_auth(struct connectdata *conn,
+ int httpcode, char *header);
+CURLcode Curl_http_auth_act(struct connectdata *conn);
+
+int Curl_http_should_fail(struct connectdata *conn);
+
+/* If only the PICKNONE bit is set, there has been a round-trip and we
+ selected to use no auth at all. Ie, we actively select no auth, as opposed
+ to not having one selected. The other CURLAUTH_* defines are present in the
+ public curl/curl.h header. */
+#define CURLAUTH_PICKNONE (1<<30) /* don't use auth */
+
+/* MAX_INITIAL_POST_SIZE indicates the number of bytes that will make the POST
+ data get included in the initial data chunk sent to the server. If the
+ data is larger than this, it will automatically get split up in multiple
+ system calls.
+
+ This value used to be fairly big (100K), but we must take into account that
+ if the server rejects the POST due for authentication reasons, this data
+ will always be uncondtionally sent and thus it may not be larger than can
+ always be afforded to send twice.
+
+ It must not be greater than 64K to work on VMS.
+*/
+#ifndef MAX_INITIAL_POST_SIZE
+#define MAX_INITIAL_POST_SIZE (64*1024)
+#endif
+
+#ifndef TINY_INITIAL_POST_SIZE
+#define TINY_INITIAL_POST_SIZE 1024
+#endif
+
+#endif
+#endif
diff --git a/Utilities/cmcurl/http_chunks.c b/Utilities/cmcurl/http_chunks.c
new file mode 100644
index 000000000..1b03a5569
--- /dev/null
+++ b/Utilities/cmcurl/http_chunks.c
@@ -0,0 +1,360 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#include "setup.h"
+
+#ifndef CURL_DISABLE_HTTP
+/* -- 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 */
+
+#include "content_encoding.h"
+#include "http.h"
+#include "memory.h"
+#include "easyif.h" /* for Curl_convert_to_network prototype */
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * Chunk format (simplified):
+ *
+ * <HEX SIZE>[ chunk extension ] CRLF
+ * <DATA> CRLF
+ *
+ * Highlights from RFC2616 section 3.6 say:
+
+ The chunked encoding modifies the body of a message in order to
+ transfer it as a series of chunks, each with its own size indicator,
+ followed by an OPTIONAL trailer containing entity-header fields. This
+ allows dynamically produced content to be transferred along with the
+ information necessary for the recipient to verify that it has
+ received the full message.
+
+ Chunked-Body = *chunk
+ last-chunk
+ trailer
+ CRLF
+
+ chunk = chunk-size [ chunk-extension ] CRLF
+ chunk-data CRLF
+ chunk-size = 1*HEX
+ last-chunk = 1*("0") [ chunk-extension ] CRLF
+
+ chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token | quoted-string
+ chunk-data = chunk-size(OCTET)
+ trailer = *(entity-header CRLF)
+
+ The chunk-size field is a string of hex digits indicating the size of
+ the chunk. The chunked encoding is ended by any chunk whose size is
+ zero, followed by the trailer, which is terminated by an empty line.
+
+ */
+
+
+void Curl_httpchunk_init(struct connectdata *conn)
+{
+ struct Curl_chunker *chunk = &conn->data->reqdata.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.
+ *
+ * This function always uses ASCII hex values to accommodate non-ASCII hosts.
+ * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
+ */
+CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
+ char *datap,
+ ssize_t datalen,
+ ssize_t *wrotep)
+{
+ CURLcode result=CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ struct Curl_chunker *ch = &data->reqdata.proto.http->chunk;
+ struct Curl_transfer_keeper *k = &data->reqdata.keep;
+ size_t piece;
+ size_t length = (size_t)datalen;
+ size_t *wrote = (size_t *)wrotep;
+
+ *wrote = 0; /* nothing's written yet */
+
+ while(length) {
+ switch(ch->state) {
+ case CHUNK_HEX:
+ /* Check for an ASCII hex digit.
+ We avoid the use of isxdigit to accommodate non-ASCII hosts. */
+ if((*datap >= 0x30 && *datap <= 0x39) /* 0-9 */
+ || (*datap >= 0x41 && *datap <= 0x46) /* A-F */
+ || (*datap >= 0x61 && *datap <= 0x66)) { /* a-f */
+ 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;
+#ifdef CURL_DOES_CONVERSIONS
+ /* convert to host encoding before calling strtoul */
+ result = Curl_convert_from_network(conn->data,
+ ch->hexbuffer,
+ ch->hexindex);
+ if(result != CURLE_OK) {
+ /* Curl_convert_from_network calls failf if unsuccessful */
+ /* Treat it as a bad hex character */
+ return(CHUNKE_ILLEGAL_HEX);
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+ ch->datasize=strtoul(ch->hexbuffer, NULL, 16);
+ ch->state = CHUNK_POSTHEX;
+ }
+ break;
+
+ case CHUNK_POSTHEX:
+ /* In this state, we're waiting for CRLF to arrive. We support
+ this to allow so called chunk-extensions to show up here
+ before the CRLF comes. */
+ if(*datap == 0x0d)
+ ch->state = CHUNK_CR;
+ length--;
+ datap++;
+ break;
+
+ case CHUNK_CR:
+ /* waiting for the LF */
+ if(*datap == 0x0a) {
+ /* we're now expecting data to come, unless size was zero! */
+ if(0 == ch->datasize) {
+ if (conn->bits.trailerHdrPresent!=TRUE) {
+ /* No Trailer: header found - revert to original Curl processing */
+ ch->state = CHUNK_STOP;
+ if (1 == length) {
+ /* This is the final byte, return right now */
+ return CHUNKE_STOP;
+ }
+ }
+ else {
+ ch->state = CHUNK_TRAILER; /* attempt to read trailers */
+ conn->trlPos=0;
+ }
+ }
+ else
+ ch->state = CHUNK_DATA;
+ }
+ else
+ /* previously we got a fake CR, go back to CR waiting! */
+ ch->state = CHUNK_CR;
+ datap++;
+ length--;
+ break;
+
+ case CHUNK_DATA:
+ /* we get pure and fine data
+
+ We expect another 'datasize' of data. We have 'length' right now,
+ it can be more or less than 'datasize'. Get the smallest piece.
+ */
+ piece = (ch->datasize >= length)?length:ch->datasize;
+
+ /* Write the data portion available */
+#ifdef HAVE_LIBZ
+ switch (data->reqdata.keep.content_encoding) {
+ case IDENTITY:
+#endif
+ if(!k->ignorebody)
+ result = Curl_client_write(conn, CLIENTWRITE_BODY, datap,
+ piece);
+#ifdef HAVE_LIBZ
+ break;
+
+ case DEFLATE:
+ /* update data->reqdata.keep.str to point to the chunk data. */
+ data->reqdata.keep.str = datap;
+ result = Curl_unencode_deflate_write(conn, &data->reqdata.keep,
+ (ssize_t)piece);
+ break;
+
+ case GZIP:
+ /* update data->reqdata.keep.str to point to the chunk data. */
+ data->reqdata.keep.str = datap;
+ result = Curl_unencode_gzip_write(conn, &data->reqdata.keep,
+ (ssize_t)piece);
+ break;
+
+ case COMPRESS:
+ default:
+ failf (conn->data,
+ "Unrecognized content encoding type. "
+ "libcurl understands `identity', `deflate' and `gzip' "
+ "content encodings.");
+ return CHUNKE_BAD_ENCODING;
+ }
+#endif
+
+ if(result)
+ return CHUNKE_WRITE_ERROR;
+
+ *wrote += piece;
+
+ ch->datasize -= piece; /* decrease amount left to expect */
+ datap += piece; /* move read pointer forward */
+ length -= piece; /* decrease space left in this round */
+
+ if(0 == ch->datasize)
+ /* end of data this round, we now expect a trailing CRLF */
+ ch->state = CHUNK_POSTCR;
+ break;
+
+ case CHUNK_POSTCR:
+ if(*datap == 0x0d) {
+ ch->state = CHUNK_POSTLF;
+ datap++;
+ length--;
+ }
+ else
+ return CHUNKE_BAD_CHUNK;
+ break;
+
+ case CHUNK_POSTLF:
+ if(*datap == 0x0a) {
+ /*
+ * The last one before we go back to hex state and start all
+ * over.
+ */
+ Curl_httpchunk_init(conn);
+ datap++;
+ length--;
+ }
+ else
+ return CHUNKE_BAD_CHUNK;
+ break;
+
+ case CHUNK_TRAILER:
+ /* conn->trailer is assumed to be freed in url.c on a
+ connection basis */
+ if (conn->trlPos >= conn->trlMax) {
+ char *ptr;
+ if(conn->trlMax) {
+ conn->trlMax *= 2;
+ ptr = (char*)realloc(conn->trailer,conn->trlMax);
+ }
+ else {
+ conn->trlMax=128;
+ ptr = (char*)malloc(conn->trlMax);
+ }
+ if(!ptr)
+ return CHUNKE_OUT_OF_MEMORY;
+ conn->trailer = ptr;
+ }
+ conn->trailer[conn->trlPos++]=*datap;
+
+ if(*datap == 0x0d)
+ ch->state = CHUNK_TRAILER_CR;
+ else {
+ datap++;
+ length--;
+ }
+ break;
+
+ case CHUNK_TRAILER_CR:
+ if(*datap == 0x0d) {
+ ch->state = CHUNK_TRAILER_POSTCR;
+ datap++;
+ length--;
+ }
+ else
+ return CHUNKE_BAD_CHUNK;
+ break;
+
+ case CHUNK_TRAILER_POSTCR:
+ if (*datap == 0x0a) {
+ conn->trailer[conn->trlPos++]=0x0a;
+ conn->trailer[conn->trlPos]=0;
+ if (conn->trlPos==2) {
+ ch->state = CHUNK_STOP;
+ return CHUNKE_STOP;
+ }
+ else {
+#ifdef CURL_DOES_CONVERSIONS
+ /* Convert to host encoding before calling Curl_client_write */
+ result = Curl_convert_from_network(conn->data,
+ conn->trailer,
+ conn->trlPos);
+ if(result != CURLE_OK) {
+ /* Curl_convert_from_network calls failf if unsuccessful */
+ /* Treat it as a bad chunk */
+ return(CHUNKE_BAD_CHUNK);
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+ Curl_client_write(conn, CLIENTWRITE_HEADER,
+ conn->trailer, conn->trlPos);
+ }
+ ch->state = CHUNK_TRAILER;
+ conn->trlPos=0;
+ 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;
+}
+#endif /* CURL_DISABLE_HTTP */
diff --git a/Utilities/cmcurl/http_chunks.h b/Utilities/cmcurl/http_chunks.h
new file mode 100644
index 000000000..211818ab7
--- /dev/null
+++ b/Utilities/cmcurl/http_chunks.h
@@ -0,0 +1,104 @@
+#ifndef __HTTP_CHUNKS_H
+#define __HTTP_CHUNKS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $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,
+
+ /* At this point optional trailer headers can be found, unless the next line
+ is CRLF */
+ CHUNK_TRAILER,
+
+ /* A trailer CR has been found - next state is CHUNK_TRAILER_POSTCR.
+ Next char must be a LF */
+ CHUNK_TRAILER_CR,
+
+ /* A trailer LF must be found now, otherwise CHUNKE_BAD_CHUNK will be
+ signalled If this is an empty trailer CHUNKE_STOP will be signalled.
+ Otherwise the trailer will be broadcasted via Curl_client_write() and the
+ next state will be CHUNK_TRAILER */
+ CHUNK_TRAILER_POSTCR,
+
+ 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_BAD_ENCODING,
+ CHUNKE_OUT_OF_MEMORY,
+ 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
diff --git a/Utilities/cmcurl/http_digest.c b/Utilities/cmcurl/http_digest.c
new file mode 100644
index 000000000..c223784f9
--- /dev/null
+++ b/Utilities/cmcurl/http_digest.c
@@ -0,0 +1,504 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#include "setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "urldata.h"
+#include "sendf.h"
+#include "strequal.h"
+#include "base64.h"
+#include "md5.h"
+#include "http_digest.h"
+#include "strtok.h"
+#include "url.h" /* for Curl_safefree() */
+#include "memory.h"
+#include "easyif.h" /* included for Curl_convert_... prototypes */
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* Test example headers:
+
+WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
+Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
+
+*/
+
+CURLdigest Curl_input_digest(struct connectdata *conn,
+ bool proxy,
+ char *header) /* rest of the *-authenticate:
+ header */
+{
+ bool more = TRUE;
+ char *token = NULL;
+ char *tmp = NULL;
+ bool foundAuth = FALSE;
+ bool foundAuthInt = FALSE;
+ struct SessionHandle *data=conn->data;
+ bool before = FALSE; /* got a nonce before */
+ struct digestdata *d;
+
+ if(proxy) {
+ d = &data->state.proxydigest;
+ }
+ else {
+ d = &data->state.digest;
+ }
+
+ /* skip initial whitespaces */
+ while(*header && ISSPACE(*header))
+ header++;
+
+ if(checkprefix("Digest", header)) {
+ header += strlen("Digest");
+
+ /* If we already have received a nonce, keep that in mind */
+ if(d->nonce)
+ before = TRUE;
+
+ /* clear off any former leftovers and init to defaults */
+ Curl_digest_cleanup_one(d);
+
+ while(more) {
+ char value[32];
+ char content[128];
+ size_t totlen=0;
+
+ while(*header && ISSPACE(*header))
+ header++;
+
+ /* how big can these strings be? */
+ if((2 == sscanf(header, "%31[^=]=\"%127[^\"]\"",
+ value, content)) ||
+ /* try the same scan but without quotes around the content but don't
+ include the possibly trailing comma */
+ (2 == sscanf(header, "%31[^=]=%127[^,]",
+ value, content)) ) {
+ if(strequal(value, "nonce")) {
+ d->nonce = strdup(content);
+ if(!d->nonce)
+ return CURLDIGEST_NOMEM;
+ }
+ else if(strequal(value, "stale")) {
+ if(strequal(content, "true")) {
+ d->stale = TRUE;
+ d->nc = 1; /* we make a new nonce now */
+ }
+ }
+ else if(strequal(value, "realm")) {
+ d->realm = strdup(content);
+ if(!d->realm)
+ return CURLDIGEST_NOMEM;
+ }
+ else if(strequal(value, "opaque")) {
+ d->opaque = strdup(content);
+ if(!d->opaque)
+ return CURLDIGEST_NOMEM;
+ }
+ else if(strequal(value, "qop")) {
+ char *tok_buf;
+ /* tokenize the list and choose auth if possible, use a temporary
+ clone of the buffer since strtok_r() ruins it */
+ tmp = strdup(content);
+ if(!tmp)
+ return CURLDIGEST_NOMEM;
+ token = strtok_r(tmp, ",", &tok_buf);
+ while (token != NULL) {
+ if (strequal(token, "auth")) {
+ foundAuth = TRUE;
+ }
+ else if (strequal(token, "auth-int")) {
+ foundAuthInt = TRUE;
+ }
+ token = strtok_r(NULL, ",", &tok_buf);
+ }
+ free(tmp);
+ /*select only auth o auth-int. Otherwise, ignore*/
+ if (foundAuth) {
+ d->qop = strdup("auth");
+ if(!d->qop)
+ return CURLDIGEST_NOMEM;
+ }
+ else if (foundAuthInt) {
+ d->qop = strdup("auth-int");
+ if(!d->qop)
+ return CURLDIGEST_NOMEM;
+ }
+ }
+ else if(strequal(value, "algorithm")) {
+ d->algorithm = strdup(content);
+ if(!d->algorithm)
+ return CURLDIGEST_NOMEM;
+ if(strequal(content, "MD5-sess"))
+ d->algo = CURLDIGESTALGO_MD5SESS;
+ else if(strequal(content, "MD5"))
+ d->algo = CURLDIGESTALGO_MD5;
+ else
+ return CURLDIGEST_BADALGO;
+ }
+ else {
+ /* unknown specifier, ignore it! */
+ }
+ totlen = strlen(value)+strlen(content)+1;
+
+ if(header[strlen(value)+1] == '\"')
+ /* the contents were within quotes, then add 2 for them to the
+ length */
+ totlen += 2;
+ }
+ else
+ break; /* we're done here */
+
+ header += totlen;
+ if(',' == *header)
+ /* allow the list to be comma-separated */
+ header++;
+ }
+ /* We had a nonce since before, and we got another one now without
+ 'stale=true'. This means we provided bad credentials in the previous
+ request */
+ if(before && !d->stale)
+ return CURLDIGEST_BAD;
+
+ /* We got this header without a nonce, that's a bad Digest line! */
+ if(!d->nonce)
+ return CURLDIGEST_BAD;
+ }
+ else
+ /* else not a digest, get out */
+ return CURLDIGEST_NONE;
+
+ return CURLDIGEST_FINE;
+}
+
+/* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
+static void md5_to_ascii(unsigned char *source, /* 16 bytes */
+ unsigned char *dest) /* 33 bytes */
+{
+ int i;
+ for(i=0; i<16; i++)
+ snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
+}
+
+CURLcode Curl_output_digest(struct connectdata *conn,
+ bool proxy,
+ unsigned char *request,
+ unsigned char *uripath)
+{
+ /* We have a Digest setup for this, use it! Now, to get all the details for
+ this sorted out, I must urge you dear friend to read up on the RFC2617
+ section 3.2.2, */
+ unsigned char md5buf[16]; /* 16 bytes/128 bits */
+ unsigned char request_digest[33];
+ unsigned char *md5this;
+ unsigned char *ha1;
+ unsigned char ha2[33];/* 32 digits and 1 zero byte */
+ char cnoncebuf[7];
+ char *cnonce;
+ char *tmp = NULL;
+ struct timeval now;
+
+ char **allocuserpwd;
+ char *userp;
+ char *passwdp;
+ struct auth *authp;
+
+ struct SessionHandle *data = conn->data;
+ struct digestdata *d;
+#ifdef CURL_DOES_CONVERSIONS
+ CURLcode rc;
+/* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
+ It converts digest text to ASCII so the MD5 will be correct for
+ what ultimately goes over the network.
+*/
+#define CURL_OUTPUT_DIGEST_CONV(a, b) \
+ rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
+ if (rc != CURLE_OK) { \
+ free(b); \
+ return rc; \
+ }
+#else
+#define CURL_OUTPUT_DIGEST_CONV(a, b)
+#endif /* CURL_DOES_CONVERSIONS */
+
+ if(proxy) {
+ d = &data->state.proxydigest;
+ allocuserpwd = &conn->allocptr.proxyuserpwd;
+ userp = conn->proxyuser;
+ passwdp = conn->proxypasswd;
+ authp = &data->state.authproxy;
+ }
+ else {
+ d = &data->state.digest;
+ allocuserpwd = &conn->allocptr.userpwd;
+ userp = conn->user;
+ passwdp = conn->passwd;
+ authp = &data->state.authhost;
+ }
+
+ /* not set means empty */
+ if(!userp)
+ userp=(char *)"";
+
+ if(!passwdp)
+ passwdp=(char *)"";
+
+ if(!d->nonce) {
+ authp->done = FALSE;
+ return CURLE_OK;
+ }
+ authp->done = TRUE;
+
+ if(!d->nc)
+ d->nc = 1;
+
+ if(!d->cnonce) {
+ /* Generate a cnonce */
+ now = Curl_tvnow();
+ snprintf(cnoncebuf, sizeof(cnoncebuf), "%06ld", now.tv_sec);
+ if(Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce))
+ d->cnonce = cnonce;
+ else
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /*
+ if the algorithm is "MD5" or unspecified (which then defaults to MD5):
+
+ A1 = unq(username-value) ":" unq(realm-value) ":" passwd
+
+ if the algorithm is "MD5-sess" then:
+
+ A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
+ ":" unq(nonce-value) ":" unq(cnonce-value)
+ */
+
+ md5this = (unsigned char *)
+ aprintf("%s:%s:%s", userp, d->realm, passwdp);
+ if(!md5this)
+ return CURLE_OUT_OF_MEMORY;
+
+ CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
+ Curl_md5it(md5buf, md5this);
+ free(md5this); /* free this again */
+
+ ha1 = (unsigned char *)malloc(33); /* 32 digits and 1 zero byte */
+ if(!ha1)
+ return CURLE_OUT_OF_MEMORY;
+
+ md5_to_ascii(md5buf, ha1);
+
+ if(d->algo == CURLDIGESTALGO_MD5SESS) {
+ /* nonce and cnonce are OUTSIDE the hash */
+ tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+ CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
+ Curl_md5it(md5buf, (unsigned char *)tmp);
+ free(tmp); /* free this again */
+ md5_to_ascii(md5buf, ha1);
+ }
+
+ /*
+ If the "qop" directive's value is "auth" or is unspecified, then A2 is:
+
+ A2 = Method ":" digest-uri-value
+
+ If the "qop" value is "auth-int", then A2 is:
+
+ A2 = Method ":" digest-uri-value ":" H(entity-body)
+
+ (The "Method" value is the HTTP request method as specified in section
+ 5.1.1 of RFC 2616)
+ */
+
+ md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
+ if(!md5this) {
+ free(ha1);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if (d->qop && strequal(d->qop, "auth-int")) {
+ /* We don't support auth-int at the moment. I can't see a easy way to get
+ entity-body here */
+ /* TODO: Append H(entity-body)*/
+ }
+ CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
+ Curl_md5it(md5buf, md5this);
+ free(md5this); /* free this again */
+ md5_to_ascii(md5buf, ha2);
+
+ if (d->qop) {
+ md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
+ ha1,
+ d->nonce,
+ d->nc,
+ d->cnonce,
+ d->qop,
+ ha2);
+ }
+ else {
+ md5this = (unsigned char *)aprintf("%s:%s:%s",
+ ha1,
+ d->nonce,
+ ha2);
+ }
+ free(ha1);
+ if(!md5this)
+ return CURLE_OUT_OF_MEMORY;
+
+ CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
+ Curl_md5it(md5buf, md5this);
+ free(md5this); /* free this again */
+ md5_to_ascii(md5buf, request_digest);
+
+ /* for test case 64 (snooped from a Mozilla 1.3a request)
+
+ Authorization: Digest username="testuser", realm="testrealm", \
+ nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
+ */
+
+ Curl_safefree(*allocuserpwd);
+
+ if (d->qop) {
+ *allocuserpwd =
+ aprintf( "%sAuthorization: Digest "
+ "username=\"%s\", "
+ "realm=\"%s\", "
+ "nonce=\"%s\", "
+ "uri=\"%s\", "
+ "cnonce=\"%s\", "
+ "nc=%08x, "
+ "qop=\"%s\", "
+ "response=\"%s\"",
+ proxy?"Proxy-":"",
+ userp,
+ d->realm,
+ d->nonce,
+ uripath, /* this is the PATH part of the URL */
+ d->cnonce,
+ d->nc,
+ d->qop,
+ request_digest);
+
+ if(strequal(d->qop, "auth"))
+ d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
+ which tells to the server how many times you are using the
+ same nonce in the qop=auth mode. */
+ }
+ else {
+ *allocuserpwd =
+ aprintf( "%sAuthorization: Digest "
+ "username=\"%s\", "
+ "realm=\"%s\", "
+ "nonce=\"%s\", "
+ "uri=\"%s\", "
+ "response=\"%s\"",
+ proxy?"Proxy-":"",
+ userp,
+ d->realm,
+ d->nonce,
+ uripath, /* this is the PATH part of the URL */
+ request_digest);
+ }
+ if(!*allocuserpwd)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Add optional fields */
+ if(d->opaque) {
+ /* append opaque */
+ tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+ free(*allocuserpwd);
+ *allocuserpwd = tmp;
+ }
+
+ if(d->algorithm) {
+ /* append algorithm */
+ tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+ free(*allocuserpwd);
+ *allocuserpwd = tmp;
+ }
+
+ /* append CRLF to the userpwd header */
+ tmp = (char*) realloc(*allocuserpwd, strlen(*allocuserpwd) + 3 + 1);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+ strcat(tmp, "\r\n");
+ *allocuserpwd = tmp;
+
+ return CURLE_OK;
+}
+
+void Curl_digest_cleanup_one(struct digestdata *d)
+{
+ if(d->nonce)
+ free(d->nonce);
+ d->nonce = NULL;
+
+ if(d->cnonce)
+ free(d->cnonce);
+ d->cnonce = NULL;
+
+ if(d->realm)
+ free(d->realm);
+ d->realm = NULL;
+
+ if(d->opaque)
+ free(d->opaque);
+ d->opaque = NULL;
+
+ if(d->qop)
+ free(d->qop);
+ d->qop = NULL;
+
+ if(d->algorithm)
+ free(d->algorithm);
+ d->algorithm = NULL;
+
+ d->nc = 0;
+ d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
+ d->stale = FALSE; /* default means normal, not stale */
+}
+
+
+void Curl_digest_cleanup(struct SessionHandle *data)
+{
+ Curl_digest_cleanup_one(&data->state.digest);
+ Curl_digest_cleanup_one(&data->state.proxydigest);
+}
+
+#endif
diff --git a/Utilities/cmcurl/http_digest.h b/Utilities/cmcurl/http_digest.h
new file mode 100644
index 000000000..6cf025975
--- /dev/null
+++ b/Utilities/cmcurl/http_digest.h
@@ -0,0 +1,58 @@
+#ifndef __HTTP_DIGEST_H
+#define __HTTP_DIGEST_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+typedef enum {
+ CURLDIGEST_NONE, /* not a digest */
+ CURLDIGEST_BAD, /* a digest, but one we don't like */
+ CURLDIGEST_BADALGO, /* unsupported algorithm requested */
+ CURLDIGEST_NOMEM,
+ CURLDIGEST_FINE, /* a digest we act on */
+
+ CURLDIGEST_LAST /* last entry in this enum, don't use */
+} CURLdigest;
+
+enum {
+ CURLDIGESTALGO_MD5,
+ CURLDIGESTALGO_MD5SESS
+};
+
+/* this is for digest header input */
+CURLdigest Curl_input_digest(struct connectdata *conn,
+ bool proxy, char *header);
+
+/* this is for creating digest header output */
+CURLcode Curl_output_digest(struct connectdata *conn,
+ bool proxy,
+ unsigned char *request,
+ unsigned char *uripath);
+void Curl_digest_cleanup_one(struct digestdata *dig);
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+void Curl_digest_cleanup(struct SessionHandle *data);
+#else
+#define Curl_digest_cleanup(x) do {} while(0)
+#endif
+
+#endif
diff --git a/Utilities/cmcurl/http_negotiate.c b/Utilities/cmcurl/http_negotiate.c
new file mode 100644
index 000000000..bdfeefa0a
--- /dev/null
+++ b/Utilities/cmcurl/http_negotiate.c
@@ -0,0 +1,327 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#include "setup.h"
+
+#ifdef HAVE_GSSAPI
+#ifdef HAVE_GSSMIT
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+ /* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "urldata.h"
+#include "sendf.h"
+#include "strequal.h"
+#include "base64.h"
+#include "http_negotiate.h"
+#include "memory.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+static int
+get_gss_name(struct connectdata *conn, gss_name_t *server)
+{
+ struct negotiatedata *neg_ctx = &conn->data->state.negotiate;
+ OM_uint32 major_status, minor_status;
+ gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
+ char name[2048];
+ const char* service;
+
+ /* GSSAPI implementation by Globus (known as GSI) requires the name to be
+ of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead
+ of at-sign). Also GSI servers are often identified as 'host' not 'khttp'.
+ Change following lines if you want to use GSI */
+
+ /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name */
+
+ if (neg_ctx->gss)
+ service = "KHTTP";
+ else
+ service = "HTTP";
+
+ token.length = strlen(service) + 1 + strlen(conn->host.name) + 1;
+ if (token.length + 1 > sizeof(name))
+ return EMSGSIZE;
+
+ snprintf(name, sizeof(name), "%s@%s", service, conn->host.name);
+
+ token.value = (void *) name;
+ major_status = gss_import_name(&minor_status,
+ &token,
+ GSS_C_NT_HOSTBASED_SERVICE,
+ server);
+
+ return GSS_ERROR(major_status) ? -1 : 0;
+}
+
+static void
+log_gss_error(struct connectdata *conn, OM_uint32 error_status, char *prefix)
+{
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 msg_ctx = 0;
+ gss_buffer_desc status_string;
+ char buf[1024];
+ size_t len;
+
+ snprintf(buf, sizeof(buf), "%s", prefix);
+ len = strlen(buf);
+ do {
+ maj_stat = gss_display_status (&min_stat,
+ error_status,
+ GSS_C_MECH_CODE,
+ GSS_C_NO_OID,
+ &msg_ctx,
+ &status_string);
+ if (sizeof(buf) > len + status_string.length + 1) {
+ snprintf(buf + len, sizeof(buf) - len,
+ ": %s", (char*) status_string.value);
+ len += status_string.length;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
+
+ infof(conn->data, "%s", buf);
+}
+
+int Curl_input_negotiate(struct connectdata *conn, char *header)
+{
+ struct negotiatedata *neg_ctx = &conn->data->state.negotiate;
+ OM_uint32 major_status, minor_status, minor_status2;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ int ret;
+ size_t len;
+ bool gss;
+ const char* protocol;
+
+ while(*header && ISSPACE(*header))
+ header++;
+ if(checkprefix("GSS-Negotiate", header)) {
+ protocol = "GSS-Negotiate";
+ gss = TRUE;
+ }
+ else if (checkprefix("Negotiate", header)) {
+ protocol = "Negotiate";
+ gss = FALSE;
+ }
+ else
+ return -1;
+
+ if (neg_ctx->context) {
+ if (neg_ctx->gss != gss) {
+ return -1;
+ }
+ }
+ else {
+ neg_ctx->protocol = protocol;
+ neg_ctx->gss = gss;
+ }
+
+ if (neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) {
+ /* We finished succesfully our part of authentication, but server
+ * rejected it (since we're again here). Exit with an error since we
+ * can't invent anything better */
+ Curl_cleanup_negotiate(conn->data);
+ return -1;
+ }
+
+ if (neg_ctx->server_name == NULL &&
+ (ret = get_gss_name(conn, &neg_ctx->server_name)))
+ return ret;
+
+ header += strlen(neg_ctx->protocol);
+ while(*header && ISSPACE(*header))
+ header++;
+
+ len = strlen(header);
+ if (len > 0) {
+ int rawlen = Curl_base64_decode(header, (unsigned char **)&input_token.value);
+ if (rawlen < 0)
+ return -1;
+ input_token.length = rawlen;
+
+#ifdef HAVE_SPNEGO /* Handle SPNEGO */
+ if (checkprefix("Negotiate", header)) {
+ ASN1_OBJECT * object = NULL;
+ int rc = 1;
+ unsigned char * spnegoToken = NULL;
+ size_t spnegoTokenLength = 0;
+ unsigned char * mechToken = NULL;
+ size_t mechTokenLength = 0;
+
+ spnegoToken = malloc(input_token.length);
+ if (input_token.value == NULL)
+ return ENOMEM;
+ spnegoTokenLength = input_token.length;
+
+ object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
+ if (!parseSpnegoTargetToken(spnegoToken,
+ spnegoTokenLength,
+ NULL,
+ NULL,
+ &mechToken,
+ &mechTokenLength,
+ NULL,
+ NULL)) {
+ free(spnegoToken);
+ spnegoToken = NULL;
+ infof(conn->data, "Parse SPNEGO Target Token failed\n");
+ }
+ else {
+ free(input_token.value);
+ input_token.value = NULL;
+ input_token.value = malloc(mechTokenLength);
+ memcpy(input_token.value, mechToken,mechTokenLength);
+ input_token.length = mechTokenLength;
+ free(mechToken);
+ mechToken = NULL;
+ infof(conn->data, "Parse SPNEGO Target Token succeeded\n");
+ }
+ }
+#endif
+ }
+
+ major_status = gss_init_sec_context(&minor_status,
+ GSS_C_NO_CREDENTIAL,
+ &neg_ctx->context,
+ neg_ctx->server_name,
+ GSS_C_NO_OID,
+ GSS_C_DELEG_FLAG,
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &input_token,
+ NULL,
+ &output_token,
+ NULL,
+ NULL);
+ if (input_token.length > 0)
+ gss_release_buffer(&minor_status2, &input_token);
+ neg_ctx->status = major_status;
+ if (GSS_ERROR(major_status)) {
+ /* Curl_cleanup_negotiate(conn->data) ??? */
+ log_gss_error(conn, minor_status,
+ (char *)"gss_init_sec_context() failed: ");
+ return -1;
+ }
+
+ if (output_token.length == 0) {
+ return -1;
+ }
+
+ neg_ctx->output_token = output_token;
+ /* conn->bits.close = FALSE; */
+
+ return 0;
+}
+
+
+CURLcode Curl_output_negotiate(struct connectdata *conn)
+{
+ struct negotiatedata *neg_ctx = &conn->data->state.negotiate;
+ OM_uint32 minor_status;
+ char *encoded = NULL;
+ int len;
+
+#ifdef HAVE_SPNEGO /* Handle SPNEGO */
+ if (checkprefix("Negotiate",neg_ctx->protocol)) {
+ ASN1_OBJECT * object = NULL;
+ int rc = 1;
+ unsigned char * spnegoToken = NULL;
+ size_t spnegoTokenLength = 0;
+ unsigned char * responseToken = NULL;
+ size_t responseTokenLength = 0;
+
+ responseToken = malloc(neg_ctx->output_token.length);
+ if ( responseToken == NULL)
+ return CURLE_OUT_OF_MEMORY;
+ memcpy(responseToken, neg_ctx->output_token.value,
+ neg_ctx->output_token.length);
+ responseTokenLength = neg_ctx->output_token.length;
+
+ object=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
+ if (!makeSpnegoInitialToken (object,
+ responseToken,
+ responseTokenLength,
+ &spnegoToken,
+ &spnegoTokenLength)) {
+ free(responseToken);
+ responseToken = NULL;
+ infof(conn->data, "Make SPNEGO Initial Token failed\n");
+ }
+ else {
+ free(neg_ctx->output_token.value);
+ responseToken = NULL;
+ neg_ctx->output_token.value = malloc(spnegoTokenLength);
+ memcpy(neg_ctx->output_token.value, spnegoToken,spnegoTokenLength);
+ neg_ctx->output_token.length = spnegoTokenLength;
+ free(spnegoToken);
+ spnegoToken = NULL;
+ infof(conn->data, "Make SPNEGO Initial Token succeeded\n");
+ }
+ }
+#endif
+ len = Curl_base64_encode(conn->data,
+ neg_ctx->output_token.value,
+ neg_ctx->output_token.length,
+ &encoded);
+
+ if (len < 0)
+ return CURLE_OUT_OF_MEMORY;
+
+ conn->allocptr.userpwd =
+ aprintf("Authorization: %s %s\r\n", neg_ctx->protocol, encoded);
+ free(encoded);
+ gss_release_buffer(&minor_status, &neg_ctx->output_token);
+ return (conn->allocptr.userpwd == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
+}
+
+void Curl_cleanup_negotiate(struct SessionHandle *data)
+{
+ OM_uint32 minor_status;
+ struct negotiatedata *neg_ctx = &data->state.negotiate;
+
+ if (neg_ctx->context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&minor_status, &neg_ctx->context, GSS_C_NO_BUFFER);
+
+ if (neg_ctx->output_token.length != 0)
+ gss_release_buffer(&minor_status, &neg_ctx->output_token);
+
+ if (neg_ctx->server_name != GSS_C_NO_NAME)
+ gss_release_name(&minor_status, &neg_ctx->server_name);
+
+ memset(neg_ctx, 0, sizeof(*neg_ctx));
+}
+
+
+#endif
+#endif
diff --git a/Utilities/cmcurl/http_negotiate.h b/Utilities/cmcurl/http_negotiate.h
new file mode 100644
index 000000000..ce0d083f9
--- /dev/null
+++ b/Utilities/cmcurl/http_negotiate.h
@@ -0,0 +1,39 @@
+#ifndef __HTTP_NEGOTIATE_H
+#define __HTTP_NEGOTIATE_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#ifdef HAVE_GSSAPI
+
+/* this is for Negotiate header input */
+int Curl_input_negotiate(struct connectdata *conn, char *header);
+
+/* this is for creating Negotiate header output */
+CURLcode Curl_output_negotiate(struct connectdata *conn);
+
+void Curl_cleanup_negotiate(struct SessionHandle *data);
+
+#endif
+
+#endif
diff --git a/Utilities/cmcurl/http_ntlm.c b/Utilities/cmcurl/http_ntlm.c
new file mode 100644
index 000000000..aff1bb1b6
--- /dev/null
+++ b/Utilities/cmcurl/http_ntlm.c
@@ -0,0 +1,1111 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#include "setup.h"
+
+/* NTLM details:
+
+ http://davenport.sourceforge.net/ntlm.html
+ http://www.innovation.ch/java/ntlm.html
+
+ Another implementation:
+ http://lxr.mozilla.org/mozilla/source/security/manager/ssl/src/nsNTLMAuthModule.cpp
+
+*/
+
+#ifndef CURL_DISABLE_HTTP
+#ifdef USE_NTLM
+
+#define DEBUG_ME 0
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "urldata.h"
+#include "easyif.h" /* for Curl_convert_... prototypes */
+#include "sendf.h"
+#include "strequal.h"
+#include "base64.h"
+#include "http_ntlm.h"
+#include "url.h"
+#include "memory.h"
+#include "ssluse.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* "NTLMSSP" signature is always in ASCII regardless of the platform */
+#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
+
+#ifndef USE_WINDOWS_SSPI
+
+#include <openssl/des.h>
+#include <openssl/md4.h>
+#include <openssl/md5.h>
+#include <openssl/ssl.h>
+#include <openssl/rand.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x00907001L
+#define DES_key_schedule des_key_schedule
+#define DES_cblock des_cblock
+#define DES_set_odd_parity des_set_odd_parity
+#define DES_set_key des_set_key
+#define DES_ecb_encrypt des_ecb_encrypt
+
+/* This is how things were done in the old days */
+#define DESKEY(x) x
+#define DESKEYARG(x) x
+#else
+/* Modern version */
+#define DESKEYARG(x) *x
+#define DESKEY(x) &x
+#endif
+
+#else
+
+#include <rpc.h>
+
+/* Handle of security.dll or secur32.dll, depending on Windows version */
+static HMODULE s_hSecDll = NULL;
+/* Pointer to SSPI dispatch table */
+static PSecurityFunctionTable s_pSecFn = NULL;
+
+#endif
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* Define this to make the type-3 message include the NT response message */
+#define USE_NTRESPONSES 1
+
+/* Define this to make the type-3 message include the NTLM2Session response
+ message, requires USE_NTRESPONSES. */
+#define USE_NTLM2SESSION 1
+
+#ifndef USE_WINDOWS_SSPI
+/* this function converts from the little endian format used in the incoming
+ package to whatever endian format we're using natively */
+static unsigned int readint_le(unsigned char *buf) /* must point to a
+ 4 bytes buffer*/
+{
+ return ((unsigned int)buf[0]) | ((unsigned int)buf[1] << 8) |
+ ((unsigned int)buf[2] << 16) | ((unsigned int)buf[3] << 24);
+}
+#endif
+
+#if DEBUG_ME
+# define DEBUG_OUT(x) x
+static void print_flags(FILE *handle, unsigned long flags)
+{
+ if(flags & NTLMFLAG_NEGOTIATE_UNICODE)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE ");
+ if(flags & NTLMFLAG_NEGOTIATE_OEM)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_OEM ");
+ if(flags & NTLMFLAG_REQUEST_TARGET)
+ fprintf(handle, "NTLMFLAG_REQUEST_TARGET ");
+ if(flags & (1<<3))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_3 ");
+ if(flags & NTLMFLAG_NEGOTIATE_SIGN)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN ");
+ if(flags & NTLMFLAG_NEGOTIATE_SEAL)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL ");
+ if(flags & NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE ");
+ if(flags & NTLMFLAG_NEGOTIATE_LM_KEY)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY ");
+ if(flags & NTLMFLAG_NEGOTIATE_NETWARE)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_NETWARE ");
+ if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY ");
+ if(flags & (1<<10))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_10 ");
+ if(flags & (1<<11))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_11 ");
+ if(flags & NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED ");
+ if(flags & NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED ");
+ if(flags & NTLMFLAG_NEGOTIATE_LOCAL_CALL)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL ");
+ if(flags & NTLMFLAG_NEGOTIATE_ALWAYS_SIGN)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN ");
+ if(flags & NTLMFLAG_TARGET_TYPE_DOMAIN)
+ fprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN ");
+ if(flags & NTLMFLAG_TARGET_TYPE_SERVER)
+ fprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER ");
+ if(flags & NTLMFLAG_TARGET_TYPE_SHARE)
+ fprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE ");
+ if(flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY ");
+ if(flags & NTLMFLAG_REQUEST_INIT_RESPONSE)
+ fprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE ");
+ if(flags & NTLMFLAG_REQUEST_ACCEPT_RESPONSE)
+ fprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE ");
+ if(flags & NTLMFLAG_REQUEST_NONNT_SESSION_KEY)
+ fprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY ");
+ if(flags & NTLMFLAG_NEGOTIATE_TARGET_INFO)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO ");
+ if(flags & (1<<24))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_24 ");
+ if(flags & (1<<25))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_25 ");
+ if(flags & (1<<26))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_26 ");
+ if(flags & (1<<27))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_27 ");
+ if(flags & (1<<28))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_28 ");
+ if(flags & NTLMFLAG_NEGOTIATE_128)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_128 ");
+ if(flags & NTLMFLAG_NEGOTIATE_KEY_EXCHANGE)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE ");
+ if(flags & NTLMFLAG_NEGOTIATE_56)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_56 ");
+}
+
+static void print_hex(FILE *handle, const char *buf, size_t len)
+{
+ const char *p = buf;
+ fprintf(stderr, "0x");
+ while (len-- > 0)
+ fprintf(stderr, "%02.2x", (unsigned int)*p++);
+}
+#else
+# define DEBUG_OUT(x)
+#endif
+
+/*
+ (*) = A "security buffer" is a triplet consisting of two shorts and one
+ long:
+
+ 1. a 'short' containing the length of the buffer in bytes
+ 2. a 'short' containing the allocated space for the buffer in bytes
+ 3. a 'long' containing the offset to the start of the buffer from the
+ beginning of the NTLM message, in bytes.
+*/
+
+
+CURLntlm Curl_input_ntlm(struct connectdata *conn,
+ bool proxy, /* if proxy or not */
+ char *header) /* rest of the www-authenticate:
+ header */
+{
+ /* point to the correct struct with this */
+ struct ntlmdata *ntlm;
+#ifndef USE_WINDOWS_SSPI
+ static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 };
+#endif
+
+ ntlm = proxy?&conn->proxyntlm:&conn->ntlm;
+
+ /* skip initial whitespaces */
+ while(*header && ISSPACE(*header))
+ header++;
+
+ if(checkprefix("NTLM", header)) {
+ header += strlen("NTLM");
+
+ while(*header && ISSPACE(*header))
+ header++;
+
+ if(*header) {
+ /* We got a type-2 message here:
+
+ Index Description Content
+ 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
+ (0x4e544c4d53535000)
+ 8 NTLM Message Type long (0x02000000)
+ 12 Target Name security buffer(*)
+ 20 Flags long
+ 24 Challenge 8 bytes
+ (32) Context (optional) 8 bytes (two consecutive longs)
+ (40) Target Information (optional) security buffer(*)
+ 32 (48) start of data block
+ */
+ size_t size;
+ unsigned char *buffer;
+ size = Curl_base64_decode(header, &buffer);
+ if(!buffer)
+ return CURLNTLM_BAD;
+
+ ntlm->state = NTLMSTATE_TYPE2; /* we got a type-2 */
+
+#ifdef USE_WINDOWS_SSPI
+ ntlm->type_2 = malloc(size+1);
+ if (ntlm->type_2 == NULL) {
+ free(buffer);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ ntlm->n_type_2 = size;
+ memcpy(ntlm->type_2, buffer, size);
+#else
+ ntlm->flags = 0;
+
+ if((size < 32) ||
+ (memcmp(buffer, NTLMSSP_SIGNATURE, 8) != 0) ||
+ (memcmp(buffer+8, type2_marker, sizeof(type2_marker)) != 0)) {
+ /* This was not a good enough type-2 message */
+ free(buffer);
+ return CURLNTLM_BAD;
+ }
+
+ ntlm->flags = readint_le(&buffer[20]);
+ memcpy(ntlm->nonce, &buffer[24], 8);
+
+ DEBUG_OUT({
+ fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags);
+ print_flags(stderr, ntlm->flags);
+ fprintf(stderr, "\n nonce=");
+ print_hex(stderr, (char *)ntlm->nonce, 8);
+ fprintf(stderr, "\n****\n");
+ fprintf(stderr, "**** Header %s\n ", header);
+ });
+
+ free(buffer);
+#endif
+ }
+ else {
+ if(ntlm->state >= NTLMSTATE_TYPE1)
+ return CURLNTLM_BAD;
+
+ ntlm->state = NTLMSTATE_TYPE1; /* we should sent away a type-1 */
+ }
+ }
+ return CURLNTLM_FINE;
+}
+
+#ifndef USE_WINDOWS_SSPI
+
+/*
+ * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The
+ * key schedule ks is also set.
+ */
+static void setup_des_key(unsigned char *key_56,
+ DES_key_schedule DESKEYARG(ks))
+{
+ DES_cblock key;
+
+ key[0] = key_56[0];
+ key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1));
+ key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2));
+ key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3));
+ key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4));
+ key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5));
+ key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
+ key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF);
+
+ DES_set_odd_parity(&key);
+ DES_set_key(&key, ks);
+}
+
+ /*
+ * takes a 21 byte array and treats it as 3 56-bit DES keys. The
+ * 8 byte plaintext is encrypted with each key and the resulting 24
+ * bytes are stored in the results array.
+ */
+static void lm_resp(unsigned char *keys,
+ unsigned char *plaintext,
+ unsigned char *results)
+{
+ DES_key_schedule ks;
+
+ setup_des_key(keys, DESKEY(ks));
+ DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results,
+ DESKEY(ks), DES_ENCRYPT);
+
+ setup_des_key(keys+7, DESKEY(ks));
+ DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+8),
+ DESKEY(ks), DES_ENCRYPT);
+
+ setup_des_key(keys+14, DESKEY(ks));
+ DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16),
+ DESKEY(ks), DES_ENCRYPT);
+}
+
+
+/*
+ * Set up lanmanager hashed password
+ */
+static void mk_lm_hash(struct SessionHandle *data,
+ char *password,
+ unsigned char *lmbuffer /* 21 bytes */)
+{
+ unsigned char pw[14];
+ static const unsigned char magic[] = {
+ 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */
+ };
+ unsigned int i;
+ size_t len = strlen(password);
+
+ if (len > 14)
+ len = 14;
+
+ for (i=0; i<len; i++)
+ pw[i] = (unsigned char)toupper(password[i]);
+
+ for (; i<14; i++)
+ pw[i] = 0;
+
+#ifdef CURL_DOES_CONVERSIONS
+ /*
+ * The LanManager hashed password needs to be created using the
+ * password in the network encoding not the host encoding.
+ */
+ if(data)
+ Curl_convert_to_network(data, (char *)pw, 14);
+#else
+ (void)data;
+#endif
+
+ {
+ /* Create LanManager hashed password. */
+
+ DES_key_schedule ks;
+
+ setup_des_key(pw, DESKEY(ks));
+ DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
+ DESKEY(ks), DES_ENCRYPT);
+
+ setup_des_key(pw+7, DESKEY(ks));
+ DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8),
+ DESKEY(ks), DES_ENCRYPT);
+
+ memset(lmbuffer + 16, 0, 21 - 16);
+ }
+ }
+
+#if USE_NTRESPONSES
+static void utf8_to_unicode_le(unsigned char *dest, const char *src,
+ size_t srclen)
+{
+ size_t i;
+ for (i=0; i<srclen; i++) {
+ dest[2*i] = (unsigned char)src[i];
+ dest[2*i+1] = '\0';
+ }
+}
+
+/*
+ * Set up nt hashed passwords
+ */
+static void mk_nt_hash(struct SessionHandle *data,
+ char *password,
+ unsigned char *ntbuffer /* 21 bytes */)
+{
+ size_t len = strlen(password);
+ unsigned char *pw = malloc(len*2);
+
+ utf8_to_unicode_le(pw, password, len);
+
+#ifdef CURL_DOES_CONVERSIONS
+ /*
+ * The NT hashed password needs to be created using the
+ * password in the network encoding not the host encoding.
+ */
+ if(data)
+ Curl_convert_to_network(data, (char *)pw, len*2);
+#else
+ (void)data;
+#endif
+
+ {
+ /* Create NT hashed password. */
+ MD4_CTX MD4;
+
+ MD4_Init(&MD4);
+ MD4_Update(&MD4, pw, 2*len);
+ MD4_Final(ntbuffer, &MD4);
+
+ memset(ntbuffer + 16, 0, 21 - 16);
+ }
+
+ free(pw);
+}
+#endif
+
+
+#endif
+
+#ifdef USE_WINDOWS_SSPI
+
+static void
+ntlm_sspi_cleanup(struct ntlmdata *ntlm)
+{
+ if (ntlm->type_2) {
+ free(ntlm->type_2);
+ ntlm->type_2 = NULL;
+ }
+ if (ntlm->has_handles) {
+ s_pSecFn->DeleteSecurityContext(&ntlm->c_handle);
+ s_pSecFn->FreeCredentialsHandle(&ntlm->handle);
+ ntlm->has_handles = 0;
+ }
+ if (ntlm->p_identity) {
+ if (ntlm->identity.User) free(ntlm->identity.User);
+ if (ntlm->identity.Password) free(ntlm->identity.Password);
+ if (ntlm->identity.Domain) free(ntlm->identity.Domain);
+ ntlm->p_identity = NULL;
+ }
+}
+
+#endif
+
+#define SHORTPAIR(x) ((x) & 0xff), (((x) >> 8) & 0xff)
+#define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
+ (((x) >>16)&0xff), (((x)>>24) & 0xff)
+
+#define HOSTNAME_MAX 1024
+
+/* this is for creating ntlm header output */
+CURLcode Curl_output_ntlm(struct connectdata *conn,
+ bool proxy)
+{
+ const char *domain=""; /* empty */
+ char host [HOSTNAME_MAX+ 1] = ""; /* empty */
+#ifndef USE_WINDOWS_SSPI
+ size_t domlen = strlen(domain);
+ size_t hostlen = strlen(host);
+ size_t hostoff; /* host name offset */
+ size_t domoff; /* domain name offset */
+#endif
+ size_t size;
+ char *base64=NULL;
+ unsigned char ntlmbuf[1024]; /* enough, unless the user+host+domain is very
+ long */
+
+ /* point to the address of the pointer that holds the string to sent to the
+ server, which is for a plain host or for a HTTP proxy */
+ char **allocuserpwd;
+
+ /* point to the name and password for this */
+ char *userp;
+ char *passwdp;
+ /* point to the correct struct with this */
+ struct ntlmdata *ntlm;
+ struct auth *authp;
+
+ curlassert(conn);
+ curlassert(conn->data);
+
+ if(proxy) {
+ allocuserpwd = &conn->allocptr.proxyuserpwd;
+ userp = conn->proxyuser;
+ passwdp = conn->proxypasswd;
+ ntlm = &conn->proxyntlm;
+ authp = &conn->data->state.authproxy;
+ }
+ else {
+ allocuserpwd = &conn->allocptr.userpwd;
+ userp = conn->user;
+ passwdp = conn->passwd;
+ ntlm = &conn->ntlm;
+ authp = &conn->data->state.authhost;
+ }
+ authp->done = FALSE;
+
+ /* not set means empty */
+ if(!userp)
+ userp=(char *)"";
+
+ if(!passwdp)
+ passwdp=(char *)"";
+
+#ifdef USE_WINDOWS_SSPI
+ /* If security interface is not yet initialized try to do this */
+ if (s_hSecDll == NULL) {
+ /* Determine Windows version. Security functions are located in
+ * security.dll on WinNT 4.0 and in secur32.dll on Win9x. Win2K and XP
+ * contain both these DLLs (security.dll just forwards calls to
+ * secur32.dll)
+ */
+ OSVERSIONINFO osver;
+ osver.dwOSVersionInfoSize = sizeof(osver);
+ GetVersionEx(&osver);
+ if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && osver.dwMajorVersion == 4)
+ s_hSecDll = LoadLibrary("security.dll");
+ else
+ s_hSecDll = LoadLibrary("secur32.dll");
+ if (s_hSecDll != NULL) {
+ INIT_SECURITY_INTERFACE pInitSecurityInterface;
+ pInitSecurityInterface =
+ (INIT_SECURITY_INTERFACE)GetProcAddress(s_hSecDll,
+ "InitSecurityInterfaceA");
+ if (pInitSecurityInterface != NULL)
+ s_pSecFn = pInitSecurityInterface();
+ }
+ }
+ if (s_pSecFn == NULL)
+ return CURLE_RECV_ERROR;
+#endif
+
+ switch(ntlm->state) {
+ case NTLMSTATE_TYPE1:
+ default: /* for the weird cases we (re)start here */
+#ifdef USE_WINDOWS_SSPI
+ {
+ SecBuffer buf;
+ SecBufferDesc desc;
+ SECURITY_STATUS status;
+ ULONG attrs;
+ const char *user;
+ int domlen;
+ TimeStamp tsDummy; /* For Windows 9x compatibility of SPPI calls */
+
+ ntlm_sspi_cleanup(ntlm);
+
+ user = strchr(userp, '\\');
+ if (!user)
+ user = strchr(userp, '/');
+
+ if (user) {
+ domain = userp;
+ domlen = user - userp;
+ user++;
+ }
+ else {
+ user = userp;
+ domain = "";
+ domlen = 0;
+ }
+
+ if (user && *user) {
+ /* note: initialize all of this before doing the mallocs so that
+ * it can be cleaned up later without leaking memory.
+ */
+ ntlm->p_identity = &ntlm->identity;
+ memset(ntlm->p_identity, 0, sizeof(*ntlm->p_identity));
+ if ((ntlm->identity.User = (unsigned char *)strdup(user)) == NULL)
+ return CURLE_OUT_OF_MEMORY;
+ ntlm->identity.UserLength = strlen(user);
+ if ((ntlm->identity.Password = (unsigned char *)strdup(passwdp)) == NULL)
+ return CURLE_OUT_OF_MEMORY;
+ ntlm->identity.PasswordLength = strlen(passwdp);
+ if ((ntlm->identity.Domain = malloc(domlen+1)) == NULL)
+ return CURLE_OUT_OF_MEMORY;
+ strncpy((char *)ntlm->identity.Domain, domain, domlen);
+ ntlm->identity.Domain[domlen] = '\0';
+ ntlm->identity.DomainLength = domlen;
+ ntlm->identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+ }
+ else {
+ ntlm->p_identity = NULL;
+ }
+
+ if (s_pSecFn->AcquireCredentialsHandle(
+ NULL, (char *)"NTLM", SECPKG_CRED_OUTBOUND, NULL, ntlm->p_identity,
+ NULL, NULL, &ntlm->handle, &tsDummy
+ ) != SEC_E_OK) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ desc.ulVersion = SECBUFFER_VERSION;
+ desc.cBuffers = 1;
+ desc.pBuffers = &buf;
+ buf.cbBuffer = sizeof(ntlmbuf);
+ buf.BufferType = SECBUFFER_TOKEN;
+ buf.pvBuffer = ntlmbuf;
+
+ status = s_pSecFn->InitializeSecurityContext(&ntlm->handle, NULL,
+ (char *) host,
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONNECTION,
+ 0, SECURITY_NETWORK_DREP,
+ NULL, 0,
+ &ntlm->c_handle, &desc,
+ &attrs, &tsDummy);
+
+ if (status == SEC_I_COMPLETE_AND_CONTINUE ||
+ status == SEC_I_CONTINUE_NEEDED) {
+ s_pSecFn->CompleteAuthToken(&ntlm->c_handle, &desc);
+ }
+ else if (status != SEC_E_OK) {
+ s_pSecFn->FreeCredentialsHandle(&ntlm->handle);
+ return CURLE_RECV_ERROR;
+ }
+
+ ntlm->has_handles = 1;
+ size = buf.cbBuffer;
+ }
+#else
+ hostoff = 0;
+ domoff = hostoff + hostlen; /* This is 0: remember that host and domain
+ are empty */
+
+ /* Create and send a type-1 message:
+
+ Index Description Content
+ 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
+ (0x4e544c4d53535000)
+ 8 NTLM Message Type long (0x01000000)
+ 12 Flags long
+ 16 Supplied Domain security buffer(*)
+ 24 Supplied Workstation security buffer(*)
+ 32 start of data block
+
+ */
+#if USE_NTLM2SESSION
+#define NTLM2FLAG NTLMFLAG_NEGOTIATE_NTLM2_KEY
+#else
+#define NTLM2FLAG 0
+#endif
+ snprintf((char *)ntlmbuf, sizeof(ntlmbuf), NTLMSSP_SIGNATURE "%c"
+ "\x01%c%c%c" /* 32-bit type = 1 */
+ "%c%c%c%c" /* 32-bit NTLM flag field */
+ "%c%c" /* domain length */
+ "%c%c" /* domain allocated space */
+ "%c%c" /* domain name offset */
+ "%c%c" /* 2 zeroes */
+ "%c%c" /* host length */
+ "%c%c" /* host allocated space */
+ "%c%c" /* host name offset */
+ "%c%c" /* 2 zeroes */
+ "%s" /* host name */
+ "%s", /* domain string */
+ 0, /* trailing zero */
+ 0,0,0, /* part of type-1 long */
+
+ LONGQUARTET(
+ NTLMFLAG_NEGOTIATE_OEM|
+ NTLMFLAG_REQUEST_TARGET|
+ NTLMFLAG_NEGOTIATE_NTLM_KEY|
+ NTLM2FLAG|
+ NTLMFLAG_NEGOTIATE_ALWAYS_SIGN
+ ),
+ SHORTPAIR(domlen),
+ SHORTPAIR(domlen),
+ SHORTPAIR(domoff),
+ 0,0,
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostoff),
+ 0,0,
+ host /* this is empty */, domain /* this is empty */);
+
+ /* initial packet length */
+ size = 32 + hostlen + domlen;
+#endif
+
+ DEBUG_OUT({
+ fprintf(stderr, "**** TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ",
+ LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM|
+ NTLMFLAG_REQUEST_TARGET|
+ NTLMFLAG_NEGOTIATE_NTLM_KEY|
+ NTLM2FLAG|
+ NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
+ NTLMFLAG_NEGOTIATE_OEM|
+ NTLMFLAG_REQUEST_TARGET|
+ NTLMFLAG_NEGOTIATE_NTLM_KEY|
+ NTLM2FLAG|
+ NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
+ print_flags(stderr,
+ NTLMFLAG_NEGOTIATE_OEM|
+ NTLMFLAG_REQUEST_TARGET|
+ NTLMFLAG_NEGOTIATE_NTLM_KEY|
+ NTLM2FLAG|
+ NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
+ fprintf(stderr, "\n****\n");
+ });
+
+ /* now size is the size of the base64 encoded package size */
+ size = Curl_base64_encode(NULL, (char *)ntlmbuf, size, &base64);
+
+ if(size >0 ) {
+ Curl_safefree(*allocuserpwd);
+ *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
+ proxy?"Proxy-":"",
+ base64);
+ DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
+ free(base64);
+ }
+ else
+ return CURLE_OUT_OF_MEMORY; /* FIX TODO */
+
+ break;
+
+ case NTLMSTATE_TYPE2:
+ /* We received the type-2 message already, create a type-3 message:
+
+ Index Description Content
+ 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
+ (0x4e544c4d53535000)
+ 8 NTLM Message Type long (0x03000000)
+ 12 LM/LMv2 Response security buffer(*)
+ 20 NTLM/NTLMv2 Response security buffer(*)
+ 28 Domain Name security buffer(*)
+ 36 User Name security buffer(*)
+ 44 Workstation Name security buffer(*)
+ (52) Session Key (optional) security buffer(*)
+ (60) Flags (optional) long
+ 52 (64) start of data block
+
+ */
+
+ {
+#ifdef USE_WINDOWS_SSPI
+ SecBuffer type_2, type_3;
+ SecBufferDesc type_2_desc, type_3_desc;
+ SECURITY_STATUS status;
+ ULONG attrs;
+ TimeStamp tsDummy; /* For Windows 9x compatibility of SPPI calls */
+
+ type_2_desc.ulVersion = type_3_desc.ulVersion = SECBUFFER_VERSION;
+ type_2_desc.cBuffers = type_3_desc.cBuffers = 1;
+ type_2_desc.pBuffers = &type_2;
+ type_3_desc.pBuffers = &type_3;
+
+ type_2.BufferType = SECBUFFER_TOKEN;
+ type_2.pvBuffer = ntlm->type_2;
+ type_2.cbBuffer = ntlm->n_type_2;
+ type_3.BufferType = SECBUFFER_TOKEN;
+ type_3.pvBuffer = ntlmbuf;
+ type_3.cbBuffer = sizeof(ntlmbuf);
+
+ status = s_pSecFn->InitializeSecurityContext(&ntlm->handle, &ntlm->c_handle,
+ (char *) host,
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONNECTION,
+ 0, SECURITY_NETWORK_DREP, &type_2_desc,
+ 0, &ntlm->c_handle, &type_3_desc,
+ &attrs, &tsDummy);
+
+ if (status != SEC_E_OK)
+ return CURLE_RECV_ERROR;
+
+ size = type_3.cbBuffer;
+
+ ntlm_sspi_cleanup(ntlm);
+
+#else
+ int lmrespoff;
+ unsigned char lmresp[24]; /* fixed-size */
+#if USE_NTRESPONSES
+ int ntrespoff;
+ unsigned char ntresp[24]; /* fixed-size */
+#endif
+ size_t useroff;
+ const char *user;
+ size_t userlen;
+
+ user = strchr(userp, '\\');
+ if(!user)
+ user = strchr(userp, '/');
+
+ if (user) {
+ domain = userp;
+ domlen = (user - domain);
+ user++;
+ }
+ else
+ user = userp;
+ userlen = strlen(user);
+
+ if (gethostname(host, HOSTNAME_MAX)) {
+ infof(conn->data, "gethostname() failed, continuing without!");
+ hostlen = 0;
+ }
+ else {
+ /* If the workstation if configured with a full DNS name (i.e.
+ * workstation.somewhere.net) gethostname() returns the fully qualified
+ * name, which NTLM doesn't like.
+ */
+ char *dot = strchr(host, '.');
+ if (dot)
+ *dot = '\0';
+ hostlen = strlen(host);
+ }
+
+#if USE_NTLM2SESSION
+ /* We don't support NTLM2 if we don't have USE_NTRESPONSES */
+ if (ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
+ unsigned char ntbuffer[0x18];
+ unsigned char tmp[0x18];
+ unsigned char md5sum[MD5_DIGEST_LENGTH];
+ MD5_CTX MD5;
+ unsigned char random[8];
+
+ /* Need to create 8 bytes random data */
+ Curl_ossl_seed(conn->data); /* Initiate the seed if not already done */
+ RAND_bytes(random,8);
+
+ /* 8 bytes random data as challenge in lmresp */
+ memcpy(lmresp,random,8);
+ /* Pad with zeros */
+ memset(lmresp+8,0,0x10);
+
+ /* Fill tmp with challenge(nonce?) + random */
+ memcpy(tmp,&ntlm->nonce[0],8);
+ memcpy(tmp+8,random,8);
+
+ MD5_Init(&MD5);
+ MD5_Update(&MD5, tmp, 16);
+ MD5_Final(md5sum, &MD5);
+ /* We shall only use the first 8 bytes of md5sum,
+ but the des code in lm_resp only encrypt the first 8 bytes */
+ mk_nt_hash(conn->data, passwdp, ntbuffer);
+ lm_resp(ntbuffer, md5sum, ntresp);
+
+ /* End of NTLM2 Session code */
+ }
+ else {
+#endif
+
+#if USE_NTRESPONSES
+ unsigned char ntbuffer[0x18];
+#endif
+ unsigned char lmbuffer[0x18];
+
+#if USE_NTRESPONSES
+ mk_nt_hash(conn->data, passwdp, ntbuffer);
+ lm_resp(ntbuffer, &ntlm->nonce[0], ntresp);
+#endif
+
+ mk_lm_hash(conn->data, passwdp, lmbuffer);
+ lm_resp(lmbuffer, &ntlm->nonce[0], lmresp);
+ /* A safer but less compatible alternative is:
+ * lm_resp(ntbuffer, &ntlm->nonce[0], lmresp);
+ * See http://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */
+#if USE_NTLM2SESSION
+ }
+#endif
+
+ lmrespoff = 64; /* size of the message header */
+#if USE_NTRESPONSES
+ ntrespoff = lmrespoff + 0x18;
+ domoff = ntrespoff + 0x18;
+#else
+ domoff = lmrespoff + 0x18;
+#endif
+ useroff = domoff + domlen;
+ hostoff = useroff + userlen;
+
+ /* Create the big type-3 message binary blob */
+ size = snprintf((char *)ntlmbuf, sizeof(ntlmbuf),
+ NTLMSSP_SIGNATURE "%c"
+ "\x03%c%c%c" /* type-3, 32 bits */
+
+ "%c%c" /* LanManager length */
+ "%c%c" /* LanManager allocated space */
+ "%c%c" /* LanManager offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* NT-response length */
+ "%c%c" /* NT-response allocated space */
+ "%c%c" /* NT-response offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* domain length */
+ "%c%c" /* domain allocated space */
+ "%c%c" /* domain name offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* user length */
+ "%c%c" /* user allocated space */
+ "%c%c" /* user offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* host length */
+ "%c%c" /* host allocated space */
+ "%c%c" /* host offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* session key length (unknown purpose) */
+ "%c%c" /* session key allocated space (unknown purpose) */
+ "%c%c" /* session key offset (unknown purpose) */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c%c%c" /* flags */
+
+ /* domain string */
+ /* user string */
+ /* host string */
+ /* LanManager response */
+ /* NT response */
+ ,
+ 0, /* zero termination */
+ 0,0,0, /* type-3 long, the 24 upper bits */
+
+ SHORTPAIR(0x18), /* LanManager response length, twice */
+ SHORTPAIR(0x18),
+ SHORTPAIR(lmrespoff),
+ 0x0, 0x0,
+
+#if USE_NTRESPONSES
+ SHORTPAIR(0x18), /* NT-response length, twice */
+ SHORTPAIR(0x18),
+ SHORTPAIR(ntrespoff),
+ 0x0, 0x0,
+#else
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
+#endif
+ SHORTPAIR(domlen),
+ SHORTPAIR(domlen),
+ SHORTPAIR(domoff),
+ 0x0, 0x0,
+
+ SHORTPAIR(userlen),
+ SHORTPAIR(userlen),
+ SHORTPAIR(useroff),
+ 0x0, 0x0,
+
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostoff),
+ 0x0, 0x0,
+
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
+
+ LONGQUARTET(ntlm->flags));
+ DEBUG_OUT(assert(size==64));
+
+ DEBUG_OUT(assert(size == lmrespoff));
+ /* We append the binary hashes */
+ if(size < (sizeof(ntlmbuf) - 0x18)) {
+ memcpy(&ntlmbuf[size], lmresp, 0x18);
+ size += 0x18;
+ }
+
+ DEBUG_OUT({
+ fprintf(stderr, "**** TYPE3 header lmresp=");
+ print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18);
+ });
+
+#if USE_NTRESPONSES
+ if(size < (sizeof(ntlmbuf) - 0x18)) {
+ DEBUG_OUT(assert(size == ntrespoff));
+ memcpy(&ntlmbuf[size], ntresp, 0x18);
+ size += 0x18;
+ }
+
+ DEBUG_OUT({
+ fprintf(stderr, "\n ntresp=");
+ print_hex(stderr, (char *)&ntlmbuf[ntrespoff], 0x18);
+ });
+
+#endif
+
+ DEBUG_OUT({
+ fprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ",
+ LONGQUARTET(ntlm->flags), ntlm->flags);
+ print_flags(stderr, ntlm->flags);
+ fprintf(stderr, "\n****\n");
+ });
+
+
+ /* Make sure that the domain, user and host strings fit in the target
+ buffer before we copy them there. */
+ if(size + userlen + domlen + hostlen >= sizeof(ntlmbuf)) {
+ failf(conn->data, "user + domain + host name too big");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ curlassert(size == domoff);
+ memcpy(&ntlmbuf[size], domain, domlen);
+ size += domlen;
+
+ curlassert(size == useroff);
+ memcpy(&ntlmbuf[size], user, userlen);
+ size += userlen;
+
+ curlassert(size == hostoff);
+ memcpy(&ntlmbuf[size], host, hostlen);
+ size += hostlen;
+
+#ifdef CURL_DOES_CONVERSIONS
+ /* convert domain, user, and host to ASCII but leave the rest as-is */
+ if(CURLE_OK != Curl_convert_to_network(conn->data,
+ (char *)&ntlmbuf[domoff],
+ size-domoff)) {
+ return CURLE_CONV_FAILED;
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+#endif
+
+ /* convert the binary blob into base64 */
+ size = Curl_base64_encode(NULL, (char *)ntlmbuf, size, &base64);
+
+ if(size >0 ) {
+ Curl_safefree(*allocuserpwd);
+ *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
+ proxy?"Proxy-":"",
+ base64);
+ DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
+ free(base64);
+ }
+ else
+ return CURLE_OUT_OF_MEMORY; /* FIX TODO */
+
+ ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
+ authp->done = TRUE;
+ }
+ break;
+
+ case NTLMSTATE_TYPE3:
+ /* connection is already authenticated,
+ * don't send a header in future requests */
+ if(*allocuserpwd) {
+ free(*allocuserpwd);
+ *allocuserpwd=NULL;
+ }
+ authp->done = TRUE;
+ break;
+ }
+
+ return CURLE_OK;
+}
+
+
+void
+Curl_ntlm_cleanup(struct connectdata *conn)
+{
+#ifdef USE_WINDOWS_SSPI
+ ntlm_sspi_cleanup(&conn->ntlm);
+ ntlm_sspi_cleanup(&conn->proxyntlm);
+ if (s_hSecDll != NULL) {
+ FreeLibrary(s_hSecDll);
+ s_hSecDll = NULL;
+ s_pSecFn = NULL;
+ }
+#else
+ (void)conn;
+#endif
+}
+
+#endif /* USE_NTLM */
+#endif /* !CURL_DISABLE_HTTP */
diff --git a/Utilities/cmcurl/http_ntlm.h b/Utilities/cmcurl/http_ntlm.h
new file mode 100644
index 000000000..a8de220a7
--- /dev/null
+++ b/Utilities/cmcurl/http_ntlm.h
@@ -0,0 +1,146 @@
+#ifndef __HTTP_NTLM_H
+#define __HTTP_NTLM_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+typedef enum {
+ CURLNTLM_NONE, /* not a ntlm */
+ CURLNTLM_BAD, /* an ntlm, but one we don't like */
+ CURLNTLM_FIRST, /* the first 401-reply we got with NTLM */
+ CURLNTLM_FINE, /* an ntlm we act on */
+
+ CURLNTLM_LAST /* last entry in this enum, don't use */
+} CURLntlm;
+
+/* this is for ntlm header input */
+CURLntlm Curl_input_ntlm(struct connectdata *conn, bool proxy, char *header);
+
+/* this is for creating ntlm header output */
+CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy);
+
+void Curl_ntlm_cleanup(struct connectdata *conn);
+#ifndef USE_NTLM
+#define Curl_ntlm_cleanup(x)
+#endif
+
+
+/* Flag bits definitions based on http://davenport.sourceforge.net/ntlm.html */
+
+#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0)
+/* Indicates that Unicode strings are supported for use in security buffer
+ data. */
+
+#define NTLMFLAG_NEGOTIATE_OEM (1<<1)
+/* Indicates that OEM strings are supported for use in security buffer data. */
+
+#define NTLMFLAG_REQUEST_TARGET (1<<2)
+/* Requests that the server's authentication realm be included in the Type 2
+ message. */
+
+/* unknown (1<<3) */
+#define NTLMFLAG_NEGOTIATE_SIGN (1<<4)
+/* Specifies that authenticated communication between the client and server
+ should carry a digital signature (message integrity). */
+
+#define NTLMFLAG_NEGOTIATE_SEAL (1<<5)
+/* Specifies that authenticated communication between the client and server
+ should be encrypted (message confidentiality). */
+
+#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6)
+/* unknown purpose */
+
+#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7)
+/* Indicates that the LAN Manager session key should be used for signing and
+ sealing authenticated communications. */
+
+#define NTLMFLAG_NEGOTIATE_NETWARE (1<<8)
+/* unknown purpose */
+
+#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9)
+/* Indicates that NTLM authentication is being used. */
+
+/* unknown (1<<10) */
+/* unknown (1<<11) */
+
+#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12)
+/* Sent by the client in the Type 1 message to indicate that a desired
+ authentication realm is included in the message. */
+
+#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13)
+/* Sent by the client in the Type 1 message to indicate that the client
+ workstation's name is included in the message. */
+
+#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14)
+/* Sent by the server to indicate that the server and client are on the same
+ machine. Implies that the client may use a pre-established local security
+ context rather than responding to the challenge. */
+
+#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15)
+/* Indicates that authenticated communication between the client and server
+ should be signed with a "dummy" signature. */
+
+#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16)
+/* Sent by the server in the Type 2 message to indicate that the target
+ authentication realm is a domain. */
+
+#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17)
+/* Sent by the server in the Type 2 message to indicate that the target
+ authentication realm is a server. */
+
+#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18)
+/* Sent by the server in the Type 2 message to indicate that the target
+ authentication realm is a share. Presumably, this is for share-level
+ authentication. Usage is unclear. */
+
+#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19)
+/* Indicates that the NTLM2 signing and sealing scheme should be used for
+ protecting authenticated communications. */
+
+#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20)
+/* unknown purpose */
+
+#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21)
+/* unknown purpose */
+
+#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22)
+/* unknown purpose */
+
+#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23)
+/* Sent by the server in the Type 2 message to indicate that it is including a
+ Target Information block in the message. */
+
+/* unknown (1<24) */
+/* unknown (1<25) */
+/* unknown (1<26) */
+/* unknown (1<27) */
+/* unknown (1<28) */
+
+#define NTLMFLAG_NEGOTIATE_128 (1<<29)
+/* Indicates that 128-bit encryption is supported. */
+
+#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30)
+/* unknown purpose */
+
+#define NTLMFLAG_NEGOTIATE_56 (1<<31)
+/* Indicates that 56-bit encryption is supported. */
+#endif
diff --git a/Utilities/cmcurl/if2ip.c b/Utilities/cmcurl/if2ip.c
new file mode 100644
index 000000000..b4a98c662
--- /dev/null
+++ b/Utilities/cmcurl/if2ip.c
@@ -0,0 +1,134 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "if2ip.h"
+
+/*
+ * This test can probably be simplified to #if defined(SIOCGIFADDR) and
+ * moved after the following includes.
+ */
+#if !defined(WIN32) && !defined(__BEOS__) && !defined(__CYGWIN__) && \
+ !defined(__riscos__) && !defined(__INTERIX) && !defined(NETWARE) && \
+ !defined(_AMIGASF) && !defined(__minix)
+
+#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
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+
+#ifdef VMS
+#include <inet.h>
+#endif
+
+#include "inet_ntop.h"
+#include "memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#define SYS_ERROR -1
+
+char *Curl_if2ip(const 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;
+ size_t len = strlen(interface);
+ memset(&req, 0, sizeof(req));
+ if(len >= sizeof(req.ifr_name))
+ return NULL; /* this can't be a fine interface name */
+ memcpy(req.ifr_name, interface, len+1);
+ 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, sizeof(in));
+ ip = (char *) Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
+ }
+ sclose(dummy);
+ }
+ return ip;
+}
+
+/* -- end of if2ip() -- */
+#else
+char *Curl_if2ip(const char *interf, char *buf, int buf_size)
+{
+ (void) interf;
+ (void) buf;
+ (void) buf_size;
+ return NULL;
+}
+#endif
diff --git a/Utilities/cmcurl/if2ip.h b/Utilities/cmcurl/if2ip.h
new file mode 100644
index 000000000..4e86e2b27
--- /dev/null
+++ b/Utilities/cmcurl/if2ip.h
@@ -0,0 +1,67 @@
+#ifndef __IF2IP_H
+#define __IF2IP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#include "setup.h"
+
+extern char *Curl_if2ip(const char *interf, char *buf, int buf_size);
+
+#ifdef __INTERIX
+#include <sys/socket.h>
+
+/* Nedelcho Stanev's work-around for SFU 3.0 */
+struct ifreq {
+#define IFNAMSIZ 16
+#define IFHWADDRLEN 6
+ union {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ } ifr_ifrn;
+
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
+ short ifru_flags;
+ int ifru_metric;
+ int ifru_mtu;
+ } ifr_ifru;
+};
+
+/* This define was added by Daniel to avoid an extra #ifdef INTERIX in the
+ C code. */
+#define ifr_dstaddr ifr_addr
+
+#define ifr_name ifr_ifrn.ifrn_name /* interface name */
+#define ifr_addr ifr_ifru.ifru_addr /* address */
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
+#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
+#define ifr_flags ifr_ifru.ifru_flags /* flags */
+#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
+#define ifr_metric ifr_ifru.ifru_metric /* metric */
+#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
+
+#define SIOCGIFADDR _IOW('s', 102, struct ifreq) /* Get if addr */
+#endif /* interix */
+
+#endif
diff --git a/Utilities/cmcurl/inet_ntoa_r.h b/Utilities/cmcurl/inet_ntoa_r.h
new file mode 100644
index 000000000..c6c9bd895
--- /dev/null
+++ b/Utilities/cmcurl/inet_ntoa_r.h
@@ -0,0 +1,44 @@
+#ifndef __INET_NTOA_R_H
+#define __INET_NTOA_R_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifdef HAVE_INET_NTOA_R_2_ARGS
+/*
+ * uClibc 0.9.26 (at least) doesn't define this prototype. The buffer
+ * must be at least 16 characters long.
+ */
+char *inet_ntoa_r(const struct in_addr in, char buffer[]);
+
+#else
+/*
+ * 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
+
+#endif
diff --git a/Utilities/cmcurl/inet_ntop.c b/Utilities/cmcurl/inet_ntop.c
new file mode 100644
index 000000000..9381963f5
--- /dev/null
+++ b/Utilities/cmcurl/inet_ntop.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 1996-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Original code by Paul Vixie. "curlified" by Gisle Vanem.
+ */
+
+#include "setup.h"
+
+#ifndef HAVE_INET_NTOP
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#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_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <string.h>
+#include <errno.h>
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "inet_ntop.h"
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+/* this platform has a inet_ntoa_r() function, but no proto declared anywhere
+ so we include our own proto to make compilers happy */
+#include "inet_ntoa_r.h"
+#endif
+
+#define IN6ADDRSZ 16
+#define INADDRSZ 4
+#define INT16SZ 2
+
+#ifdef USE_WINSOCK
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#define SET_ERRNO(e) WSASetLastError(errno = (e))
+#else
+#define SET_ERRNO(e) errno = e
+#endif
+
+/*
+ * Format an IPv4 address, more or less like inet_ntoa().
+ *
+ * Returns `dst' (as a const)
+ * Note:
+ * - uses no statics
+ * - takes a unsigned char* not an in_addr as input
+ */
+static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size)
+{
+#if defined(HAVE_INET_NTOA_R_2_ARGS)
+ const char *ptr;
+ curlassert(size >= 16);
+ ptr = inet_ntoa_r(*(struct in_addr*)src, dst);
+ return (char *)memmove(dst, ptr, strlen(ptr)+1);
+
+#elif defined(HAVE_INET_NTOA_R)
+ return inet_ntoa_r(*(struct in_addr*)src, dst, size);
+
+#else
+ const char *addr = inet_ntoa(*(struct in_addr*)src);
+
+ if (strlen(addr) >= size)
+ {
+ SET_ERRNO(ENOSPC);
+ return (NULL);
+ }
+ return strcpy(dst, addr);
+#endif
+}
+
+#ifdef ENABLE_IPV6
+/*
+ * Convert IPv6 binary address into presentation (printable) format.
+ */
+static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size)
+{
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ char *tp;
+ struct {
+ long base;
+ long len;
+ } best, cur;
+ unsigned long words[IN6ADDRSZ / INT16SZ];
+ int i;
+
+ /* Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof(words));
+ for (i = 0; i < IN6ADDRSZ; i++)
+ words[i/2] |= (src[i] << ((1 - (i % 2)) << 3));
+
+ best.base = -1;
+ cur.base = -1;
+ best.len = 0;
+ cur.len = 0;
+
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
+ {
+ if (words[i] == 0)
+ {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ }
+ else if (cur.base != -1)
+ {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ if ((cur.base != -1) && (best.base == -1 || cur.len > best.len))
+ best = cur;
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /* Format the result.
+ */
+ tp = tmp;
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
+ {
+ /* Are we inside the best run of 0x00's?
+ */
+ if (best.base != -1 && i >= best.base && i < (best.base + best.len))
+ {
+ if (i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+
+ /* Are we following an initial run of 0x00s or any real hex?
+ */
+ if (i != 0)
+ *tp++ = ':';
+
+ /* Is this address an encapsulated IPv4?
+ */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
+ {
+ if (!inet_ntop4(src+12, tp, sizeof(tmp) - (tp - tmp)))
+ {
+ SET_ERRNO(ENOSPC);
+ return (NULL);
+ }
+ tp += strlen(tp);
+ break;
+ }
+ tp += snprintf(tp, 5, "%lx", words[i]);
+ }
+
+ /* Was it a trailing run of 0x00's?
+ */
+ if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
+ *tp++ = ':';
+ *tp++ = '\0';
+
+ /* Check for overflow, copy, and we're done.
+ */
+ if ((size_t)(tp - tmp) > size)
+ {
+ SET_ERRNO(ENOSPC);
+ return (NULL);
+ }
+ return strcpy (dst, tmp);
+}
+#endif /* ENABLE_IPV6 */
+
+/*
+ * Convert a network format address to presentation format.
+ *
+ * Returns pointer to presentation format address (`buf'),
+ * Returns NULL on error (see errno).
+ */
+char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
+{
+ switch (af) {
+ case AF_INET:
+ return inet_ntop4((const unsigned char*)src, buf, size);
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ return inet_ntop6((const unsigned char*)src, buf, size);
+#endif
+ default:
+ SET_ERRNO(EAFNOSUPPORT);
+ return NULL;
+ }
+}
+#endif /* HAVE_INET_NTOP */
diff --git a/Utilities/cmcurl/inet_ntop.h b/Utilities/cmcurl/inet_ntop.h
new file mode 100644
index 000000000..54d64bd19
--- /dev/null
+++ b/Utilities/cmcurl/inet_ntop.h
@@ -0,0 +1,37 @@
+#ifndef __INET_NTOP_H
+#define __INET_NTOP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size);
+
+#ifdef HAVE_INET_NTOP
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#define Curl_inet_ntop(af,addr,buf,size) inet_ntop(af,addr,buf,size)
+#endif
+
+#endif /* __INET_NTOP_H */
diff --git a/Utilities/cmcurl/inet_pton.c b/Utilities/cmcurl/inet_pton.c
new file mode 100644
index 000000000..9b9f88b5e
--- /dev/null
+++ b/Utilities/cmcurl/inet_pton.c
@@ -0,0 +1,241 @@
+/* This is from the BIND 4.9.4 release, modified to compile by itself */
+
+/* Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "setup.h"
+
+#ifndef HAVE_INET_PTON
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#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_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <string.h>
+#include <errno.h>
+
+#include "inet_pton.h"
+
+#define IN6ADDRSZ 16
+#define INADDRSZ 4
+#define INT16SZ 2
+
+#ifdef USE_WINSOCK
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#endif
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int inet_pton4(const char *src, unsigned char *dst);
+#ifdef ENABLE_IPV6
+static int inet_pton6(const char *src, unsigned char *dst);
+#endif
+
+/* int
+ * inet_pton(af, src, dst)
+ * convert from presentation format (which usually means ASCII printable)
+ * to network format (which is usually some kind of binary format).
+ * return:
+ * 1 if the address was valid for the specified address family
+ * 0 if the address wasn't valid (`dst' is untouched in this case)
+ * -1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ * Paul Vixie, 1996.
+ */
+int
+Curl_inet_pton(int af, const char *src, void *dst)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_pton4(src, (unsigned char *)dst));
+#ifdef ENABLE_IPV6
+#ifndef AF_INET6
+#define AF_INET6 (AF_MAX+1) /* just to let this compile */
+#endif
+ case AF_INET6:
+ return (inet_pton6(src, (unsigned char *)dst));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ /* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ * like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ * 1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, unsigned char *dst)
+{
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ unsigned char tmp[INADDRSZ], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ tp = tmp;
+ *tp = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr(digits, ch)) != NULL) {
+ unsigned int val = *tp * 10 + (unsigned int)(pch - digits);
+
+ if (val > 255)
+ return (0);
+ *tp = val;
+ if (! saw_digit) {
+ if (++octets > 4)
+ return (0);
+ saw_digit = 1;
+ }
+ } else if (ch == '.' && saw_digit) {
+ if (octets == 4)
+ return (0);
+ *++tp = 0;
+ saw_digit = 0;
+ } else
+ return (0);
+ }
+ if (octets < 4)
+ return (0);
+ /* bcopy(tmp, dst, INADDRSZ); */
+ memcpy(dst, tmp, INADDRSZ);
+ return (1);
+}
+
+#ifdef ENABLE_IPV6
+/* int
+ * inet_pton6(src, dst)
+ * convert presentation level address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton6(const char *src, unsigned char *dst)
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, saw_xdigit;
+ unsigned int val;
+
+ memset((tp = tmp), 0, IN6ADDRSZ);
+ endp = tp + IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return (0);
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff)
+ return (0);
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ }
+ if (tp + INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+ inet_pton4(curtok, tp) > 0) {
+ tp += INADDRSZ;
+ saw_xdigit = 0;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return (0);
+ }
+ if (saw_xdigit) {
+ if (tp + INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return (0);
+ /* bcopy(tmp, dst, IN6ADDRSZ); */
+ memcpy(dst, tmp, IN6ADDRSZ);
+ return (1);
+}
+#endif /* ENABLE_IPV6 */
+
+#endif /* HAVE_INET_PTON */
diff --git a/Utilities/cmcurl/inet_pton.h b/Utilities/cmcurl/inet_pton.h
new file mode 100644
index 000000000..a659a9774
--- /dev/null
+++ b/Utilities/cmcurl/inet_pton.h
@@ -0,0 +1,42 @@
+#ifndef __INET_PTON_H
+#define __INET_PTON_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+int Curl_inet_pton(int, const char *, void *);
+
+#ifdef HAVE_INET_PTON
+
+#if defined(HAVE_NO_INET_PTON_PROTO)
+int inet_pton(int af, const char *src, void *dst);
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#define Curl_inet_pton(x,y,z) inet_pton(x,y,z)
+#endif
+
+#endif /* __INET_PTON_H */
diff --git a/Utilities/cmcurl/krb4.c b/Utilities/cmcurl/krb4.c
new file mode 100644
index 000000000..f2b91df69
--- /dev/null
+++ b/Utilities/cmcurl/krb4.c
@@ -0,0 +1,425 @@
+/* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for
+ * use in Curl. Martin's latest changes were done 2000-09-18.
+ *
+ * It has since been patched away like a madman by Daniel Stenberg to make it
+ * better applied to curl conditions, and to make it not use globals, pollute
+ * name space and more.
+ *
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * Copyright (c) 2004 - 2007 Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include "setup.h"
+
+#ifndef CURL_DISABLE_FTP
+#ifdef HAVE_KRB4
+
+#include <stdlib.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <string.h>
+#include <krb.h>
+#include <des.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for getpid() */
+#endif
+
+#include "urldata.h"
+#include "base64.h"
+#include "ftp.h"
+#include "sendf.h"
+#include "krb4.h"
+#include "memory.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: */
+#include "memdebug.h"
+
+#define LOCAL_ADDR (&conn->local_addr)
+#define REMOTE_ADDR conn->ip_addr->ai_addr
+#define myctladdr LOCAL_ADDR
+#define hisctladdr REMOTE_ADDR
+
+struct krb4_data {
+ des_cblock key;
+ des_key_schedule schedule;
+ char name[ANAME_SZ];
+ char instance[INST_SZ];
+ char realm[REALM_SZ];
+};
+
+#ifndef HAVE_STRLCPY
+/* if it ever goes non-static, make it Curl_ prefixed! */
+static size_t
+strlcpy (char *dst, const char *src, size_t dst_sz)
+{
+ size_t n;
+ char *p;
+
+ for (p = dst, n = 0;
+ n + 1 < dst_sz && *src != '\0';
+ ++p, ++src, ++n)
+ *p = *src;
+ *p = '\0';
+ if (*src == '\0')
+ return n;
+ else
+ return n + strlen (src);
+}
+#else
+size_t strlcpy (char *dst, const char *src, size_t dst_sz);
+#endif
+
+static int
+krb4_check_prot(void *app_data, int level)
+{
+ app_data = NULL; /* prevent compiler warning */
+ if(level == prot_confidential)
+ return -1;
+ return 0;
+}
+
+static int
+krb4_decode(void *app_data, void *buf, int len, int level,
+ struct connectdata *conn)
+{
+ MSG_DAT m;
+ int e;
+ struct krb4_data *d = app_data;
+
+ if(level == prot_safe)
+ e = krb_rd_safe(buf, len, &d->key,
+ (struct sockaddr_in *)REMOTE_ADDR,
+ (struct sockaddr_in *)LOCAL_ADDR, &m);
+ else
+ e = krb_rd_priv(buf, len, d->schedule, &d->key,
+ (struct sockaddr_in *)REMOTE_ADDR,
+ (struct sockaddr_in *)LOCAL_ADDR, &m);
+ if(e) {
+ struct SessionHandle *data = conn->data;
+ infof(data, "krb4_decode: %s\n", krb_get_err_text(e));
+ return -1;
+ }
+ memmove(buf, m.app_data, m.app_length);
+ return m.app_length;
+}
+
+static int
+krb4_overhead(void *app_data, int level, int len)
+{
+ /* no arguments are used, just init them to prevent compiler warnings */
+ app_data = NULL;
+ level = 0;
+ len = 0;
+ return 31;
+}
+
+static int
+krb4_encode(void *app_data, 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;
+}
+
+#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM
+int krb_get_our_ip_for_realm(char *, struct in_addr *);
+#endif
+
+static int
+krb4_auth(void *app_data, struct connectdata *conn)
+{
+ int ret;
+ char *p;
+ unsigned char *ptr;
+ size_t len;
+ KTEXT_ST adat;
+ MSG_DAT msg_data;
+ int checksum;
+ u_int32_t cs;
+ struct krb4_data *d = app_data;
+ char *host = conn->host.name;
+ ssize_t nread;
+ int l = sizeof(conn->local_addr);
+ struct SessionHandle *data = conn->data;
+ CURLcode result;
+
+ if(getsockname(conn->sock[FIRSTSOCKET],
+ (struct sockaddr *)LOCAL_ADDR, &l) < 0)
+ perror("getsockname()");
+
+ checksum = getpid();
+ ret = mk_auth(d, &adat, "ftp", host, checksum);
+ if(ret == KDC_PR_UNKNOWN)
+ ret = mk_auth(d, &adat, "rcmd", host, checksum);
+ if(ret) {
+ infof(data, "%s\n", krb_get_err_text(ret));
+ return AUTH_CONTINUE;
+ }
+
+#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM
+ if (krb_get_config_bool("nat_in_use")) {
+ struct sockaddr_in *localaddr = (struct sockaddr_in *)LOCAL_ADDR;
+ struct in_addr natAddr;
+
+ if (krb_get_our_ip_for_realm(krb_realmofhost(host),
+ &natAddr) != KSUCCESS
+ && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS)
+ infof(data, "Can't get address for realm %s\n",
+ krb_realmofhost(host));
+ else {
+ if (natAddr.s_addr != localaddr->sin_addr.s_addr) {
+#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
+ infof(data, "Using NAT IP address (%s) for kerberos 4\n", ip);
+ localaddr->sin_addr = natAddr;
+ }
+ }
+ }
+#endif
+
+ if(Curl_base64_encode(conn->data, (char *)adat.dat, adat.length, &p) < 1) {
+ Curl_failf(data, "Out of memory base64-encoding");
+ return AUTH_CONTINUE;
+ }
+
+ result = Curl_ftpsendf(conn, "ADAT %s", p);
+
+ free(p);
+
+ if(result)
+ return -2;
+
+ if(Curl_GetFTPResponse(&nread, conn, NULL))
+ return -1;
+
+ if(data->state.buffer[0] != '2'){
+ Curl_failf(data, "Server didn't accept auth data");
+ return AUTH_ERROR;
+ }
+
+ p = strstr(data->state.buffer, "ADAT=");
+ if(!p) {
+ Curl_failf(data, "Remote host didn't send adat reply");
+ return AUTH_ERROR;
+ }
+ p += 5;
+ len = Curl_base64_decode(p, &ptr);
+ if(len > sizeof(adat.dat)-1) {
+ free(ptr);
+ len=0;
+ }
+ if(!len || !ptr) {
+ Curl_failf(data, "Failed to decode base64 from server");
+ return AUTH_ERROR;
+ }
+ memcpy((char *)adat.dat, ptr, len);
+ free(ptr);
+ adat.length = len;
+ ret = krb_rd_safe(adat.dat, adat.length, &d->key,
+ (struct sockaddr_in *)hisctladdr,
+ (struct sockaddr_in *)myctladdr, &msg_data);
+ if(ret) {
+ Curl_failf(data, "Error reading reply from server: %s",
+ krb_get_err_text(ret));
+ return AUTH_ERROR;
+ }
+ krb_get_int(msg_data.app_data, &cs, 4, 0);
+ if(cs - checksum != 1) {
+ Curl_failf(data, "Bad checksum returned from server");
+ return AUTH_ERROR;
+ }
+ return AUTH_OK;
+}
+
+struct Curl_sec_client_mech Curl_krb4_client_mech = {
+ "KERBEROS_V4",
+ sizeof(struct krb4_data),
+ NULL, /* init */
+ krb4_auth,
+ NULL, /* end */
+ krb4_check_prot,
+ krb4_overhead,
+ krb4_encode,
+ krb4_decode
+};
+
+CURLcode Curl_krb_kauth(struct connectdata *conn)
+{
+ des_cblock key;
+ des_key_schedule schedule;
+ KTEXT_ST tkt, tktcopy;
+ char *name;
+ char *p;
+ char passwd[100];
+ size_t tmp;
+ ssize_t nread;
+ int save;
+ CURLcode result;
+ unsigned char *ptr;
+
+ save = Curl_set_command_prot(conn, prot_private);
+
+ result = Curl_ftpsendf(conn, "SITE KAUTH %s", conn->user);
+
+ if(result)
+ return result;
+
+ result = Curl_GetFTPResponse(&nread, conn, NULL);
+ if(result)
+ return result;
+
+ if(conn->data->state.buffer[0] != '3'){
+ Curl_set_command_prot(conn, save);
+ return CURLE_FTP_WEIRD_SERVER_REPLY;
+ }
+
+ p = strstr(conn->data->state.buffer, "T=");
+ if(!p) {
+ Curl_failf(conn->data, "Bad reply from server");
+ Curl_set_command_prot(conn, save);
+ return CURLE_FTP_WEIRD_SERVER_REPLY;
+ }
+
+ p += 2;
+ tmp = Curl_base64_decode(p, &ptr);
+ if(tmp >= sizeof(tkt.dat)) {
+ free(ptr);
+ tmp=0;
+ }
+ if(!tmp || !ptr) {
+ Curl_failf(conn->data, "Failed to decode base64 in reply.\n");
+ Curl_set_command_prot(conn, save);
+ return CURLE_FTP_WEIRD_SERVER_REPLY;
+ }
+ memcpy((char *)tkt.dat, ptr, tmp);
+ free(ptr);
+ tkt.length = tmp;
+ tktcopy.length = tkt.length;
+
+ p = strstr(conn->data->state.buffer, "P=");
+ if(!p) {
+ Curl_failf(conn->data, "Bad reply from server");
+ Curl_set_command_prot(conn, save);
+ return CURLE_FTP_WEIRD_SERVER_REPLY;
+ }
+ name = p + 2;
+ for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++);
+ *p = 0;
+
+ des_string_to_key (conn->passwd, &key);
+ des_key_sched(&key, schedule);
+
+ des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat,
+ tkt.length,
+ schedule, &key, DES_DECRYPT);
+ if (strcmp ((char*)tktcopy.dat + 8,
+ KRB_TICKET_GRANTING_TICKET) != 0) {
+ afs_string_to_key(passwd,
+ krb_realmofhost(conn->host.name),
+ &key);
+ des_key_sched(&key, schedule);
+ des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat,
+ tkt.length,
+ schedule, &key, DES_DECRYPT);
+ }
+ memset(key, 0, sizeof(key));
+ memset(schedule, 0, sizeof(schedule));
+ memset(passwd, 0, sizeof(passwd));
+ if(Curl_base64_encode(conn->data, (char *)tktcopy.dat, tktcopy.length, &p)
+ < 1) {
+ failf(conn->data, "Out of memory base64-encoding.");
+ Curl_set_command_prot(conn, save);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memset (tktcopy.dat, 0, tktcopy.length);
+
+ result = Curl_ftpsendf(conn, "SITE KAUTH %s %s", name, p);
+ free(p);
+ if(result)
+ return result;
+
+ result = Curl_GetFTPResponse(&nread, conn, NULL);
+ if(result)
+ return result;
+ Curl_set_command_prot(conn, save);
+
+ return CURLE_OK;
+}
+
+#endif /* HAVE_KRB4 */
+#endif /* CURL_DISABLE_FTP */
diff --git a/Utilities/cmcurl/krb4.h b/Utilities/cmcurl/krb4.h
new file mode 100644
index 000000000..f46416e62
--- /dev/null
+++ b/Utilities/cmcurl/krb4.h
@@ -0,0 +1,70 @@
+#ifndef __KRB4_H
+#define __KRB4_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+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;
+
+CURLcode Curl_krb_kauth(struct connectdata *conn);
+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);
+ssize_t Curl_sec_send(struct connectdata *conn, int, char *, int);
+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
diff --git a/Utilities/cmcurl/ldap.c b/Utilities/cmcurl/ldap.c
new file mode 100644
index 000000000..3e1144d4f
--- /dev/null
+++ b/Utilities/cmcurl/ldap.c
@@ -0,0 +1,702 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef CURL_DISABLE_LDAP
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef NEED_MALLOC_H
+#include <malloc.h>
+#endif
+#include <errno.h>
+
+#if defined(WIN32)
+# include <winldap.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_DLFCN_H
+# include <dlfcn.h>
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "sendf.h"
+#include "escape.h"
+#include "transfer.h"
+#include "strequal.h"
+#include "strtok.h"
+#include "ldap.h"
+#include "memory.h"
+#include "base64.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "memdebug.h"
+
+/* WLdap32.dll functions are *not* stdcall. Must call these via __cdecl
+ * pointers in case libcurl was compiled as fastcall (cl -Gr). Watcom
+ * uses fastcall by default.
+ */
+#if !defined(WIN32) && !defined(__cdecl)
+#define __cdecl
+#endif
+
+#ifndef LDAP_SIZELIMIT_EXCEEDED
+#define LDAP_SIZELIMIT_EXCEEDED 4
+#endif
+#ifndef LDAP_VERSION2
+#define LDAP_VERSION2 2
+#endif
+#ifndef LDAP_VERSION3
+#define LDAP_VERSION3 3
+#endif
+#ifndef LDAP_OPT_PROTOCOL_VERSION
+#define LDAP_OPT_PROTOCOL_VERSION 0x0011
+#endif
+
+#define DLOPEN_MODE RTLD_LAZY /*! assume all dlopen() implementations have
+ this */
+
+#if defined(RTLD_LAZY_GLOBAL) /* It turns out some systems use this: */
+# undef DLOPEN_MODE
+# define DLOPEN_MODE RTLD_LAZY_GLOBAL
+#elif defined(RTLD_GLOBAL)
+# undef DLOPEN_MODE
+# define DLOPEN_MODE (RTLD_LAZY | RTLD_GLOBAL)
+#endif
+
+#define DYNA_GET_FUNCTION(type, fnc) do { \
+ (fnc) = (type)DynaGetFunction(#fnc); \
+ if ((fnc) == NULL) \
+ return CURLE_FUNCTION_NOT_FOUND; \
+ } while (0)
+
+/*! CygWin etc. configure could set these, but we don't want it.
+ * Must use WLdap32.dll code.
+ */
+#if defined(WIN32)
+#undef HAVE_DLOPEN
+#undef HAVE_LIBDL
+#endif
+
+/*
+ * We use this ZERO_NULL to avoid picky compiler warnings,
+ * when assigning a NULL pointer to a function pointer var.
+ */
+
+#define ZERO_NULL 0
+
+typedef void * (*dynafunc)(void *input);
+
+/***********************************************************************
+ */
+#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) || defined(WIN32)
+static void *libldap = NULL;
+#if defined(DL_LBER_FILE)
+static void *liblber = NULL;
+#endif
+#endif
+
+struct bv {
+ unsigned long bv_len;
+ char *bv_val;
+};
+
+static int DynaOpen(const char **mod_name)
+{
+#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
+ if (libldap == NULL) {
+ /*
+ * libldap.so can normally resolve its dependency on liblber.so
+ * automatically, but in broken installation it does not so
+ * handle it here by opening liblber.so as global.
+ */
+#ifdef DL_LBER_FILE
+ *mod_name = DL_LBER_FILE;
+ liblber = dlopen(*mod_name, DLOPEN_MODE);
+ if (!liblber)
+ return 0;
+#endif
+
+ /* Assume loading libldap.so will fail if loading of liblber.so failed
+ */
+ *mod_name = DL_LDAP_FILE;
+ libldap = dlopen(*mod_name, RTLD_LAZY);
+ }
+ return (libldap != NULL);
+
+#elif defined(WIN32)
+ *mod_name = DL_LDAP_FILE;
+ if (!libldap)
+ libldap = (void*)LoadLibrary(*mod_name);
+ return (libldap != NULL);
+
+#else
+ *mod_name = "";
+ return (0);
+#endif
+}
+
+static void DynaClose(void)
+{
+#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
+ if (libldap) {
+ dlclose(libldap);
+ libldap=NULL;
+ }
+#ifdef DL_LBER_FILE
+ if (liblber) {
+ dlclose(liblber);
+ liblber=NULL;
+ }
+#endif
+#elif defined(WIN32)
+ if (libldap) {
+ FreeLibrary ((HMODULE)libldap);
+ libldap = NULL;
+ }
+#endif
+}
+
+static dynafunc DynaGetFunction(const char *name)
+{
+ dynafunc func = (dynafunc)ZERO_NULL;
+
+#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
+ if (libldap) {
+ /* This typecast magic below was brought by Joe Halpin. In ISO C, you
+ * cannot typecast a data pointer to a function pointer, but that's
+ * exactly what we need to do here to avoid compiler warnings on picky
+ * compilers! */
+ *(void**) (&func) = dlsym(libldap, name);
+ }
+#elif defined(WIN32)
+ if (libldap) {
+ func = (dynafunc)GetProcAddress((HINSTANCE)libldap, name);
+ }
+#else
+ (void) name;
+#endif
+ return func;
+}
+
+/***********************************************************************
+ */
+typedef struct ldap_url_desc {
+ struct ldap_url_desc *lud_next;
+ char *lud_scheme;
+ char *lud_host;
+ int lud_port;
+ char *lud_dn;
+ char **lud_attrs;
+ int lud_scope;
+ char *lud_filter;
+ char **lud_exts;
+ int lud_crit_exts;
+} LDAPURLDesc;
+
+#ifdef WIN32
+static int _ldap_url_parse (const struct connectdata *conn,
+ LDAPURLDesc **ludp);
+static void _ldap_free_urldesc (LDAPURLDesc *ludp);
+
+static void (*ldap_free_urldesc)(LDAPURLDesc *) = _ldap_free_urldesc;
+#endif
+
+#ifdef DEBUG_LDAP
+ #define LDAP_TRACE(x) do { \
+ _ldap_trace ("%u: ", __LINE__); \
+ _ldap_trace x; \
+ } while (0)
+
+ static void _ldap_trace (const char *fmt, ...);
+#else
+ #define LDAP_TRACE(x) ((void)0)
+#endif
+
+
+CURLcode Curl_ldap(struct connectdata *conn, bool *done)
+{
+ CURLcode status = CURLE_OK;
+ int rc = 0;
+#ifndef WIN32
+ int (*ldap_url_parse)(char *, LDAPURLDesc **);
+ void (*ldap_free_urldesc)(void *);
+#endif
+ void *(__cdecl *ldap_init)(char *, int);
+ int (__cdecl *ldap_simple_bind_s)(void *, char *, char *);
+ int (__cdecl *ldap_unbind_s)(void *);
+ int (__cdecl *ldap_search_s)(void *, char *, int, char *, char **,
+ int, void **);
+ void *(__cdecl *ldap_first_entry)(void *, void *);
+ void *(__cdecl *ldap_next_entry)(void *, void *);
+ char *(__cdecl *ldap_err2string)(int);
+ char *(__cdecl *ldap_get_dn)(void *, void *);
+ char *(__cdecl *ldap_first_attribute)(void *, void *, void **);
+ char *(__cdecl *ldap_next_attribute)(void *, void *, void *);
+ void **(__cdecl *ldap_get_values_len)(void *, void *, const char *);
+ void (__cdecl *ldap_value_free_len)(void **);
+ void (__cdecl *ldap_memfree)(void *);
+ void (__cdecl *ber_free)(void *, int);
+ int (__cdecl *ldap_set_option)(void *, int, void *);
+
+ void *server;
+ LDAPURLDesc *ludp = NULL;
+ const char *mod_name;
+ void *result;
+ void *entryIterator; /*! type should be 'LDAPMessage *' */
+ int num = 0;
+ struct SessionHandle *data=conn->data;
+ int ldap_proto;
+ char *val_b64;
+ size_t val_b64_sz;
+
+ *done = TRUE; /* unconditionally */
+ infof(data, "LDAP local: %s\n", data->change.url);
+
+ if (!DynaOpen(&mod_name)) {
+ failf(data, "The %s LDAP library/libraries couldn't be opened", mod_name);
+ return CURLE_LIBRARY_NOT_FOUND;
+ }
+
+ /* The types are needed because ANSI C distinguishes between
+ * pointer-to-object (data) and pointer-to-function.
+ */
+ DYNA_GET_FUNCTION(void *(__cdecl *)(char *, int), ldap_init);
+ DYNA_GET_FUNCTION(int (__cdecl *)(void *, char *, char *),
+ ldap_simple_bind_s);
+ DYNA_GET_FUNCTION(int (__cdecl *)(void *), ldap_unbind_s);
+#ifndef WIN32
+ DYNA_GET_FUNCTION(int (*)(char *, LDAPURLDesc **), ldap_url_parse);
+ DYNA_GET_FUNCTION(void (*)(void *), ldap_free_urldesc);
+#endif
+ DYNA_GET_FUNCTION(int (__cdecl *)(void *, char *, int, char *, char **, int,
+ void **), ldap_search_s);
+ DYNA_GET_FUNCTION(void *(__cdecl *)(void *, void *), ldap_first_entry);
+ DYNA_GET_FUNCTION(void *(__cdecl *)(void *, void *), ldap_next_entry);
+ DYNA_GET_FUNCTION(char *(__cdecl *)(int), ldap_err2string);
+ DYNA_GET_FUNCTION(char *(__cdecl *)(void *, void *), ldap_get_dn);
+ DYNA_GET_FUNCTION(char *(__cdecl *)(void *, void *, void **),
+ ldap_first_attribute);
+ DYNA_GET_FUNCTION(char *(__cdecl *)(void *, void *, void *),
+ ldap_next_attribute);
+ DYNA_GET_FUNCTION(void **(__cdecl *)(void *, void *, const char *),
+ ldap_get_values_len);
+ DYNA_GET_FUNCTION(void (__cdecl *)(void **), ldap_value_free_len);
+ DYNA_GET_FUNCTION(void (__cdecl *)(void *), ldap_memfree);
+ DYNA_GET_FUNCTION(void (__cdecl *)(void *, int), ber_free);
+ DYNA_GET_FUNCTION(int (__cdecl *)(void *, int, void *), ldap_set_option);
+
+ server = (*ldap_init)(conn->host.name, (int)conn->port);
+ if (server == NULL) {
+ failf(data, "LDAP local: Cannot connect to %s:%d",
+ conn->host.name, conn->port);
+ status = CURLE_COULDNT_CONNECT;
+ goto quit;
+ }
+
+ ldap_proto = LDAP_VERSION3;
+ (*ldap_set_option)(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
+ rc = (*ldap_simple_bind_s)(server,
+ conn->bits.user_passwd ? conn->user : NULL,
+ conn->bits.user_passwd ? conn->passwd : NULL);
+ if (rc != 0) {
+ ldap_proto = LDAP_VERSION2;
+ (*ldap_set_option)(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
+ rc = (*ldap_simple_bind_s)(server,
+ conn->bits.user_passwd ? conn->user : NULL,
+ conn->bits.user_passwd ? conn->passwd : NULL);
+ }
+ if (rc != 0) {
+ failf(data, "LDAP local: %s", (*ldap_err2string)(rc));
+ status = CURLE_LDAP_CANNOT_BIND;
+ goto quit;
+ }
+
+#ifdef WIN32
+ rc = _ldap_url_parse(conn, &ludp);
+#else
+ rc = (*ldap_url_parse)(data->change.url, &ludp);
+#endif
+
+ if (rc != 0) {
+ failf(data, "LDAP local: %s", (*ldap_err2string)(rc));
+ status = CURLE_LDAP_INVALID_URL;
+ goto quit;
+ }
+
+ rc = (*ldap_search_s)(server, ludp->lud_dn, ludp->lud_scope,
+ ludp->lud_filter, ludp->lud_attrs, 0, &result);
+
+ if (rc != 0 && rc != LDAP_SIZELIMIT_EXCEEDED) {
+ failf(data, "LDAP remote: %s", (*ldap_err2string)(rc));
+ status = CURLE_LDAP_SEARCH_FAILED;
+ goto quit;
+ }
+
+ for(num = 0, entryIterator = (*ldap_first_entry)(server, result);
+ entryIterator;
+ entryIterator = (*ldap_next_entry)(server, entryIterator), num++)
+ {
+ void *ber = NULL; /*! is really 'BerElement **' */
+ void *attribute; /*! suspicious that this isn't 'const' */
+ char *dn = (*ldap_get_dn)(server, entryIterator);
+ int i;
+
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *)dn, 0);
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
+
+ for (attribute = (*ldap_first_attribute)(server, entryIterator, &ber);
+ attribute;
+ attribute = (*ldap_next_attribute)(server, entryIterator, ber))
+ {
+ struct bv **vals = (struct bv **)
+ (*ldap_get_values_len)(server, entryIterator, attribute);
+
+ if (vals != NULL)
+ {
+ for (i = 0; (vals[i] != NULL); i++)
+ {
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *) attribute, 0);
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
+ if ((strlen(attribute) > 7) &&
+ (strcmp(";binary",
+ (char *)attribute +
+ (strlen((char *)attribute) - 7)) == 0)) {
+ /* Binary attribute, encode to base64. */
+ val_b64_sz = Curl_base64_encode(conn->data,
+ vals[i]->bv_val,
+ vals[i]->bv_len,
+ &val_b64);
+ if (val_b64_sz > 0) {
+ Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
+ free(val_b64);
+ }
+ } else
+ Curl_client_write(conn, CLIENTWRITE_BODY, vals[i]->bv_val,
+ vals[i]->bv_len);
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
+ }
+
+ /* Free memory used to store values */
+ (*ldap_value_free_len)((void **)vals);
+ }
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
+
+ (*ldap_memfree)(attribute);
+ }
+ (*ldap_memfree)(dn);
+ if (ber)
+ (*ber_free)(ber, 0);
+ }
+
+quit:
+ LDAP_TRACE (("Received %d entries\n", num));
+ if (rc == LDAP_SIZELIMIT_EXCEEDED)
+ infof(data, "There are more than %d entries\n", num);
+ if (ludp)
+ (*ldap_free_urldesc)(ludp);
+ if (server)
+ (*ldap_unbind_s)(server);
+
+ DynaClose();
+
+ /* no data to transfer */
+ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ conn->bits.close = TRUE;
+
+ return status;
+}
+
+#ifdef DEBUG_LDAP
+static void _ldap_trace (const char *fmt, ...)
+{
+ static int do_trace = -1;
+ va_list args;
+
+ if (do_trace == -1) {
+ const char *env = getenv("CURL_TRACE");
+ do_trace = (env && atoi(env) > 0);
+ }
+ if (!do_trace)
+ return;
+
+ va_start (args, fmt);
+ vfprintf (stderr, fmt, args);
+ va_end (args);
+}
+#endif
+
+#ifdef WIN32
+/*
+ * Return scope-value for a scope-string.
+ */
+static int str2scope (const char *p)
+{
+ if (!stricmp(p, "one"))
+ return LDAP_SCOPE_ONELEVEL;
+ if (!stricmp(p, "onetree"))
+ return LDAP_SCOPE_ONELEVEL;
+ if (!stricmp(p, "base"))
+ return LDAP_SCOPE_BASE;
+ if (!stricmp(p, "sub"))
+ return LDAP_SCOPE_SUBTREE;
+ if (!stricmp( p, "subtree"))
+ return LDAP_SCOPE_SUBTREE;
+ return (-1);
+}
+
+/*
+ * Split 'str' into strings separated by commas.
+ * Note: res[] points into 'str'.
+ */
+static char **split_str (char *str)
+{
+ char **res, *lasts, *s;
+ int i;
+
+ for (i = 2, s = strchr(str,','); s; i++)
+ s = strchr(++s,',');
+
+ res = calloc(i, sizeof(char*));
+ if (!res)
+ return NULL;
+
+ for (i = 0, s = strtok_r(str, ",", &lasts); s;
+ s = strtok_r(NULL, ",", &lasts), i++)
+ res[i] = s;
+ return res;
+}
+
+/*
+ * Unescape the LDAP-URL components
+ */
+static bool unescape_elements (void *data, LDAPURLDesc *ludp)
+{
+ int i;
+
+ if (ludp->lud_filter) {
+ ludp->lud_filter = curl_easy_unescape(data, ludp->lud_filter, 0, NULL);
+ if (!ludp->lud_filter)
+ return (FALSE);
+ }
+
+ for (i = 0; ludp->lud_attrs && ludp->lud_attrs[i]; i++) {
+ ludp->lud_attrs[i] = curl_easy_unescape(data, ludp->lud_attrs[i], 0, NULL);
+ if (!ludp->lud_attrs[i])
+ return (FALSE);
+ }
+
+ for (i = 0; ludp->lud_exts && ludp->lud_exts[i]; i++) {
+ ludp->lud_exts[i] = curl_easy_unescape(data, ludp->lud_exts[i], 0, NULL);
+ if (!ludp->lud_exts[i])
+ return (FALSE);
+ }
+
+ if (ludp->lud_dn) {
+ char *dn = ludp->lud_dn;
+ char *new_dn = curl_easy_unescape(data, dn, 0, NULL);
+
+ free(dn);
+ ludp->lud_dn = new_dn;
+ if (!new_dn)
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+/*
+ * Break apart the pieces of an LDAP URL.
+ * Syntax:
+ * ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext>
+ *
+ * <hostname> already known from 'conn->host.name'.
+ * <port> already known from 'conn->remote_port'.
+ * extract the rest from 'conn->data->reqdata.path+1'. All fields are optional.
+ * e.g.
+ * ldap://<hostname>:<port>/?<attributes>?<scope>?<filter>
+ * yields ludp->lud_dn = "".
+ *
+ * Ref. http://developer.netscape.com/docs/manuals/dirsdk/csdk30/url.htm#2831915
+ */
+static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp)
+{
+ char *p, *q;
+ int i;
+
+ if (!conn->data ||
+ !conn->data->reqdata.path ||
+ conn->data->reqdata.path[0] != '/' ||
+ !checkprefix(conn->protostr, conn->data->change.url))
+ return LDAP_INVALID_SYNTAX;
+
+ ludp->lud_scope = LDAP_SCOPE_BASE;
+ ludp->lud_port = conn->remote_port;
+ ludp->lud_host = conn->host.name;
+
+ /* parse DN (Distinguished Name).
+ */
+ ludp->lud_dn = strdup(conn->data->reqdata.path+1);
+ if (!ludp->lud_dn)
+ return LDAP_NO_MEMORY;
+
+ p = strchr(ludp->lud_dn, '?');
+ LDAP_TRACE (("DN '%.*s'\n", p ? (size_t)(p-ludp->lud_dn) :
+ strlen(ludp->lud_dn), ludp->lud_dn));
+
+ if (!p)
+ goto success;
+
+ *p++ = '\0';
+
+ /* parse attributes. skip "??".
+ */
+ q = strchr(p, '?');
+ if (q)
+ *q++ = '\0';
+
+ if (*p && *p != '?') {
+ ludp->lud_attrs = split_str(p);
+ if (!ludp->lud_attrs)
+ return LDAP_NO_MEMORY;
+
+ for (i = 0; ludp->lud_attrs[i]; i++)
+ LDAP_TRACE (("attr[%d] '%s'\n", i, ludp->lud_attrs[i]));
+ }
+
+ p = q;
+ if (!p)
+ goto success;
+
+ /* parse scope. skip "??"
+ */
+ q = strchr(p, '?');
+ if (q)
+ *q++ = '\0';
+
+ if (*p && *p != '?') {
+ ludp->lud_scope = str2scope(p);
+ if (ludp->lud_scope == -1)
+ return LDAP_INVALID_SYNTAX;
+ LDAP_TRACE (("scope %d\n", ludp->lud_scope));
+ }
+
+ p = q;
+ if (!p)
+ goto success;
+
+ /* parse filter
+ */
+ q = strchr(p, '?');
+ if (q)
+ *q++ = '\0';
+ if (!*p)
+ return LDAP_INVALID_SYNTAX;
+
+ ludp->lud_filter = p;
+ LDAP_TRACE (("filter '%s'\n", ludp->lud_filter));
+
+ p = q;
+ if (!p)
+ goto success;
+
+ /* parse extensions
+ */
+ ludp->lud_exts = split_str(p);
+ if (!ludp->lud_exts)
+ return LDAP_NO_MEMORY;
+
+ for (i = 0; ludp->lud_exts[i]; i++)
+ LDAP_TRACE (("exts[%d] '%s'\n", i, ludp->lud_exts[i]));
+
+success:
+ if (!unescape_elements(conn->data, ludp))
+ return LDAP_NO_MEMORY;
+ return LDAP_SUCCESS;
+}
+
+static int _ldap_url_parse (const struct connectdata *conn,
+ LDAPURLDesc **ludpp)
+{
+ LDAPURLDesc *ludp = calloc(sizeof(*ludp), 1);
+ int rc;
+
+ *ludpp = NULL;
+ if (!ludp)
+ return LDAP_NO_MEMORY;
+
+ rc = _ldap_url_parse2 (conn, ludp);
+ if (rc != LDAP_SUCCESS) {
+ _ldap_free_urldesc(ludp);
+ ludp = NULL;
+ }
+ *ludpp = ludp;
+ return (rc);
+}
+
+static void _ldap_free_urldesc (LDAPURLDesc *ludp)
+{
+ int i;
+
+ if (!ludp)
+ return;
+
+ if (ludp->lud_dn)
+ free(ludp->lud_dn);
+
+ if (ludp->lud_filter)
+ free(ludp->lud_filter);
+
+ if (ludp->lud_attrs) {
+ for (i = 0; ludp->lud_attrs[i]; i++)
+ free(ludp->lud_attrs[i]);
+ free(ludp->lud_attrs);
+ }
+
+ if (ludp->lud_exts) {
+ for (i = 0; ludp->lud_exts[i]; i++)
+ free(ludp->lud_exts[i]);
+ free(ludp->lud_exts);
+ }
+ free (ludp);
+}
+#endif /* WIN32 */
+#endif /* CURL_DISABLE_LDAP */
diff --git a/Utilities/cmcurl/ldap.h b/Utilities/cmcurl/ldap.h
new file mode 100644
index 000000000..b2d4f3973
--- /dev/null
+++ b/Utilities/cmcurl/ldap.h
@@ -0,0 +1,29 @@
+#ifndef __LDAP_H
+#define __LDAP_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#ifndef CURL_DISABLE_LDAP
+CURLcode Curl_ldap(struct connectdata *conn, bool *done);
+#endif
+#endif /* __LDAP_H */
diff --git a/Utilities/cmcurl/llist.c b/Utilities/cmcurl/llist.c
new file mode 100644
index 000000000..921d1d1f9
--- /dev/null
+++ b/Utilities/cmcurl/llist.c
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "llist.h"
+#include "memory.h"
+
+/* this must be the last include file */
+#include "memdebug.h"
+
+void
+Curl_llist_init(struct curl_llist *l, curl_llist_dtor dtor)
+{
+ l->size = 0;
+ l->dtor = dtor;
+ l->head = NULL;
+ l->tail = NULL;
+}
+
+struct curl_llist *
+Curl_llist_alloc(curl_llist_dtor dtor)
+{
+ struct curl_llist *list;
+
+ list = (struct curl_llist *)malloc(sizeof(struct curl_llist));
+ if(NULL == list)
+ return NULL;
+
+ Curl_llist_init(list, dtor);
+
+ return list;
+}
+
+/*
+ * Curl_llist_insert_next() returns 1 on success and 0 on failure.
+ */
+int
+Curl_llist_insert_next(struct curl_llist *list, struct curl_llist_element *e,
+ const void *p)
+{
+ struct curl_llist_element *ne =
+ (struct curl_llist_element *) malloc(sizeof(struct curl_llist_element));
+ if(!ne)
+ return 0;
+
+ ne->ptr = (void *) p;
+ if (list->size == 0) {
+ list->head = ne;
+ list->head->prev = NULL;
+ list->head->next = NULL;
+ list->tail = ne;
+ }
+ else {
+ 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_remove(struct curl_llist *list, struct curl_llist_element *e,
+ void *user)
+{
+ if (e == NULL || list->size == 0)
+ return 1;
+
+ if (e == list->head) {
+ list->head = e->next;
+
+ if (list->head == NULL)
+ list->tail = NULL;
+ else
+ e->next->prev = NULL;
+ } else {
+ e->prev->next = e->next;
+ if (!e->next)
+ list->tail = e->prev;
+ else
+ e->next->prev = e->prev;
+ }
+
+ list->dtor(user, e->ptr);
+ free(e);
+ --list->size;
+
+ return 1;
+}
+
+void
+Curl_llist_destroy(struct curl_llist *list, void *user)
+{
+ if(list) {
+ while (list->size > 0)
+ Curl_llist_remove(list, list->tail, user);
+
+ free(list);
+ }
+}
+
+size_t
+Curl_llist_count(struct curl_llist *list)
+{
+ return list->size;
+}
diff --git a/Utilities/cmcurl/llist.h b/Utilities/cmcurl/llist.h
new file mode 100644
index 000000000..5c7a8a008
--- /dev/null
+++ b/Utilities/cmcurl/llist.h
@@ -0,0 +1,60 @@
+#ifndef __LLIST_H
+#define __LLIST_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+#include <stddef.h>
+
+typedef void (*curl_llist_dtor)(void *, void *);
+
+struct curl_llist_element {
+ void *ptr;
+
+ struct curl_llist_element *prev;
+ struct curl_llist_element *next;
+};
+
+struct curl_llist {
+ struct curl_llist_element *head;
+ struct curl_llist_element *tail;
+
+ curl_llist_dtor dtor;
+
+ size_t size;
+};
+
+void Curl_llist_init(struct curl_llist *, curl_llist_dtor);
+struct curl_llist *Curl_llist_alloc(curl_llist_dtor);
+int Curl_llist_insert_next(struct curl_llist *, struct curl_llist_element *,
+ const void *);
+int Curl_llist_insert_prev(struct curl_llist *, struct curl_llist_element *,
+ const void *);
+int Curl_llist_remove(struct curl_llist *, struct curl_llist_element *,
+ void *);
+int Curl_llist_remove_next(struct curl_llist *, struct curl_llist_element *,
+ void *);
+size_t Curl_llist_count(struct curl_llist *);
+void Curl_llist_destroy(struct curl_llist *, void *);
+
+#endif
diff --git a/Utilities/cmcurl/md5.c b/Utilities/cmcurl/md5.c
new file mode 100644
index 000000000..4cfb073ea
--- /dev/null
+++ b/Utilities/cmcurl/md5.c
@@ -0,0 +1,352 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+
+#if !defined(USE_SSLEAY) || !defined(USE_OPENSSL)
+/* This code segment is only used if OpenSSL is not provided, as if it is
+ we use the MD5-function provided there instead. No good duplicating
+ code! */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+#include <string.h>
+
+/* UINT4 defines a four byte word */
+typedef unsigned int UINT4;
+
+/* MD5 context. */
+struct md5_ctx {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+};
+
+typedef struct md5_ctx MD5_CTX;
+
+static void MD5_Init(struct md5_ctx *);
+static void MD5_Update(struct md5_ctx *, const unsigned char *, unsigned int);
+static void MD5_Final(unsigned char [16], struct md5_ctx *);
+
+/* Constants for MD5Transform routine.
+ */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform(UINT4 [4], const unsigned char [64]);
+static void Encode(unsigned char *, UINT4 *, unsigned int);
+static void Decode(UINT4 *, const unsigned char *, unsigned int);
+
+static const unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+static void MD5_Init(struct md5_ctx *context)
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants. */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+static void MD5_Update (struct md5_ctx *context, /* context */
+ const unsigned char *input, /* input block */
+ unsigned int inputLen) /* length of input block */
+{
+ unsigned int i, bufindex, partLen;
+
+ /* Compute number of bytes mod 64 */
+ bufindex = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - bufindex;
+
+ /* Transform as many times as possible. */
+ if (inputLen >= partLen) {
+ memcpy((void *)&context->buffer[bufindex], (void *)input, partLen);
+ MD5Transform(context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform(context->state, &input[i]);
+
+ bufindex = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy((void *)&context->buffer[bufindex], (void *)&input[i], inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ the message digest and zeroizing the context.
+*/
+static void MD5_Final(unsigned char digest[16], /* message digest */
+ struct md5_ctx *context) /* context */
+{
+ unsigned char bits[8];
+ unsigned int count, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64. */
+ count = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (count < 56) ? (56 - count) : (120 - count);
+ MD5_Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5_Update (context, bits, 8);
+
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information. */
+ memset ((void *)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block. */
+static void MD5Transform(UINT4 state[4],
+ const unsigned char block[64])
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information. */
+ memset((void *)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void Encode (unsigned char *output,
+ UINT4 *input,
+ unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+*/
+static void Decode (UINT4 *output,
+ const unsigned char *input,
+ unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+#else
+/* If OpenSSL is present */
+#include <openssl/md5.h>
+#include <string.h>
+#endif
+
+#include "md5.h"
+
+void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */
+ const unsigned char *input)
+{
+ MD5_CTX ctx;
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, input, (unsigned int)strlen((char *)input));
+ MD5_Final(outbuffer, &ctx);
+}
+
+#endif
diff --git a/Utilities/cmcurl/md5.h b/Utilities/cmcurl/md5.h
new file mode 100644
index 000000000..63cc70e84
--- /dev/null
+++ b/Utilities/cmcurl/md5.h
@@ -0,0 +1,29 @@
+#ifndef __MD5_H
+#define __MD5_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+void Curl_md5it(unsigned char *output,
+ const unsigned char *input);
+
+#endif
diff --git a/Utilities/cmcurl/memdebug.c b/Utilities/cmcurl/memdebug.c
new file mode 100644
index 000000000..a8cca44cb
--- /dev/null
+++ b/Utilities/cmcurl/memdebug.c
@@ -0,0 +1,298 @@
+#ifdef CURLDEBUG
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <curl/curl.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#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
+
+#define MEMDEBUG_NODEFINES /* don't redefine the standard functions */
+#include "memory.h"
+#include "memdebug.h"
+
+struct memdebug {
+ size_t size;
+ double mem[1];
+ /* I'm hoping this is the thing with the strictest alignment
+ * requirements. That also means we waste some space :-( */
+};
+
+/*
+ * Note that these debug functions are very simple and they are meant to
+ * remain so. For advanced analysis, record a log file and write perl scripts
+ * to analyze them!
+ *
+ * Don't use these with multithreaded test programs!
+ */
+
+#define logfile curl_debuglogfile
+FILE *curl_debuglogfile = NULL;
+static bool memlimit = FALSE; /* enable memory limit */
+static long memsize = 0; /* set number of mallocs allowed */
+
+/* this sets the log file name */
+void curl_memdebug(const char *logname)
+{
+ if (!logfile) {
+ if(logname)
+ logfile = fopen(logname, "w");
+ else
+ logfile = stderr;
+ }
+}
+
+/* This function sets the number of malloc() calls that should return
+ successfully! */
+void curl_memlimit(long limit)
+{
+ if (!memlimit) {
+ memlimit = TRUE;
+ memsize = limit;
+ }
+}
+
+/* returns TRUE if this isn't allowed! */
+static bool countcheck(const char *func, int line, const char *source)
+{
+ /* if source is NULL, then the call is made internally and this check
+ should not be made */
+ if(memlimit && source) {
+ if(!memsize) {
+ if(logfile && source)
+ fprintf(logfile, "LIMIT %s:%d %s reached memlimit\n",
+ source, line, func);
+ if(source)
+ fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
+ source, line, func);
+ errno = ENOMEM;
+ return TRUE; /* RETURN ERROR! */
+ }
+ else
+ memsize--; /* countdown */
+
+ /* log the countdown */
+ if(logfile && source)
+ fprintf(logfile, "LIMIT %s:%d %ld ALLOCS left\n",
+ source, line, memsize);
+
+ }
+
+ return FALSE; /* allow this */
+}
+
+void *curl_domalloc(size_t wantedsize, int line, const char *source)
+{
+ struct memdebug *mem;
+ size_t size;
+
+ if(countcheck("malloc", line, source))
+ return NULL;
+
+ /* alloc at least 64 bytes */
+ size = sizeof(struct memdebug)+wantedsize;
+
+ mem=(struct memdebug *)(Curl_cmalloc)(size);
+ if(mem) {
+ /* fill memory with junk */
+ memset(mem->mem, 0xA5, wantedsize);
+ mem->size = wantedsize;
+ }
+
+ if(logfile && source)
+ fprintf(logfile, "MEM %s:%d malloc(%zd) = %p\n",
+ source, line, wantedsize, mem ? mem->mem : 0);
+ return (mem ? mem->mem : NULL);
+}
+
+void *curl_docalloc(size_t wanted_elements, size_t wanted_size,
+ int line, const char *source)
+{
+ struct memdebug *mem;
+ size_t size, user_size;
+
+ if(countcheck("calloc", line, source))
+ return NULL;
+
+ /* alloc at least 64 bytes */
+ user_size = wanted_size * wanted_elements;
+ size = sizeof(struct memdebug) + user_size;
+
+ mem = (struct memdebug *)(Curl_cmalloc)(size);
+ if(mem) {
+ /* fill memory with zeroes */
+ memset(mem->mem, 0, user_size);
+ mem->size = user_size;
+ }
+
+ if(logfile && source)
+ fprintf(logfile, "MEM %s:%d calloc(%u,%u) = %p\n",
+ source, line, wanted_elements, wanted_size, mem ? mem->mem : 0);
+ return (mem ? mem->mem : NULL);
+}
+
+char *curl_dostrdup(const char *str, int line, const char *source)
+{
+ char *mem;
+ size_t len;
+
+ curlassert(str != NULL);
+
+ if(countcheck("strdup", line, source))
+ return NULL;
+
+ len=strlen(str)+1;
+
+ mem=curl_domalloc(len, 0, NULL); /* NULL prevents logging */
+ if (mem)
+ memcpy(mem, str, len);
+
+ if(logfile)
+ fprintf(logfile, "MEM %s:%d strdup(%p) (%zd) = %p\n",
+ source, line, str, len, mem);
+
+ return mem;
+}
+
+/* We provide a realloc() that accepts a NULL as pointer, which then
+ performs a malloc(). In order to work with ares. */
+void *curl_dorealloc(void *ptr, size_t wantedsize,
+ int line, const char *source)
+{
+ struct memdebug *mem=NULL;
+
+ size_t size = sizeof(struct memdebug)+wantedsize;
+
+ if(countcheck("realloc", line, source))
+ return NULL;
+
+ if(ptr)
+ mem = (struct memdebug *)((char *)ptr - offsetof(struct memdebug, mem));
+
+ mem=(struct memdebug *)(Curl_crealloc)(mem, size);
+ if(logfile)
+ fprintf(logfile, "MEM %s:%d realloc(%p, %zd) = %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;
+
+ curlassert(ptr != NULL);
+
+ mem = (struct memdebug *)((char *)ptr - offsetof(struct memdebug, mem));
+
+ /* destroy */
+ memset(mem->mem, 0x13, mem->size);
+
+ /* free for real */
+ (Curl_cfree)(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,
+ const char *source)
+{
+ int sockfd=(socket)(domain, type, protocol);
+ if(logfile && (sockfd!=-1))
+ fprintf(logfile, "FD %s:%d socket() = %d\n",
+ source, line, sockfd);
+ return sockfd;
+}
+
+int curl_accept(int s, void *saddr, void *saddrlen,
+ int line, const char *source)
+{
+ struct sockaddr *addr = (struct sockaddr *)saddr;
+ socklen_t *addrlen = (socklen_t *)saddrlen;
+ 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, const 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\",\"%s\") = %p\n",
+ source, line, file, mode, res);
+ return res;
+}
+
+int curl_fclose(FILE *file, int line, const char *source)
+{
+ int res;
+
+ curlassert(file != NULL);
+
+ 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;
+#else
+/* we provide a fake do-nothing function here to avoid compiler warnings */
+void curl_memdebug(void) {}
+#endif /* VMS */
+#endif /* CURLDEBUG */
diff --git a/Utilities/cmcurl/memdebug.h b/Utilities/cmcurl/memdebug.h
new file mode 100644
index 000000000..a4ce7e59a
--- /dev/null
+++ b/Utilities/cmcurl/memdebug.h
@@ -0,0 +1,125 @@
+#ifdef CURLDEBUG
+#ifndef _CURL_MEDEBUG_H
+#define _CURL_MEDEBUG_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ * CAUTION: this header is designed to work when included by the app-side
+ * as well as the library. Do not mix with library internals!
+ */
+
+#include "setup.h"
+
+#include <curl/curl.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
+
+#define logfile curl_debuglogfile
+
+extern FILE *logfile;
+
+/* memory functions */
+CURL_EXTERN void *curl_domalloc(size_t size, int line, const char *source);
+CURL_EXTERN void *curl_docalloc(size_t elements, size_t size, int line, const char *source);
+CURL_EXTERN void *curl_dorealloc(void *ptr, size_t size, int line, const char *source);
+CURL_EXTERN void curl_dofree(void *ptr, int line, const char *source);
+CURL_EXTERN char *curl_dostrdup(const char *str, int line, const char *source);
+CURL_EXTERN void curl_memdebug(const char *logname);
+CURL_EXTERN void curl_memlimit(long limit);
+
+/* file descriptor manipulators */
+CURL_EXTERN int curl_socket(int domain, int type, int protocol, int line , const char *);
+CURL_EXTERN int curl_sclose(int sockfd, int, const char *source);
+CURL_EXTERN int curl_accept(int s, void *addr, void *addrlen,
+ int line, const char *source);
+
+/* FILE functions */
+CURL_EXTERN FILE *curl_fopen(const char *file, const char *mode, int line,
+ const char *source);
+CURL_EXTERN int curl_fclose(FILE *file, int line, const char *source);
+
+#ifndef MEMDEBUG_NODEFINES
+
+/* Set this symbol on the command-line, recompile all lib-sources */
+#undef strdup
+#define strdup(ptr) curl_dostrdup(ptr, __LINE__, __FILE__)
+#define malloc(size) curl_domalloc(size, __LINE__, __FILE__)
+#define calloc(nbelem,size) curl_docalloc(nbelem, 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__)
+#undef accept /* for those with accept as a macro */
+#define accept(sock,addr,len)\
+ curl_accept(sock,addr,len,__LINE__,__FILE__)
+
+#if defined(getaddrinfo) && defined(__osf__)
+/* OSF/1 and Tru64 have getaddrinfo as a define already, so we cannot define
+ our macro as for other platforms. Instead, we redefine the new name they
+ define getaddrinfo to become! */
+#define ogetaddrinfo(host,serv,hint,res) \
+ curl_dogetaddrinfo(host,serv,hint,res,__LINE__,__FILE__)
+#else
+#undef getaddrinfo
+#define getaddrinfo(host,serv,hint,res) \
+ curl_dogetaddrinfo(host,serv,hint,res,__LINE__,__FILE__)
+#endif
+
+#ifdef HAVE_GETNAMEINFO
+#undef getnameinfo
+#define getnameinfo(sa,salen,host,hostlen,serv,servlen,flags) \
+ curl_dogetnameinfo(sa,salen,host,hostlen,serv,servlen,flags, __LINE__, \
+ __FILE__)
+#endif
+
+#undef freeaddrinfo
+#define freeaddrinfo(data) \
+ curl_dofreeaddrinfo(data,__LINE__,__FILE__)
+
+/* sclose is probably already defined, redefine it! */
+#undef sclose
+#define sclose(sockfd) curl_sclose(sockfd,__LINE__,__FILE__)
+/* ares-adjusted define: */
+#undef closesocket
+#define closesocket(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 /* MEMDEBUG_NODEFINES */
+
+#endif /* _CURL_MEDEBUG_H */
+#endif /* CURLDEBUG */
diff --git a/Utilities/cmcurl/memory.h b/Utilities/cmcurl/memory.h
new file mode 100644
index 000000000..076d20a28
--- /dev/null
+++ b/Utilities/cmcurl/memory.h
@@ -0,0 +1,50 @@
+#ifndef _CURL_MEMORY_H
+#define _CURL_MEMORY_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include <curl/curl.h> /* for the typedefs */
+
+extern curl_malloc_callback Curl_cmalloc;
+extern curl_free_callback Curl_cfree;
+extern curl_realloc_callback Curl_crealloc;
+extern curl_strdup_callback Curl_cstrdup;
+extern curl_calloc_callback Curl_ccalloc;
+
+#ifndef CURLDEBUG
+/* Only do this define-mania if we're not using the memdebug system, as that
+ has preference on this magic. */
+#undef strdup
+#define strdup(ptr) Curl_cstrdup(ptr)
+#undef malloc
+#define malloc(size) Curl_cmalloc(size)
+#undef calloc
+#define calloc(nbelem,size) Curl_ccalloc(nbelem, size)
+#undef realloc
+#define realloc(ptr,size) Curl_crealloc(ptr, size)
+#undef free
+#define free(ptr) Curl_cfree(ptr)
+
+#endif
+
+#endif /* _CURL_MEMORY_H */
diff --git a/Utilities/cmcurl/mprintf.c b/Utilities/cmcurl/mprintf.c
new file mode 100644
index 000000000..8b2f3d07e
--- /dev/null
+++ b/Utilities/cmcurl/mprintf.c
@@ -0,0 +1,1218 @@
+/****************************************************************************
+ *
+ * $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>
+
+#if defined(DJGPP) && (DJGPP_MINOR < 4)
+#undef _MPRINTF_REPLACE /* don't use x_was_used() here */
+#endif
+
+#include <curl/mprintf.h>
+
+#ifndef SIZEOF_SIZE_T
+/* default to 4 bytes for size_t unless defined in the config.h */
+#define SIZEOF_SIZE_T 4
+#endif
+
+#ifdef DPRINTF_DEBUG
+#define HAVE_LONGLONG
+#define LONG_LONG long long
+#define ENABLE_64BIT
+#endif
+
+#include "memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#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
+
+#ifdef _AMIGASF
+# undef FORMAT_INT
+#endif
+
+/* Lower-case digits. */
+static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+/* Upper-case digits. */
+static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+#define OUTCHAR(x) \
+ do{ \
+ if(stream((unsigned char)(x), (FILE *)data) != -1) \
+ done++; \
+ else \
+ return done; /* return immediately on failure */ \
+ } while(0)
+
+/* 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;
+ long width; /* width OR width parameter number */
+ long precision; /* precision OR precision parameter number */
+ union {
+ char *str;
+ void *ptr;
+ long num;
+#ifdef ENABLE_64BIT
+ LONG_LONG lnum;
+#endif
+ double dnum;
+ } data;
+} va_stack_t;
+
+struct nsprintf {
+ char *buffer;
+ size_t length;
+ size_t max;
+};
+
+struct asprintf {
+ char *buffer; /* allocated buffer */
+ size_t len; /* length of string */
+ size_t alloc; /* length of alloc */
+ bool fail; /* TRUE if an alloc has failed and thus the output is not
+ the complete data */
+};
+
+int curl_msprintf(char *buffer, const char *format, ...);
+
+static long dprintf_DollarString(char *input, char **end)
+{
+ int number=0;
+ while(ISDIGIT(*input)) {
+ number *= 10;
+ number += *input-'0';
+ input++;
+ }
+ if(number && ('$'==*input++)) {
+ *end = input;
+ return number;
+ }
+ return 0;
+}
+
+static 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':
+ case '*': case 'O':
+ 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 long dprintf_Pass1(char *format, va_stack_t *vto, char **endpos,
+ va_list arglist)
+{
+ char *fmt = format;
+ int param_num = 0;
+ long this_param;
+ long width;
+ long precision;
+ int flags;
+ long max_param=0;
+ long i;
+
+ while (*fmt) {
+ if (*fmt++ == '%') {
+ if (*fmt == '%') {
+ fmt++;
+ continue; /* while */
+ }
+
+ flags = FLAGS_NEW;
+
+ /* Handle the positional case (N$) */
+
+ param_num++;
+
+ this_param = dprintf_DollarString(fmt, &fmt);
+ if (0 == this_param)
+ /* we got no positional, get the next counter */
+ this_param = param_num;
+
+ if (this_param > max_param)
+ max_param = this_param;
+
+ /*
+ * The parameter with number 'i' should be used. Next, we need
+ * to get SIZE and TYPE of the parameter. Add the information
+ * to our array.
+ */
+
+ width = 0;
+ precision = 0;
+
+ /* Handle the flags */
+
+ while (dprintf_IsQualifierNoDollar(*fmt)) {
+ switch (*fmt++) {
+ case ' ':
+ flags |= FLAGS_SPACE;
+ break;
+ case '+':
+ flags |= FLAGS_SHOWSIGN;
+ break;
+ case '-':
+ flags |= FLAGS_LEFT;
+ flags &= ~FLAGS_PAD_NIL;
+ break;
+ case '#':
+ flags |= FLAGS_ALT;
+ break;
+ case '.':
+ flags |= FLAGS_PREC;
+ if ('*' == *fmt) {
+ /* The precision is picked from a specified parameter */
+
+ flags |= FLAGS_PRECPARAM;
+ fmt++;
+ param_num++;
+
+ i = dprintf_DollarString(fmt, &fmt);
+ if (i)
+ precision = i;
+ else
+ precision = param_num;
+
+ if (precision > max_param)
+ max_param = precision;
+ }
+ else {
+ flags |= FLAGS_PREC;
+ precision = strtol(fmt, &fmt, 10);
+ }
+ break;
+ case 'h':
+ flags |= FLAGS_SHORT;
+ break;
+ case 'l':
+ if (flags & FLAGS_LONG)
+ flags |= FLAGS_LONGLONG;
+ else
+ flags |= FLAGS_LONG;
+ break;
+ case 'L':
+ flags |= FLAGS_LONGDOUBLE;
+ break;
+ case 'q':
+ flags |= FLAGS_LONGLONG;
+ break;
+ case 'z':
+ /* the code below generates a warning if -Wunreachable-code is
+ used */
+#if SIZEOF_SIZE_T>4
+ flags |= FLAGS_LONGLONG;
+#else
+ flags |= FLAGS_LONG;
+#endif
+ break;
+ case 'O':
+#if SIZEOF_CURL_OFF_T > 4
+ flags |= FLAGS_LONGLONG;
+#else
+ flags |= FLAGS_LONG;
+#endif
+ break;
+ case '0':
+ if (!(flags & FLAGS_LEFT))
+ flags |= FLAGS_PAD_NIL;
+ /* FALLTHROUGH */
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ flags |= FLAGS_WIDTH;
+ width = strtol(fmt-1, &fmt, 10);
+ break;
+ case '*': /* Special case */
+ flags |= FLAGS_WIDTHPARAM;
+ param_num++;
+
+ i = dprintf_DollarString(fmt, &fmt);
+ if(i)
+ width = i;
+ else
+ width = param_num;
+ if(width > max_param)
+ max_param=width;
+ break;
+ default:
+ break;
+ }
+ } /* switch */
+
+ /* Handle the specifier */
+
+ i = this_param - 1;
+
+ switch (*fmt) {
+ case 'S':
+ flags |= FLAGS_ALT;
+ /* FALLTHROUGH */
+ case 's':
+ vto[i].type = FORMAT_STRING;
+ break;
+ case 'n':
+ vto[i].type = FORMAT_INTPTR;
+ break;
+ case 'p':
+ vto[i].type = FORMAT_PTR;
+ break;
+ case 'd': case 'i':
+ vto[i].type = FORMAT_INT;
+ break;
+ case 'u':
+ vto[i].type = FORMAT_INT;
+ flags |= FLAGS_UNSIGNED;
+ break;
+ case 'o':
+ vto[i].type = FORMAT_INT;
+ flags |= FLAGS_OCTAL;
+ break;
+ case 'x':
+ vto[i].type = FORMAT_INT;
+ flags |= FLAGS_HEX;
+ break;
+ case 'X':
+ vto[i].type = FORMAT_INT;
+ flags |= FLAGS_HEX|FLAGS_UPPER;
+ break;
+ case 'c':
+ vto[i].type = FORMAT_INT;
+ flags |= FLAGS_CHAR;
+ break;
+ case 'f':
+ vto[i].type = FORMAT_DOUBLE;
+ break;
+ case 'e':
+ vto[i].type = FORMAT_DOUBLE;
+ flags |= FLAGS_FLOATE;
+ break;
+ case 'E':
+ vto[i].type = FORMAT_DOUBLE;
+ flags |= FLAGS_FLOATE|FLAGS_UPPER;
+ break;
+ case 'g':
+ vto[i].type = FORMAT_DOUBLE;
+ flags |= FLAGS_FLOATG;
+ break;
+ case 'G':
+ vto[i].type = FORMAT_DOUBLE;
+ flags |= FLAGS_FLOATG|FLAGS_UPPER;
+ break;
+ default:
+ vto[i].type = FORMAT_UNKNOWN;
+ break;
+ } /* switch */
+
+ vto[i].flags = flags;
+ vto[i].width = width;
+ vto[i].precision = precision;
+
+ if (flags & FLAGS_WIDTHPARAM) {
+ /* we have the width specified from a parameter, so we make that
+ parameter's info setup properly */
+ vto[i].width = width - 1;
+ i = width - 1;
+ vto[i].type = FORMAT_WIDTH;
+ vto[i].flags = FLAGS_NEW;
+ vto[i].precision = vto[i].width = 0; /* can't use width or precision
+ of width! */
+ }
+ if (flags & FLAGS_PRECPARAM) {
+ /* we have the precision specified from a parameter, so we make that
+ parameter's info setup properly */
+ vto[i].precision = precision - 1;
+ i = precision - 1;
+ vto[i].type = FORMAT_WIDTH;
+ vto[i].flags = FLAGS_NEW;
+ vto[i].precision = vto[i].width = 0; /* can't use width or precision
+ of width! */
+ }
+ *endpos++ = fmt + 1; /* end of this sequence */
+ }
+ }
+
+#ifdef DPRINTF_DEBUG2
+ dprintf_Pass1Report(vto, max_param);
+#endif
+
+ /* Read the arg list parameters into our data list */
+ for (i=0; i<max_param; i++) {
+ if ((i + 1 < max_param) && (vto[i + 1].type == FORMAT_WIDTH))
+ {
+ /* Width/precision arguments must be read before the main argument
+ * they are attached to
+ */
+ vto[i + 1].data.num = va_arg(arglist, int);
+ }
+
+ switch (vto[i].type)
+ {
+ case FORMAT_STRING:
+ vto[i].data.str = va_arg(arglist, char *);
+ break;
+
+ case FORMAT_INTPTR:
+ case FORMAT_UNKNOWN:
+ case FORMAT_PTR:
+ vto[i].data.ptr = va_arg(arglist, void *);
+ break;
+
+ case FORMAT_INT:
+#ifdef ENABLE_64BIT
+ 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:
+ vto[i].data.dnum = va_arg(arglist, double);
+ break;
+
+ case FORMAT_WIDTH:
+ /* Argument has been read. Silently convert it into an integer
+ * for later use
+ */
+ vto[i].type = FORMAT_INT;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return max_param;
+
+}
+
+static int dprintf_formatf(
+ void *data, /* untouched by format(), just sent to the stream() function in
+ the second argument */
+ /* function pointer called for each output character */
+ int (*stream)(int, FILE *),
+ const char *format, /* %-formatted string */
+ va_list ap_save) /* list of parameters */
+{
+ /* Base-36 digits for numbers. */
+ const char *digits = lower_digits;
+
+ /* Pointer into the format string. */
+ char *f;
+
+ /* Number of characters written. */
+ int done = 0;
+
+ long param; /* current parameter to read */
+ long param_num=0; /* parameter counter */
+
+ va_stack_t vto[MAX_PARAMETERS];
+ char *endpos[MAX_PARAMETERS];
+ char **end;
+
+ char work[BUFFSIZE];
+
+ va_stack_t *p;
+
+ /* Do the actual %-code parsing */
+ dprintf_Pass1((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. */
+ 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. */
+#ifdef ENABLE_64BIT
+ 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)?TRUE:FALSE;
+
+ 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;
+
+#ifdef ENABLE_64BIT
+ 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];
+ char *w;
+
+ /* Supply a default precision if none was given. */
+ if (prec == -1)
+ prec = 1;
+
+ /* Put the number in WORK. */
+ w = workend;
+ while (num > 0) {
+ *w-- = digits[num % base];
+ num /= base;
+ }
+ width -= (long)(workend - w);
+ prec -= (long)(workend - w);
+
+ if (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 const char null[] = "(nil)";
+ const char *str;
+ size_t len;
+
+ str = (char *) p->data.str;
+ if ( str == NULL) {
+ /* Write null[] if there's space. */
+ if (prec == -1 || prec >= (long) sizeof(null) - 1) {
+ str = null;
+ len = sizeof(null) - 1;
+ /* Disable quotes around (nil) */
+ p->flags &= (~FLAGS_ALT);
+ }
+ else {
+ str = "";
+ len = 0;
+ }
+ }
+ else
+ len = strlen(str);
+
+ if (prec != -1 && (size_t) prec < len)
+ len = prec;
+ width -= (long)len;
+
+ if (p->flags & FLAGS_ALT)
+ OUTCHAR('"');
+
+ if (!(p->flags&FLAGS_LEFT))
+ while (width-- > 0)
+ OUTCHAR(' ');
+
+ while (len-- > 0)
+ OUTCHAR(*str++);
+ if (p->flags&FLAGS_LEFT)
+ while (width-- > 0)
+ OUTCHAR(' ');
+
+ if (p->flags & FLAGS_ALT)
+ OUTCHAR('"');
+ }
+ break;
+
+ case FORMAT_PTR:
+ /* Generic pointer. */
+ {
+ void *ptr;
+ ptr = (void *) p->data.ptr;
+ if (ptr != NULL) {
+ /* If the pointer is not NULL, write it as a %#x spec. */
+ base = 16;
+ digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
+ alt = 1;
+ num = (size_t) ptr;
+ is_neg = 0;
+ goto number;
+ }
+ else {
+ /* Write "(nil)" for a nil pointer. */
+ static const char strnil[] = "(nil)";
+ const char *point;
+
+ width -= sizeof(strnil) - 1;
+ if (p->flags & FLAGS_LEFT)
+ while (width-- > 0)
+ OUTCHAR(' ');
+ for (point = strnil; *point != '\0'; ++point)
+ OUTCHAR(*point);
+ if (! (p->flags & FLAGS_LEFT))
+ while (width-- > 0)
+ OUTCHAR(' ');
+ }
+ }
+ break;
+
+ case FORMAT_DOUBLE:
+ {
+ char formatbuf[32]="%";
+ char *fptr;
+ size_t left = sizeof(formatbuf)-strlen(formatbuf);
+ int len;
+
+ width = -1;
+ if (p->flags & FLAGS_WIDTH)
+ width = p->width;
+ else if (p->flags & FLAGS_WIDTHPARAM)
+ width = 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 */
+ len = curl_msnprintf(fptr, left, "%ld", width);
+ fptr += len;
+ left -= len;
+ }
+ if(prec >= 0) {
+ /* RECURSIVE USAGE */
+ len = curl_msnprintf(fptr, left, ".%ld", prec);
+ fptr += len;
+ left -= len;
+ }
+ if (p->flags & FLAGS_LONG)
+ *fptr++ = 'l';
+
+ if (p->flags & FLAGS_FLOATE)
+ *fptr++ = p->flags&FLAGS_UPPER ? 'E':'e';
+ else if (p->flags & FLAGS_FLOATG)
+ *fptr++ = p->flags & FLAGS_UPPER ? 'G' : 'g';
+ else
+ *fptr++ = 'f';
+
+ *fptr = 0; /* and a final zero termination */
+
+ /* NOTE NOTE NOTE!! Not all sprintf() implementations returns number
+ of output characters */
+ (sprintf)(work, formatbuf, p->data.dnum);
+
+ for(fptr=work; *fptr; fptr++)
+ OUTCHAR(*fptr);
+ }
+ break;
+
+ case FORMAT_INTPTR:
+ /* Answer the count of characters written. */
+#ifdef ENABLE_64BIT
+ if (p->flags & FLAGS_LONGLONG)
+ *(LONG_LONG *) p->data.ptr = (LONG_LONG)done;
+ else
+#endif
+ if (p->flags & FLAGS_LONG)
+ *(long *) p->data.ptr = (long)done;
+ else if (!(p->flags & FLAGS_SHORT))
+ *(int *) p->data.ptr = (int)done;
+ else
+ *(short *) p->data.ptr = (short)done;
+ break;
+
+ default:
+ break;
+ }
+ f = *end++; /* goto end of %-code */
+
+ }
+ return done;
+}
+
+/* fputc() look-alike */
+static int addbyter(int output, FILE *data)
+{
+ struct nsprintf *infop=(struct nsprintf *)data;
+ unsigned char outc = (unsigned char)output;
+
+ if(infop->length < infop->max) {
+ /* only do this if we haven't reached max length yet */
+ infop->buffer[0] = outc; /* store */
+ infop->buffer++; /* increase pointer */
+ infop->length++; /* we are now one byte larger */
+ return outc; /* fputc() returns like this on success */
+ }
+ return -1;
+}
+
+int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
+ va_list ap_save)
+{
+ int retcode;
+ struct nsprintf info;
+
+ info.buffer = buffer;
+ info.length = 0;
+ info.max = maxlength;
+
+ retcode = dprintf_formatf(&info, addbyter, format, ap_save);
+ if(info.max) {
+ /* we terminate this with a zero byte */
+ if(info.max == info.length)
+ /* we're at maximum, scrap the last letter */
+ info.buffer[-1] = 0;
+ else
+ info.buffer[0] = 0;
+ }
+ return retcode;
+}
+
+int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
+{
+ int retcode;
+ va_list ap_save; /* argument pointer */
+ va_start(ap_save, format);
+ retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
+ va_end(ap_save);
+ return retcode;
+}
+
+/* fputc() look-alike */
+static int alloc_addbyter(int output, FILE *data)
+{
+ struct asprintf *infop=(struct asprintf *)data;
+ unsigned char outc = (unsigned char)output;
+
+ if(!infop->buffer) {
+ infop->buffer=(char *)malloc(32);
+ if(!infop->buffer) {
+ infop->fail = TRUE;
+ 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) {
+ infop->fail = TRUE;
+ return -1;
+ }
+ infop->buffer = newptr;
+ infop->alloc *= 2;
+ }
+
+ infop->buffer[ infop->len ] = outc;
+
+ infop->len++;
+
+ return outc; /* fputc() returns like this on success */
+}
+
+char *curl_maprintf(const char *format, ...)
+{
+ va_list ap_save; /* argument pointer */
+ int retcode;
+ struct asprintf info;
+
+ info.buffer = NULL;
+ info.len = 0;
+ info.alloc = 0;
+ info.fail = FALSE;
+
+ va_start(ap_save, format);
+ retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
+ va_end(ap_save);
+ if((-1 == retcode) || info.fail) {
+ if(info.alloc)
+ free(info.buffer);
+ return NULL;
+ }
+ if(info.alloc) {
+ info.buffer[info.len] = 0; /* we terminate this with a zero byte */
+ return info.buffer;
+ }
+ else
+ return strdup("");
+}
+
+char *curl_mvaprintf(const char *format, va_list ap_save)
+{
+ int retcode;
+ struct asprintf info;
+
+ info.buffer = NULL;
+ info.len = 0;
+ info.alloc = 0;
+ info.fail = FALSE;
+
+ retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
+ if((-1 == retcode) || info.fail) {
+ if(info.alloc)
+ free(info.buffer);
+ return NULL;
+ }
+
+ if(info.alloc) {
+ info.buffer[info.len] = 0; /* we terminate this with a zero byte */
+ return info.buffer;
+ }
+ else
+ return strdup("");
+}
+
+static int storebuffer(int output, FILE *data)
+{
+ char **buffer = (char **)data;
+ unsigned char outc = (unsigned char)output;
+ **buffer = outc;
+ (*buffer)++;
+ return outc; /* act like fputc() ! */
+}
+
+int curl_msprintf(char *buffer, const char *format, ...)
+{
+ va_list ap_save; /* argument pointer */
+ int retcode;
+ va_start(ap_save, format);
+ retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
+ va_end(ap_save);
+ *buffer=0; /* we terminate this with a zero byte */
+ return retcode;
+}
+
+int curl_mprintf(const char *format, ...)
+{
+ int retcode;
+ va_list ap_save; /* argument pointer */
+ va_start(ap_save, format);
+
+ retcode = dprintf_formatf(stdout, fputc, format, ap_save);
+ va_end(ap_save);
+ return retcode;
+}
+
+int curl_mfprintf(FILE *whereto, const char *format, ...)
+{
+ int retcode;
+ va_list ap_save; /* argument pointer */
+ va_start(ap_save, format);
+ retcode = dprintf_formatf(whereto, fputc, format, ap_save);
+ va_end(ap_save);
+ return retcode;
+}
+
+int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
+{
+ int retcode;
+ retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
+ *buffer=0; /* we terminate this with a zero byte */
+ return retcode;
+}
+
+int curl_mvprintf(const char *format, va_list ap_save)
+{
+ return dprintf_formatf(stdout, fputc, format, ap_save);
+}
+
+int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
+{
+ return dprintf_formatf(whereto, fputc, format, ap_save);
+}
+
+#ifdef DPRINTF_DEBUG
+int main()
+{
+ char buffer[129];
+ char *ptr;
+#ifdef ENABLE_64BIT
+ long long one=99;
+ long long two=100;
+ long long test = 0x1000000000LL;
+ curl_mprintf("%lld %lld %lld\n", one, two, test);
+#endif
+
+ curl_mprintf("%3d %5d\n", 10, 1998);
+
+ ptr=curl_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
+ curl_mprintf(buffer, "%s %s %d", "daniel", "stenberg", 19988);
+ puts(buffer);
+
+ curl_mfprintf(stderr, "%s %#08x\n", "dummy", 65);
+
+ printf("%s %#08x\n", "dummy", 65);
+ {
+ double tryout = 3.14156592;
+ curl_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
diff --git a/Utilities/cmcurl/multi.c b/Utilities/cmcurl/multi.c
new file mode 100644
index 000000000..b501f296f
--- /dev/null
+++ b/Utilities/cmcurl/multi.c
@@ -0,0 +1,1988 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "transfer.h"
+#include "url.h"
+#include "connect.h"
+#include "progress.h"
+#include "memory.h"
+#include "easyif.h"
+#include "multiif.h"
+#include "sendf.h"
+#include "timeval.h"
+
+/* The last #include file should be: */
+#include "memdebug.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, /* start in this state */
+ CURLM_STATE_CONNECT, /* resolve/connect has been sent off */
+ CURLM_STATE_WAITRESOLVE, /* awaiting the resolve to finalize */
+ CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */
+ CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect
+ phase */
+ CURLM_STATE_WAITDO, /* wait for our turn to send the request */
+ CURLM_STATE_DO, /* start send off the request (part 1) */
+ CURLM_STATE_DOING, /* sending off the request (part 1) */
+ CURLM_STATE_DO_MORE, /* send off the request (part 2) */
+ CURLM_STATE_DO_DONE, /* done sending off request */
+ CURLM_STATE_WAITPERFORM, /* wait for our turn to read the response */
+ CURLM_STATE_PERFORM, /* transfer data */
+ CURLM_STATE_TOOFAST, /* wait because limit-rate exceeded */
+ CURLM_STATE_DONE, /* post data transfer operation */
+ CURLM_STATE_COMPLETED, /* operation complete */
+ CURLM_STATE_CANCELLED, /* cancelled */
+
+ CURLM_STATE_LAST /* not a true state, never use this */
+} CURLMstate;
+
+/* we support N sockets per easy handle. Set the corresponding bit to what
+ action we should wait for */
+#define MAX_SOCKSPEREASYHANDLE 5
+#define GETSOCK_READABLE (0x00ff)
+#define GETSOCK_WRITABLE (0xff00)
+
+struct closure {
+ struct closure *next; /* a simple one-way list of structs */
+ struct SessionHandle *easy_handle;
+};
+
+struct Curl_one_easy {
+ /* first, two fields for the linked list of these */
+ struct Curl_one_easy *next;
+ struct Curl_one_easy *prev;
+
+ struct SessionHandle *easy_handle; /* the easy handle for this unit */
+ struct connectdata *easy_conn; /* the "unit's" connection */
+
+ CURLMstate state; /* the handle's state */
+ CURLcode result; /* previous result */
+
+ struct Curl_message *msg; /* A pointer to one single posted message.
+ Cleanup should be done on this pointer NOT on
+ the linked list in Curl_multi. This message
+ will be deleted when this handle is removed
+ from the multi-handle */
+ int msg_num; /* number of messages left in 'msg' to return */
+
+ /* Array with the plain socket numbers this handle takes care of, in no
+ particular order. Note that all sockets are added to the sockhash, where
+ the state etc are also kept. This array is mostly used to detect when a
+ socket is to be removed from the hash. See singlesocket(). */
+ curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE];
+ int numsocks;
+};
+
+#define CURL_MULTI_HANDLE 0x000bab1e
+
+#define GOOD_MULTI_HANDLE(x) \
+ ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE))
+#define GOOD_EASY_HANDLE(x) \
+ (((struct SessionHandle *)x)->magic == CURLEASY_MAGIC_NUMBER)
+
+/* This is the struct known as CURLM on the outside */
+struct Curl_multi {
+ /* First a simple identifier to easier detect if a user mix up
+ this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */
+ long type;
+
+ /* We have a linked list with easy handles */
+ struct Curl_one_easy easy;
+
+ int num_easy; /* amount of entries in the linked list above. */
+ int num_msgs; /* amount of messages in the easy handles */
+ int num_alive; /* amount of easy handles that are added but have not yet
+ reached COMPLETE state */
+
+ /* callback function and user data pointer for the *socket() API */
+ curl_socket_callback socket_cb;
+ void *socket_userp;
+
+ /* Hostname cache */
+ struct curl_hash *hostcache;
+
+ /* timetree points to the splay-tree of time nodes to figure out expire
+ times of all currently set timers */
+ struct Curl_tree *timetree;
+
+ /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
+ the pluralis form, there can be more than one easy handle waiting on the
+ same actual socket) */
+ struct curl_hash *sockhash;
+
+ /* Whether pipelining is enabled for this multi handle */
+ bool pipelining_enabled;
+
+ /* shared connection cache */
+ struct conncache *connc;
+
+ /* list of easy handles kept around for doing nice connection closures */
+ struct closure *closure;
+
+ /* timer callback and user data pointer for the *socket() API */
+ curl_multi_timer_callback timer_cb;
+ void *timer_userp;
+ time_t timer_lastcall; /* the fixed time for the timeout for the previous
+ callback */
+};
+
+static bool multi_conn_using(struct Curl_multi *multi,
+ struct SessionHandle *data);
+static void singlesocket(struct Curl_multi *multi,
+ struct Curl_one_easy *easy);
+static void add_closure(struct Curl_multi *multi,
+ struct SessionHandle *data);
+static int update_timer(struct Curl_multi *multi);
+
+#ifdef CURLDEBUG
+static const char *statename[]={
+ "INIT",
+ "CONNECT",
+ "WAITRESOLVE",
+ "WAITCONNECT",
+ "PROTOCONNECT",
+ "WAITDO",
+ "DO",
+ "DOING",
+ "DO_MORE",
+ "DO_DONE",
+ "WAITPERFORM",
+ "PERFORM",
+ "TOOFAST",
+ "DONE",
+ "COMPLETED",
+ "CANCELLED"
+};
+
+void curl_multi_dump(CURLM *multi_handle);
+#endif
+
+/* always use this function to change state, to make debugging easier */
+static void multistate(struct Curl_one_easy *easy, CURLMstate state)
+{
+#ifdef CURLDEBUG
+ long index = -1;
+#endif
+ CURLMstate oldstate = easy->state;
+
+ if(oldstate == state)
+ /* don't bother when the new state is the same as the old state */
+ return;
+
+ easy->state = state;
+
+#ifdef CURLDEBUG
+ if(easy->state > CURLM_STATE_CONNECT &&
+ easy->state < CURLM_STATE_COMPLETED)
+ index = easy->easy_conn->connectindex;
+
+ infof(easy->easy_handle,
+ "STATE: %s => %s handle %p; (connection #%ld) \n",
+ statename[oldstate], statename[easy->state],
+ (char *)easy, index);
+#endif
+ if(state == CURLM_STATE_COMPLETED)
+ /* changing to COMPLETED means there's one less easy handle 'alive' */
+ easy->easy_handle->multi->num_alive--;
+}
+
+/*
+ * We add one of these structs to the sockhash for a particular socket
+ */
+
+struct Curl_sh_entry {
+ struct SessionHandle *easy;
+ time_t timestamp;
+ long inuse;
+ int action; /* what action READ/WRITE this socket waits for */
+ curl_socket_t socket; /* mainly to ease debugging */
+ void *socketp; /* settable by users with curl_multi_assign() */
+};
+/* bits for 'action' having no bits means this socket is not expecting any
+ action */
+#define SH_READ 1
+#define SH_WRITE 2
+
+/* make sure this socket is present in the hash for this handle */
+static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh,
+ curl_socket_t s,
+ struct SessionHandle *data)
+{
+ struct Curl_sh_entry *there =
+ Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
+ struct Curl_sh_entry *check;
+
+ if(there)
+ /* it is present, return fine */
+ return there;
+
+ /* not present, add it */
+ check = calloc(sizeof(struct Curl_sh_entry), 1);
+ if(!check)
+ return NULL; /* major failure */
+ check->easy = data;
+ check->socket = s;
+
+ /* make/add new hash entry */
+ if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
+ free(check);
+ return NULL; /* major failure */
+ }
+
+ return check; /* things are good in sockhash land */
+}
+
+
+/* delete the given socket + handle from the hash */
+static void sh_delentry(struct curl_hash *sh, curl_socket_t s)
+{
+ struct Curl_sh_entry *there =
+ Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
+
+ if(there) {
+ /* this socket is in the hash */
+ /* We remove the hash entry. (This'll end up in a call to
+ sh_freeentry().) */
+ Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
+ }
+}
+
+/*
+ * free a sockhash entry
+ */
+static void sh_freeentry(void *freethis)
+{
+ struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis;
+
+ free(p);
+}
+
+/*
+ * sh_init() creates a new socket hash and returns the handle for it.
+ *
+ * Quote from README.multi_socket:
+ *
+ * "Some tests at 7000 and 9000 connections showed that the socket hash lookup
+ * is somewhat of a bottle neck. Its current implementation may be a bit too
+ * limiting. It simply has a fixed-size array, and on each entry in the array
+ * it has a linked list with entries. So the hash only checks which list to
+ * scan through. The code I had used so for used a list with merely 7 slots
+ * (as that is what the DNS hash uses) but with 7000 connections that would
+ * make an average of 1000 nodes in each list to run through. I upped that to
+ * 97 slots (I believe a prime is suitable) and noticed a significant speed
+ * increase. I need to reconsider the hash implementation or use a rather
+ * large default value like this. At 9000 connections I was still below 10us
+ * per call."
+ *
+ */
+static struct curl_hash *sh_init(void)
+{
+ return Curl_hash_alloc(97, sh_freeentry);
+}
+
+CURLM *curl_multi_init(void)
+{
+ struct Curl_multi *multi = (void *)calloc(sizeof(struct Curl_multi), 1);
+
+ if(!multi)
+ return NULL;
+
+ multi->type = CURL_MULTI_HANDLE;
+
+ multi->hostcache = Curl_mk_dnscache();
+ if(!multi->hostcache) {
+ /* failure, free mem and bail out */
+ free(multi);
+ return NULL;
+ }
+
+ multi->sockhash = sh_init();
+ if(!multi->sockhash) {
+ /* failure, free mem and bail out */
+ Curl_hash_destroy(multi->hostcache);
+ free(multi);
+ return NULL;
+ }
+
+ multi->connc = Curl_mk_connc(CONNCACHE_MULTI, -1);
+ if(!multi->connc) {
+ Curl_hash_destroy(multi->hostcache);
+ free(multi);
+ return NULL;
+ }
+
+ 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;
+ struct closure *cl;
+ struct closure *prev=NULL;
+
+ /* First, make some basic checks that the CURLM handle is a good handle */
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ /* Verify that we got a somewhat good easy handle too */
+ if(!GOOD_EASY_HANDLE(easy_handle))
+ return CURLM_BAD_EASY_HANDLE;
+
+ /* Prevent users to add the same handle more than once! */
+ if(((struct SessionHandle *)easy_handle)->multi)
+ /* possibly we should create a new unique error code for this condition */
+ return CURLM_BAD_EASY_HANDLE;
+
+ /* Now, time to add an easy handle to the multi stack */
+ easy = (struct Curl_one_easy *)calloc(sizeof(struct Curl_one_easy), 1);
+ if(!easy)
+ return CURLM_OUT_OF_MEMORY;
+
+ cl = multi->closure;
+ while(cl) {
+ struct closure *next = cl->next;
+ if(cl->easy_handle == (struct SessionHandle *)easy_handle) {
+ /* remove this handle from the closure list */
+ free(cl);
+ if(prev)
+ prev->next = next;
+ else
+ multi->closure = next;
+ break; /* no need to continue since this handle can only be present once
+ in the list */
+ }
+ cl = next;
+ }
+
+ /* set the easy handle */
+ easy->easy_handle = easy_handle;
+ multistate(easy, CURLM_STATE_INIT);
+
+ /* for multi interface connections, we share DNS cache automatically if the
+ easy handle's one is currently private. */
+ if (easy->easy_handle->dns.hostcache &&
+ (easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) {
+ Curl_hash_destroy(easy->easy_handle->dns.hostcache);
+ easy->easy_handle->dns.hostcache = NULL;
+ easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
+ }
+
+ if (!easy->easy_handle->dns.hostcache ||
+ (easy->easy_handle->dns.hostcachetype == HCACHE_NONE)) {
+ easy->easy_handle->dns.hostcache = multi->hostcache;
+ easy->easy_handle->dns.hostcachetype = HCACHE_MULTI;
+ }
+
+ if(easy->easy_handle->state.connc) {
+ if(easy->easy_handle->state.connc->type == CONNCACHE_PRIVATE) {
+ /* kill old private version */
+ Curl_rm_connc(easy->easy_handle->state.connc);
+ /* point out our shared one instead */
+ easy->easy_handle->state.connc = multi->connc;
+ }
+ /* else it is already using multi? */
+ }
+ else
+ /* point out our shared one */
+ easy->easy_handle->state.connc = multi->connc;
+
+ /* Make sure the type is setup correctly */
+ easy->easy_handle->state.connc->type = CONNCACHE_MULTI;
+
+ /* 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;
+
+ Curl_easy_addmulti(easy_handle, multi_handle);
+
+ /* make the SessionHandle struct refer back to this struct */
+ easy->easy_handle->set.one_easy = easy;
+
+ /* increase the node-counter */
+ multi->num_easy++;
+
+ if((multi->num_easy * 4) > multi->connc->num) {
+ /* We want the connection cache to have plenty room. Before we supported
+ the shared cache every single easy handle had 5 entries in their cache
+ by default. */
+ CURLcode res = Curl_ch_connc(easy_handle, multi->connc,
+ multi->connc->num*4);
+ if(res != CURLE_OK)
+ /* TODO: we need to do some cleaning up here! */
+ return CURLM_OUT_OF_MEMORY;
+ }
+
+ /* increase the alive-counter */
+ multi->num_alive++;
+
+ update_timer(multi);
+ return CURLM_OK;
+}
+
+#if 0
+/* Debug-function, used like this:
+ *
+ * Curl_hash_print(multi->sockhash, debug_print_sock_hash);
+ *
+ * Enable the hash print function first by editing hash.c
+ */
+static void debug_print_sock_hash(void *p)
+{
+ struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p;
+
+ fprintf(stderr, " [easy %p/magic %x/socket %d]",
+ (void *)sh->easy, sh->easy->magic, sh->socket);
+}
+#endif
+
+CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
+ CURL *curl_handle)
+{
+ struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+ struct Curl_one_easy *easy;
+
+ /* 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 == (struct SessionHandle *)curl_handle)
+ break;
+ easy=easy->next;
+ }
+
+ if(easy) {
+ bool premature = (bool)(easy->state != CURLM_STATE_COMPLETED);
+
+ /* If the 'state' is not INIT or COMPLETED, we might need to do something
+ nice to put the easy_handle in a good known state when this returns. */
+ if(premature)
+ /* this handle is "alive" so we need to count down the total number of
+ alive connections when this is removed */
+ multi->num_alive--;
+
+ if (easy->easy_handle->state.is_in_pipeline &&
+ easy->state > CURLM_STATE_DO) {
+ /* If the handle is in a pipeline and has finished sending off its
+ request, we need to remember the fact that we want to remove this
+ handle but do the actual removal at a later time */
+ easy->easy_handle->state.cancelled = TRUE;
+ return CURLM_OK;
+ }
+
+ /* The timer must be shut down before easy->multi is set to NULL,
+ else the timenode will remain in the splay tree after
+ curl_easy_cleanup is called. */
+ Curl_expire(easy->easy_handle, 0);
+
+ if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
+ /* clear out the usage of the shared DNS cache */
+ easy->easy_handle->dns.hostcache = NULL;
+ easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
+ }
+
+ /* if we have a connection we must call Curl_done() here so that we
+ don't leave a half-baked one around */
+ if(easy->easy_conn) {
+ /* Set up the association right */
+ easy->easy_conn->data = easy->easy_handle;
+
+ /* Curl_done() clears the conn->data field to lose the association
+ between the easy handle and the connection */
+ Curl_done(&easy->easy_conn, easy->result, premature);
+
+ if(easy->easy_conn)
+ /* the connection is still alive, set back the association to enable
+ the check below to trigger TRUE */
+ easy->easy_conn->data = easy->easy_handle;
+ }
+
+ /* If this easy_handle was the last one in charge for one or more
+ connections a the shared connection cache, we might need to keep this
+ handle around until either A) the connection is closed and killed
+ properly, or B) another easy_handle uses the connection.
+
+ The reason why we need to have a easy_handle associated with a live
+ connection is simply that some connections will need a handle to get
+ closed down properly. Currently, the only connections that need to keep
+ a easy_handle handle around are using FTP(S). Such connections have
+ the PROT_CLOSEACTION bit set.
+
+ Thus, we need to check for all connections in the shared cache that
+ points to this handle and are using PROT_CLOSEACTION. If there's any,
+ we need to add this handle to the list of "easy handles kept around for
+ nice connection closures".
+ */
+ if(multi_conn_using(multi, easy->easy_handle)) {
+ /* There's at least one connection using this handle so we must keep
+ this handle around. We also keep the connection cache pointer
+ pointing to the shared one since that will be used on close as
+ well. */
+ easy->easy_handle->state.shared_conn = multi;
+
+ /* this handle is still being used by a shared connection cache and
+ thus we leave it around for now */
+ add_closure(multi, easy->easy_handle);
+ }
+
+ if(easy->easy_handle->state.connc->type == CONNCACHE_MULTI) {
+ /* if this was using the shared connection cache we clear the pointer
+ to that since we're not part of that handle anymore */
+ easy->easy_handle->state.connc = NULL;
+
+ /* and modify the connectindex since this handle can't point to the
+ connection cache anymore */
+ if(easy->easy_conn)
+ easy->easy_conn->connectindex = -1;
+ }
+
+ /* change state without using multistate(), only to make singlesocket() do
+ what we want */
+ easy->state = CURLM_STATE_COMPLETED;
+ singlesocket(multi, easy); /* to let the application know what sockets
+ that vanish with this handle */
+
+ Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association
+ to this multi handle */
+
+ /* make the previous node point to our next */
+ if(easy->prev)
+ easy->prev->next = easy->next;
+ /* make our next point to our previous node */
+ if(easy->next)
+ easy->next->prev = easy->prev;
+
+ easy->easy_handle->set.one_easy = NULL; /* detached */
+
+ /* NOTE NOTE NOTE
+ We do not touch the easy handle here! */
+ if (easy->msg)
+ free(easy->msg);
+ free(easy);
+
+ multi->num_easy--; /* one less to care about now */
+
+ update_timer(multi);
+ return CURLM_OK;
+ }
+ else
+ return CURLM_BAD_EASY_HANDLE; /* twasn't found */
+}
+
+bool Curl_multi_canPipeline(struct Curl_multi* multi)
+{
+ return multi->pipelining_enabled;
+}
+
+static int waitconnect_getsock(struct connectdata *conn,
+ curl_socket_t *sock,
+ int numsocks)
+{
+ if(!numsocks)
+ return GETSOCK_BLANK;
+
+ sock[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_WRITESOCK(0);
+}
+
+static int domore_getsock(struct connectdata *conn,
+ curl_socket_t *sock,
+ int numsocks)
+{
+ if(!numsocks)
+ return GETSOCK_BLANK;
+
+ /* When in DO_MORE state, we could be either waiting for us
+ to connect to a remote site, or we could wait for that site
+ to connect to us. It makes a difference in the way: if we
+ connect to the site we wait for the socket to become writable, if
+ the site connects to us we wait for it to become readable */
+ sock[0] = conn->sock[SECONDARYSOCKET];
+
+ return GETSOCK_WRITESOCK(0);
+}
+
+/* returns bitmapped flags for this handle and its sockets */
+static int multi_getsock(struct Curl_one_easy *easy,
+ curl_socket_t *socks, /* points to numsocks number
+ of sockets */
+ int numsocks)
+{
+ if (easy->easy_handle->state.pipe_broke) {
+ return 0;
+ }
+
+ if (easy->state > CURLM_STATE_CONNECT &&
+ easy->state < CURLM_STATE_COMPLETED) {
+ /* Set up ownership correctly */
+ easy->easy_conn->data = easy->easy_handle;
+ }
+
+ switch(easy->state) {
+ case CURLM_STATE_TOOFAST: /* returns 0, so will not select. */
+ default:
+ /* this will get called with CURLM_STATE_COMPLETED when a handle is
+ removed */
+ return 0;
+
+ case CURLM_STATE_WAITRESOLVE:
+ return Curl_resolv_getsock(easy->easy_conn, socks, numsocks);
+
+ case CURLM_STATE_PROTOCONNECT:
+ return Curl_protocol_getsock(easy->easy_conn, socks, numsocks);
+
+ case CURLM_STATE_DOING:
+ return Curl_doing_getsock(easy->easy_conn, socks, numsocks);
+
+ case CURLM_STATE_WAITCONNECT:
+ return waitconnect_getsock(easy->easy_conn, socks, numsocks);
+
+ case CURLM_STATE_DO_MORE:
+ return domore_getsock(easy->easy_conn, socks, numsocks);
+
+ case CURLM_STATE_PERFORM:
+ case CURLM_STATE_WAITPERFORM:
+ return Curl_single_getsock(easy->easy_conn, socks, numsocks);
+ }
+
+}
+
+CURLMcode curl_multi_fdset(CURLM *multi_handle,
+ fd_set *read_fd_set, fd_set *write_fd_set,
+ fd_set *exc_fd_set, int *max_fd)
+{
+ /* Scan through all the easy handles to get the file descriptors set.
+ Some easy handles may not have connected to the remote host yet,
+ and then we must make sure that is done. */
+ struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+ struct Curl_one_easy *easy;
+ int this_max_fd=-1;
+ curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
+ int bitmap;
+ int i;
+ (void)exc_fd_set; /* not used */
+
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ easy=multi->easy.next;
+ while(easy) {
+ bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
+
+ for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
+ curl_socket_t s = CURL_SOCKET_BAD;
+
+ if(bitmap & GETSOCK_READSOCK(i)) {
+ FD_SET(sockbunch[i], read_fd_set);
+ s = sockbunch[i];
+ }
+ if(bitmap & GETSOCK_WRITESOCK(i)) {
+ FD_SET(sockbunch[i], write_fd_set);
+ s = sockbunch[i];
+ }
+ if(s == CURL_SOCKET_BAD)
+ /* this socket is unused, break out of loop */
+ break;
+ else {
+ if((int)s > this_max_fd)
+ this_max_fd = (int)s;
+ }
+ }
+
+ easy = easy->next; /* check next handle */
+ }
+
+ *max_fd = this_max_fd;
+
+ return CURLM_OK;
+}
+
+static CURLMcode multi_runsingle(struct Curl_multi *multi,
+ struct Curl_one_easy *easy)
+{
+ struct Curl_message *msg = NULL;
+ bool connected;
+ bool async;
+ bool protocol_connect;
+ bool dophase_done;
+ bool done;
+ CURLMcode result = CURLM_OK;
+ struct Curl_transfer_keeper *k;
+
+ do {
+
+ if(!GOOD_EASY_HANDLE(easy->easy_handle))
+ return CURLM_BAD_EASY_HANDLE;
+
+ if (easy->easy_handle->state.pipe_broke) {
+ infof(easy->easy_handle, "Pipe broke: handle 0x%x, url = %s\n",
+ easy, easy->easy_handle->reqdata.path);
+ if(easy->easy_handle->state.is_in_pipeline) {
+ /* Head back to the CONNECT state */
+ multistate(easy, CURLM_STATE_CONNECT);
+ result = CURLM_CALL_MULTI_PERFORM;
+ easy->result = CURLE_OK;
+ } else {
+ easy->result = CURLE_COULDNT_CONNECT;
+ multistate(easy, CURLM_STATE_COMPLETED);
+ }
+
+ easy->easy_handle->state.pipe_broke = FALSE;
+ easy->easy_conn = NULL;
+ break;
+ }
+
+ if (easy->state > CURLM_STATE_CONNECT &&
+ easy->state < CURLM_STATE_COMPLETED) {
+ /* Make sure we set the connection's current owner */
+ easy->easy_conn->data = easy->easy_handle;
+ }
+
+ if (CURLM_STATE_WAITCONNECT <= easy->state &&
+ easy->state <= CURLM_STATE_DO &&
+ easy->easy_handle->change.url_changed) {
+ char *gotourl;
+ Curl_posttransfer(easy->easy_handle);
+
+ easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
+ /* We make sure that the pipe broken flag is reset
+ because in this case, it isn't an actual break */
+ easy->easy_handle->state.pipe_broke = FALSE;
+ if(CURLE_OK == easy->result) {
+ gotourl = strdup(easy->easy_handle->change.url);
+ if(gotourl) {
+ easy->easy_handle->change.url_changed = FALSE;
+ easy->result = Curl_follow(easy->easy_handle, gotourl, FALSE);
+ if(CURLE_OK == easy->result)
+ multistate(easy, CURLM_STATE_CONNECT);
+ else
+ free(gotourl);
+ }
+ else {
+ easy->result = CURLE_OUT_OF_MEMORY;
+ multistate(easy, CURLM_STATE_COMPLETED);
+ break;
+ }
+ }
+ }
+
+ easy->easy_handle->change.url_changed = FALSE;
+
+ 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 */
+ multistate(easy, CURLM_STATE_CONNECT);
+ result = CURLM_CALL_MULTI_PERFORM;
+
+ easy->easy_handle->state.used_interface = Curl_if_multi;
+ }
+ break;
+
+ case CURLM_STATE_CONNECT:
+ /* Connect. We get a connection identifier filled in. */
+ Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
+ easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn,
+ &async, &protocol_connect);
+
+ if(CURLE_OK == easy->result) {
+ /* Add this handle to the send pipeline */
+ Curl_addHandleToPipeline(easy->easy_handle,
+ easy->easy_conn->send_pipe);
+
+ if(async)
+ /* We're now waiting for an asynchronous name lookup */
+ multistate(easy, CURLM_STATE_WAITRESOLVE);
+ else {
+ /* after the connect has been sent off, go WAITCONNECT unless the
+ protocol connect is already done and we can go directly to
+ WAITDO! */
+ result = CURLM_CALL_MULTI_PERFORM;
+
+ if(protocol_connect) {
+ multistate(easy, CURLM_STATE_WAITDO);
+ } else {
+ multistate(easy, CURLM_STATE_WAITCONNECT);
+ }
+ }
+ }
+ break;
+
+ case CURLM_STATE_WAITRESOLVE:
+ /* awaiting an asynch name resolve to complete */
+ {
+ struct Curl_dns_entry *dns = NULL;
+
+ /* check if we have the name resolved by now */
+ easy->result = Curl_is_resolved(easy->easy_conn, &dns);
+
+ if(dns) {
+ /* Perform the next step in the connection phase, and then move on
+ to the WAITCONNECT state */
+ easy->result = Curl_async_resolved(easy->easy_conn,
+ &protocol_connect);
+
+ if(CURLE_OK != easy->result)
+ /* if Curl_async_resolved() returns failure, the connection struct
+ is already freed and gone */
+ easy->easy_conn = NULL; /* no more connection */
+ else {
+ /* call again please so that we get the next socket setup */
+ result = CURLM_CALL_MULTI_PERFORM;
+ if(protocol_connect)
+ multistate(easy, CURLM_STATE_DO);
+ else
+ multistate(easy, CURLM_STATE_WAITCONNECT);
+ }
+ }
+
+ if(CURLE_OK != easy->result) {
+ /* failure detected */
+ Curl_disconnect(easy->easy_conn); /* disconnect properly */
+ easy->easy_conn = NULL; /* no more connection */
+ break;
+ }
+ }
+ break;
+
+ case CURLM_STATE_WAITCONNECT:
+ /* awaiting a completion of an asynch connect */
+ easy->result = Curl_is_connected(easy->easy_conn,
+ FIRSTSOCKET,
+ &connected);
+ if(connected)
+ easy->result = Curl_protocol_connect(easy->easy_conn,
+ &protocol_connect);
+
+ if(CURLE_OK != easy->result) {
+ /* failure detected */
+ Curl_disconnect(easy->easy_conn); /* close the connection */
+ easy->easy_conn = NULL; /* no more connection */
+ break;
+ }
+
+ if(connected) {
+ if(!protocol_connect) {
+ /* We have a TCP connection, but 'protocol_connect' may be false
+ and then we continue to 'STATE_PROTOCONNECT'. If protocol
+ connect is TRUE, we move on to STATE_DO. */
+ multistate(easy, CURLM_STATE_PROTOCONNECT);
+ }
+ else {
+ /* after the connect has completed, go WAITDO */
+ multistate(easy, CURLM_STATE_WAITDO);
+
+ result = CURLM_CALL_MULTI_PERFORM;
+ }
+ }
+ break;
+
+ case CURLM_STATE_PROTOCONNECT:
+ /* protocol-specific connect phase */
+ easy->result = Curl_protocol_connecting(easy->easy_conn,
+ &protocol_connect);
+ if(protocol_connect) {
+ /* after the connect has completed, go WAITDO */
+ multistate(easy, CURLM_STATE_WAITDO);
+ result = CURLM_CALL_MULTI_PERFORM;
+ }
+ else if(easy->result) {
+ /* failure detected */
+ Curl_posttransfer(easy->easy_handle);
+ Curl_done(&easy->easy_conn, easy->result, FALSE);
+ Curl_disconnect(easy->easy_conn); /* close the connection */
+ easy->easy_conn = NULL; /* no more connection */
+ }
+ break;
+
+ case CURLM_STATE_WAITDO:
+ /* Wait for our turn to DO when we're pipelining requests */
+#ifdef CURLDEBUG
+ infof(easy->easy_handle, "Conn %d send pipe %d inuse %d athead %d\n",
+ easy->easy_conn->connectindex,
+ easy->easy_conn->send_pipe->size,
+ easy->easy_conn->writechannel_inuse,
+ Curl_isHandleAtHead(easy->easy_handle,
+ easy->easy_conn->send_pipe));
+#endif
+ if (!easy->easy_conn->writechannel_inuse &&
+ Curl_isHandleAtHead(easy->easy_handle,
+ easy->easy_conn->send_pipe)) {
+ /* Grab the channel */
+ easy->easy_conn->writechannel_inuse = TRUE;
+ multistate(easy, CURLM_STATE_DO);
+ result = CURLM_CALL_MULTI_PERFORM;
+ }
+ break;
+
+ case CURLM_STATE_DO:
+ if(easy->easy_handle->set.connect_only) {
+ /* keep connection open for application to use the socket */
+ easy->easy_conn->bits.close = FALSE;
+ multistate(easy, CURLM_STATE_DONE);
+ easy->result = CURLE_OK;
+ result = CURLM_OK;
+ }
+ else {
+ /* Perform the protocol's DO action */
+ easy->result = Curl_do(&easy->easy_conn,
+ &dophase_done);
+
+ if(CURLE_OK == easy->result) {
+
+ if(!dophase_done) {
+ /* DO was not completed in one function call, we must continue
+ DOING... */
+ multistate(easy, CURLM_STATE_DOING);
+ result = CURLM_OK;
+ }
+
+ /* after DO, go DO_DONE... or DO_MORE */
+ else if(easy->easy_conn->bits.do_more) {
+ /* we're supposed to do more, but we need to sit down, relax
+ and wait a little while first */
+ multistate(easy, CURLM_STATE_DO_MORE);
+ result = CURLM_OK;
+ }
+ else {
+ /* we're done with the DO, now DO_DONE */
+ easy->result = Curl_readwrite_init(easy->easy_conn);
+ if(CURLE_OK == easy->result) {
+ multistate(easy, CURLM_STATE_DO_DONE);
+ result = CURLM_CALL_MULTI_PERFORM;
+ }
+ }
+ }
+ else {
+ /* failure detected */
+ Curl_posttransfer(easy->easy_handle);
+ Curl_done(&easy->easy_conn, easy->result, FALSE);
+ Curl_disconnect(easy->easy_conn); /* close the connection */
+ easy->easy_conn = NULL; /* no more connection */
+ }
+ }
+ break;
+
+ case CURLM_STATE_DOING:
+ /* we continue DOING until the DO phase is complete */
+ easy->result = Curl_protocol_doing(easy->easy_conn,
+ &dophase_done);
+ if(CURLE_OK == easy->result) {
+ if(dophase_done) {
+ /* after DO, go PERFORM... or DO_MORE */
+ if(easy->easy_conn->bits.do_more) {
+ /* we're supposed to do more, but we need to sit down, relax
+ and wait a little while first */
+ multistate(easy, CURLM_STATE_DO_MORE);
+ result = CURLM_OK;
+ }
+ else {
+ /* we're done with the DO, now DO_DONE */
+ easy->result = Curl_readwrite_init(easy->easy_conn);
+ if(CURLE_OK == easy->result) {
+ multistate(easy, CURLM_STATE_DO_DONE);
+ result = CURLM_CALL_MULTI_PERFORM;
+ }
+ }
+ } /* dophase_done */
+ }
+ else {
+ /* failure detected */
+ Curl_posttransfer(easy->easy_handle);
+ Curl_done(&easy->easy_conn, easy->result, FALSE);
+ Curl_disconnect(easy->easy_conn); /* close the connection */
+ easy->easy_conn = NULL; /* no more connection */
+ }
+ break;
+
+ case CURLM_STATE_DO_MORE:
+ /* Ready to do more? */
+ easy->result = Curl_is_connected(easy->easy_conn,
+ SECONDARYSOCKET,
+ &connected);
+ if(connected) {
+ /*
+ * When we are connected, DO MORE and then go DO_DONE
+ */
+ easy->result = Curl_do_more(easy->easy_conn);
+
+ if(CURLE_OK == easy->result)
+ easy->result = Curl_readwrite_init(easy->easy_conn);
+ else
+ /* Remove ourselves from the send pipeline */
+ Curl_removeHandleFromPipeline(easy->easy_handle,
+ easy->easy_conn->send_pipe);
+
+ if(CURLE_OK == easy->result) {
+ multistate(easy, CURLM_STATE_DO_DONE);
+ result = CURLM_CALL_MULTI_PERFORM;
+ }
+ }
+ break;
+
+ case CURLM_STATE_DO_DONE:
+ /* Remove ourselves from the send pipeline */
+ Curl_removeHandleFromPipeline(easy->easy_handle,
+ easy->easy_conn->send_pipe);
+ /* Add ourselves to the recv pipeline */
+ Curl_addHandleToPipeline(easy->easy_handle,
+ easy->easy_conn->recv_pipe);
+ multistate(easy, CURLM_STATE_WAITPERFORM);
+ result = CURLM_CALL_MULTI_PERFORM;
+ break;
+
+ case CURLM_STATE_WAITPERFORM:
+#ifdef CURLDEBUG
+ infof(easy->easy_handle, "Conn %d recv pipe %d inuse %d athead %d\n",
+ easy->easy_conn->connectindex,
+ easy->easy_conn->recv_pipe->size,
+ easy->easy_conn->readchannel_inuse,
+ Curl_isHandleAtHead(easy->easy_handle,
+ easy->easy_conn->recv_pipe));
+#endif
+ /* Wait for our turn to PERFORM */
+ if (!easy->easy_conn->readchannel_inuse &&
+ Curl_isHandleAtHead(easy->easy_handle,
+ easy->easy_conn->recv_pipe)) {
+ /* Grab the channel */
+ easy->easy_conn->readchannel_inuse = TRUE;
+ multistate(easy, CURLM_STATE_PERFORM);
+ result = CURLM_CALL_MULTI_PERFORM;
+ }
+ break;
+
+ case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
+ /* if both rates are within spec, resume transfer */
+ Curl_pgrsUpdate(easy->easy_conn);
+ if ( ( ( easy->easy_handle->set.max_send_speed == 0 ) ||
+ ( easy->easy_handle->progress.ulspeed <
+ easy->easy_handle->set.max_send_speed ) ) &&
+ ( ( easy->easy_handle->set.max_recv_speed == 0 ) ||
+ ( easy->easy_handle->progress.dlspeed <
+ easy->easy_handle->set.max_recv_speed ) )
+ )
+ multistate(easy, CURLM_STATE_PERFORM);
+ break;
+
+ case CURLM_STATE_PERFORM:
+ /* check if over speed */
+ if ( ( ( easy->easy_handle->set.max_send_speed > 0 ) &&
+ ( easy->easy_handle->progress.ulspeed >
+ easy->easy_handle->set.max_send_speed ) ) ||
+ ( ( easy->easy_handle->set.max_recv_speed > 0 ) &&
+ ( easy->easy_handle->progress.dlspeed >
+ easy->easy_handle->set.max_recv_speed ) )
+ ) {
+ /* Transfer is over the speed limit. Change state. TODO: Call
+ * Curl_expire() with the time left until we're targeted to be below
+ * the speed limit again. */
+ multistate(easy, CURLM_STATE_TOOFAST );
+ break;
+ }
+
+ /* read/write data if it is ready to do so */
+ easy->result = Curl_readwrite(easy->easy_conn, &done);
+
+ k = &easy->easy_handle->reqdata.keep;
+
+ if (!(k->keepon & KEEP_READ)) {
+ /* We're done reading */
+ easy->easy_conn->readchannel_inuse = FALSE;
+ }
+
+ if (!(k->keepon & KEEP_WRITE)) {
+ /* We're done writing */
+ easy->easy_conn->writechannel_inuse = FALSE;
+ }
+
+ if(easy->result) {
+ /* 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. */
+ easy->easy_conn->bits.close = TRUE;
+
+ if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
+ /* if we failed anywhere, we must clean up the secondary socket if
+ it was used */
+ sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
+ easy->easy_conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+ }
+ Curl_posttransfer(easy->easy_handle);
+ Curl_done(&easy->easy_conn, easy->result, FALSE);
+ }
+ else if(TRUE == done) {
+ char *newurl;
+ bool retry = Curl_retry_request(easy->easy_conn, &newurl);
+
+ /* call this even if the readwrite function returned error */
+ Curl_posttransfer(easy->easy_handle);
+
+ /* When we follow redirects, must to go back to the CONNECT state */
+ if(easy->easy_handle->reqdata.newurl || retry) {
+ Curl_removeHandleFromPipeline(easy->easy_handle,
+ easy->easy_conn->recv_pipe);
+ if(!retry) {
+ /* if the URL is a follow-location and not just a retried request
+ then figure out the URL here */
+ newurl = easy->easy_handle->reqdata.newurl;
+ easy->easy_handle->reqdata.newurl = NULL;
+ }
+ easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
+ if(easy->result == CURLE_OK)
+ easy->result = Curl_follow(easy->easy_handle, newurl, retry);
+ if(CURLE_OK == easy->result) {
+ multistate(easy, CURLM_STATE_CONNECT);
+ result = CURLM_CALL_MULTI_PERFORM;
+ }
+ else
+ /* Since we "took it", we are in charge of freeing this on
+ failure */
+ free(newurl);
+ }
+ else {
+ /* after the transfer is done, go DONE */
+ multistate(easy, CURLM_STATE_DONE);
+ result = CURLM_CALL_MULTI_PERFORM;
+ }
+ }
+
+ break;
+
+ case CURLM_STATE_DONE:
+ /* Remove ourselves from the receive pipeline */
+ Curl_removeHandleFromPipeline(easy->easy_handle,
+ easy->easy_conn->recv_pipe);
+ easy->easy_handle->state.is_in_pipeline = FALSE;
+
+ if (easy->easy_conn->bits.stream_was_rewound) {
+ /* This request read past its response boundary so we quickly
+ let the other requests consume those bytes since there is no
+ guarantee that the socket will become active again */
+ result = CURLM_CALL_MULTI_PERFORM;
+ }
+
+ if (!easy->easy_handle->state.cancelled) {
+ /* post-transfer command */
+ easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
+
+ /* after we have DONE what we're supposed to do, go COMPLETED, and
+ it doesn't matter what the Curl_done() returned! */
+ multistate(easy, CURLM_STATE_COMPLETED);
+ }
+
+ break;
+
+ case CURLM_STATE_COMPLETED:
+ if (easy->easy_handle->state.cancelled)
+ /* Go into the CANCELLED state if we were cancelled */
+ multistate(easy, CURLM_STATE_CANCELLED);
+
+ /* 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;
+
+ case CURLM_STATE_CANCELLED:
+ /* Cancelled transfer, wait to be cleaned up */
+ break;
+
+ default:
+ return CURLM_INTERNAL_ERROR;
+ }
+
+ if(CURLM_STATE_COMPLETED != easy->state) {
+ if(CURLE_OK != easy->result) {
+ /*
+ * If an error was returned, and we aren't in completed state now,
+ * then we go to completed and consider this transfer aborted.
+ */
+ easy->easy_handle->state.is_in_pipeline = FALSE;
+ easy->easy_handle->state.pipe_broke = FALSE;
+
+ if(easy->easy_conn) {
+ /* if this has a connection, unsubscribe from the pipelines */
+ easy->easy_conn->writechannel_inuse = FALSE;
+ easy->easy_conn->readchannel_inuse = FALSE;
+ }
+ multistate(easy, CURLM_STATE_COMPLETED);
+ }
+ }
+
+ } while (easy->easy_handle->change.url_changed);
+
+ if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
+ if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
+ /* clear out the usage of the shared DNS cache */
+ easy->easy_handle->dns.hostcache = NULL;
+ easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
+ }
+
+ /* now add a node to the Curl_message linked list with this info */
+ msg = (struct Curl_message *)malloc(sizeof(struct Curl_message));
+
+ if(!msg)
+ return CURLM_OUT_OF_MEMORY;
+
+ msg->extmsg.msg = CURLMSG_DONE;
+ msg->extmsg.easy_handle = easy->easy_handle;
+ msg->extmsg.data.result = easy->result;
+ msg->next = NULL;
+
+ easy->msg = msg;
+ easy->msg_num = 1; /* there is one unread message here */
+
+ multi->num_msgs++; /* increase message counter */
+ }
+
+ return result;
+}
+
+
+CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
+{
+ struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+ struct Curl_one_easy *easy;
+ CURLMcode returncode=CURLM_OK;
+ struct Curl_tree *t;
+
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ easy=multi->easy.next;
+ while(easy) {
+ CURLMcode result;
+
+ if (easy->easy_handle->state.cancelled &&
+ easy->state == CURLM_STATE_CANCELLED) {
+ /* Remove cancelled handles once it's safe to do so */
+ Curl_multi_rmeasy(multi_handle, easy->easy_handle);
+ easy->easy_handle = NULL;
+ easy = easy->next;
+ continue;
+ }
+
+ result = multi_runsingle(multi, easy);
+ if(result)
+ returncode = result;
+
+ easy = easy->next; /* operate on next handle */
+ }
+
+ /*
+ * Simply remove all expired timers from the splay since handles are dealt
+ * with unconditionally by this function and curl_multi_timeout() requires
+ * that already passed/handled expire times are removed from the splay.
+ */
+ do {
+ struct timeval now = Curl_tvnow();
+ int key = now.tv_sec; /* drop the usec part */
+
+ multi->timetree = Curl_splaygetbest(key, multi->timetree, &t);
+ if (t) {
+ struct SessionHandle *d = t->payload;
+ struct timeval* tv = &d->state.expiretime;
+
+ /* clear the expire times within the handles that we remove from the
+ splay tree */
+ tv->tv_sec = 0;
+ tv->tv_usec = 0;
+ }
+
+ } while(t);
+
+ *running_handles = multi->num_alive;
+
+ if ( CURLM_OK == returncode )
+ update_timer(multi);
+ return returncode;
+}
+
+/* This is called when an easy handle is cleanup'ed that is part of a multi
+ handle */
+void Curl_multi_rmeasy(void *multi_handle, CURL *easy_handle)
+{
+ curl_multi_remove_handle(multi_handle, easy_handle);
+}
+
+
+CURLMcode curl_multi_cleanup(CURLM *multi_handle)
+{
+ struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+ struct Curl_one_easy *easy;
+ struct Curl_one_easy *nexteasy;
+ int i;
+ struct closure *cl;
+ struct closure *n;
+
+ if(GOOD_MULTI_HANDLE(multi)) {
+ multi->type = 0; /* not good anymore */
+ Curl_hash_destroy(multi->hostcache);
+ Curl_hash_destroy(multi->sockhash);
+
+ /* go over all connections that have close actions */
+ for(i=0; i< multi->connc->num; i++) {
+ if(multi->connc->connects[i] &&
+ multi->connc->connects[i]->protocol & PROT_CLOSEACTION) {
+ Curl_disconnect(multi->connc->connects[i]);
+ multi->connc->connects[i] = NULL;
+ }
+ }
+ /* now walk through the list of handles we kept around only to be
+ able to close connections "properly" */
+ cl = multi->closure;
+ while(cl) {
+ cl->easy_handle->state.shared_conn = NULL; /* no more shared */
+ if(cl->easy_handle->state.closed)
+ /* close handle only if curl_easy_cleanup() already has been called
+ for this easy handle */
+ Curl_close(cl->easy_handle);
+ n = cl->next;
+ free(cl);
+ cl= n;
+ }
+
+ Curl_rm_connc(multi->connc);
+
+ /* remove all easy handles */
+ easy = multi->easy.next;
+ while(easy) {
+ nexteasy=easy->next;
+ if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
+ /* clear out the usage of the shared DNS cache */
+ easy->easy_handle->dns.hostcache = NULL;
+ easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
+ }
+
+ /* Clear the pointer to the connection cache */
+ easy->easy_handle->state.connc = NULL;
+
+ Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */
+
+ if (easy->msg)
+ free(easy->msg);
+ free(easy);
+ easy = nexteasy;
+ }
+
+ free(multi);
+
+ return CURLM_OK;
+ }
+ else
+ return CURLM_BAD_HANDLE;
+}
+
+CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
+{
+ struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+
+ *msgs_in_queue = 0; /* default to none */
+
+ if(GOOD_MULTI_HANDLE(multi)) {
+ struct Curl_one_easy *easy;
+
+ if(!multi->num_msgs)
+ return NULL; /* no messages left to return */
+
+ easy=multi->easy.next;
+ while(easy) {
+ if(easy->msg_num) {
+ easy->msg_num--;
+ break;
+ }
+ easy = easy->next;
+ }
+ if(!easy)
+ return NULL; /* this means internal count confusion really */
+
+ multi->num_msgs--;
+ *msgs_in_queue = multi->num_msgs;
+
+ return &easy->msg->extmsg;
+ }
+ else
+ return NULL;
+}
+
+/*
+ * singlesocket() checks what sockets we deal with and their "action state"
+ * and if we have a different state in any of those sockets from last time we
+ * call the callback accordingly.
+ */
+static void singlesocket(struct Curl_multi *multi,
+ struct Curl_one_easy *easy)
+{
+ curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
+ int i;
+ struct Curl_sh_entry *entry;
+ curl_socket_t s;
+ int num;
+ unsigned int curraction;
+
+ memset(&socks, 0, sizeof(socks));
+ for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
+ socks[i] = CURL_SOCKET_BAD;
+
+ /* Fill in the 'current' struct with the state as it is now: what sockets to
+ supervise and for what actions */
+ curraction = multi_getsock(easy, socks, MAX_SOCKSPEREASYHANDLE);
+
+ /* We have 0 .. N sockets already and we get to know about the 0 .. M
+ sockets we should have from now on. Detect the differences, remove no
+ longer supervised ones and add new ones */
+
+ /* walk over the sockets we got right now */
+ for(i=0; (i< MAX_SOCKSPEREASYHANDLE) &&
+ (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)));
+ i++) {
+ int action = CURL_POLL_NONE;
+
+ s = socks[i];
+
+ /* get it from the hash */
+ entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
+
+ if(curraction & GETSOCK_READSOCK(i))
+ action |= CURL_POLL_IN;
+ if(curraction & GETSOCK_WRITESOCK(i))
+ action |= CURL_POLL_OUT;
+
+ if(entry) {
+ /* yeps, already present so check if it has the same action set */
+ if(entry->action == action)
+ /* same, continue */
+ continue;
+ }
+ else {
+ /* this is a socket we didn't have before, add it! */
+ entry = sh_addentry(multi->sockhash, s, easy->easy_handle);
+ if(!entry)
+ /* fatal */
+ return;
+ }
+
+ multi->socket_cb(easy->easy_handle,
+ s,
+ action,
+ multi->socket_userp,
+ entry ? entry->socketp : NULL);
+
+ entry->action = action; /* store the current action state */
+ }
+
+ num = i; /* number of sockets */
+
+ /* when we've walked over all the sockets we should have right now, we must
+ make sure to detect sockets that are removed */
+ for(i=0; i< easy->numsocks; i++) {
+ int j;
+ s = easy->sockets[i];
+ for(j=0; j<num; j++) {
+ if(s == socks[j]) {
+ /* this is still supervised */
+ s = CURL_SOCKET_BAD;
+ break;
+ }
+ }
+ if(s != CURL_SOCKET_BAD) {
+ /* this socket has been removed. Remove it */
+
+ entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
+ if(entry) {
+ /* just a precaution, this socket really SHOULD be in the hash already
+ but in case it isn't, we don't have to tell the app to remove it
+ either since it never got to know about it */
+ multi->socket_cb(easy->easy_handle,
+ s,
+ CURL_POLL_REMOVE,
+ multi->socket_userp,
+ entry ? entry->socketp : NULL);
+
+ sh_delentry(multi->sockhash, s);
+ }
+ }
+ }
+
+ memcpy(easy->sockets, socks, num*sizeof(curl_socket_t));
+ easy->numsocks = num;
+}
+
+static CURLMcode multi_socket(struct Curl_multi *multi,
+ bool checkall,
+ curl_socket_t s,
+ int *running_handles)
+{
+ CURLMcode result = CURLM_OK;
+ struct SessionHandle *data = NULL;
+ struct Curl_tree *t;
+
+ if(checkall) {
+ struct Curl_one_easy *easyp;
+ /* *perform() deals with running_handles on its own */
+ result = curl_multi_perform(multi, running_handles);
+
+ /* walk through each easy handle and do the socket state change magic
+ and callbacks */
+ easyp=multi->easy.next;
+ while(easyp) {
+ singlesocket(multi, easyp);
+ easyp = easyp->next;
+ }
+
+ /* or should we fall-through and do the timer-based stuff? */
+ return result;
+ }
+ else if (s != CURL_SOCKET_TIMEOUT) {
+
+ struct Curl_sh_entry *entry =
+ Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
+
+ if(!entry)
+ /* unmatched socket, major problemo! */
+ return CURLM_BAD_SOCKET; /* better return code? */
+
+ data = entry->easy;
+
+ if(data->magic != CURLEASY_MAGIC_NUMBER)
+ /* bad bad bad bad bad bad bad */
+ return CURLM_INTERNAL_ERROR;
+
+ result = multi_runsingle(multi, data->set.one_easy);
+
+ if(result == CURLM_OK)
+ /* get the socket(s) and check if the state has been changed since
+ last */
+ singlesocket(multi, data->set.one_easy);
+
+ /* Now we fall-through and do the timer-based stuff, since we don't want
+ to force the user to have to deal with timeouts as long as at least one
+ connection in fact has traffic. */
+
+ data = NULL; /* set data to NULL again to avoid calling multi_runsingle()
+ in case there's no need to */
+ }
+
+ /*
+ * The loop following here will go on as long as there are expire-times left
+ * to process in the splay and 'data' will be re-assigned for every expired
+ * handle we deal with.
+ */
+ do {
+ int key;
+ struct timeval now;
+
+ /* the first loop lap 'data' can be NULL */
+ if(data) {
+ result = multi_runsingle(multi, data->set.one_easy);
+
+ if(result == CURLM_OK)
+ /* get the socket(s) and check if the state has been changed since
+ last */
+ singlesocket(multi, data->set.one_easy);
+ }
+
+ /* Check if there's one (more) expired timer to deal with! This function
+ extracts a matching node if there is one */
+
+ now = Curl_tvnow();
+ key = now.tv_sec; /* drop the usec part */
+
+ multi->timetree = Curl_splaygetbest(key, multi->timetree, &t);
+ if(t) {
+ /* assign 'data' to be the easy handle we just removed from the splay
+ tree */
+ data = t->payload;
+ /* clear the expire time within the handle we removed from the
+ splay tree */
+ data->state.expiretime.tv_sec = 0;
+ data->state.expiretime.tv_usec = 0;
+ }
+
+ } while(t);
+
+ *running_handles = multi->num_alive;
+ return result;
+}
+
+CURLMcode curl_multi_setopt(CURLM *multi_handle,
+ CURLMoption option, ...)
+{
+ struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+ CURLMcode res = CURLM_OK;
+ va_list param;
+
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ va_start(param, option);
+
+ switch(option) {
+ case CURLMOPT_SOCKETFUNCTION:
+ multi->socket_cb = va_arg(param, curl_socket_callback);
+ break;
+ case CURLMOPT_SOCKETDATA:
+ multi->socket_userp = va_arg(param, void *);
+ break;
+ case CURLMOPT_PIPELINING:
+ multi->pipelining_enabled = (bool)(0 != va_arg(param, long));
+ break;
+ case CURLMOPT_TIMERFUNCTION:
+ multi->timer_cb = va_arg(param, curl_multi_timer_callback);
+ break;
+ case CURLMOPT_TIMERDATA:
+ multi->timer_userp = va_arg(param, void *);
+ break;
+ default:
+ res = CURLM_UNKNOWN_OPTION;
+ break;
+ }
+ va_end(param);
+ return res;
+}
+
+
+CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
+ int *running_handles)
+{
+ CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s,
+ running_handles);
+ if (CURLM_OK == result)
+ update_timer((struct Curl_multi *)multi_handle);
+ return result;
+}
+
+CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles)
+
+{
+ CURLMcode result = multi_socket((struct Curl_multi *)multi_handle,
+ TRUE, CURL_SOCKET_BAD, running_handles);
+ if (CURLM_OK == result)
+ update_timer((struct Curl_multi *)multi_handle);
+ return result;
+}
+
+static CURLMcode multi_timeout(struct Curl_multi *multi,
+ long *timeout_ms)
+{
+ if(multi->timetree) {
+ /* we have a tree of expire times */
+ struct timeval now = Curl_tvnow();
+
+ /* splay the lowest to the bottom */
+ multi->timetree = Curl_splay(0, multi->timetree);
+
+ /* At least currently, the splay key is a time_t for the expire time */
+ *timeout_ms = (multi->timetree->key - now.tv_sec) * 1000 -
+ now.tv_usec/1000;
+ if(*timeout_ms < 0)
+ /* 0 means immediately */
+ *timeout_ms = 0;
+ }
+ else
+ *timeout_ms = -1;
+
+ return CURLM_OK;
+}
+
+CURLMcode curl_multi_timeout(CURLM *multi_handle,
+ long *timeout_ms)
+{
+ struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+
+ /* First, make some basic checks that the CURLM handle is a good handle */
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ return multi_timeout(multi, timeout_ms);
+}
+
+/*
+ * Tell the application it should update its timers, if it subscribes to the
+ * update timer callback.
+ */
+static int update_timer(struct Curl_multi *multi)
+{
+ long timeout_ms;
+ if (!multi->timer_cb)
+ return 0;
+ if ( multi_timeout(multi, &timeout_ms) != CURLM_OK )
+ return -1;
+ if ( timeout_ms < 0 )
+ return 0;
+
+ /* When multi_timeout() is done, multi->timetree points to the node with the
+ * timeout we got the (relative) time-out time for. We can thus easily check
+ * if this is the same (fixed) time as we got in a previous call and then
+ * avoid calling the callback again. */
+ if(multi->timetree->key == multi->timer_lastcall)
+ return 0;
+
+ multi->timer_lastcall = multi->timetree->key;
+
+ return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp);
+}
+
+/* given a number of milliseconds from now to use to set the 'act before
+ this'-time for the transfer, to be extracted by curl_multi_timeout() */
+void Curl_expire(struct SessionHandle *data, long milli)
+{
+ struct Curl_multi *multi = data->multi;
+ struct timeval *nowp = &data->state.expiretime;
+ int rc;
+
+ /* this is only interesting for multi-interface using libcurl, and only
+ while there is still a multi interface struct remaining! */
+ if(!multi)
+ return;
+
+ if(!milli) {
+ /* No timeout, clear the time data. */
+ if(nowp->tv_sec) {
+ /* Since this is an cleared time, we must remove the previous entry from
+ the splay tree */
+ rc = Curl_splayremovebyaddr(multi->timetree,
+ &data->state.timenode,
+ &multi->timetree);
+ if(rc)
+ infof(data, "Internal error clearing splay node = %d\n", rc);
+ infof(data, "Expire cleared\n");
+ nowp->tv_sec = 0;
+ nowp->tv_usec = 0;
+ }
+ }
+ else {
+ struct timeval set;
+ int rest;
+
+ set = Curl_tvnow();
+ set.tv_sec += milli/1000;
+ set.tv_usec += (milli%1000)*1000;
+
+ rest = (int)(set.tv_usec - 1000000);
+ if(rest > 0) {
+ /* bigger than a full microsec */
+ set.tv_sec++;
+ set.tv_usec -= 1000000;
+ }
+
+ if(nowp->tv_sec) {
+ /* This means that the struct is added as a node in the splay tree.
+ Compare if the new time is earlier, and only remove-old/add-new if it
+ is. */
+ long diff = curlx_tvdiff(set, *nowp);
+ if(diff > 0)
+ /* the new expire time was later so we don't change this */
+ return;
+
+ /* Since this is an updated time, we must remove the previous entry from
+ the splay tree first and then re-add the new value */
+ rc = Curl_splayremovebyaddr(multi->timetree,
+ &data->state.timenode,
+ &multi->timetree);
+ if(rc)
+ infof(data, "Internal error removing splay node = %d\n", rc);
+ }
+
+ *nowp = set;
+#if 0
+ infof(data, "Expire at %ld / %ld (%ldms)\n",
+ (long)nowp->tv_sec, (long)nowp->tv_usec, milli);
+#endif
+ data->state.timenode.payload = data;
+ multi->timetree = Curl_splayinsert((int)nowp->tv_sec,
+ multi->timetree,
+ &data->state.timenode);
+ }
+#if 0
+ Curl_splayprint(multi->timetree, 0, TRUE);
+#endif
+}
+
+CURLMcode curl_multi_assign(CURLM *multi_handle,
+ curl_socket_t s, void *hashp)
+{
+ struct Curl_sh_entry *there = NULL;
+ struct Curl_multi *multi = (struct Curl_multi *)multi_handle;
+
+ if(s != CURL_SOCKET_BAD)
+ there = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(curl_socket_t));
+
+ if(!there)
+ return CURLM_BAD_SOCKET;
+
+ there->socketp = hashp;
+
+ return CURLM_OK;
+}
+
+static bool multi_conn_using(struct Curl_multi *multi,
+ struct SessionHandle *data)
+{
+ /* any live CLOSEACTION-connections pointing to the give 'data' ? */
+ int i;
+
+ for(i=0; i< multi->connc->num; i++) {
+ if(multi->connc->connects[i] &&
+ (multi->connc->connects[i]->data == data) &&
+ multi->connc->connects[i]->protocol & PROT_CLOSEACTION)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Add the given data pointer to the list of 'closure handles' that are kept
+ around only to be able to close some connections nicely - just make sure
+ that this handle isn't already added, like for the cases when an easy
+ handle is removed, added and removed again... */
+static void add_closure(struct Curl_multi *multi,
+ struct SessionHandle *data)
+{
+ int i;
+ struct closure *cl = (struct closure *)calloc(sizeof(struct closure), 1);
+ struct closure *p=NULL;
+ struct closure *n;
+ if(cl) {
+ cl->easy_handle = data;
+ cl->next = multi->closure;
+ multi->closure = cl;
+ }
+
+ p = multi->closure;
+ cl = p->next; /* start immediately on the second since the first is the one
+ we just added and it is _very_ likely to actually exist
+ used in the cache since that's the whole purpose of adding
+ it to this list! */
+
+ /* When adding, scan through all the other currently kept handles and see if
+ there are any connections still referring to them and kill them if not. */
+ while(cl) {
+ bool inuse = FALSE;
+ for(i=0; i< multi->connc->num; i++) {
+ if(multi->connc->connects[i] &&
+ (multi->connc->connects[i]->data == cl->easy_handle)) {
+ inuse = TRUE;
+ break;
+ }
+ }
+
+ n = cl->next;
+
+ if(!inuse) {
+ /* cl->easy_handle is now killable */
+ infof(data, "Delayed kill of easy handle %p\n", cl->easy_handle);
+ /* unmark it as not having a connection around that uses it anymore */
+ cl->easy_handle->state.shared_conn= NULL;
+ Curl_close(cl->easy_handle);
+ if(p)
+ p->next = n;
+ else
+ multi->closure = n;
+ free(cl);
+ }
+ else
+ p = cl;
+
+ cl = n;
+ }
+
+}
+
+#ifdef CURLDEBUG
+void curl_multi_dump(CURLM *multi_handle)
+{
+ struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+ struct Curl_one_easy *easy;
+ int i;
+ fprintf(stderr, "* Multi status: %d handles, %d alive\n",
+ multi->num_easy, multi->num_alive);
+ for(easy=multi->easy.next; easy; easy = easy->next) {
+ if(easy->state != CURLM_STATE_COMPLETED) {
+ /* only display handles that are not completed */
+ fprintf(stderr, "handle %p, state %s, %d sockets\n",
+ (void *)easy->easy_handle,
+ statename[easy->state], easy->numsocks);
+ for(i=0; i < easy->numsocks; i++) {
+ curl_socket_t s = easy->sockets[i];
+ struct Curl_sh_entry *entry =
+ Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
+
+ fprintf(stderr, "%d ", (int)s);
+ if(!entry) {
+ fprintf(stderr, "INTERNAL CONFUSION\n");
+ continue;
+ }
+ fprintf(stderr, "[%s %s] ",
+ entry->action&CURL_POLL_IN?"RECVING":"",
+ entry->action&CURL_POLL_OUT?"SENDING":"");
+ }
+ if(easy->numsocks)
+ fprintf(stderr, "\n");
+ }
+ }
+}
+#endif
diff --git a/Utilities/cmcurl/multiif.h b/Utilities/cmcurl/multiif.h
new file mode 100644
index 000000000..800aa0f5d
--- /dev/null
+++ b/Utilities/cmcurl/multiif.h
@@ -0,0 +1,46 @@
+#ifndef __MULTIIF_H
+#define __MULTIIF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ * Prototypes for library-wide functions provided by multi.c
+ */
+void Curl_expire(struct SessionHandle *data, long milli);
+
+void Curl_multi_rmeasy(void *multi, CURL *data);
+
+bool Curl_multi_canPipeline(struct Curl_multi* multi);
+
+/* the write bits start at bit 16 for the *getsock() bitmap */
+#define GETSOCK_WRITEBITSTART 16
+
+#define GETSOCK_BLANK 0 /* no bits set */
+
+/* set the bit for the given sock number to make the bitmap for writable */
+#define GETSOCK_WRITESOCK(x) (1 << (GETSOCK_WRITEBITSTART + (x)))
+
+/* set the bit for the given sock number to make the bitmap for readable */
+#define GETSOCK_READSOCK(x) (1 << (x))
+
+#endif /* __MULTIIF_H */
diff --git a/Utilities/cmcurl/netrc.c b/Utilities/cmcurl/netrc.c
new file mode 100644
index 000000000..54d175989
--- /dev/null
+++ b/Utilities/cmcurl/netrc.c
@@ -0,0 +1,247 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $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 "netrc.h"
+
+#include "strequal.h"
+#include "strtok.h"
+#include "memory.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* 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
+
+/* returns -1 on failure, 0 if the host is found, 1 is the host isn't found */
+int Curl_parsenetrc(char *host,
+ char *login,
+ char *password,
+ char *netrcfile)
+{
+ FILE *file;
+ int retcode=1;
+ int specific_login = (login[0] != 0);
+ char *home = NULL;
+ bool home_alloc = FALSE;
+ bool netrc_alloc = FALSE;
+ int state=NOTHING;
+
+ char state_login=0; /* Found a login keyword */
+ char state_password=0; /* Found a password keyword */
+ int state_our_login=FALSE; /* With specific_login, found *our* login name */
+
+#define NETRC DOT_CHAR "netrc"
+
+#ifdef CURLDEBUG
+ {
+ /* This is a hack to allow testing.
+ * If compiled with --enable-debug and CURL_DEBUG_NETRC is defined,
+ * then it's the path to a substitute .netrc for testing purposes *only* */
+
+ char *override = curl_getenv("CURL_DEBUG_NETRC");
+
+ if (override) {
+ fprintf(stderr, "NETRC: overridden " NETRC " file: %s\n", override);
+ netrcfile = override;
+ netrc_alloc = TRUE;
+ }
+ }
+#endif /* CURLDEBUG */
+ if(!netrcfile) {
+ home = curl_getenv("HOME"); /* portable environment reader */
+ if(home) {
+ home_alloc = TRUE;
+#if defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
+ }
+ else {
+ struct passwd *pw;
+ pw= getpwuid(geteuid());
+ if (pw) {
+#ifdef VMS
+ home = decc$translate_vms(pw->pw_dir);
+#else
+ home = pw->pw_dir;
+#endif
+ }
+#endif
+ }
+
+ if(!home)
+ return -1;
+
+ netrcfile = curl_maprintf("%s%s%s", home, DIR_CHAR, NETRC);
+ if(!netrcfile) {
+ if(home_alloc)
+ free(home);
+ return -1;
+ }
+ netrc_alloc = TRUE;
+ }
+
+ file = fopen(netrcfile, "r");
+ if(file) {
+ char *tok;
+ char *tok_buf;
+ bool done=FALSE;
+ char netrcbuffer[256];
+
+ while(!done && fgets(netrcbuffer, sizeof(netrcbuffer), file)) {
+ tok=strtok_r(netrcbuffer, " \t\n", &tok_buf);
+ while(!done && tok) {
+
+ if (login[0] && password[0]) {
+ done=TRUE;
+ break;
+ }
+
+ switch(state) {
+ case NOTHING:
+ if(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
+ fprintf(stderr, "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) {
+ if (specific_login) {
+ state_our_login = strequal(login, tok);
+ }
+ else {
+ strncpy(login, tok, LOGINSIZE-1);
+#ifdef _NETRC_DEBUG
+ fprintf(stderr, "LOGIN: %s\n", login);
+#endif
+ }
+ state_login=0;
+ }
+ else if(state_password) {
+ if (state_our_login || !specific_login) {
+ strncpy(password, tok, PASSWORDSIZE-1);
+#ifdef _NETRC_DEBUG
+ fprintf(stderr, "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;
+ state_our_login = FALSE;
+ }
+ break;
+ } /* switch (state) */
+
+ tok = strtok_r(NULL, " \t\n", &tok_buf);
+ } /* while (tok) */
+ } /* while fgets() */
+
+ fclose(file);
+ }
+
+ if(home_alloc)
+ free(home);
+ if(netrc_alloc)
+ free(netrcfile);
+
+ return retcode;
+}
+
+#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
diff --git a/Utilities/cmcurl/netrc.h b/Utilities/cmcurl/netrc.h
new file mode 100644
index 000000000..939c552df
--- /dev/null
+++ b/Utilities/cmcurl/netrc.h
@@ -0,0 +1,34 @@
+#ifndef __NETRC_H
+#define __NETRC_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+int Curl_parsenetrc(char *host,
+ char *login,
+ char *password,
+ char *filename);
+ /* Assume: password[0]=0, host[0] != 0.
+ * If login[0] = 0, search for login and password within a machine section
+ * in the netrc.
+ * If login[0] != 0, search for password within machine and login.
+ */
+#endif
diff --git a/Utilities/cmcurl/nwlib.c b/Utilities/cmcurl/nwlib.c
new file mode 100644
index 000000000..b0eea56c7
--- /dev/null
+++ b/Utilities/cmcurl/nwlib.c
@@ -0,0 +1,300 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <library.h>
+#include <netware.h>
+#include <screen.h>
+#include <nks/thread.h>
+#include <nks/synch.h>
+
+#include "memory.h"
+#include "memdebug.h"
+
+typedef struct
+{
+ int _errno;
+ void *twentybytes;
+} libthreaddata_t;
+
+typedef struct
+{
+ int x;
+ int y;
+ int z;
+ void *tenbytes;
+ NXKey_t perthreadkey; /* if -1, no key obtained... */
+ NXMutex_t *lock;
+} libdata_t;
+
+int gLibId = -1;
+void *gLibHandle = (void *) NULL;
+rtag_t gAllocTag = (rtag_t) NULL;
+NXMutex_t *gLibLock = (NXMutex_t *) NULL;
+
+/* internal library function prototypes... */
+int DisposeLibraryData ( void * );
+void DisposeThreadData ( void * );
+int GetOrSetUpData ( int id, libdata_t **data, libthreaddata_t **threaddata );
+
+
+int _NonAppStart( void *NLMHandle,
+ void *errorScreen,
+ const char *cmdLine,
+ const char *loadDirPath,
+ size_t uninitializedDataLength,
+ void *NLMFileHandle,
+ int (*readRoutineP)( int conn,
+ void *fileHandle, size_t offset,
+ size_t nbytes,
+ size_t *bytesRead,
+ void *buffer ),
+ size_t customDataOffset,
+ size_t customDataSize,
+ int messageCount,
+ const char **messages )
+{
+ NX_LOCK_INFO_ALLOC(liblock, "Per-Application Data Lock", 0);
+
+#ifndef __GNUC__
+#pragma unused(cmdLine)
+#pragma unused(loadDirPath)
+#pragma unused(uninitializedDataLength)
+#pragma unused(NLMFileHandle)
+#pragma unused(readRoutineP)
+#pragma unused(customDataOffset)
+#pragma unused(customDataSize)
+#pragma unused(messageCount)
+#pragma unused(messages)
+#endif
+
+/*
+** Here we process our command line, post errors (to the error screen),
+** perform initializations and anything else we need to do before being able
+** to accept calls into us. If we succeed, we return non-zero and the NetWare
+** Loader will leave us up, otherwise we fail to load and get dumped.
+*/
+ gAllocTag = AllocateResourceTag(NLMHandle,
+ "<library-name> memory allocations",
+ AllocSignature);
+
+ if (!gAllocTag) {
+ OutputToScreen(errorScreen, "Unable to allocate resource tag for "
+ "library memory allocations.\n");
+ return -1;
+ }
+
+ gLibId = register_library(DisposeLibraryData);
+
+ if (gLibId < -1) {
+ OutputToScreen(errorScreen, "Unable to register library with kernel.\n");
+ return -1;
+ }
+
+ gLibHandle = NLMHandle;
+
+ gLibLock = NXMutexAlloc(0, 0, &liblock);
+
+ if (!gLibLock) {
+ OutputToScreen(errorScreen, "Unable to allocate library data lock.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+** Here we clean up any resources we allocated. Resource tags is a big part
+** of what we created, but NetWare doesn't ask us to free those.
+*/
+void _NonAppStop( void )
+{
+ (void) unregister_library(gLibId);
+ NXMutexFree(gLibLock);
+}
+
+/*
+** This function cannot be the first in the file for if the file is linked
+** first, then the check-unload function's offset will be nlmname.nlm+0
+** which is how to tell that there isn't one. When the check function is
+** first in the linked objects, it is ambiguous. For this reason, we will
+** put it inside this file after the stop function.
+**
+** Here we check to see if it's alright to ourselves to be unloaded. If not,
+** we return a non-zero value. Right now, there isn't any reason not to allow
+** it.
+*/
+int _NonAppCheckUnload( void )
+{
+ return 0;
+}
+
+int GetOrSetUpData(int id, libdata_t **appData,
+ libthreaddata_t **threadData )
+{
+ int err;
+ libdata_t *app_data;
+ libthreaddata_t *thread_data;
+ NXKey_t key;
+ NX_LOCK_INFO_ALLOC(liblock, "Application Data Lock", 0);
+
+ err = 0;
+ thread_data = (libthreaddata_t *) NULL;
+
+/*
+** Attempt to get our data for the application calling us. This is where we
+** store whatever application-specific information we need to carry in support
+** of calling applications.
+*/
+ app_data = (libdata_t *) get_app_data(id);
+
+ if (!app_data) {
+/*
+** This application hasn't called us before; set up application AND per-thread
+** data. Of course, just in case a thread from this same application is calling
+** us simultaneously, we better lock our application data-creation mutex. We
+** also need to recheck for data after we acquire the lock because WE might be
+** that other thread that was too late to create the data and the first thread
+** in will have created it.
+*/
+ NXLock(gLibLock);
+
+ if (!(app_data = (libdata_t *) get_app_data(id))) {
+ app_data = (libdata_t *) malloc(sizeof(libdata_t));
+
+ if (app_data) {
+ memset(app_data, 0, sizeof(libdata_t));
+
+ app_data->tenbytes = malloc(10);
+ app_data->lock = NXMutexAlloc(0, 0, &liblock);
+
+ if (!app_data->tenbytes || !app_data->lock) {
+ if (app_data->lock)
+ NXMutexFree(app_data->lock);
+
+ free(app_data);
+ app_data = (libdata_t *) NULL;
+ err = ENOMEM;
+ }
+
+ if (app_data) {
+/*
+** Here we burn in the application data that we were trying to get by calling
+** get_app_data(). Next time we call the first function, we'll get this data
+** we're just now setting. We also go on here to establish the per-thread data
+** for the calling thread, something we'll have to do on each application
+** thread the first time it calls us.
+*/
+ err = set_app_data(gLibId, app_data);
+
+ if (err) {
+ free(app_data);
+ app_data = (libdata_t *) NULL;
+ err = ENOMEM;
+ }
+ else {
+ /* create key for thread-specific data... */
+ err = NXKeyCreate(DisposeThreadData, (void *) NULL, &key);
+
+ if (err) /* (no more keys left?) */
+ key = -1;
+
+ app_data->perthreadkey = key;
+ }
+ }
+ }
+ }
+
+ NXUnlock(gLibLock);
+ }
+
+ if (app_data) {
+ key = app_data->perthreadkey;
+
+ if (key != -1 /* couldn't create a key? no thread data */
+ && !(err = NXKeyGetValue(key, (void **) &thread_data))
+ && !thread_data) {
+/*
+** Allocate the per-thread data for the calling thread. Regardless of whether
+** there was already application data or not, this may be the first call by a
+** a new thread. The fact that we allocation 20 bytes on a pointer is not very
+** important, this just helps to demonstrate that we can have arbitrarily
+** complex per-thread data.
+*/
+ thread_data = (libthreaddata_t *) malloc(sizeof(libthreaddata_t));
+
+ if (thread_data) {
+ thread_data->_errno = 0;
+ thread_data->twentybytes = malloc(20);
+
+ if (!thread_data->twentybytes) {
+ free(thread_data);
+ thread_data = (libthreaddata_t *) NULL;
+ err = ENOMEM;
+ }
+
+ if ((err = NXKeySetValue(key, thread_data))) {
+ free(thread_data->twentybytes);
+ free(thread_data);
+ thread_data = (libthreaddata_t *) NULL;
+ }
+ }
+ }
+ }
+
+ if (appData)
+ *appData = app_data;
+
+ if (threadData)
+ *threadData = thread_data;
+
+ return err;
+}
+
+int DisposeLibraryData( void *data)
+{
+ if (data) {
+ void *tenbytes = ((libdata_t *) data)->tenbytes;
+
+ if (tenbytes)
+ free(tenbytes);
+
+ free(data);
+ }
+
+ return 0;
+}
+
+void DisposeThreadData(void *data)
+{
+ if (data) {
+ void *twentybytes = ((libthreaddata_t *) data)->twentybytes;
+
+ if (twentybytes)
+ free(twentybytes);
+
+ free(data);
+ }
+}
diff --git a/Utilities/cmcurl/parsedate.c b/Utilities/cmcurl/parsedate.c
new file mode 100644
index 000000000..ef91585e5
--- /dev/null
+++ b/Utilities/cmcurl/parsedate.c
@@ -0,0 +1,425 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+/*
+ A brief summary of the date string formats this parser groks:
+
+ RFC 2616 3.3.1
+
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+
+ we support dates without week day name:
+
+ 06 Nov 1994 08:49:37 GMT
+ 06-Nov-94 08:49:37 GMT
+ Nov 6 08:49:37 1994
+
+ without the time zone:
+
+ 06 Nov 1994 08:49:37
+ 06-Nov-94 08:49:37
+
+ weird order:
+
+ 1994 Nov 6 08:49:37 (GNU date fails)
+ GMT 08:49:37 06-Nov-94 Sunday
+ 94 6 Nov 08:49:37 (GNU date fails)
+
+ time left out:
+
+ 1994 Nov 6
+ 06-Nov-94
+ Sun Nov 6 94
+
+ unusual separators:
+
+ 1994.Nov.6
+ Sun/Nov/6/94/GMT
+
+ commonly used time zone names:
+
+ Sun, 06 Nov 1994 08:49:37 CET
+ 06 Nov 1994 08:49:37 EST
+
+ time zones specified using RFC822 style:
+
+ Sun, 12 Sep 2004 15:05:58 -0700
+ Sat, 11 Sep 2004 21:32:11 +0200
+
+ compact numerical date strings:
+
+ 20040912 15:05:58 -0700
+ 20040911 +0200
+
+*/
+#include "setup.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* for strtol() */
+#endif
+
+#include <curl/curl.h>
+
+static time_t Curl_parsedate(const char *date);
+
+const char * const Curl_wkday[] =
+{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
+static const char * const weekday[] =
+{ "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday", "Sunday" };
+const char * const Curl_month[]=
+{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+struct tzinfo {
+ const char *name;
+ int offset; /* +/- in minutes */
+};
+
+/* Here's a bunch of frequently used time zone names. These were supported
+ by the old getdate parser. */
+#define tDAYZONE -60 /* offset for daylight savings time */
+static const struct tzinfo tz[]= {
+ {"GMT", 0}, /* Greenwich Mean */
+ {"UTC", 0}, /* Universal (Coordinated) */
+ {"WET", 0}, /* Western European */
+ {"BST", 0 tDAYZONE}, /* British Summer */
+ {"WAT", 60}, /* West Africa */
+ {"AST", 240}, /* Atlantic Standard */
+ {"ADT", 240 tDAYZONE}, /* Atlantic Daylight */
+ {"EST", 300}, /* Eastern Standard */
+ {"EDT", 300 tDAYZONE}, /* Eastern Daylight */
+ {"CST", 360}, /* Central Standard */
+ {"CDT", 360 tDAYZONE}, /* Central Daylight */
+ {"MST", 420}, /* Mountain Standard */
+ {"MDT", 420 tDAYZONE}, /* Mountain Daylight */
+ {"PST", 480}, /* Pacific Standard */
+ {"PDT", 480 tDAYZONE}, /* Pacific Daylight */
+ {"YST", 540}, /* Yukon Standard */
+ {"YDT", 540 tDAYZONE}, /* Yukon Daylight */
+ {"HST", 600}, /* Hawaii Standard */
+ {"HDT", 600 tDAYZONE}, /* Hawaii Daylight */
+ {"CAT", 600}, /* Central Alaska */
+ {"AHST", 600}, /* Alaska-Hawaii Standard */
+ {"NT", 660}, /* Nome */
+ {"IDLW", 720}, /* International Date Line West */
+ {"CET", -60}, /* Central European */
+ {"MET", -60}, /* Middle European */
+ {"MEWT", -60}, /* Middle European Winter */
+ {"MEST", -60 tDAYZONE}, /* Middle European Summer */
+ {"CEST", -60 tDAYZONE}, /* Central European Summer */
+ {"MESZ", -60 tDAYZONE}, /* Middle European Summer */
+ {"FWT", -60}, /* French Winter */
+ {"FST", -60 tDAYZONE}, /* French Summer */
+ {"EET", -120}, /* Eastern Europe, USSR Zone 1 */
+ {"WAST", -420}, /* West Australian Standard */
+ {"WADT", -420 tDAYZONE}, /* West Australian Daylight */
+ {"CCT", -480}, /* China Coast, USSR Zone 7 */
+ {"JST", -540}, /* Japan Standard, USSR Zone 8 */
+ {"EAST", -600}, /* Eastern Australian Standard */
+ {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */
+ {"GST", -600}, /* Guam Standard, USSR Zone 9 */
+ {"NZT", -720}, /* New Zealand */
+ {"NZST", -720}, /* New Zealand Standard */
+ {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */
+ {"IDLE", -720}, /* International Date Line East */
+};
+
+/* returns:
+ -1 no day
+ 0 monday - 6 sunday
+*/
+
+static int checkday(char *check, size_t len)
+{
+ int i;
+ const char * const *what;
+ bool found= FALSE;
+ if(len > 3)
+ what = &weekday[0];
+ else
+ what = &Curl_wkday[0];
+ for(i=0; i<7; i++) {
+ if(curl_strequal(check, what[0])) {
+ found=TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?i:-1;
+}
+
+static int checkmonth(char *check)
+{
+ int i;
+ const char * const *what;
+ bool found= FALSE;
+
+ what = &Curl_month[0];
+ for(i=0; i<12; i++) {
+ if(curl_strequal(check, what[0])) {
+ found=TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?i:-1; /* return the offset or -1, no real offset is -1 */
+}
+
+/* return the time zone offset between GMT and the input one, in number
+ of seconds or -1 if the timezone wasn't found/legal */
+
+static int checktz(char *check)
+{
+ unsigned int i;
+ const struct tzinfo *what;
+ bool found= FALSE;
+
+ what = tz;
+ for(i=0; i< sizeof(tz)/sizeof(tz[0]); i++) {
+ if(curl_strequal(check, what->name)) {
+ found=TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?what->offset*60:-1;
+}
+
+static void skip(const char **date)
+{
+ /* skip everything that aren't letters or digits */
+ while(**date && !ISALNUM(**date))
+ (*date)++;
+}
+
+enum assume {
+ DATE_MDAY,
+ DATE_YEAR,
+ DATE_TIME
+};
+
+static time_t Curl_parsedate(const char *date)
+{
+ time_t t = 0;
+ int wdaynum=-1; /* day of the week number, 0-6 (mon-sun) */
+ int monnum=-1; /* month of the year number, 0-11 */
+ int mdaynum=-1; /* day of month, 1 - 31 */
+ int hournum=-1;
+ int minnum=-1;
+ int secnum=-1;
+ int yearnum=-1;
+ int tzoff=-1;
+ struct tm tm;
+ enum assume dignext = DATE_MDAY;
+ const char *indate = date; /* save the original pointer */
+ int part = 0; /* max 6 parts */
+
+ while(*date && (part < 6)) {
+ bool found=FALSE;
+
+ skip(&date);
+
+ if(ISALPHA(*date)) {
+ /* a name coming up */
+ char buf[32]="";
+ size_t len;
+ sscanf(date, "%31[A-Za-z]", buf);
+ len = strlen(buf);
+
+ if(wdaynum == -1) {
+ wdaynum = checkday(buf, len);
+ if(wdaynum != -1)
+ found = TRUE;
+ }
+ if(!found && (monnum == -1)) {
+ monnum = checkmonth(buf);
+ if(monnum != -1)
+ found = TRUE;
+ }
+
+ if(!found && (tzoff == -1)) {
+ /* this just must be a time zone string */
+ tzoff = checktz(buf);
+ if(tzoff != -1)
+ found = TRUE;
+ }
+
+ if(!found)
+ return -1; /* bad string */
+
+ date += len;
+ }
+ else if(ISDIGIT(*date)) {
+ /* a digit */
+ int val;
+ char *end;
+ if((secnum == -1) &&
+ (3 == sscanf(date, "%02d:%02d:%02d", &hournum, &minnum, &secnum))) {
+ /* time stamp! */
+ date += 8;
+ found = TRUE;
+ }
+ else {
+ val = (int)strtol(date, &end, 10);
+
+ if((tzoff == -1) &&
+ ((end - date) == 4) &&
+ (val < 1300) &&
+ (indate< date) &&
+ ((date[-1] == '+' || date[-1] == '-'))) {
+ /* four digits and a value less than 1300 and it is preceeded with
+ a plus or minus. This is a time zone indication. */
+ found = TRUE;
+ tzoff = (val/100 * 60 + val%100)*60;
+
+ /* the + and - prefix indicates the local time compared to GMT,
+ this we need ther reversed math to get what we want */
+ tzoff = date[-1]=='+'?-tzoff:tzoff;
+ }
+
+ if(((end - date) == 8) &&
+ (yearnum == -1) &&
+ (monnum == -1) &&
+ (mdaynum == -1)) {
+ /* 8 digits, no year, month or day yet. This is YYYYMMDD */
+ found = TRUE;
+ yearnum = val/10000;
+ monnum = (val%10000)/100-1; /* month is 0 - 11 */
+ mdaynum = val%100;
+ }
+
+ if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) {
+ if((val > 0) && (val<32)) {
+ mdaynum = val;
+ found = TRUE;
+ }
+ dignext = DATE_YEAR;
+ }
+
+ if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) {
+ yearnum = val;
+ found = TRUE;
+ if(yearnum < 1900) {
+ if (yearnum > 70)
+ yearnum += 1900;
+ else
+ yearnum += 2000;
+ }
+ if(mdaynum == -1)
+ dignext = DATE_MDAY;
+ }
+
+ if(!found)
+ return -1;
+
+ date = end;
+ }
+ }
+
+ part++;
+ }
+
+ if(-1 == secnum)
+ secnum = minnum = hournum = 0; /* no time, make it zero */
+
+ if((-1 == mdaynum) ||
+ (-1 == monnum) ||
+ (-1 == yearnum))
+ /* lacks vital info, fail */
+ return -1;
+
+#if SIZEOF_TIME_T < 5
+ /* 32 bit time_t can only hold dates to the beginning of 2038 */
+ if(yearnum > 2037)
+ return 0x7fffffff;
+#endif
+
+ tm.tm_sec = secnum;
+ tm.tm_min = minnum;
+ tm.tm_hour = hournum;
+ tm.tm_mday = mdaynum;
+ tm.tm_mon = monnum;
+ tm.tm_year = yearnum - 1900;
+ tm.tm_wday = 0;
+ tm.tm_yday = 0;
+ tm.tm_isdst = 0;
+
+ /* mktime() returns a time_t. time_t is often 32 bits, even on many
+ architectures that feature 64 bit 'long'.
+
+ Some systems have 64 bit time_t and deal with years beyond 2038. However,
+ even some of the systems with 64 bit time_t returns -1 for dates beyond
+ 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06)
+ */
+ t = mktime(&tm);
+
+ /* time zone adjust (cast t to int to compare to negative one) */
+ if(-1 != (int)t) {
+ struct tm *gmt;
+ long delta;
+ time_t t2;
+
+#ifdef HAVE_GMTIME_R
+ /* thread-safe version */
+ struct tm keeptime2;
+ gmt = (struct tm *)gmtime_r(&t, &keeptime2);
+ if(!gmt)
+ return -1; /* illegal date/time */
+ t2 = mktime(gmt);
+#else
+ /* It seems that at least the MSVC version of mktime() doesn't work
+ properly if it gets the 'gmt' pointer passed in (which is a pointer
+ returned from gmtime() pointing to static memory), so instead we copy
+ the tm struct to a local struct and pass a pointer to that struct as
+ input to mktime(). */
+ struct tm gmt2;
+ gmt = gmtime(&t); /* use gmtime_r() if available */
+ if(!gmt)
+ return -1; /* illegal date/time */
+ gmt2 = *gmt;
+ t2 = mktime(&gmt2);
+#endif
+
+ /* Add the time zone diff (between the given timezone and GMT) and the
+ diff between the local time zone and GMT. */
+ delta = (long)((tzoff!=-1?tzoff:0) + (t - t2));
+
+ if((delta>0) && (t + delta < t))
+ return -1; /* time_t overflow */
+
+ t += delta;
+ }
+
+ return t;
+}
+
+time_t curl_getdate(const char *p, const time_t *now)
+{
+ (void)now;
+ return Curl_parsedate(p);
+}
diff --git a/Utilities/cmcurl/parsedate.h b/Utilities/cmcurl/parsedate.h
new file mode 100644
index 000000000..0ea2d83c3
--- /dev/null
+++ b/Utilities/cmcurl/parsedate.h
@@ -0,0 +1,28 @@
+#ifndef __PARSEDATE_H
+#define __PARSEDATEL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+extern const char * const Curl_wkday[7];
+extern const char * const Curl_month[12];
+
+#endif
diff --git a/Utilities/cmcurl/progress.c b/Utilities/cmcurl/progress.c
new file mode 100644
index 000000000..5ba829a03
--- /dev/null
+++ b/Utilities/cmcurl/progress.c
@@ -0,0 +1,424 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <time.h>
+
+#if defined(__EMX__)
+#include <stdlib.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "progress.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
+ byte) */
+static void time2str(char *r, long t)
+{
+ long h;
+ if(!t) {
+ strcpy(r, "--:--:--");
+ return;
+ }
+ h = (t/3600);
+ if(h <= 99) {
+ long m = (t-(h*3600))/60;
+ long s = (t-(h*3600)-(m*60));
+ snprintf(r, 9, "%2ld:%02ld:%02ld",h,m,s);
+ }
+ else {
+ /* this equals to more than 99 hours, switch to a more suitable output
+ format to fit within the limits. */
+ if(h/24 <= 999)
+ snprintf(r, 9, "%3ldd %02ldh", h/24, h-(h/24)*24);
+ else
+ snprintf(r, 9, "%7ldd", h/24);
+ }
+}
+
+/* The point of this function would be to return a string of the input data,
+ but never longer than 5 columns (+ one zero byte).
+ Add suffix k, M, G when suitable... */
+static char *max5data(curl_off_t bytes, char *max5)
+{
+#define ONE_KILOBYTE 1024
+#define ONE_MEGABYTE (1024* ONE_KILOBYTE)
+#define ONE_GIGABYTE (1024* ONE_MEGABYTE)
+#define ONE_TERRABYTE ((curl_off_t)1024* ONE_GIGABYTE)
+#define ONE_PETABYTE ((curl_off_t)1024* ONE_TERRABYTE)
+
+ if(bytes < 100000) {
+ snprintf(max5, 6, "%5" FORMAT_OFF_T, bytes);
+ }
+ else if(bytes < (10000*ONE_KILOBYTE)) {
+ snprintf(max5, 6, "%4" FORMAT_OFF_T "k", (curl_off_t)(bytes/ONE_KILOBYTE));
+ }
+ else if(bytes < (100*ONE_MEGABYTE)) {
+ /* 'XX.XM' is good as long as we're less than 100 megs */
+ snprintf(max5, 6, "%2d.%0dM",
+ (int)(bytes/ONE_MEGABYTE),
+ (int)(bytes%ONE_MEGABYTE)/(ONE_MEGABYTE/10) );
+ }
+#if SIZEOF_CURL_OFF_T > 4
+ else if(bytes < ( (curl_off_t)10000*ONE_MEGABYTE))
+ /* 'XXXXM' is good until we're at 10000MB or above */
+ snprintf(max5, 6, "%4" FORMAT_OFF_T "M", (curl_off_t)(bytes/ONE_MEGABYTE));
+
+ else if(bytes < (curl_off_t)100*ONE_GIGABYTE)
+ /* 10000 MB - 100 GB, we show it as XX.XG */
+ snprintf(max5, 6, "%2d.%0dG",
+ (int)(bytes/ONE_GIGABYTE),
+ (int)(bytes%ONE_GIGABYTE)/(ONE_GIGABYTE/10) );
+
+ else if(bytes < (curl_off_t)10000 * ONE_GIGABYTE)
+ /* up to 10000GB, display without decimal: XXXXG */
+ snprintf(max5, 6, "%4dG", (int)(bytes/ONE_GIGABYTE));
+
+ else if(bytes < (curl_off_t)10000 * ONE_TERRABYTE)
+ /* up to 10000TB, display without decimal: XXXXT */
+ snprintf(max5, 6, "%4dT", (int)(bytes/ONE_TERRABYTE));
+ else {
+ /* up to 10000PB, display without decimal: XXXXP */
+ snprintf(max5, 6, "%4dP", (int)(bytes/ONE_PETABYTE));
+
+ /* 16384 petabytes (16 exabytes) is maximum a 64 bit number can hold,
+ but this type is signed so 8192PB will be max.*/
+ }
+
+#else
+ else
+ snprintf(max5, 6, "%4" FORMAT_OFF_T "M", (curl_off_t)(bytes/ONE_MEGABYTE));
+#endif
+
+ return max5;
+}
+
+/*
+
+ New proposed interface, 9th of February 2000:
+
+ pgrsStartNow() - sets start time
+ pgrsSetDownloadSize(x) - known expected download size
+ pgrsSetUploadSize(x) - known expected upload size
+ pgrsSetDownloadCounter() - amount of data currently downloaded
+ pgrsSetUploadCounter() - amount of data currently uploaded
+ pgrsUpdate() - show progress
+ pgrsDone() - transfer complete
+
+*/
+
+void Curl_pgrsDone(struct connectdata *conn)
+{
+ struct SessionHandle *data = conn->data;
+ data->progress.lastshow=0;
+ Curl_pgrsUpdate(conn); /* the final (forced) update */
+
+ data->progress.speeder_c = 0; /* reset the progress meter display */
+}
+
+/* reset all times except redirect */
+void Curl_pgrsResetTimes(struct SessionHandle *data)
+{
+ data->progress.t_nslookup = 0.0;
+ data->progress.t_connect = 0.0;
+ data->progress.t_pretransfer = 0.0;
+ data->progress.t_starttransfer = 0.0;
+}
+
+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 =
+ Curl_tvdiff_secs(Curl_tvnow(), data->progress.t_startsingle);
+ break;
+ case TIMER_CONNECT:
+ data->progress.t_connect =
+ Curl_tvdiff_secs(Curl_tvnow(), data->progress.t_startsingle);
+ break;
+ case TIMER_PRETRANSFER:
+ data->progress.t_pretransfer =
+ Curl_tvdiff_secs(Curl_tvnow(), data->progress.t_startsingle);
+ break;
+ case TIMER_STARTTRANSFER:
+ data->progress.t_starttransfer =
+ Curl_tvdiff_secs(Curl_tvnow(), data->progress.t_startsingle);
+ break;
+ case TIMER_POSTRANSFER:
+ /* this is the normal end-of-transfer thing */
+ break;
+ case TIMER_REDIRECT:
+ data->progress.t_redirect =
+ Curl_tvdiff_secs(Curl_tvnow(), data->progress.start);
+ break;
+ }
+}
+
+void Curl_pgrsStartNow(struct SessionHandle *data)
+{
+ data->progress.speeder_c = 0; /* reset the progress meter display */
+ data->progress.start = Curl_tvnow();
+}
+
+void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, curl_off_t size)
+{
+ data->progress.downloaded = size;
+}
+
+void Curl_pgrsSetUploadCounter(struct SessionHandle *data, curl_off_t size)
+{
+ data->progress.uploaded = size;
+}
+
+void Curl_pgrsSetDownloadSize(struct SessionHandle *data, curl_off_t size)
+{
+ data->progress.size_dl = size;
+ if(size > 0)
+ data->progress.flags |= PGRS_DL_SIZE_KNOWN;
+ else
+ data->progress.flags &= ~PGRS_DL_SIZE_KNOWN;
+}
+
+void Curl_pgrsSetUploadSize(struct SessionHandle *data, curl_off_t size)
+{
+ data->progress.size_ul = size;
+ if(size > 0)
+ data->progress.flags |= PGRS_UL_SIZE_KNOWN;
+ else
+ data->progress.flags &= ~PGRS_UL_SIZE_KNOWN;
+}
+
+int Curl_pgrsUpdate(struct connectdata *conn)
+{
+ struct timeval now;
+ int result;
+ char max5[6][10];
+ int dlpercen=0;
+ int ulpercen=0;
+ int total_percen=0;
+ curl_off_t total_transfer;
+ curl_off_t total_expected_transfer;
+ long timespent;
+ struct SessionHandle *data = conn->data;
+ int nowindex = data->progress.speeder_c% CURR_TIME;
+ int checkindex;
+ int countindex; /* amount of seconds stored in the speeder array */
+ char time_left[10];
+ char time_total[10];
+ char time_spent[10];
+ long ulestimate=0;
+ long dlestimate=0;
+ long 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(data->reqdata.resume_from)
+ fprintf(data->set.err,
+ "** Resuming transfer from byte position %" FORMAT_OFF_T
+ "\n",
+ data->reqdata.resume_from);
+ fprintf(data->set.err,
+ " %% Total %% Received %% Xferd Average Speed Time Time Time Current\n"
+ " Dload Upload Total Spent Left Speed\n");
+ }
+ data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
+ }
+
+ now = Curl_tvnow(); /* what time is it */
+
+ /* The time spent so far (from the start) */
+ data->progress.timespent = Curl_tvdiff_secs(now, data->progress.start);
+ timespent = (long)data->progress.timespent;
+
+ /* The average download speed this far */
+ data->progress.dlspeed = (curl_off_t)
+ ((double)data->progress.downloaded/
+ (data->progress.timespent>0?data->progress.timespent:1));
+
+ /* The average upload speed this far */
+ data->progress.ulspeed = (curl_off_t)
+ ((double)data->progress.uploaded/
+ (data->progress.timespent>0?data->progress.timespent:1));
+
+ 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 'span_ms' milliseconds */
+ {
+ curl_off_t amount = data->progress.speeder[nowindex]-
+ data->progress.speeder[checkindex];
+
+ if(amount > 4294967 /* 0xffffffff/1000 */)
+ /* the 'amount' value is bigger than would fit in 32 bits if
+ multiplied with 1000, so we use the double math for this */
+ data->progress.current_speed = (curl_off_t)
+ ((double)amount/((double)span_ms/1000.0));
+ else
+ /* the 'amount' value is small enough to fit within 32 bits even
+ when multiplied with 1000 */
+ data->progress.current_speed = amount*1000/span_ms;
+ }
+ }
+ else
+ /* the first second we use the main average */
+ data->progress.current_speed =
+ (data->progress.ulspeed>data->progress.dlspeed)?
+ data->progress.ulspeed:data->progress.dlspeed;
+
+ 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,
+ (double)data->progress.size_dl,
+ (double)data->progress.downloaded,
+ (double)data->progress.size_ul,
+ (double)data->progress.uploaded);
+ if(result)
+ failf(data, "Callback aborted");
+ return result;
+ }
+
+ /* Figure out the estimated time of arrival for the upload */
+ if((data->progress.flags & PGRS_UL_SIZE_KNOWN) &&
+ (data->progress.ulspeed>0) &&
+ (data->progress.size_ul > 100) ) {
+ ulestimate = (long)(data->progress.size_ul / data->progress.ulspeed);
+ ulpercen = (int)(100*(data->progress.uploaded/100) /
+ (data->progress.size_ul/100) );
+ }
+
+ /* ... and the download */
+ if((data->progress.flags & PGRS_DL_SIZE_KNOWN) &&
+ (data->progress.dlspeed>0) &&
+ (data->progress.size_dl>100)) {
+ dlestimate = (long)(data->progress.size_dl / data->progress.dlspeed);
+ dlpercen = (int)(100*(data->progress.downloaded/100) /
+ (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;
+
+ /* create the three time strings */
+ time2str(time_left, total_estimate > 0?(total_estimate - timespent):0);
+ time2str(time_total, total_estimate);
+ time2str(time_spent, timespent);
+
+ /* Get the total amount of data expected to get 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 > 100)
+ total_percen=(int)(100*(total_transfer/100) /
+ (total_expected_transfer/100) );
+
+ fprintf(data->set.err,
+ "\r%3d %s %3d %s %3d %s %s %s %s %s %s %s",
+ total_percen, /* 3 letters */ /* total % */
+ max5data(total_expected_transfer, max5[2]), /* total size */
+ dlpercen, /* 3 letters */ /* rcvd % */
+ max5data(data->progress.downloaded, max5[0]), /* rcvd size */
+ ulpercen, /* 3 letters */ /* xfer % */
+ max5data(data->progress.uploaded, max5[1]), /* xfer size */
+ max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */
+ max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */
+ time_total, /* 8 letters */ /* total time */
+ time_spent, /* 8 letters */ /* time spent */
+ time_left, /* 8 letters */ /* time left */
+ max5data(data->progress.current_speed, max5[5]) /* current speed */
+ );
+
+ /* we flush the output stream to make it appear as soon as possible */
+ fflush(data->set.err);
+
+ return 0;
+}
diff --git a/Utilities/cmcurl/progress.h b/Utilities/cmcurl/progress.h
new file mode 100644
index 000000000..ad9d6623e
--- /dev/null
+++ b/Utilities/cmcurl/progress.h
@@ -0,0 +1,70 @@
+#ifndef __PROGRESS_H
+#define __PROGRESS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "timeval.h"
+
+
+typedef enum {
+ TIMER_NONE,
+ TIMER_NAMELOOKUP,
+ TIMER_CONNECT,
+ TIMER_PRETRANSFER,
+ TIMER_STARTTRANSFER,
+ TIMER_POSTRANSFER,
+ TIMER_STARTSINGLE,
+ TIMER_REDIRECT,
+ TIMER_LAST /* must be last */
+} timerid;
+
+void Curl_pgrsDone(struct connectdata *);
+void Curl_pgrsStartNow(struct SessionHandle *data);
+void Curl_pgrsSetDownloadSize(struct SessionHandle *data, curl_off_t size);
+void Curl_pgrsSetUploadSize(struct SessionHandle *data, curl_off_t size);
+void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, curl_off_t size);
+void Curl_pgrsSetUploadCounter(struct SessionHandle *data, curl_off_t size);
+int Curl_pgrsUpdate(struct connectdata *);
+void Curl_pgrsResetTimes(struct SessionHandle *data);
+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 */
diff --git a/Utilities/cmcurl/security.c b/Utilities/cmcurl/security.c
new file mode 100644
index 000000000..4c9aed812
--- /dev/null
+++ b/Utilities/cmcurl/security.c
@@ -0,0 +1,493 @@
+/* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for
+ * use in Curl. His latest changes were done 2000-09-18.
+ *
+ * It has since been patched and modified a lot by Daniel Stenberg
+ * <daniel@haxx.se> to make it better applied to curl conditions, and to make
+ * it not use globals, pollute name space and more. This source code awaits a
+ * rewrite to work around the paragraph 2 in the BSD licenses as explained
+ * below.
+ *
+ * Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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"
+
+#ifndef CURL_DISABLE_FTP
+#ifdef HAVE_KRB4
+
+#define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
+#include <curl/mprintf.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "urldata.h"
+#include "krb4.h"
+#include "base64.h"
+#include "sendf.h"
+#include "ftp.h"
+#include "memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+static const struct {
+ enum protection_level level;
+ const char *name;
+} level_names[] = {
+ { prot_clear, "clear" },
+ { prot_safe, "safe" },
+ { prot_confidential, "confidential" },
+ { prot_private, "private" }
+};
+
+static enum protection_level
+name_to_level(const char *name)
+{
+ int i;
+ for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++)
+ if(curl_strnequal(level_names[i].name, name, strlen(name)))
+ return level_names[i].level;
+ return (enum protection_level)-1;
+}
+
+static const struct Curl_sec_client_mech * const mechs[] = {
+#ifdef KRB5
+ /* not supported */
+#endif
+#ifdef HAVE_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;
+}
+
+ssize_t
+Curl_sec_send(struct connectdata *conn, int num, char *buffer, int length)
+{
+ curl_socket_t fd = conn->sock[num];
+ return (ssize_t)Curl_sec_write(conn, fd, buffer, length);
+}
+
+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;
+ unsigned char *buf;
+ int code;
+
+ len = Curl_base64_decode(s + 4, &buf); /* XXX */
+ if(len > 0)
+ len = (conn->mech->decode)(conn->app_data, buf, len, level, conn);
+ else
+ return -1;
+
+ if(len < 0) {
+ free(buf);
+ return -1;
+ }
+
+ buf[len] = '\0';
+
+ if(buf[3] == '-')
+ code = 0;
+ else
+ sscanf((char *)buf, "%d", &code);
+ if(buf[len-1] == '\n')
+ buf[len-1] = '\0';
+ strcpy(s, (char *)buf);
+ free(buf);
+ return code;
+}
+
+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){
+ int code;
+ if(Curl_ftpsendf(conn, "PBSZ %u", s))
+ return -1;
+
+ if(Curl_GetFTPResponse(&nread, conn, &code))
+ return -1;
+
+ if(code/100 != '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;
+
+ if(Curl_GetFTPResponse(&nread, conn, NULL))
+ 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;
+ const struct Curl_sec_client_mech * const *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;
+
+ if(Curl_GetFTPResponse(&nread, conn, &ftpcode))
+ 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 /* HAVE_KRB4 */
+#endif /* CURL_DISABLE_FTP */
diff --git a/Utilities/cmcurl/select.c b/Utilities/cmcurl/select.c
new file mode 100644
index 000000000..51adbcf37
--- /dev/null
+++ b/Utilities/cmcurl/select.c
@@ -0,0 +1,315 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifndef HAVE_SELECT
+#error "We can't compile without select() support!"
+#endif
+
+#if defined(__BEOS__) && !defined(__HAIKU__)
+/* BeOS has FD_SET defined in socket.h */
+#include <socket.h>
+#endif
+
+#ifdef __MSDOS__
+#include <dos.h> /* delay() */
+#endif
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "connect.h"
+#include "select.h"
+
+#if defined(USE_WINSOCK) || defined(TPF)
+#define VERIFY_SOCK(x) /* sockets are not in range [0..FD_SETSIZE] */
+#else
+#define VALID_SOCK(s) (((s) >= 0) && ((s) < FD_SETSIZE))
+#define VERIFY_SOCK(x) do { \
+ if(!VALID_SOCK(x)) { \
+ errno = EINVAL; \
+ return -1; \
+ } \
+} while(0)
+#endif
+
+/*
+ * This is an internal function used for waiting for read or write
+ * events on single file descriptors. It attempts to replace select()
+ * in order to avoid limits with FD_SETSIZE.
+ *
+ * Return values:
+ * -1 = system call error
+ * 0 = timeout
+ * CSELECT_IN | CSELECT_OUT | CSELECT_ERR
+ */
+int Curl_select(curl_socket_t readfd, curl_socket_t writefd, int timeout_ms)
+{
+#if (defined(HAVE_POLL) && !defined(_POLL_EMUL_H_)) || defined(CURL_HAVE_WSAPOLL)
+ struct pollfd pfd[2];
+ int num;
+ int r;
+ int ret;
+
+ num = 0;
+ if (readfd != CURL_SOCKET_BAD) {
+ pfd[num].fd = readfd;
+ pfd[num].events = POLLIN;
+ num++;
+ }
+ if (writefd != CURL_SOCKET_BAD) {
+ pfd[num].fd = writefd;
+ pfd[num].events = POLLOUT;
+ num++;
+ }
+
+#if defined(HAVE_POLL) && !defined(_POLL_EMUL_H_)
+ do {
+ r = poll(pfd, num, timeout_ms);
+ } while((r == -1) && (errno == EINTR));
+#else
+ r = WSAPoll(pfd, num, timeout_ms);
+#endif
+
+ if (r < 0)
+ return -1;
+ if (r == 0)
+ return 0;
+
+ ret = 0;
+ num = 0;
+ if (readfd != CURL_SOCKET_BAD) {
+ if (pfd[num].revents & (POLLIN|POLLHUP))
+ ret |= CSELECT_IN;
+ if (pfd[num].revents & POLLERR) {
+#ifdef __CYGWIN__
+ /* Cygwin 1.5.21 needs this hack to pass test 160 */
+ if (errno == EINPROGRESS)
+ ret |= CSELECT_IN;
+ else
+#endif
+ ret |= CSELECT_ERR;
+ }
+ num++;
+ }
+ if (writefd != CURL_SOCKET_BAD) {
+ if (pfd[num].revents & POLLOUT)
+ ret |= CSELECT_OUT;
+ if (pfd[num].revents & (POLLERR|POLLHUP))
+ ret |= CSELECT_ERR;
+ }
+
+ return ret;
+#else
+ struct timeval timeout;
+ fd_set fds_read;
+ fd_set fds_write;
+ fd_set fds_err;
+ curl_socket_t maxfd;
+ int r;
+ int ret;
+
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+
+ if((readfd == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) {
+ /* According to POSIX we should pass in NULL pointers if we don't want to
+ wait for anything in particular but just use the timeout function.
+ Windows however returns immediately if done so. I copied the MSDOS
+ delay() use from src/main.c that already had this work-around. */
+#ifdef WIN32
+ Sleep(timeout_ms);
+#elif defined(__MSDOS__)
+ delay(timeout_ms);
+#else
+ select(0, NULL, NULL, NULL, &timeout);
+#endif
+ return 0;
+ }
+
+ FD_ZERO(&fds_err);
+ maxfd = (curl_socket_t)-1;
+
+ FD_ZERO(&fds_read);
+ if (readfd != CURL_SOCKET_BAD) {
+ VERIFY_SOCK(readfd);
+ FD_SET(readfd, &fds_read);
+ FD_SET(readfd, &fds_err);
+ maxfd = readfd;
+ }
+
+ FD_ZERO(&fds_write);
+ if (writefd != CURL_SOCKET_BAD) {
+ VERIFY_SOCK(writefd);
+ FD_SET(writefd, &fds_write);
+ FD_SET(writefd, &fds_err);
+ if (writefd > maxfd)
+ maxfd = writefd;
+ }
+
+ do {
+ r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout);
+ } while((r == -1) && (Curl_sockerrno() == EINTR));
+
+ if (r < 0)
+ return -1;
+ if (r == 0)
+ return 0;
+
+ ret = 0;
+ if (readfd != CURL_SOCKET_BAD) {
+ if (FD_ISSET(readfd, &fds_read))
+ ret |= CSELECT_IN;
+ if (FD_ISSET(readfd, &fds_err))
+ ret |= CSELECT_ERR;
+ }
+ if (writefd != CURL_SOCKET_BAD) {
+ if (FD_ISSET(writefd, &fds_write))
+ ret |= CSELECT_OUT;
+ if (FD_ISSET(writefd, &fds_err))
+ ret |= CSELECT_ERR;
+ }
+
+ return ret;
+#endif
+}
+
+/*
+ * This is a wrapper around poll(). If poll() does not exist, then
+ * select() is used instead. An error is returned if select() is
+ * being used and a file descriptor too large for FD_SETSIZE.
+ *
+ * Return values:
+ * -1 = system call error or fd >= FD_SETSIZE
+ * 0 = timeout
+ * 1 = number of structures with non zero revent fields
+ */
+int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
+{
+ int r;
+#if defined(HAVE_POLL) && !defined(_POLL_EMUL_H_)
+ do {
+ r = poll(ufds, nfds, timeout_ms);
+ } while((r == -1) && (errno == EINTR));
+#elif defined(CURL_HAVE_WSAPOLL)
+ r = WSAPoll(ufds, nfds, timeout_ms);
+#else
+ struct timeval timeout;
+ struct timeval *ptimeout;
+ fd_set fds_read;
+ fd_set fds_write;
+ fd_set fds_err;
+ curl_socket_t maxfd;
+ unsigned int i;
+
+ FD_ZERO(&fds_read);
+ FD_ZERO(&fds_write);
+ FD_ZERO(&fds_err);
+ maxfd = (curl_socket_t)-1;
+
+ for (i = 0; i < nfds; i++) {
+ if (ufds[i].fd == CURL_SOCKET_BAD)
+ continue;
+#ifndef USE_WINSOCK /* winsock sockets are not in range [0..FD_SETSIZE] */
+ if (ufds[i].fd >= FD_SETSIZE) {
+ errno = EINVAL;
+ return -1;
+ }
+#endif
+ if (ufds[i].fd > maxfd)
+ maxfd = ufds[i].fd;
+ if (ufds[i].events & POLLIN)
+ FD_SET(ufds[i].fd, &fds_read);
+ if (ufds[i].events & POLLOUT)
+ FD_SET(ufds[i].fd, &fds_write);
+ if (ufds[i].events & POLLERR)
+ FD_SET(ufds[i].fd, &fds_err);
+ }
+
+ if (timeout_ms < 0) {
+ ptimeout = NULL; /* wait forever */
+ } else {
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+ ptimeout = &timeout;
+ }
+
+ do {
+ r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
+ } while((r == -1) && (Curl_sockerrno() == EINTR));
+
+ if (r < 0)
+ return -1;
+ if (r == 0)
+ return 0;
+
+ r = 0;
+ for (i = 0; i < nfds; i++) {
+ ufds[i].revents = 0;
+ if (ufds[i].fd == CURL_SOCKET_BAD)
+ continue;
+ if (FD_ISSET(ufds[i].fd, &fds_read))
+ ufds[i].revents |= POLLIN;
+ if (FD_ISSET(ufds[i].fd, &fds_write))
+ ufds[i].revents |= POLLOUT;
+ if (FD_ISSET(ufds[i].fd, &fds_err))
+ ufds[i].revents |= POLLERR;
+ if (ufds[i].revents != 0)
+ r++;
+ }
+#endif
+ return r;
+}
+
+#ifdef TPF
+/*
+ * This is a replacement for select() on the TPF platform.
+ * It is used whenever libcurl calls select().
+ * The call below to tpf_process_signals() is required because
+ * TPF's select calls are not signal interruptible.
+ *
+ * Return values are the same as select's.
+ */
+int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes,
+ fd_set* excepts, struct timeval* tv)
+{
+ int rc;
+
+ rc = tpf_select_bsd(maxfds, reads, writes, excepts, tv);
+ tpf_process_signals();
+ return(rc);
+}
+#endif /* TPF */
diff --git a/Utilities/cmcurl/select.h b/Utilities/cmcurl/select.h
new file mode 100644
index 000000000..7573b1f54
--- /dev/null
+++ b/Utilities/cmcurl/select.h
@@ -0,0 +1,60 @@
+#ifndef __SELECT_H
+#define __SELECT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#else
+#ifndef POLLIN
+#define POLLIN 0x01
+#define POLLPRI 0x02
+#define POLLOUT 0x04
+#define POLLERR 0x08
+#define POLLHUP 0x10
+#define POLLNVAL 0x20
+
+struct pollfd
+{
+ curl_socket_t fd;
+ short events;
+ short revents;
+};
+#endif
+
+#endif
+
+#define CSELECT_IN 0x01
+#define CSELECT_OUT 0x02
+#define CSELECT_ERR 0x04
+
+int Curl_select(curl_socket_t readfd, curl_socket_t writefd, int timeout_ms);
+
+int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms);
+
+#ifdef TPF
+int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes,
+ fd_set* excepts, struct timeval* tv);
+#endif
+
+#endif
diff --git a/Utilities/cmcurl/sendf.c b/Utilities/cmcurl/sendf.c
new file mode 100644
index 000000000..500bf66f0
--- /dev/null
+++ b/Utilities/cmcurl/sendf.c
@@ -0,0 +1,663 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $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
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "connect.h" /* for the Curl_sockerrno() proto */
+#include "sslgen.h"
+#include "ssh.h"
+#include "multiif.h"
+
+#define _MPRINTF_REPLACE /* use the internal *printf() functions */
+#include <curl/mprintf.h>
+
+#ifdef HAVE_KRB4
+#include "krb4.h"
+#else
+#define Curl_sec_send(a,b,c,d) -1
+#define Curl_sec_read(a,b,c,d) -1
+#endif
+
+#include <string.h>
+#include "memory.h"
+#include "strerror.h"
+#include "easyif.h" /* for the Curl_convert_from_network prototype */
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* returns last node in linked list */
+static struct curl_slist *slist_get_last(struct curl_slist *list)
+{
+ struct curl_slist *item;
+
+ /* if caller passed us a NULL, return now */
+ if (!list)
+ return NULL;
+
+ /* loop through to find the last item */
+ item = list;
+ while (item->next) {
+ item = item->next;
+ }
+ return item;
+}
+
+/*
+ * curl_slist_append() appends a string to the linked list. It always 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) {
+ char *dup = strdup(data);
+ if(dup) {
+ new_item->next = NULL;
+ new_item->data = dup;
+ }
+ else {
+ free(new_item);
+ return NULL;
+ }
+ }
+ else
+ return NULL;
+
+ if (list) {
+ last = slist_get_last(list);
+ last->next = new_item;
+ return list;
+ }
+
+ /* if this is the first item, then new_item *is* the list */
+ return new_item;
+}
+
+/* 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);
+}
+
+#ifdef CURL_DO_LINEEND_CONV
+/*
+ * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
+ * (\n), with special processing for CRLF sequences that are split between two
+ * blocks of data. Remaining, bare CRs are changed to LFs. The possibly new
+ * size of the data is returned.
+ */
+static size_t convert_lineends(struct SessionHandle *data,
+ char *startPtr, size_t size)
+{
+ char *inPtr, *outPtr;
+
+ /* sanity check */
+ if ((startPtr == NULL) || (size < 1)) {
+ return(size);
+ }
+
+ if (data->state.prev_block_had_trailing_cr == TRUE) {
+ /* The previous block of incoming data
+ had a trailing CR, which was turned into a LF. */
+ if (*startPtr == '\n') {
+ /* This block of incoming data starts with the
+ previous block's LF so get rid of it */
+ memcpy(startPtr, startPtr+1, size-1);
+ size--;
+ /* and it wasn't a bare CR but a CRLF conversion instead */
+ data->state.crlf_conversions++;
+ }
+ data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */
+ }
+
+ /* find 1st CR, if any */
+ inPtr = outPtr = memchr(startPtr, '\r', size);
+ if (inPtr) {
+ /* at least one CR, now look for CRLF */
+ while (inPtr < (startPtr+size-1)) {
+ /* note that it's size-1, so we'll never look past the last byte */
+ if (memcmp(inPtr, "\r\n", 2) == 0) {
+ /* CRLF found, bump past the CR and copy the NL */
+ inPtr++;
+ *outPtr = *inPtr;
+ /* keep track of how many CRLFs we converted */
+ data->state.crlf_conversions++;
+ }
+ else {
+ if (*inPtr == '\r') {
+ /* lone CR, move LF instead */
+ *outPtr = '\n';
+ }
+ else {
+ /* not a CRLF nor a CR, just copy whatever it is */
+ *outPtr = *inPtr;
+ }
+ }
+ outPtr++;
+ inPtr++;
+ } /* end of while loop */
+
+ if (inPtr < startPtr+size) {
+ /* handle last byte */
+ if (*inPtr == '\r') {
+ /* deal with a CR at the end of the buffer */
+ *outPtr = '\n'; /* copy a NL instead */
+ /* note that a CRLF might be split across two blocks */
+ data->state.prev_block_had_trailing_cr = TRUE;
+ }
+ else {
+ /* copy last byte */
+ *outPtr = *inPtr;
+ }
+ outPtr++;
+ inPtr++;
+ }
+ if (outPtr < startPtr+size) {
+ /* tidy up by null terminating the now shorter data */
+ *outPtr = '\0';
+ }
+ return(outPtr - startPtr);
+ }
+ return(size);
+}
+#endif /* CURL_DO_LINEEND_CONV */
+
+/* Curl_infof() is for info message along the way */
+
+void Curl_infof(struct SessionHandle *data, const char *fmt, ...)
+{
+ if(data && data->set.verbose) {
+ va_list ap;
+ size_t len;
+ char print_buffer[1024 + 1];
+ va_start(ap, fmt);
+ vsnprintf(print_buffer, 1024, fmt, ap);
+ va_end(ap);
+ len = strlen(print_buffer);
+ Curl_debug(data, CURLINFO_TEXT, print_buffer, len, NULL);
+ }
+}
+
+/* Curl_failf() is for messages stating why we failed.
+ * The message SHALL NOT include any LF or CR.
+ */
+
+void Curl_failf(struct SessionHandle *data, const char *fmt, ...)
+{
+ va_list ap;
+ size_t len;
+ va_start(ap, fmt);
+
+ vsnprintf(data->state.buffer, BUFSIZE, fmt, ap);
+
+ if(data->set.errorbuffer && !data->state.errorbuf) {
+ snprintf(data->set.errorbuffer, CURL_ERROR_SIZE, "%s", data->state.buffer);
+ data->state.errorbuf = TRUE; /* wrote error string */
+ }
+ if(data->set.verbose) {
+ len = strlen(data->state.buffer);
+ if(len < BUFSIZE - 1) {
+ data->state.buffer[len] = '\n';
+ data->state.buffer[++len] = '\0';
+ }
+ Curl_debug(data, CURLINFO_TEXT, data->state.buffer, len, NULL);
+ }
+
+ va_end(ap);
+}
+
+/* Curl_sendf() sends formated data to the server */
+CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn,
+ const char *fmt, ...)
+{
+ struct SessionHandle *data = conn->data;
+ ssize_t bytes_written;
+ size_t write_len;
+ CURLcode res = CURLE_OK;
+ char *s;
+ char *sptr;
+ va_list ap;
+ va_start(ap, fmt);
+ s = vaprintf(fmt, ap); /* returns an allocated string */
+ va_end(ap);
+ if(!s)
+ return CURLE_OUT_OF_MEMORY; /* failure */
+
+ bytes_written=0;
+ write_len = strlen(s);
+ sptr = s;
+
+ while (1) {
+ /* Write the buffer to the socket */
+ res = Curl_write(conn, sockfd, sptr, write_len, &bytes_written);
+
+ if(CURLE_OK != res)
+ break;
+
+ if(data->set.verbose)
+ Curl_debug(data, CURLINFO_DATA_OUT, sptr, (size_t)bytes_written, conn);
+
+ if((size_t)bytes_written != write_len) {
+ /* if not all was written at once, we must advance the pointer, decrease
+ the size left and try again! */
+ write_len -= bytes_written;
+ sptr += bytes_written;
+ }
+ else
+ break;
+ }
+
+ free(s); /* free the output string */
+
+ return res;
+}
+
+static ssize_t Curl_plain_send(struct connectdata *conn,
+ int num,
+ void *mem,
+ size_t len)
+{
+ curl_socket_t sockfd = conn->sock[num];
+ ssize_t bytes_written = swrite(sockfd, mem, len);
+
+ if(-1 == bytes_written) {
+ int err = Curl_sockerrno();
+
+ if(
+#ifdef WSAEWOULDBLOCK
+ /* This is how Windows does it */
+ (WSAEWOULDBLOCK == err)
+#else
+ /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
+ due to its inability to send off data without blocking. We therefor
+ treat both error codes the same here */
+ (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
+#endif
+ )
+ /* this is just a case of EWOULDBLOCK */
+ bytes_written=0;
+ else
+ failf(conn->data, "Send failure: %s",
+ Curl_strerror(conn, err));
+ }
+ return bytes_written;
+}
+
+/*
+ * Curl_write() is an internal write function that sends data to the
+ * server. Works with plain sockets, SCP, SSL or kerberos.
+ */
+CURLcode Curl_write(struct connectdata *conn,
+ curl_socket_t sockfd,
+ void *mem,
+ size_t len,
+ ssize_t *written)
+{
+ ssize_t bytes_written;
+ CURLcode retcode;
+ int num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+ if (conn->ssl[num].use)
+ /* only TRUE if SSL enabled */
+ bytes_written = Curl_ssl_send(conn, num, mem, len);
+#ifdef USE_LIBSSH2
+ else if (conn->protocol & PROT_SCP)
+ bytes_written = Curl_scp_send(conn, num, mem, len);
+ else if (conn->protocol & PROT_SFTP)
+ bytes_written = Curl_sftp_send(conn, num, mem, len);
+#endif /* !USE_LIBSSH2 */
+ else if(conn->sec_complete)
+ /* only TRUE if krb4 enabled */
+ bytes_written = Curl_sec_send(conn, num, mem, len);
+ else
+ bytes_written = Curl_plain_send(conn, num, mem, len);
+
+ *written = bytes_written;
+ retcode = (-1 != bytes_written)?CURLE_OK:CURLE_SEND_ERROR;
+
+ return retcode;
+}
+
+/* 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 connectdata *conn,
+ int type,
+ char *ptr,
+ size_t len)
+{
+ struct SessionHandle *data = conn->data;
+ size_t wrote;
+
+ if (data->state.cancelled) {
+ /* We just suck everything into a black hole */
+ return CURLE_OK;
+ }
+
+ if(0 == len)
+ len = strlen(ptr);
+
+ if(type & CLIENTWRITE_BODY) {
+ if((conn->protocol&PROT_FTP) && conn->proto.ftpc.transfertype == 'A') {
+#ifdef CURL_DOES_CONVERSIONS
+ /* convert from the network encoding */
+ size_t rc;
+ rc = Curl_convert_from_network(data, ptr, len);
+ /* Curl_convert_from_network calls failf if unsuccessful */
+ if(rc != CURLE_OK)
+ return rc;
+#endif /* CURL_DOES_CONVERSIONS */
+
+#ifdef CURL_DO_LINEEND_CONV
+ /* convert end-of-line markers */
+ len = convert_lineends(data, ptr, len);
+#endif /* CURL_DO_LINEEND_CONV */
+ }
+ /* If the previous block of data ended with CR and this block of data is
+ just a NL, then the length might be zero */
+ if (len) {
+ wrote = data->set.fwrite(ptr, 1, len, data->set.out);
+ }
+ else {
+ wrote = len;
+ }
+
+ 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;
+
+ /* Note: The header is in the host encoding
+ regardless of the ftp transfer mode (ASCII/Image) */
+
+ wrote = writeit(ptr, 1, len, data->set.writeheader);
+ if(wrote != len) {
+ failf (data, "Failed writing header");
+ return CURLE_WRITE_ERROR;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * 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, /* connection data */
+ curl_socket_t sockfd, /* read from this socket */
+ char *buf, /* store read data here */
+ size_t sizerequested, /* max amount to read */
+ ssize_t *n) /* amount bytes read */
+{
+ ssize_t nread;
+ size_t bytesfromsocket = 0;
+ char *buffertofill = NULL;
+ bool pipelining = (bool)(conn->data->multi &&
+ Curl_multi_canPipeline(conn->data->multi));
+
+ /* Set 'num' to 0 or 1, depending on which socket that has been sent here.
+ If it is the second socket, we set num to 1. Otherwise to 0. This lets
+ us use the correct ssl handle. */
+ int num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+ *n=0; /* reset amount to zero */
+
+ /* If session can pipeline, check connection buffer */
+ if(pipelining) {
+ size_t bytestocopy = MIN(conn->buf_len - conn->read_pos, sizerequested);
+
+ /* Copy from our master buffer first if we have some unread data there*/
+ if (bytestocopy > 0) {
+ memcpy(buf, conn->master_buffer + conn->read_pos, bytestocopy);
+ conn->read_pos += bytestocopy;
+ conn->bits.stream_was_rewound = FALSE;
+
+ *n = (ssize_t)bytestocopy;
+ return CURLE_OK;
+ }
+ /* If we come here, it means that there is no data to read from the buffer,
+ * so we read from the socket */
+ bytesfromsocket = MIN(sizerequested, sizeof(conn->master_buffer));
+ buffertofill = conn->master_buffer;
+ }
+ else {
+ bytesfromsocket = MIN((long)sizerequested, conn->data->set.buffer_size ?
+ conn->data->set.buffer_size : BUFSIZE);
+ buffertofill = buf;
+ }
+
+ if(conn->ssl[num].use) {
+ nread = Curl_ssl_recv(conn, num, buffertofill, bytesfromsocket);
+
+ if(nread == -1) {
+ return -1; /* -1 from Curl_ssl_recv() means EWOULDBLOCK */
+ }
+ }
+#ifdef USE_LIBSSH2
+ else if (conn->protocol & PROT_SCP) {
+ nread = Curl_scp_recv(conn, num, buffertofill, bytesfromsocket);
+ /* TODO: return CURLE_OK also for nread <= 0
+ read failures and timeouts ? */
+ }
+ else if (conn->protocol & PROT_SFTP) {
+ nread = Curl_sftp_recv(conn, num, buffertofill, bytesfromsocket);
+ }
+#endif /* !USE_LIBSSH2 */
+ else {
+ if(conn->sec_complete)
+ nread = Curl_sec_read(conn, sockfd, buffertofill,
+ bytesfromsocket);
+ else
+ nread = sread(sockfd, buffertofill, bytesfromsocket);
+
+ if(-1 == nread) {
+ int err = Curl_sockerrno();
+#ifdef USE_WINSOCK
+ if(WSAEWOULDBLOCK == err)
+#else
+ if((EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err))
+#endif
+ return -1;
+ }
+ }
+
+ if (nread >= 0) {
+ if(pipelining) {
+ memcpy(buf, conn->master_buffer, nread);
+ conn->buf_len = nread;
+ conn->read_pos = nread;
+ }
+
+ *n += nread;
+ }
+
+ return CURLE_OK;
+}
+
+/* return 0 on success */
+static int showit(struct SessionHandle *data, curl_infotype type,
+ char *ptr, size_t size)
+{
+ static const char * const s_infotype[CURLINFO_END] = {
+ "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
+
+#ifdef CURL_DOES_CONVERSIONS
+ char buf[BUFSIZE+1];
+ size_t conv_size = 0;
+
+ switch(type) {
+ case CURLINFO_HEADER_OUT:
+ /* assume output headers are ASCII */
+ /* copy the data into my buffer so the original is unchanged */
+ if (size > BUFSIZE) {
+ size = BUFSIZE; /* truncate if necessary */
+ buf[BUFSIZE] = '\0';
+ }
+ conv_size = size;
+ memcpy(buf, ptr, size);
+ /* Special processing is needed for this block if it
+ * contains both headers and data (separated by CRLFCRLF).
+ * We want to convert just the headers, leaving the data as-is.
+ */
+ if(size > 4) {
+ size_t i;
+ for(i = 0; i < size-4; i++) {
+ if(memcmp(&buf[i], "\x0d\x0a\x0d\x0a", 4) == 0) {
+ /* convert everthing through this CRLFCRLF but no further */
+ conv_size = i + 4;
+ break;
+ }
+ }
+ }
+
+ Curl_convert_from_network(data, buf, conv_size);
+ /* Curl_convert_from_network calls failf if unsuccessful */
+ /* we might as well continue even if it fails... */
+ ptr = buf; /* switch pointer to use my buffer instead */
+ break;
+ default:
+ /* leave everything else as-is */
+ break;
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+ if(data->set.fdebug)
+ return (*data->set.fdebug)(data, type, ptr, size,
+ data->set.debugdata);
+
+ switch(type) {
+ case CURLINFO_TEXT:
+ case CURLINFO_HEADER_OUT:
+ case CURLINFO_HEADER_IN:
+ fwrite(s_infotype[type], 2, 1, data->set.err);
+ fwrite(ptr, size, 1, data->set.err);
+#ifdef CURL_DOES_CONVERSIONS
+ if(size != conv_size) {
+ /* we had untranslated data so we need an explicit newline */
+ fwrite("\n", 1, 1, data->set.err);
+ }
+#endif
+ break;
+ default: /* nada */
+ break;
+ }
+ return 0;
+}
+
+int Curl_debug(struct SessionHandle *data, curl_infotype type,
+ char *ptr, size_t size,
+ struct connectdata *conn)
+{
+ int rc;
+ if(data->set.printhost && conn && conn->host.dispname) {
+ char buffer[160];
+ const char *t=NULL;
+ const char *w="Data";
+ switch (type) {
+ case CURLINFO_HEADER_IN:
+ w = "Header";
+ case CURLINFO_DATA_IN:
+ t = "from";
+ break;
+ case CURLINFO_HEADER_OUT:
+ w = "Header";
+ case CURLINFO_DATA_OUT:
+ t = "to";
+ break;
+ default:
+ break;
+ }
+
+ if(t) {
+ snprintf(buffer, sizeof(buffer), "[%s %s %s]", w, t,
+ conn->host.dispname);
+ rc = showit(data, CURLINFO_TEXT, buffer, strlen(buffer));
+ if(rc)
+ return rc;
+ }
+ }
+ rc = showit(data, type, ptr, size);
+ return rc;
+}
diff --git a/Utilities/cmcurl/sendf.h b/Utilities/cmcurl/sendf.h
new file mode 100644
index 000000000..7877df823
--- /dev/null
+++ b/Utilities/cmcurl/sendf.h
@@ -0,0 +1,72 @@
+#ifndef __SENDF_H
+#define __SENDF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *,
+ const char *fmt, ...);
+void Curl_infof(struct SessionHandle *, const char *fmt, ...);
+void Curl_failf(struct SessionHandle *, const char *fmt, ...);
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+#if defined(__GNUC__)
+/* This style of variable argument macros is a gcc extension */
+#define infof(x...) /*ignore*/
+#else
+/* C99 compilers could use this if we could detect them */
+/*#define infof(...) */
+/* Cast the args to void to make them a noop, side effects notwithstanding */
+#define infof (void)
+#endif
+#else
+#define infof Curl_infof
+#endif
+#define failf Curl_failf
+
+#define CLIENTWRITE_BODY 1
+#define CLIENTWRITE_HEADER 2
+#define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER)
+
+CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr,
+ size_t len);
+
+void Curl_read_rewind(struct connectdata *conn,
+ size_t extraBytesRead);
+
+/* internal read-function, does plain socket, SSL and krb4 */
+int Curl_read(struct connectdata *conn, curl_socket_t sockfd,
+ char *buf, size_t buffersize,
+ ssize_t *n);
+/* internal write-function, does plain socket, SSL and krb4 */
+CURLcode Curl_write(struct connectdata *conn,
+ curl_socket_t sockfd,
+ void *mem, size_t len,
+ ssize_t *written);
+
+/* the function used to output verbose information */
+int Curl_debug(struct SessionHandle *handle, curl_infotype type,
+ char *data, size_t size,
+ struct connectdata *conn);
+
+
+#endif
diff --git a/Utilities/cmcurl/setup.h b/Utilities/cmcurl/setup.h
new file mode 100644
index 000000000..5ae881fba
--- /dev/null
+++ b/Utilities/cmcurl/setup.h
@@ -0,0 +1,390 @@
+#ifndef __LIB_CURL_SETUP_H
+#define __LIB_CURL_SETUP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#ifdef HTTP_ONLY
+#define CURL_DISABLE_TFTP
+#define CURL_DISABLE_FTP
+#define CURL_DISABLE_LDAP
+#define CURL_DISABLE_TELNET
+#define CURL_DISABLE_DICT
+#define CURL_DISABLE_FILE
+#endif /* HTTP_ONLY */
+
+#if !defined(WIN32) && defined(__WIN32__)
+/* Borland fix */
+#define WIN32
+#endif
+
+#if !defined(WIN32) && defined(_WIN32)
+/* VS2005 on x64 fix */
+#define WIN32
+#endif
+
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wcast-align"
+#endif
+
+/*
+ * Include configuration script results or hand-crafted
+ * configuration file for platforms which lack config tool.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+
+/*
+#ifdef _WIN32_WCE
+#include "config-win32ce.h"
+#else
+#ifdef WIN32
+#include "config-win32.h"
+#endif
+#endif
+*/
+
+#ifdef macintosh
+#include "config-mac.h"
+#endif
+
+#ifdef AMIGA
+#include "amigaos.h"
+#endif
+
+#ifdef TPF
+#include "config-tpf.h" /* hand-modified TPF config.h */
+/* change which select is used for libcurl */
+#define select(a,b,c,d,e) tpf_select_libcurl(a,b,c,d,e)
+#endif
+
+#endif /* HAVE_CONFIG_H */
+
+/*
+ * Include header files for windows builds before redefining anything.
+ * Use this preproessor block only to include or exclude windows.h,
+ * winsock2.h, ws2tcpip.h or winsock.h. Any other windows thing belongs
+ * to any other further and independant block. Under Cygwin things work
+ * just as under linux (e.g. <sys/socket.h>) and the winsock headers should
+ * never be included when __CYGWIN__ is defined. configure script takes
+ * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK_H, HAVE_WINSOCK2_H,
+ * neither HAVE_WS2TCPIP_H when __CYGWIN__ is defined.
+ */
+
+#ifdef HAVE_WINDOWS_H
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <windows.h>
+# ifdef HAVE_WINSOCK2_H
+# include <winsock2.h>
+# ifdef HAVE_WS2TCPIP_H
+# include <ws2tcpip.h>
+# endif
+# else
+# ifdef HAVE_WINSOCK_H
+# include <winsock.h>
+# endif
+# endif
+#endif
+
+/*
+ * Define USE_WINSOCK to 2 if we have and use WINSOCK2 API, else
+ * define USE_WINSOCK to 1 if we have and use WINSOCK API, else
+ * undefine USE_WINSOCK.
+ */
+
+#undef USE_WINSOCK
+
+#ifdef HAVE_WINSOCK2_H
+# define USE_WINSOCK 2
+#else
+# ifdef HAVE_WINSOCK_H
+# define USE_WINSOCK 1
+# endif
+#endif
+
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#if !defined(__cplusplus) && !defined(__BEOS__) && !defined(__ECOS) && !defined(typedef_bool)
+typedef unsigned char bool;
+#define typedef_bool
+#endif
+
+#ifdef HAVE_LONGLONG
+#define LONG_LONG long long
+#define ENABLE_64BIT
+#else
+#ifdef _MSC_VER
+#define LONG_LONG __int64
+#define ENABLE_64BIT
+#endif /* _MSC_VER */
+#endif /* HAVE_LONGLONG */
+
+#ifndef SIZEOF_CURL_OFF_T
+/* If we don't know the size here, we assume a conservative size: 4. When
+ building libcurl, the actual size of this variable should be define in the
+ config*.h file. */
+#define SIZEOF_CURL_OFF_T 4
+#endif
+
+/* We set up our internal preferred (CURL_)FORMAT_OFF_T here */
+#if SIZEOF_CURL_OFF_T > 4
+#define FORMAT_OFF_T "lld"
+#else
+#define FORMAT_OFF_T "ld"
+#endif /* SIZEOF_CURL_OFF_T */
+
+#ifndef _REENTRANT
+/* Solaris 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. Unixware needs it to build proper reentrant code. Others may also
+ need it. */
+#define _REENTRANT
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+#include <errno.h>
+
+#ifdef __TANDEM /* for nsr-tandem-nsk systems */
+#include <floss.h>
+#endif
+
+#ifndef STDC_HEADERS /* no standard C headers! */
+#include <curl/stdcheaders.h>
+#endif
+
+/*
+ * PellesC cludge section (yikes);
+ * - It has 'ssize_t', but it is in <unistd.h>. The way the headers
+ * on Win32 are included, forces me to include this header here.
+ * - sys_nerr, EINTR is missing in v4.0 or older.
+ */
+#ifdef __POCC__
+ #include <sys/types.h>
+ #include <unistd.h>
+ #if (__POCC__ <= 400)
+ #define sys_nerr EILSEQ /* for strerror.c */
+ #define EINTR -1 /* for select.c */
+ #endif
+#endif
+
+/*
+ * Salford-C cludge section (mostly borrowed from wxWidgets).
+ */
+#ifdef __SALFORDC__
+ #pragma suppress 353 /* Possible nested comments */
+ #pragma suppress 593 /* Define not used */
+ #pragma suppress 61 /* enum has no name */
+ #pragma suppress 106 /* unnamed, unused parameter */
+ #include <clib.h>
+#endif
+
+#if defined(CURLDEBUG) && defined(HAVE_ASSERT_H)
+#define curlassert(x) assert(x)
+#else
+/* does nothing without CURLDEBUG defined */
+#define curlassert(x)
+#endif
+
+
+/* To make large file support transparent even on Windows */
+#if defined(WIN32) && (SIZEOF_CURL_OFF_T > 4)
+#include <sys/stat.h> /* must come first before we redefine stat() */
+#include <io.h>
+#define lseek(x,y,z) _lseeki64(x, y, z)
+#define struct_stat struct _stati64
+#define stat(file,st) _stati64(file,st)
+#define fstat(fd,st) _fstati64(fd,st)
+#else
+#define struct_stat struct stat
+#endif /* Win32 with large file support */
+
+
+/* Below we define some functions. They should
+ 1. close a socket
+
+ 4. set the SIGALRM signal timeout
+ 5. set dir/file naming defines
+ */
+
+#ifdef WIN32
+
+#if !defined(__CYGWIN__)
+#define sclose(x) closesocket(x)
+
+#undef HAVE_ALARM
+#else
+ /* gcc-for-win is still good :) */
+#define sclose(x) close(x)
+#define HAVE_ALARM
+#endif /* !GNU or mingw */
+
+#define DIR_CHAR "\\"
+#define DOT_CHAR "_"
+
+#else /* WIN32 */
+
+#ifdef MSDOS /* Watt-32 */
+#include <sys/ioctl.h>
+#define sclose(x) close_s(x)
+#define select(n,r,w,x,t) select_s(n,r,w,x,t)
+#define ioctl(x,y,z) ioctlsocket(x,y,(char *)(z))
+#define IOCTL_3_ARGS
+#include <tcp.h>
+#ifdef word
+#undef word
+#endif
+
+#else /* MSDOS */
+
+#ifdef __VMS
+#define IOCTL_3_ARGS 1
+#endif
+
+#ifdef __BEOS__
+#define sclose(x) closesocket(x)
+#else /* __BEOS__ */
+#define sclose(x) close(x)
+#endif /* __BEOS__ */
+
+#define HAVE_ALARM
+
+#endif /* MSDOS */
+
+#ifdef _AMIGASF
+#undef HAVE_ALARM
+#undef sclose
+#define sclose(x) CloseSocket(x)
+#endif
+
+#define DIR_CHAR "/"
+#ifndef DOT_CHAR
+#define DOT_CHAR "."
+#endif
+
+#ifdef MSDOS
+#undef DOT_CHAR
+#define DOT_CHAR "_"
+#endif
+
+#ifndef fileno /* sunos 4 have this as a macro! */
+int fileno( FILE *stream);
+#endif
+
+#endif /* WIN32 */
+
+#if defined(WIN32) && !defined(__CYGWIN__) && !defined(USE_ARES) && \
+ !defined(__LCC__) /* lcc-win32 doesn't have _beginthreadex() */
+#ifdef ENABLE_IPV6
+#define USE_THREADING_GETADDRINFO
+#else
+#define USE_THREADING_GETHOSTBYNAME /* Cygwin uses alarm() function */
+#endif
+#endif
+
+/* "cl -ML" or "cl -MLd" implies a single-threaded runtime library where
+ _beginthreadex() is not available */
+#if (defined(_MSC_VER) && !defined(__POCC__)) && !defined(_MT) && !defined(USE_ARES)
+#undef USE_THREADING_GETADDRINFO
+#undef USE_THREADING_GETHOSTBYNAME
+#define CURL_NO__BEGINTHREADEX
+#endif
+
+/*
+ * msvc 6.0 does not have struct sockaddr_storage and
+ * does not define IPPROTO_ESP in winsock2.h. But both
+ * are available if PSDK is properly installed.
+ */
+
+#ifdef _MSC_VER
+#if !defined(HAVE_WINSOCK2_H) || ((_MSC_VER < 1300) && !defined(IPPROTO_ESP))
+#undef HAVE_STRUCT_SOCKADDR_STORAGE
+#endif
+#endif
+
+#ifdef mpeix
+#define IOCTL_3_ARGS
+#endif
+
+#ifdef NETWARE
+#undef HAVE_ALARM
+#endif
+
+#if defined(HAVE_LIBIDN) && defined(HAVE_TLD_H)
+/* The lib was present and the tld.h header (which is missing in libidn 0.3.X
+ but we only work with libidn 0.4.1 or later) */
+#define USE_LIBIDN
+#endif
+
+#ifndef SIZEOF_TIME_T
+/* assume default size of time_t to be 32 bit */
+#define SIZEOF_TIME_T 4
+#endif
+
+#define LIBIDN_REQUIRED_VERSION "0.4.1"
+
+#ifdef __UCLIBC__
+#define HAVE_INET_NTOA_R_2_ARGS 1
+#endif
+
+#if defined(USE_GNUTLS) || defined(USE_SSLEAY)
+#define USE_SSL /* Either OpenSSL || GnuTLS */
+#endif
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_NTLM)
+#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI)
+#define USE_NTLM
+#endif
+#endif
+
+#ifdef CURLDEBUG
+#define DEBUGF(x) x
+#else
+#define DEBUGF(x)
+#endif
+
+/* non-configure builds may define CURL_WANTS_CA_BUNDLE_ENV */
+#if defined(CURL_WANTS_CA_BUNDLE_ENV) && !defined(CURL_CA_BUNDLE)
+#define CURL_CA_BUNDLE getenv("CURL_CA_BUNDLE")
+#endif
+
+/*
+ * Include macros and defines that should only be processed once.
+ */
+
+#ifndef __SETUP_ONCE_H
+#include "setup_once.h"
+#endif
+
+#endif /* __LIB_CURL_SETUP_H */
diff --git a/Utilities/cmcurl/setup_once.h b/Utilities/cmcurl/setup_once.h
new file mode 100644
index 000000000..ee6864158
--- /dev/null
+++ b/Utilities/cmcurl/setup_once.h
@@ -0,0 +1,153 @@
+#ifndef __SETUP_ONCE_H
+#define __SETUP_ONCE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+
+/********************************************************************
+ * NOTICE *
+ * ======== *
+ * *
+ * Content of header files lib/setup_once.h and ares/setup_once.h *
+ * must be kept in sync. Modify the other one if you change this. *
+ * *
+ ********************************************************************/
+
+
+/*
+ * If we have the MSG_NOSIGNAL define, make sure we use
+ * it as the fourth argument of function send()
+ */
+
+#ifdef HAVE_MSG_NOSIGNAL
+#define SEND_4TH_ARG MSG_NOSIGNAL
+#else
+#define SEND_4TH_ARG 0
+#endif
+
+
+/*
+ * The definitions for the return type and arguments types
+ * of functions recv() and send() belong and come from the
+ * configuration file. Do not define them in any other place.
+ *
+ * HAVE_RECV is defined if you have a function named recv()
+ * which is used to read incoming data from sockets. If your
+ * function has another name then don't define HAVE_RECV.
+ *
+ * If HAVE_RECV is defined then RECV_TYPE_ARG1, RECV_TYPE_ARG2,
+ * RECV_TYPE_ARG3, RECV_TYPE_ARG4 and RECV_TYPE_RETV must also
+ * be defined.
+ *
+ * HAVE_SEND is defined if you have a function named send()
+ * which is used to write outgoing data on a connected socket.
+ * If yours has another name then don't define HAVE_SEND.
+ *
+ * If HAVE_SEND is defined then SEND_TYPE_ARG1, SEND_QUAL_ARG2,
+ * SEND_TYPE_ARG2, SEND_TYPE_ARG3, SEND_TYPE_ARG4 and
+ * SEND_TYPE_RETV must also be defined.
+ */
+
+#ifdef HAVE_RECV
+#if !defined(RECV_TYPE_ARG1) || \
+ !defined(RECV_TYPE_ARG2) || \
+ !defined(RECV_TYPE_ARG3) || \
+ !defined(RECV_TYPE_ARG4) || \
+ !defined(RECV_TYPE_RETV)
+ /* */
+ Error Missing_definition_of_return_and_arguments_types_of_recv
+ /* */
+#else
+#define sread(x,y,z) (ssize_t)recv((RECV_TYPE_ARG1)(x), \
+ (RECV_TYPE_ARG2)(y), \
+ (RECV_TYPE_ARG3)(z), \
+ (RECV_TYPE_ARG4)(0))
+#endif
+#else /* HAVE_RECV */
+#ifndef sread
+ /* */
+ Error Missing_definition_of_macro_sread
+ /* */
+#endif
+#endif /* HAVE_RECV */
+
+#ifdef HAVE_SEND
+#if !defined(SEND_TYPE_ARG1) || \
+ !defined(SEND_QUAL_ARG2) || \
+ !defined(SEND_TYPE_ARG2) || \
+ !defined(SEND_TYPE_ARG3) || \
+ !defined(SEND_TYPE_ARG4) || \
+ !defined(SEND_TYPE_RETV)
+ /* */
+ Error Missing_definition_of_return_and_arguments_types_of_send
+ /* */
+#else
+#define swrite(x,y,z) (ssize_t)send((SEND_TYPE_ARG1)(x), \
+ (SEND_TYPE_ARG2)(y), \
+ (SEND_TYPE_ARG3)(z), \
+ (SEND_TYPE_ARG4)(SEND_4TH_ARG))
+#endif
+#else /* HAVE_SEND */
+#ifndef swrite
+ /* */
+ Error Missing_definition_of_macro_swrite
+ /* */
+#endif
+#endif /* HAVE_SEND */
+
+
+/*
+ * Uppercase macro versions of ANSI/ISO is*() functions/macros which
+ * avoid negative number inputs with argument byte codes > 127.
+ */
+
+#define ISSPACE(x) (isspace((int) ((unsigned char)x)))
+#define ISDIGIT(x) (isdigit((int) ((unsigned char)x)))
+#define ISALNUM(x) (isalnum((int) ((unsigned char)x)))
+#define ISXDIGIT(x) (isxdigit((int) ((unsigned char)x)))
+#define ISGRAPH(x) (isgraph((int) ((unsigned char)x)))
+#define ISALPHA(x) (isalpha((int) ((unsigned char)x)))
+#define ISPRINT(x) (isprint((int) ((unsigned char)x)))
+
+
+/*
+ * Typedef to 'int' if sig_atomic_t is not an available 'typedefed' type.
+ */
+
+#ifndef HAVE_SIG_ATOMIC_T
+typedef int sig_atomic_t;
+#define HAVE_SIG_ATOMIC_T
+#endif
+
+
+/*
+ * Default return type for signal handlers.
+ */
+
+#ifndef RETSIGTYPE
+#define RETSIGTYPE void
+#endif
+
+
+#endif /* __SETUP_ONCE_H */
+
diff --git a/Utilities/cmcurl/share.c b/Utilities/cmcurl/share.c
new file mode 100644
index 000000000..de13b6021
--- /dev/null
+++ b/Utilities/cmcurl/share.c
@@ -0,0 +1,219 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <curl/curl.h>
+#include "urldata.h"
+#include "share.h"
+#include "memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+CURLSH *
+curl_share_init(void)
+{
+ struct Curl_share *share =
+ (struct Curl_share *)malloc(sizeof(struct Curl_share));
+ if (share) {
+ memset (share, 0, sizeof(struct Curl_share));
+ share->specifier |= (1<<CURL_LOCK_DATA_SHARE);
+ }
+
+ return share;
+}
+
+CURLSHcode
+curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
+{
+ struct Curl_share *share = (struct Curl_share *)sh;
+ va_list param;
+ int type;
+ curl_lock_function lockfunc;
+ curl_unlock_function unlockfunc;
+ void *ptr;
+
+ if (share->dirty)
+ /* don't allow setting options while one or more handles are already
+ using this share */
+ return CURLSHE_IN_USE;
+
+ va_start(param, option);
+
+ switch(option) {
+ case CURLSHOPT_SHARE:
+ /* this is a type this share will share */
+ type = va_arg(param, int);
+ share->specifier |= (1<<type);
+ switch( type ) {
+ case CURL_LOCK_DATA_DNS:
+ if (!share->hostcache) {
+ share->hostcache = Curl_mk_dnscache();
+ if(!share->hostcache)
+ return CURLSHE_NOMEM;
+ }
+ break;
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ case CURL_LOCK_DATA_COOKIE:
+ if (!share->cookies) {
+ share->cookies = Curl_cookie_init(NULL, NULL, NULL, TRUE );
+ if(!share->cookies)
+ return CURLSHE_NOMEM;
+ }
+ break;
+#endif /* CURL_DISABLE_HTTP */
+
+ case CURL_LOCK_DATA_SSL_SESSION: /* not supported (yet) */
+ case CURL_LOCK_DATA_CONNECT: /* not supported (yet) */
+
+ default:
+ return CURLSHE_BAD_OPTION;
+ }
+ break;
+
+ case CURLSHOPT_UNSHARE:
+ /* this is a type this share will no longer share */
+ type = va_arg(param, int);
+ share->specifier &= ~(1<<type);
+ switch( type )
+ {
+ case CURL_LOCK_DATA_DNS:
+ if (share->hostcache) {
+ Curl_hash_destroy(share->hostcache);
+ share->hostcache = NULL;
+ }
+ break;
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ case CURL_LOCK_DATA_COOKIE:
+ if (share->cookies) {
+ Curl_cookie_cleanup(share->cookies);
+ share->cookies = NULL;
+ }
+ break;
+#endif /* CURL_DISABLE_HTTP */
+
+ case CURL_LOCK_DATA_SSL_SESSION:
+ break;
+
+ case CURL_LOCK_DATA_CONNECT:
+ break;
+
+ default:
+ return CURLSHE_BAD_OPTION;
+ }
+ break;
+
+ case CURLSHOPT_LOCKFUNC:
+ lockfunc = va_arg(param, curl_lock_function);
+ share->lockfunc = lockfunc;
+ break;
+
+ case CURLSHOPT_UNLOCKFUNC:
+ unlockfunc = va_arg(param, curl_unlock_function);
+ share->unlockfunc = unlockfunc;
+ break;
+
+ case CURLSHOPT_USERDATA:
+ ptr = va_arg(param, void *);
+ share->clientdata = ptr;
+ break;
+
+ default:
+ return CURLSHE_BAD_OPTION;
+ }
+
+ return CURLSHE_OK;
+}
+
+CURLSHcode
+curl_share_cleanup(CURLSH *sh)
+{
+ struct Curl_share *share = (struct Curl_share *)sh;
+
+ if (share == NULL)
+ return CURLSHE_INVALID;
+
+ if(share->lockfunc)
+ share->lockfunc(NULL, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE,
+ share->clientdata);
+
+ if (share->dirty) {
+ if(share->unlockfunc)
+ share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
+ return CURLSHE_IN_USE;
+ }
+
+ if(share->hostcache)
+ Curl_hash_destroy(share->hostcache);
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ if(share->cookies)
+ Curl_cookie_cleanup(share->cookies);
+#endif /* CURL_DISABLE_HTTP */
+
+ if(share->unlockfunc)
+ share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
+ free(share);
+
+ return CURLSHE_OK;
+}
+
+
+CURLSHcode
+Curl_share_lock(struct SessionHandle *data, curl_lock_data type,
+ curl_lock_access accesstype)
+{
+ struct Curl_share *share = data->share;
+
+ if (share == NULL)
+ return CURLSHE_INVALID;
+
+ if(share->specifier & (1<<type)) {
+ if(share->lockfunc) /* only call this if set! */
+ share->lockfunc(data, type, accesstype, share->clientdata);
+ }
+ /* else if we don't share this, pretend successful lock */
+
+ return CURLSHE_OK;
+}
+
+CURLSHcode
+Curl_share_unlock(struct SessionHandle *data, curl_lock_data type)
+{
+ struct Curl_share *share = data->share;
+
+ if (share == NULL)
+ return CURLSHE_INVALID;
+
+ if(share->specifier & (1<<type)) {
+ if(share->unlockfunc) /* only call this if set! */
+ share->unlockfunc (data, type, share->clientdata);
+ }
+
+ return CURLSHE_OK;
+}
diff --git a/Utilities/cmcurl/share.h b/Utilities/cmcurl/share.h
new file mode 100644
index 000000000..5cfe8c792
--- /dev/null
+++ b/Utilities/cmcurl/share.h
@@ -0,0 +1,56 @@
+#ifndef __CURL_SHARE_H
+#define __CURL_SHARE_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+#include <curl/curl.h>
+#include "cookie.h"
+
+/* SalfordC says "A structure member may not be volatile". Hence:
+ */
+#ifdef __SALFORDC__
+#define CURL_VOLATILE
+#else
+#define CURL_VOLATILE volatile
+#endif
+
+/* this struct is libcurl-private, don't export details */
+struct Curl_share {
+ unsigned int specifier;
+ CURL_VOLATILE unsigned int dirty;
+
+ curl_lock_function lockfunc;
+ curl_unlock_function unlockfunc;
+ void *clientdata;
+
+ struct curl_hash *hostcache;
+ struct CookieInfo *cookies;
+};
+
+CURLSHcode Curl_share_lock (struct SessionHandle *, curl_lock_data,
+ curl_lock_access);
+CURLSHcode Curl_share_unlock (struct SessionHandle *, curl_lock_data);
+
+#endif /* __CURL_SHARE_H */
diff --git a/Utilities/cmcurl/sockaddr.h b/Utilities/cmcurl/sockaddr.h
new file mode 100644
index 000000000..78dad4d9e
--- /dev/null
+++ b/Utilities/cmcurl/sockaddr.h
@@ -0,0 +1,38 @@
+#ifndef __SOCKADDR_H
+#define __SOCKADDR_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
+struct Curl_sockaddr_storage {
+ struct sockaddr_storage buffer;
+};
+#else
+struct Curl_sockaddr_storage {
+ char buffer[256]; /* this should be big enough to fit a lot */
+};
+#endif
+
+#endif /* __SOCKADDR_H */
diff --git a/Utilities/cmcurl/socks.c b/Utilities/cmcurl/socks.c
new file mode 100644
index 000000000..3319e697e
--- /dev/null
+++ b/Utilities/cmcurl/socks.c
@@ -0,0 +1,585 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+
+#ifdef NEED_MALLOC_H
+#include <malloc.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "strequal.h"
+#include "select.h"
+#include "connect.h"
+#include "timeval.h"
+#include "socks.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * Helper read-from-socket functions. Does the same as Curl_read() but it
+ * blocks until all bytes amount of buffersize will be read. No more, no less.
+ *
+ * This is STUPID BLOCKING behaviour which we frown upon, but right now this
+ * is what we have...
+ */
+static int blockread_all(struct connectdata *conn, /* connection data */
+ curl_socket_t sockfd, /* read from this socket */
+ char *buf, /* store read data here */
+ ssize_t buffersize, /* max amount to read */
+ ssize_t *n, /* amount bytes read */
+ long conn_timeout) /* timeout for data wait
+ relative to
+ conn->created */
+{
+ ssize_t nread;
+ ssize_t allread = 0;
+ int result;
+ struct timeval tvnow;
+ long conntime;
+ *n = 0;
+ do {
+ tvnow = Curl_tvnow();
+ /* calculating how long connection is establishing */
+ conntime = Curl_tvdiff(tvnow, conn->created);
+ if(conntime > conn_timeout) {
+ /* we already got the timeout */
+ result = ~CURLE_OK;
+ break;
+ }
+ if(Curl_select(sockfd, CURL_SOCKET_BAD,
+ (int)(conn_timeout - conntime)) <= 0) {
+ result = ~CURLE_OK;
+ break;
+ }
+ result = Curl_read(conn, sockfd, buf, buffersize, &nread);
+ if(result)
+ break;
+
+ if(buffersize == nread) {
+ allread += nread;
+ *n = allread;
+ result = CURLE_OK;
+ break;
+ }
+ buffersize -= nread;
+ buf += nread;
+ allread += nread;
+ } while(1);
+ return result;
+}
+
+/*
+* This function logs in to a SOCKS4 proxy and sends the specifics to the final
+* destination server.
+*
+* Reference :
+* http://socks.permeo.com/protocol/socks4.protocol
+*
+* Note :
+* Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
+* Nonsupport "Identification Protocol (RFC1413)"
+*/
+CURLcode Curl_SOCKS4(const char *proxy_name,
+ struct connectdata *conn)
+{
+ unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */
+ int result;
+ CURLcode code;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ long timeout;
+ struct SessionHandle *data = conn->data;
+
+ /* get timeout */
+ if(data->set.timeout && data->set.connecttimeout) {
+ if (data->set.timeout < data->set.connecttimeout)
+ timeout = data->set.timeout*1000;
+ else
+ timeout = data->set.connecttimeout*1000;
+ }
+ else if(data->set.timeout)
+ timeout = data->set.timeout*1000;
+ else if(data->set.connecttimeout)
+ timeout = data->set.connecttimeout*1000;
+ else
+ timeout = DEFAULT_CONNECT_TIMEOUT;
+
+ Curl_nonblock(sock, FALSE);
+
+ /*
+ * Compose socks4 request
+ *
+ * Request format
+ *
+ * +----+----+----+----+----+----+----+----+----+----+....+----+
+ * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
+ * +----+----+----+----+----+----+----+----+----+----+....+----+
+ * # of bytes: 1 1 2 4 variable 1
+ */
+
+ socksreq[0] = 4; /* version (SOCKS4) */
+ socksreq[1] = 1; /* connect */
+ *((unsigned short*)&socksreq[2]) = htons(conn->remote_port);
+
+ /* DNS resolve */
+ {
+ struct Curl_dns_entry *dns;
+ Curl_addrinfo *hp=NULL;
+ int rc;
+
+ rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns);
+
+ if(rc == CURLRESOLV_ERROR)
+ return CURLE_COULDNT_RESOLVE_PROXY;
+
+ if(rc == CURLRESOLV_PENDING)
+ /* this requires that we're in "wait for resolve" state */
+ rc = Curl_wait_for_resolv(conn, &dns);
+
+ /*
+ * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
+ * returns a Curl_addrinfo pointer that may not always look the same.
+ */
+ if(dns)
+ hp=dns->addr;
+ if (hp) {
+ char buf[64];
+ unsigned short ip[4];
+ Curl_printable_address(hp, buf, sizeof(buf));
+
+ if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
+ &ip[0], &ip[1], &ip[2], &ip[3])) {
+ /* Set DSTIP */
+ socksreq[4] = (unsigned char)ip[0];
+ socksreq[5] = (unsigned char)ip[1];
+ socksreq[6] = (unsigned char)ip[2];
+ socksreq[7] = (unsigned char)ip[3];
+ }
+ else
+ hp = NULL; /* fail! */
+
+ Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+
+ }
+ if(!hp) {
+ failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
+ conn->host.name);
+ return CURLE_COULDNT_RESOLVE_HOST;
+ }
+ }
+
+ /*
+ * This is currently not supporting "Identification Protocol (RFC1413)".
+ */
+ socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
+ if (proxy_name)
+ strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
+
+ /*
+ * Make connection
+ */
+ {
+ ssize_t actualread;
+ ssize_t written;
+ int packetsize = 9 +
+ (int)strlen((char*)socksreq + 8); /* size including NUL */
+
+ /* Send request */
+ code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
+ if ((code != CURLE_OK) || (written != packetsize)) {
+ failf(data, "Failed to send SOCKS4 connect request.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ packetsize = 8; /* receive data size */
+
+ /* Receive response */
+ result = blockread_all(conn, sock, (char *)socksreq, packetsize,
+ &actualread, timeout);
+ if ((result != CURLE_OK) || (actualread != packetsize)) {
+ failf(data, "Failed to receive SOCKS4 connect request ack.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ /*
+ * Response format
+ *
+ * +----+----+----+----+----+----+----+----+
+ * | VN | CD | DSTPORT | DSTIP |
+ * +----+----+----+----+----+----+----+----+
+ * # of bytes: 1 1 2 4
+ *
+ * VN is the version of the reply code and should be 0. CD is the result
+ * code with one of the following values:
+ *
+ * 90: request granted
+ * 91: request rejected or failed
+ * 92: request rejected because SOCKS server cannot connect to
+ * identd on the client
+ * 93: request rejected because the client program and identd
+ * report different user-ids
+ */
+
+ /* wrong version ? */
+ if (socksreq[0] != 0) {
+ failf(data,
+ "SOCKS4 reply has wrong version, version should be 4.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ /* Result */
+ switch(socksreq[1])
+ {
+ case 90:
+ infof(data, "SOCKS4 request granted.\n");
+ break;
+ case 91:
+ failf(data,
+ "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+ ", request rejected or failed.",
+ (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+ (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+ (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
+ socksreq[1]);
+ return CURLE_COULDNT_CONNECT;
+ case 92:
+ failf(data,
+ "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+ ", request rejected because SOCKS server cannot connect to "
+ "identd on the client.",
+ (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+ (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+ (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
+ socksreq[1]);
+ return CURLE_COULDNT_CONNECT;
+ case 93:
+ failf(data,
+ "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+ ", request rejected because the client program and identd "
+ "report different user-ids.",
+ (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+ (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+ (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
+ socksreq[1]);
+ return CURLE_COULDNT_CONNECT;
+ default:
+ failf(data,
+ "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+ ", Unknown.",
+ (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+ (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+ (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
+ socksreq[1]);
+ return CURLE_COULDNT_CONNECT;
+ }
+ }
+
+ Curl_nonblock(sock, TRUE);
+
+ return CURLE_OK; /* Proxy was successful! */
+}
+
+/*
+ * This function logs in to a SOCKS5 proxy and sends the specifics to the final
+ * destination server.
+ */
+CURLcode Curl_SOCKS5(const char *proxy_name,
+ const char *proxy_password,
+ struct connectdata *conn)
+{
+ /*
+ According to the RFC1928, section "6. Replies". This is what a SOCK5
+ replies:
+
+ +----+-----+-------+------+----------+----------+
+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+ +----+-----+-------+------+----------+----------+
+ | 1 | 1 | X'00' | 1 | Variable | 2 |
+ +----+-----+-------+------+----------+----------+
+
+ Where:
+
+ o VER protocol version: X'05'
+ o REP Reply field:
+ o X'00' succeeded
+ */
+
+ unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
+ ssize_t actualread;
+ ssize_t written;
+ int result;
+ CURLcode code;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ struct SessionHandle *data = conn->data;
+ long timeout;
+
+ /* get timeout */
+ if(data->set.timeout && data->set.connecttimeout) {
+ if (data->set.timeout < data->set.connecttimeout)
+ timeout = data->set.timeout*1000;
+ else
+ timeout = data->set.connecttimeout*1000;
+ }
+ else if(data->set.timeout)
+ timeout = data->set.timeout*1000;
+ else if(data->set.connecttimeout)
+ timeout = data->set.connecttimeout*1000;
+ else
+ timeout = DEFAULT_CONNECT_TIMEOUT;
+
+ Curl_nonblock(sock, TRUE);
+
+ /* wait until socket gets connected */
+ result = Curl_select(CURL_SOCKET_BAD, sock, (int)timeout);
+
+ if(-1 == result) {
+ failf(conn->data, "SOCKS5: no connection here");
+ return CURLE_COULDNT_CONNECT;
+ }
+ else if(0 == result) {
+ failf(conn->data, "SOCKS5: connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ if(result & CSELECT_ERR) {
+ failf(conn->data, "SOCKS5: error occured during connection");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ socksreq[0] = 5; /* version */
+ socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
+ socksreq[2] = 0; /* no authentication */
+ socksreq[3] = 2; /* username/password */
+
+ Curl_nonblock(sock, FALSE);
+
+ code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
+ &written);
+ if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
+ failf(data, "Unable to send initial SOCKS5 request.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ Curl_nonblock(sock, TRUE);
+
+ result = Curl_select(sock, CURL_SOCKET_BAD, (int)timeout);
+
+ if(-1 == result) {
+ failf(conn->data, "SOCKS5 nothing to read");
+ return CURLE_COULDNT_CONNECT;
+ }
+ else if(0 == result) {
+ failf(conn->data, "SOCKS5 read timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ if(result & CSELECT_ERR) {
+ failf(conn->data, "SOCKS5 read error occured");
+ return CURLE_RECV_ERROR;
+ }
+
+ Curl_nonblock(sock, FALSE);
+
+ result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout);
+ if ((result != CURLE_OK) || (actualread != 2)) {
+ failf(data, "Unable to receive initial SOCKS5 response.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if (socksreq[0] != 5) {
+ failf(data, "Received invalid version in initial SOCKS5 response.");
+ return CURLE_COULDNT_CONNECT;
+ }
+ if (socksreq[1] == 0) {
+ /* Nothing to do, no authentication needed */
+ ;
+ }
+ else if (socksreq[1] == 2) {
+ /* Needs user name and password */
+ size_t userlen, pwlen;
+ int len;
+ if(proxy_name && proxy_password) {
+ userlen = strlen(proxy_name);
+ pwlen = proxy_password?strlen(proxy_password):0;
+ }
+ else {
+ userlen = 0;
+ pwlen = 0;
+ }
+
+ /* username/password request looks like
+ * +----+------+----------+------+----------+
+ * |VER | ULEN | UNAME | PLEN | PASSWD |
+ * +----+------+----------+------+----------+
+ * | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
+ * +----+------+----------+------+----------+
+ */
+ len = 0;
+ socksreq[len++] = 1; /* username/pw subnegotiation version */
+ socksreq[len++] = (char) userlen;
+ memcpy(socksreq + len, proxy_name, (int) userlen);
+ len += userlen;
+ socksreq[len++] = (char) pwlen;
+ memcpy(socksreq + len, proxy_password, (int) pwlen);
+ len += pwlen;
+
+ code = Curl_write(conn, sock, (char *)socksreq, len, &written);
+ if ((code != CURLE_OK) || (len != written)) {
+ failf(data, "Failed to send SOCKS5 sub-negotiation request.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
+ timeout);
+ if ((result != CURLE_OK) || (actualread != 2)) {
+ failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ /* ignore the first (VER) byte */
+ if (socksreq[1] != 0) { /* status */
+ failf(data, "User was rejected by the SOCKS5 server (%d %d).",
+ socksreq[0], socksreq[1]);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ /* Everything is good so far, user was authenticated! */
+ }
+ else {
+ /* error */
+ if (socksreq[1] == 1) {
+ failf(data,
+ "SOCKS5 GSSAPI per-message authentication is not supported.");
+ return CURLE_COULDNT_CONNECT;
+ }
+ else if (socksreq[1] == 255) {
+ if (!proxy_name || !*proxy_name) {
+ failf(data,
+ "No authentication method was acceptable. (It is quite likely"
+ " that the SOCKS5 server wanted a username/password, since none"
+ " was supplied to the server on this connection.)");
+ }
+ else {
+ failf(data, "No authentication method was acceptable.");
+ }
+ return CURLE_COULDNT_CONNECT;
+ }
+ else {
+ failf(data,
+ "Undocumented SOCKS5 mode attempted to be used by server.");
+ return CURLE_COULDNT_CONNECT;
+ }
+ }
+
+ /* Authentication is complete, now specify destination to the proxy */
+ socksreq[0] = 5; /* version (SOCKS5) */
+ socksreq[1] = 1; /* connect */
+ socksreq[2] = 0; /* must be zero */
+ socksreq[3] = 1; /* IPv4 = 1 */
+
+ {
+ struct Curl_dns_entry *dns;
+ Curl_addrinfo *hp=NULL;
+ int rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns);
+
+ if(rc == CURLRESOLV_ERROR)
+ return CURLE_COULDNT_RESOLVE_HOST;
+
+ if(rc == CURLRESOLV_PENDING)
+ /* this requires that we're in "wait for resolve" state */
+ rc = Curl_wait_for_resolv(conn, &dns);
+
+ /*
+ * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
+ * returns a Curl_addrinfo pointer that may not always look the same.
+ */
+ if(dns)
+ hp=dns->addr;
+ if (hp) {
+ char buf[64];
+ unsigned short ip[4];
+ Curl_printable_address(hp, buf, sizeof(buf));
+
+ if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
+ &ip[0], &ip[1], &ip[2], &ip[3])) {
+ socksreq[4] = (unsigned char)ip[0];
+ socksreq[5] = (unsigned char)ip[1];
+ socksreq[6] = (unsigned char)ip[2];
+ socksreq[7] = (unsigned char)ip[3];
+ }
+ else
+ hp = NULL; /* fail! */
+
+ Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+ }
+ if(!hp) {
+ failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
+ conn->host.name);
+ return CURLE_COULDNT_RESOLVE_HOST;
+ }
+ }
+
+ *((unsigned short*)&socksreq[8]) = htons(conn->remote_port);
+
+ {
+ const int packetsize = 10;
+
+ code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
+ if ((code != CURLE_OK) || (written != packetsize)) {
+ failf(data, "Failed to send SOCKS5 connect request.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ result = blockread_all(conn, sock, (char *)socksreq, packetsize,
+ &actualread, timeout);
+ if ((result != CURLE_OK) || (actualread != packetsize)) {
+ failf(data, "Failed to receive SOCKS5 connect request ack.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if (socksreq[0] != 5) { /* version */
+ failf(data,
+ "SOCKS5 reply has wrong version, version should be 5.");
+ return CURLE_COULDNT_CONNECT;
+ }
+ if (socksreq[1] != 0) { /* Anything besides 0 is an error */
+ failf(data,
+ "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
+ (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+ (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+ (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
+ socksreq[1]);
+ return CURLE_COULDNT_CONNECT;
+ }
+ }
+
+ Curl_nonblock(sock, TRUE);
+ return CURLE_OK; /* Proxy was successful! */
+}
diff --git a/Utilities/cmcurl/socks.h b/Utilities/cmcurl/socks.h
new file mode 100644
index 000000000..0da987998
--- /dev/null
+++ b/Utilities/cmcurl/socks.h
@@ -0,0 +1,41 @@
+#ifndef __SOCKS_H
+#define __SOCKS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ * This function logs in to a SOCKS4 proxy and sends the specifics to the
+ * final destination server.
+ */
+CURLcode Curl_SOCKS4(const char *proxy_name,
+ struct connectdata *conn);
+
+/*
+ * This function logs in to a SOCKS5 proxy and sends the specifics to the
+ * final destination server.
+ */
+CURLcode Curl_SOCKS5(const char *proxy_name,
+ const char *proxy_password,
+ struct connectdata *conn);
+
+#endif
diff --git a/Utilities/cmcurl/speedcheck.c b/Utilities/cmcurl/speedcheck.c
new file mode 100644
index 000000000..adda8a963
--- /dev/null
+++ b/Utilities/cmcurl/speedcheck.c
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "multiif.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)) {
+ long howlong = Curl_tvdiff(now, data->state.keeps_speed);
+
+ /* 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( (howlong/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;
+ }
+ Curl_expire(data, howlong);
+ }
+ else {
+ /* we keep up the required speed all right */
+ data->state.keeps_speed = now;
+
+ if(data->set.low_speed_limit)
+ /* if there is a low speed limit enabled, we set the expire timer to
+ make this connection's speed get checked again no later than when
+ this time is up */
+ Curl_expire(data, data->set.low_speed_time*1000);
+ }
+ return CURLE_OK;
+}
diff --git a/Utilities/cmcurl/speedcheck.h b/Utilities/cmcurl/speedcheck.h
new file mode 100644
index 000000000..62475f640
--- /dev/null
+++ b/Utilities/cmcurl/speedcheck.h
@@ -0,0 +1,34 @@
+#ifndef __SPEEDCHECK_H
+#define __SPEEDCHECK_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include "timeval.h"
+
+void Curl_speedinit(struct SessionHandle *data);
+CURLcode Curl_speedcheck(struct SessionHandle *data,
+ struct timeval now);
+
+#endif
diff --git a/Utilities/cmcurl/splay.c b/Utilities/cmcurl/splay.c
new file mode 100644
index 000000000..9fb66c76a
--- /dev/null
+++ b/Utilities/cmcurl/splay.c
@@ -0,0 +1,425 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1997 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "splay.h"
+
+#define compare(i,j) ((i)-(j))
+
+/* Set this to a key value that will *NEVER* appear otherwise */
+#define KEY_NOTUSED -1
+
+/*
+ * Splay using the key i (which may or may not be in the tree.) The starting
+ * root is t.
+ */
+struct Curl_tree *Curl_splay(int i, struct Curl_tree *t)
+{
+ struct Curl_tree N, *l, *r, *y;
+ int comp;
+
+ if (t == NULL)
+ return t;
+ N.smaller = N.larger = NULL;
+ l = r = &N;
+
+ for (;;) {
+ comp = compare(i, t->key);
+ if (comp < 0) {
+ if (t->smaller == NULL)
+ break;
+ if (compare(i, t->smaller->key) < 0) {
+ y = t->smaller; /* rotate smaller */
+ t->smaller = y->larger;
+ y->larger = t;
+ t = y;
+ if (t->smaller == NULL)
+ break;
+ }
+ r->smaller = t; /* link smaller */
+ r = t;
+ t = t->smaller;
+ }
+ else if (comp > 0) {
+ if (t->larger == NULL)
+ break;
+ if (compare(i, t->larger->key) > 0) {
+ y = t->larger; /* rotate larger */
+ t->larger = y->smaller;
+ y->smaller = t;
+ t = y;
+ if (t->larger == NULL)
+ break;
+ }
+ l->larger = t; /* link larger */
+ l = t;
+ t = t->larger;
+ }
+ else
+ break;
+ }
+
+ l->larger = t->smaller; /* assemble */
+ r->smaller = t->larger;
+ t->smaller = N.larger;
+ t->larger = N.smaller;
+
+ return t;
+}
+
+/* Insert key i into the tree t. Return a pointer to the resulting tree or
+ NULL if something went wrong. */
+struct Curl_tree *Curl_splayinsert(int i,
+ struct Curl_tree *t,
+ struct Curl_tree *node)
+{
+ if (node == NULL)
+ return t;
+
+ if (t != NULL) {
+ t = Curl_splay(i,t);
+ if (compare(i, t->key)==0) {
+ /* There already exists a node in the tree with the very same key. Build
+ a linked list of nodes. We make the new 'node' struct the new master
+ node and make the previous node the first one in the 'same' list. */
+
+ node->same = t;
+ node->key = i;
+ node->smaller = t->smaller;
+ node->larger = t->larger;
+
+ t->smaller = node; /* in the sub node for this same key, we use the
+ smaller pointer to point back to the master
+ node */
+
+ t->key = KEY_NOTUSED; /* and we set the key in the sub node to NOTUSED
+ to quickly identify this node as a subnode */
+
+ return node; /* new root node */
+ }
+ }
+
+ if (t == NULL) {
+ node->smaller = node->larger = NULL;
+ }
+ else if (compare(i, t->key) < 0) {
+ node->smaller = t->smaller;
+ node->larger = t;
+ t->smaller = NULL;
+
+ }
+ else {
+ node->larger = t->larger;
+ node->smaller = t;
+ t->larger = NULL;
+ }
+ node->key = i;
+
+ node->same = NULL; /* no identical node (yet) */
+ return node;
+}
+
+#if 0
+/* Deletes 'i' from the tree if it's there (with an exact match). Returns a
+ pointer to the resulting tree.
+
+ Function not used in libcurl.
+*/
+struct Curl_tree *Curl_splayremove(int i, struct Curl_tree *t,
+ struct Curl_tree **removed)
+{
+ struct Curl_tree *x;
+
+ *removed = NULL; /* default to no removed */
+
+ if (t==NULL)
+ return NULL;
+
+ t = Curl_splay(i,t);
+ if (compare(i, t->key) == 0) { /* found it */
+
+ /* FIRST! Check if there is a list with identical sizes */
+ if((x = t->same)) {
+ /* there is, pick one from the list */
+
+ /* 'x' is the new root node */
+
+ x->key = t->key;
+ x->larger = t->larger;
+ x->smaller = t->smaller;
+
+ *removed = t;
+ return x; /* new root */
+ }
+
+ if (t->smaller == NULL) {
+ x = t->larger;
+ }
+ else {
+ x = Curl_splay(i, t->smaller);
+ x->larger = t->larger;
+ }
+ *removed = t;
+
+ return x;
+ }
+ else
+ return t; /* It wasn't there */
+}
+#endif
+
+/* Finds and deletes the best-fit node from the tree. Return a pointer to the
+ resulting tree. best-fit means the node with the given or lower number */
+struct Curl_tree *Curl_splaygetbest(int i, struct Curl_tree *t,
+ struct Curl_tree **removed)
+{
+ struct Curl_tree *x;
+
+ if (!t) {
+ *removed = NULL; /* none removed since there was no root */
+ return NULL;
+ }
+
+ t = Curl_splay(i,t);
+ if(compare(i, t->key) < 0) {
+ /* too big node, try the smaller chain */
+ if(t->smaller)
+ t=Curl_splay(t->smaller->key, t);
+ else {
+ /* fail */
+ *removed = NULL;
+ return t;
+ }
+ }
+
+ if (compare(i, t->key) >= 0) { /* found it */
+ /* FIRST! Check if there is a list with identical sizes */
+ x = t->same;
+ if(x) {
+ /* there is, pick one from the list */
+
+ /* 'x' is the new root node */
+
+ x->key = t->key;
+ x->larger = t->larger;
+ x->smaller = t->smaller;
+
+ *removed = t;
+ return x; /* new root */
+ }
+
+ if (t->smaller == NULL) {
+ x = t->larger;
+ }
+ else {
+ x = Curl_splay(i, t->smaller);
+ x->larger = t->larger;
+ }
+ *removed = t;
+
+ return x;
+ }
+ else {
+ *removed = NULL; /* no match */
+ return t; /* It wasn't there */
+ }
+}
+
+
+/* Deletes the very node we point out from the tree if it's there. Stores a
+ pointer to the new resulting tree in 'newroot'.
+
+ Returns zero on success and non-zero on errors! TODO: document error codes.
+ When returning error, it does not touch the 'newroot' pointer.
+
+ NOTE: when the last node of the tree is removed, there's no tree left so
+ 'newroot' will be made to point to NULL.
+*/
+int Curl_splayremovebyaddr(struct Curl_tree *t,
+ struct Curl_tree *remove,
+ struct Curl_tree **newroot)
+{
+ struct Curl_tree *x;
+
+ if (!t || !remove)
+ return 1;
+
+ if(KEY_NOTUSED == remove->key) {
+ /* Key set to NOTUSED means it is a subnode within a 'same' linked list
+ and thus we can unlink it easily. The 'smaller' link of a subnode
+ links to the parent node. */
+ if (remove->smaller == NULL)
+ return 3;
+
+ remove->smaller->same = remove->same;
+ if(remove->same)
+ remove->same->smaller = remove->smaller;
+
+ /* Ensures that double-remove gets caught. */
+ remove->smaller = NULL;
+
+ /* voila, we're done! */
+ *newroot = t; /* return the same root */
+ return 0;
+ }
+
+ t = Curl_splay(remove->key, t);
+
+ /* First make sure that we got the same root node as the one we want
+ to remove, as otherwise we might be trying to remove a node that
+ isn't actually in the tree.
+
+ We cannot just compare the keys here as a double remove in quick
+ succession of a node with key != KEY_NOTUSED && same != NULL
+ could return the same key but a different node. */
+ if(t != remove)
+ return 2;
+
+ /* Check if there is a list with identical sizes, as then we're trying to
+ remove the root node of a list of nodes with identical keys. */
+ x = t->same;
+ if(x) {
+ /* 'x' is the new root node, we just make it use the root node's
+ smaller/larger links */
+
+ x->key = t->key;
+ x->larger = t->larger;
+ x->smaller = t->smaller;
+ }
+ else {
+ /* Remove the root node */
+ if (t->smaller == NULL)
+ x = t->larger;
+ else {
+ x = Curl_splay(remove->key, t->smaller);
+ x->larger = t->larger;
+ }
+ }
+
+ *newroot = x; /* store new root pointer */
+
+ return 0;
+}
+
+#ifdef CURLDEBUG
+
+void Curl_splayprint(struct Curl_tree * t, int d, char output)
+{
+ struct Curl_tree *node;
+ int i;
+ int count;
+ if (t == NULL)
+ return;
+
+ Curl_splayprint(t->larger, d+1, output);
+ for (i=0; i<d; i++)
+ if(output)
+ printf(" ");
+
+ if(output) {
+ printf("%d[%d]", t->key, i);
+ }
+
+ for(count=0, node = t->same; node; node = node->same, count++)
+ ;
+
+ if(output) {
+ if(count)
+ printf(" [%d more]\n", count);
+ else
+ printf("\n");
+ }
+
+ Curl_splayprint(t->smaller, d+1, output);
+}
+#endif
+
+#ifdef TEST_SPLAY
+
+/*#define TEST2 */
+#define MAX 50
+#define TEST2
+
+/* A sample use of these functions. Start with the empty tree, insert some
+ stuff into it, and then delete it */
+int main(int argc, char **argv)
+{
+ struct Curl_tree *root, *t;
+ void *ptrs[MAX];
+ int adds=0;
+ int rc;
+
+ long sizes[]={
+ 50, 60, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, 200, 300,
+ 220, 80, 90, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, 200,
+ 300, 220, 80, 90, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120,
+ 200, 300, 220, 80, 90};
+ int i;
+ root = NULL; /* the empty tree */
+
+ for (i = 0; i < MAX; i++) {
+ int key;
+ ptrs[i] = t = (struct Curl_tree *)malloc(sizeof(struct Curl_tree));
+
+#ifdef TEST2
+ key = sizes[i];
+#elif defined(TEST1)
+ key = (541*i)%1023;
+#elif defined(TEST3)
+ key = 100;
+#endif
+
+ t->payload = (void *)key; /* for simplicity */
+ if(!t) {
+ puts("out of memory!");
+ return 0;
+ }
+ root = Curl_splayinsert(key, root, t);
+ }
+
+#if 0
+ puts("Result:");
+ Curl_splayprint(root, 0, 1);
+#endif
+
+#if 1
+ for (i = 0; i < MAX; i++) {
+ int rem = (i+7)%MAX;
+ struct Curl_tree *r;
+ printf("Tree look:\n");
+ Curl_splayprint(root, 0, 1);
+ printf("remove pointer %d, payload %d\n", rem,
+ (int)((struct Curl_tree *)ptrs[rem])->payload);
+ rc = Curl_splayremovebyaddr(root, (struct Curl_tree *)ptrs[rem], &root);
+ if(rc)
+ /* failed! */
+ printf("remove %d failed!\n", rem);
+ }
+#endif
+
+ return 0;
+}
+
+#endif /* TEST_SPLAY */
diff --git a/Utilities/cmcurl/splay.h b/Utilities/cmcurl/splay.h
new file mode 100644
index 000000000..16745341e
--- /dev/null
+++ b/Utilities/cmcurl/splay.h
@@ -0,0 +1,54 @@
+#ifndef __SPLAY_H
+#define __SPLAY_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1997 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+struct Curl_tree {
+ struct Curl_tree *smaller; /* smaller node */
+ struct Curl_tree *larger; /* larger node */
+ struct Curl_tree *same; /* points to a node with identical key */
+ int key; /* the "sort" key */
+ void *payload; /* data the splay code doesn't care about */
+};
+
+struct Curl_tree *Curl_splay(int i, struct Curl_tree *t);
+struct Curl_tree *Curl_splayinsert(int key, struct Curl_tree *t,
+ struct Curl_tree *newnode);
+#if 0
+struct Curl_tree *Curl_splayremove(int key, struct Curl_tree *t,
+ struct Curl_tree **removed);
+#endif
+
+struct Curl_tree *Curl_splaygetbest(int key, struct Curl_tree *t,
+ struct Curl_tree **removed);
+int Curl_splayremovebyaddr(struct Curl_tree *t,
+ struct Curl_tree *remove,
+ struct Curl_tree **newroot);
+
+#ifdef CURLDEBUG
+void Curl_splayprint(struct Curl_tree * t, int d, char output);
+#else
+#define Curl_splayprint(x,y,z)
+#endif
+
+#endif
diff --git a/Utilities/cmcurl/ssh.c b/Utilities/cmcurl/ssh.c
new file mode 100644
index 000000000..24cba1bac
--- /dev/null
+++ b/Utilities/cmcurl/ssh.c
@@ -0,0 +1,979 @@
+/***************************************************************************
+* _ _ ____ _
+* Project ___| | | | _ \| |
+* / __| | | | |_) | |
+* | (__| |_| | _ <| |___
+* \___|\___/|_| \_\_____|
+*
+* Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+*
+* This software is licensed as described in the file COPYING, which
+* you should have received as part of this distribution. The terms
+* are also available at http://curl.haxx.se/docs/copyright.html.
+*
+* You may opt to use, copy, modify, merge, publish, distribute and/or sell
+* copies of the Software, and permit persons to whom the Software is
+* furnished to do so, under the terms of the COPYING file.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+* $Id$
+***************************************************************************/
+
+/* #define CURL_LIBSSH2_DEBUG */
+
+#include "setup.h"
+
+#ifdef USE_LIBSSH2
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef WIN32
+
+#else /* probably 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
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#endif
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "easyif.h" /* for Curl_convert_... prototypes */
+
+#include "if2ip.h"
+#include "hostip.h"
+#include "progress.h"
+#include "transfer.h"
+#include "escape.h"
+#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "ssh.h"
+#include "url.h"
+#include "speedcheck.h"
+#include "getinfo.h"
+
+#include "strtoofft.h"
+#include "strequal.h"
+#include "sslgen.h"
+#include "connect.h"
+#include "strerror.h"
+#include "memory.h"
+#include "inet_ntop.h"
+#include "select.h"
+#include "parsedate.h" /* for the week day and month names */
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "multiif.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>
+
+#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
+#define DIRSEP '\\'
+#else
+#define DIRSEP '/'
+#endif
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#ifdef CURLDEBUG
+#include "memdebug.h"
+#endif
+
+#ifndef LIBSSH2_SFTP_S_IRUSR
+/* Here's a work-around for those of you who happend to run a libssh2 version
+ that is 0.14 or older. We should remove this kludge as soon as we can
+ require a more recent libssh2 release. */
+#ifndef S_IRGRP
+#define S_IRGRP 0
+#endif
+
+#ifndef S_IROTH
+#define S_IROTH 0
+#endif
+
+#define LIBSSH2_SFTP_S_IRUSR S_IRUSR
+#define LIBSSH2_SFTP_S_IWUSR S_IWUSR
+#define LIBSSH2_SFTP_S_IRGRP S_IRGRP
+#define LIBSSH2_SFTP_S_IROTH S_IROTH
+#define LIBSSH2_SFTP_S_IRUSR S_IRUSR
+#define LIBSSH2_SFTP_S_IWUSR S_IWUSR
+#define LIBSSH2_SFTP_S_IRGRP S_IRGRP
+#define LIBSSH2_SFTP_S_IROTH S_IROTH
+#define LIBSSH2_SFTP_S_IFMT S_IFMT
+#define LIBSSH2_SFTP_S_IFDIR S_IFDIR
+#define LIBSSH2_SFTP_S_IFLNK S_IFLNK
+#define LIBSSH2_SFTP_S_IFSOCK S_IFSOCK
+#define LIBSSH2_SFTP_S_IFCHR S_IFCHR
+#define LIBSSH2_SFTP_S_IFBLK S_IFBLK
+#define LIBSSH2_SFTP_S_IXUSR S_IXUSR
+#define LIBSSH2_SFTP_S_IWGRP S_IWGRP
+#define LIBSSH2_SFTP_S_IXGRP S_IXGRP
+#define LIBSSH2_SFTP_S_IWOTH S_IWOTH
+#define LIBSSH2_SFTP_S_IXOTH S_IXOTH
+#endif
+
+static LIBSSH2_ALLOC_FUNC(libssh2_malloc);
+static LIBSSH2_REALLOC_FUNC(libssh2_realloc);
+static LIBSSH2_FREE_FUNC(libssh2_free);
+
+static void
+kbd_callback(const char *name, int name_len, const char *instruction,
+ int instruction_len, int num_prompts,
+ const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
+ void **abstract)
+{
+ struct SSHPROTO *ssh = (struct SSHPROTO *)*abstract;
+
+#ifdef CURL_LIBSSH2_DEBUG
+ fprintf(stderr, "name=%s\n", name);
+ fprintf(stderr, "name_len=%d\n", name_len);
+ fprintf(stderr, "instruction=%s\n", instruction);
+ fprintf(stderr, "instruction_len=%d\n", instruction_len);
+ fprintf(stderr, "num_prompts=%d\n", num_prompts);
+#else
+ (void)name;
+ (void)name_len;
+ (void)instruction;
+ (void)instruction_len;
+#endif /* CURL_LIBSSH2_DEBUG */
+ if (num_prompts == 1) {
+ responses[0].text = strdup(ssh->passwd);
+ responses[0].length = strlen(ssh->passwd);
+ }
+ (void)prompts;
+ (void)abstract;
+} /* kbd_callback */
+
+static CURLcode libssh2_error_to_CURLE(struct connectdata *conn)
+{
+ int errorcode;
+ struct SSHPROTO *scp = conn->data->reqdata.proto.ssh;
+
+ /* Get the libssh2 error code and string */
+ errorcode = libssh2_session_last_error(scp->ssh_session, &scp->errorstr,
+ NULL, 0);
+ if (errorcode == LIBSSH2_FX_OK)
+ return CURLE_OK;
+
+ infof(conn->data, "libssh2 error %d, '%s'\n", errorcode, scp->errorstr);
+
+ /* TODO: map some of the libssh2 errors to the more appropriate CURLcode
+ error code, and possibly add a few new SSH-related one. We must however
+ not return or even depend on libssh2 errors in the public libcurl API */
+
+ return CURLE_SSH;
+}
+
+static LIBSSH2_ALLOC_FUNC(libssh2_malloc)
+{
+ return malloc(count);
+ (void)abstract;
+}
+
+static LIBSSH2_REALLOC_FUNC(libssh2_realloc)
+{
+ return realloc(ptr, count);
+ (void)abstract;
+}
+
+static LIBSSH2_FREE_FUNC(libssh2_free)
+{
+ free(ptr);
+ (void)abstract;
+}
+
+static CURLcode ssh_init(struct connectdata *conn)
+{
+ struct SessionHandle *data = conn->data;
+ struct SSHPROTO *ssh;
+ if (data->reqdata.proto.ssh)
+ return CURLE_OK;
+
+ ssh = (struct SSHPROTO *)calloc(sizeof(struct SSHPROTO), 1);
+ if (!ssh)
+ return CURLE_OUT_OF_MEMORY;
+
+ data->reqdata.proto.ssh = ssh;
+
+ /* get some initial data into the ssh struct */
+ ssh->bytecountp = &data->reqdata.keep.bytecount;
+
+ /* no need to duplicate them, this connectdata struct won't change */
+ ssh->user = conn->user;
+ ssh->passwd = conn->passwd;
+
+ ssh->errorstr = NULL;
+
+ ssh->ssh_session = NULL;
+ ssh->ssh_channel = NULL;
+ ssh->sftp_session = NULL;
+ ssh->sftp_handle = NULL;
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
+ * do protocol-specific actions at connect-time.
+ */
+CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done)
+{
+ int i;
+ struct SSHPROTO *ssh;
+ const char *fingerprint;
+ const char *authlist;
+ char *home;
+ char rsa_pub[PATH_MAX];
+ char rsa[PATH_MAX];
+ char tempHome[PATH_MAX];
+ curl_socket_t sock;
+ char *real_path;
+ char *working_path;
+ int working_path_len;
+ bool authed = FALSE;
+ CURLcode result;
+ struct SessionHandle *data = conn->data;
+
+ rsa_pub[0] = rsa[0] = '\0';
+
+ result = ssh_init(conn);
+ if (result)
+ return result;
+
+ ssh = data->reqdata.proto.ssh;
+
+ working_path = curl_easy_unescape(data, data->reqdata.path, 0,
+ &working_path_len);
+ if (!working_path)
+ return CURLE_OUT_OF_MEMORY;
+
+#ifdef CURL_LIBSSH2_DEBUG
+ if (ssh->user) {
+ infof(data, "User: %s\n", ssh->user);
+ }
+ if (ssh->passwd) {
+ infof(data, "Password: %s\n", ssh->passwd);
+ }
+#endif /* CURL_LIBSSH2_DEBUG */
+ sock = conn->sock[FIRSTSOCKET];
+ ssh->ssh_session = libssh2_session_init_ex(libssh2_malloc, libssh2_free,
+ libssh2_realloc, ssh);
+ if (ssh->ssh_session == NULL) {
+ failf(data, "Failure initialising ssh session\n");
+ Curl_safefree(ssh->path);
+ return CURLE_FAILED_INIT;
+ }
+#ifdef CURL_LIBSSH2_DEBUG
+ infof(data, "SSH socket: %d\n", sock);
+#endif /* CURL_LIBSSH2_DEBUG */
+
+ if (libssh2_session_startup(ssh->ssh_session, sock)) {
+ failf(data, "Failure establishing ssh session\n");
+ libssh2_session_free(ssh->ssh_session);
+ ssh->ssh_session = NULL;
+ Curl_safefree(ssh->path);
+ return CURLE_FAILED_INIT;
+ }
+
+ /*
+ * Before we authenticate we should check the hostkey's fingerprint against
+ * our known hosts. How that is handled (reading from file, whatever) is
+ * up to us. As for know not much is implemented, besides showing how to
+ * get the fingerprint.
+ */
+ fingerprint = libssh2_hostkey_hash(ssh->ssh_session,
+ LIBSSH2_HOSTKEY_HASH_MD5);
+
+#ifdef CURL_LIBSSH2_DEBUG
+ /* The fingerprint points to static storage (!), don't free() it. */
+ infof(data, "Fingerprint: ");
+ for (i = 0; i < 16; i++) {
+ infof(data, "%02X ", (unsigned char) fingerprint[i]);
+ }
+ infof(data, "\n");
+#endif /* CURL_LIBSSH2_DEBUG */
+
+ /* TBD - methods to check the host keys need to be done */
+
+ /*
+ * Figure out authentication methods
+ * NB: As soon as we have provided a username to an openssh server we must
+ * never change it later. Thus, always specify the correct username here,
+ * even though the libssh2 docs kind of indicate that it should be possible
+ * to get a 'generic' list (not user-specific) of authentication methods,
+ * presumably with a blank username. That won't work in my experience.
+ * So always specify it here.
+ */
+ authlist = libssh2_userauth_list(ssh->ssh_session, ssh->user,
+ strlen(ssh->user));
+
+ /*
+ * Check the supported auth types in the order I feel is most secure with the
+ * requested type of authentication
+ */
+ if ((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
+ (strstr(authlist, "publickey") != NULL)) {
+ /* To ponder about: should really the lib be messing about with the HOME
+ environment variable etc? */
+ home = curl_getenv("HOME");
+
+ if (data->set.ssh_public_key)
+ snprintf(rsa_pub, sizeof(rsa_pub), "%s", data->set.ssh_public_key);
+ else if (home)
+ snprintf(rsa_pub, sizeof(rsa_pub), "%s/.ssh/id_dsa.pub", home);
+
+ if (data->set.ssh_private_key)
+ snprintf(rsa, sizeof(rsa), "%s", data->set.ssh_private_key);
+ else if (home)
+ snprintf(rsa, sizeof(rsa), "%s/.ssh/id_dsa", home);
+
+ curl_free(home);
+
+ if (rsa_pub[0]) {
+ /* The function below checks if the files exists, no need to stat() here.
+ */
+ if (libssh2_userauth_publickey_fromfile(ssh->ssh_session, ssh->user,
+ rsa_pub, rsa, "") == 0) {
+ authed = TRUE;
+ }
+ }
+ }
+ if (!authed &&
+ (data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
+ (strstr(authlist, "password") != NULL)) {
+ if (!libssh2_userauth_password(ssh->ssh_session, ssh->user, ssh->passwd))
+ authed = TRUE;
+ }
+ if (!authed && (data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
+ (strstr(authlist, "hostbased") != NULL)) {
+ }
+ if (!authed && (data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
+ && (strstr(authlist, "keyboard-interactive") != NULL)) {
+ /* Authentication failed. Continue with keyboard-interactive now. */
+ if (libssh2_userauth_keyboard_interactive_ex(ssh->ssh_session, ssh->user,
+ strlen(ssh->user),
+ &kbd_callback) == 0) {
+ authed = TRUE;
+ }
+ }
+
+ if (!authed) {
+ failf(data, "Authentication failure\n");
+ libssh2_session_free(ssh->ssh_session);
+ ssh->ssh_session = NULL;
+ Curl_safefree(ssh->path);
+ return CURLE_FAILED_INIT;
+ }
+
+ /*
+ * At this point we have an authenticated ssh session.
+ */
+ conn->sockfd = sock;
+ conn->writesockfd = CURL_SOCKET_BAD;
+
+ if (conn->protocol == PROT_SFTP) {
+ /*
+ * Start the libssh2 sftp session
+ */
+ ssh->sftp_session = libssh2_sftp_init(ssh->ssh_session);
+ if (ssh->sftp_session == NULL) {
+ failf(data, "Failure initialising sftp session\n");
+ libssh2_sftp_shutdown(ssh->sftp_session);
+ ssh->sftp_session = NULL;
+ libssh2_session_free(ssh->ssh_session);
+ ssh->ssh_session = NULL;
+ return CURLE_FAILED_INIT;
+ }
+
+ /*
+ * Get the "home" directory
+ */
+ i = libssh2_sftp_realpath(ssh->sftp_session, ".", tempHome, PATH_MAX-1);
+ if (i > 0) {
+ /* It seems that this string is not always NULL terminated */
+ tempHome[i] = '\0';
+ ssh->homedir = (char *)strdup(tempHome);
+ if (!ssh->homedir) {
+ libssh2_sftp_shutdown(ssh->sftp_session);
+ ssh->sftp_session = NULL;
+ libssh2_session_free(ssh->ssh_session);
+ ssh->ssh_session = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ /* Return the error type */
+ i = libssh2_sftp_last_error(ssh->sftp_session);
+ DEBUGF(infof(data, "error = %d\n", i));
+ }
+ }
+
+ /* Check for /~/ , indicating realative to the users home directory */
+ if (conn->protocol == PROT_SCP) {
+ real_path = (char *)malloc(working_path_len+1);
+ if (real_path == NULL) {
+ Curl_safefree(working_path);
+ libssh2_session_free(ssh->ssh_session);
+ ssh->ssh_session = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ if (working_path[1] == '~')
+ /* It is referenced to the home directory, so strip the leading '/' */
+ memcpy(real_path, working_path+1, 1 + working_path_len-1);
+ else
+ memcpy(real_path, working_path, 1 + working_path_len);
+ }
+ else if (conn->protocol == PROT_SFTP) {
+ if (working_path[1] == '~') {
+ real_path = (char *)malloc(strlen(ssh->homedir) +
+ working_path_len + 1);
+ if (real_path == NULL) {
+ libssh2_sftp_shutdown(ssh->sftp_session);
+ ssh->sftp_session = NULL;
+ libssh2_session_free(ssh->ssh_session);
+ ssh->ssh_session = NULL;
+ Curl_safefree(working_path);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ /* It is referenced to the home directory, so strip the leading '/' */
+ memcpy(real_path, ssh->homedir, strlen(ssh->homedir));
+ real_path[strlen(ssh->homedir)] = '/';
+ real_path[strlen(ssh->homedir)+1] = '\0';
+ if (working_path_len > 3) {
+ memcpy(real_path+strlen(ssh->homedir)+1, working_path + 3,
+ 1 + working_path_len -3);
+ }
+ }
+ else {
+ real_path = (char *)malloc(working_path_len+1);
+ if (real_path == NULL) {
+ libssh2_session_free(ssh->ssh_session);
+ ssh->ssh_session = NULL;
+ Curl_safefree(working_path);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memcpy(real_path, working_path, 1+working_path_len);
+ }
+ }
+ else
+ return CURLE_FAILED_INIT;
+
+ Curl_safefree(working_path);
+ ssh->path = real_path;
+
+ *done = TRUE;
+ return CURLE_OK;
+}
+
+CURLcode Curl_scp_do(struct connectdata *conn, bool *done)
+{
+ struct stat sb;
+ struct SSHPROTO *scp = conn->data->reqdata.proto.ssh;
+ CURLcode res = CURLE_OK;
+
+ *done = TRUE; /* unconditionally */
+
+ if (conn->data->set.upload) {
+ /*
+ * NOTE!!! libssh2 requires that the destination path is a full path
+ * that includes the destination file and name OR ends in a "/" .
+ * If this is not done the destination file will be named the
+ * same name as the last directory in the path.
+ */
+ scp->ssh_channel = libssh2_scp_send_ex(scp->ssh_session, scp->path,
+ LIBSSH2_SFTP_S_IRUSR|
+ LIBSSH2_SFTP_S_IWUSR|
+ LIBSSH2_SFTP_S_IRGRP|
+ LIBSSH2_SFTP_S_IROTH,
+ conn->data->set.infilesize, 0, 0);
+ if (!scp->ssh_channel)
+ return CURLE_FAILED_INIT;
+
+ /* upload data */
+ res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
+ }
+ else {
+ /*
+ * We must check the remote file, if it is a directory no vaules will
+ * be set in sb
+ */
+ curl_off_t bytecount;
+ memset(&sb, 0, sizeof(struct stat));
+ scp->ssh_channel = libssh2_scp_recv(scp->ssh_session, scp->path, &sb);
+ if (!scp->ssh_channel) {
+ if ((sb.st_mode == 0) && (sb.st_atime == 0) && (sb.st_mtime == 0) &&
+ (sb.st_size == 0)) {
+ /* Since sb is still empty, it is likely the file was not found */
+ return CURLE_REMOTE_FILE_NOT_FOUND;
+ }
+ return libssh2_error_to_CURLE(conn);
+ }
+ /* download data */
+ bytecount = (curl_off_t) sb.st_size;
+ conn->data->reqdata.maxdownload = (curl_off_t) sb.st_size;
+ res = Curl_setup_transfer(conn, FIRSTSOCKET,
+ bytecount, FALSE, NULL, -1, NULL);
+ }
+
+ return res;
+}
+
+CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status,
+ bool premature)
+{
+ struct SSHPROTO *scp = conn->data->reqdata.proto.ssh;
+ (void)premature; /* not used */
+
+ Curl_safefree(scp->path);
+ scp->path = NULL;
+
+ if (scp->ssh_channel) {
+ if (libssh2_channel_close(scp->ssh_channel) < 0) {
+ infof(conn->data, "Failed to stop libssh2 channel subsystem\n");
+ }
+ }
+
+ if (scp->ssh_session) {
+ libssh2_session_disconnect(scp->ssh_session, "Shutdown");
+ libssh2_session_free(scp->ssh_session);
+ scp->ssh_session = NULL;
+ }
+
+ free(conn->data->reqdata.proto.ssh);
+ conn->data->reqdata.proto.ssh = NULL;
+ Curl_pgrsDone(conn);
+
+ (void)status; /* unused */
+
+ return CURLE_OK;
+}
+
+/* return number of received (decrypted) bytes */
+ssize_t Curl_scp_send(struct connectdata *conn, int sockindex,
+ void *mem, size_t len)
+{
+ ssize_t nwrite;
+
+ /* libssh2_channel_write() returns int
+ *
+ * NOTE: we should not store nor rely on connection-related data to be
+ * in the SessionHandle struct
+ */
+ nwrite = (ssize_t)
+ libssh2_channel_write(conn->data->reqdata.proto.ssh->ssh_channel,
+ mem, len);
+ (void)sockindex;
+ return nwrite;
+}
+
+/*
+ * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
+ * a regular CURLcode value.
+ */
+ssize_t Curl_scp_recv(struct connectdata *conn, int sockindex,
+ char *mem, size_t len)
+{
+ ssize_t nread;
+
+ /* libssh2_channel_read() returns int
+ *
+ * NOTE: we should not store nor rely on connection-related data to be
+ * in the SessionHandle struct
+ */
+
+ nread = (ssize_t)
+ libssh2_channel_read(conn->data->reqdata.proto.ssh->ssh_channel,
+ mem, len);
+ (void)sockindex;
+ return nread;
+}
+
+/*
+ * =============== SFTP ===============
+ */
+
+CURLcode Curl_sftp_do(struct connectdata *conn, bool *done)
+{
+ LIBSSH2_SFTP_ATTRIBUTES attrs;
+ struct SSHPROTO *sftp = conn->data->reqdata.proto.ssh;
+ CURLcode res = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ curl_off_t bytecount = 0;
+ char *buf = data->state.buffer;
+
+ *done = TRUE; /* unconditionally */
+
+ if (data->set.upload) {
+ /*
+ * NOTE!!! libssh2 requires that the destination path is a full path
+ * that includes the destination file and name OR ends in a "/" .
+ * If this is not done the destination file will be named the
+ * same name as the last directory in the path.
+ */
+ sftp->sftp_handle =
+ libssh2_sftp_open(sftp->sftp_session, sftp->path,
+ LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT,
+ LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|
+ LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH);
+ if (!sftp->sftp_handle)
+ return CURLE_FAILED_INIT;
+
+ /* upload data */
+ res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
+ }
+ else {
+ if (sftp->path[strlen(sftp->path)-1] == '/') {
+ /*
+ * This is a directory that we are trying to get, so produce a
+ * directory listing
+ *
+ * **BLOCKING behaviour** This should be made into a state machine and
+ * get a separate function called from Curl_sftp_recv() when there is
+ * data to read from the network, instead of "hanging" here.
+ */
+ char filename[PATH_MAX+1];
+ int len, totalLen, currLen;
+ char *line;
+
+ sftp->sftp_handle =
+ libssh2_sftp_opendir(sftp->sftp_session, sftp->path);
+ if (!sftp->sftp_handle)
+ return CURLE_SSH;
+
+ while ((len = libssh2_sftp_readdir(sftp->sftp_handle, filename,
+ PATH_MAX, &attrs)) > 0) {
+ filename[len] = '\0';
+
+ if (data->set.ftp_list_only) {
+ if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
+ ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
+ LIBSSH2_SFTP_S_IFDIR)) {
+ infof(data, "%s\n", filename);
+ }
+ }
+ else {
+ totalLen = 80 + len;
+ line = (char *)malloc(totalLen);
+ if (!line)
+ return CURLE_OUT_OF_MEMORY;
+
+ if (!(attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID))
+ attrs.uid = attrs.gid =0;
+
+ currLen = snprintf(line, totalLen, "---------- 1 %5d %5d",
+ attrs.uid, attrs.gid);
+
+ if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
+ if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
+ LIBSSH2_SFTP_S_IFDIR) {
+ line[0] = 'd';
+ }
+ else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
+ LIBSSH2_SFTP_S_IFLNK) {
+ line[0] = 'l';
+ }
+ else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
+ LIBSSH2_SFTP_S_IFSOCK) {
+ line[0] = 's';
+ }
+ else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
+ LIBSSH2_SFTP_S_IFCHR) {
+ line[0] = 'c';
+ }
+ else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
+ LIBSSH2_SFTP_S_IFBLK) {
+ line[0] = 'b';
+ }
+ if (attrs.permissions & LIBSSH2_SFTP_S_IRUSR) {
+ line[1] = 'r';
+ }
+ if (attrs.permissions & LIBSSH2_SFTP_S_IWUSR) {
+ line[2] = 'w';
+ }
+ if (attrs.permissions & LIBSSH2_SFTP_S_IXUSR) {
+ line[3] = 'x';
+ }
+ if (attrs.permissions & LIBSSH2_SFTP_S_IRGRP) {
+ line[4] = 'r';
+ }
+ if (attrs.permissions & LIBSSH2_SFTP_S_IWGRP) {
+ line[5] = 'w';
+ }
+ if (attrs.permissions & LIBSSH2_SFTP_S_IXGRP) {
+ line[6] = 'x';
+ }
+ if (attrs.permissions & LIBSSH2_SFTP_S_IROTH) {
+ line[7] = 'r';
+ }
+ if (attrs.permissions & LIBSSH2_SFTP_S_IWOTH) {
+ line[8] = 'w';
+ }
+ if (attrs.permissions & LIBSSH2_SFTP_S_IXOTH) {
+ line[9] = 'x';
+ }
+ }
+ if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) {
+ currLen += snprintf(line+currLen, totalLen-currLen, "%11lld",
+ attrs.filesize);
+ }
+ if (attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
+ const char *months[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+ struct tm *nowParts;
+ time_t now, remoteTime;
+
+ now = time(NULL);
+ remoteTime = (time_t)attrs.mtime;
+ nowParts = localtime(&remoteTime);
+
+ if ((time_t)attrs.mtime > (now - (3600 * 24 * 180))) {
+ currLen += snprintf(line+currLen, totalLen-currLen,
+ " %s %2d %2d:%02d", months[nowParts->tm_mon],
+ nowParts->tm_mday, nowParts->tm_hour,
+ nowParts->tm_min);
+ }
+ else {
+ currLen += snprintf(line+currLen, totalLen-currLen,
+ " %s %2d %5d", months[nowParts->tm_mon],
+ nowParts->tm_mday, 1900+nowParts->tm_year);
+ }
+ }
+ currLen += snprintf(line+currLen, totalLen-currLen, " %s", filename);
+ if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
+ ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
+ LIBSSH2_SFTP_S_IFLNK)) {
+ char linkPath[PATH_MAX + 1];
+
+ snprintf(linkPath, PATH_MAX, "%s%s", sftp->path, filename);
+ len = libssh2_sftp_readlink(sftp->sftp_session, linkPath, filename,
+ PATH_MAX);
+ line = realloc(line, totalLen + 4 + len);
+ if (!line)
+ return CURLE_OUT_OF_MEMORY;
+
+ currLen += snprintf(line+currLen, totalLen-currLen, " -> %s",
+ filename);
+ }
+
+ currLen += snprintf(line+currLen, totalLen-currLen, "\n");
+ res = Curl_client_write(conn, CLIENTWRITE_BODY, line, 0);
+ free(line);
+ }
+ }
+ libssh2_sftp_closedir(sftp->sftp_handle);
+ sftp->sftp_handle = NULL;
+
+ /* no data to transfer */
+ res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ }
+ else {
+ /*
+ * Work on getting the specified file
+ */
+ sftp->sftp_handle =
+ libssh2_sftp_open(sftp->sftp_session, sftp->path, LIBSSH2_FXF_READ,
+ LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|
+ LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH);
+ if (!sftp->sftp_handle)
+ return CURLE_SSH;
+
+ if (libssh2_sftp_stat(sftp->sftp_session, sftp->path, &attrs)) {
+ /*
+ * libssh2_sftp_open() didn't return an error, so maybe the server
+ * just doesn't support stat()
+ */
+ data->reqdata.size = -1;
+ data->reqdata.maxdownload = -1;
+ }
+ else {
+ data->reqdata.size = attrs.filesize;
+ data->reqdata.maxdownload = attrs.filesize;
+ Curl_pgrsSetDownloadSize(data, attrs.filesize);
+ }
+
+ Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
+ /* Now download data. The libssh2 0.14 doesn't offer any way to do this
+ without using this BLOCKING approach, so here's room for improvement
+ once libssh2 can return EWOULDBLOCK to us. */
+#if 0
+ /* code left here just because this is what this function will use the
+ day libssh2 is improved */
+ res = Curl_setup_transfer(conn, FIRSTSOCKET,
+ bytecount, FALSE, NULL, -1, NULL);
+#endif
+ while (res == CURLE_OK) {
+ size_t nread;
+ /* NOTE: most *read() functions return ssize_t but this returns size_t
+ which normally is unsigned! */
+ nread = libssh2_sftp_read(data->reqdata.proto.ssh->sftp_handle,
+ buf, BUFSIZE-1);
+
+ if (nread > 0)
+ buf[nread] = 0;
+
+ /* this check can be changed to a <= 0 when nread is changed to a
+ signed variable type */
+ if ((nread == 0) || (nread == (size_t)~0))
+ break;
+
+ bytecount += nread;
+
+ res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
+ if(res)
+ return res;
+
+ Curl_pgrsSetDownloadCounter(data, bytecount);
+
+ if(Curl_pgrsUpdate(conn))
+ res = CURLE_ABORTED_BY_CALLBACK;
+ else {
+ struct timeval now = Curl_tvnow();
+ res = Curl_speedcheck(data, now);
+ }
+ }
+ if(Curl_pgrsUpdate(conn))
+ res = CURLE_ABORTED_BY_CALLBACK;
+
+ /* no (more) data to transfer */
+ res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ }
+ }
+
+ return res;
+}
+
+CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode status,
+ bool premature)
+{
+ struct SSHPROTO *sftp = conn->data->reqdata.proto.ssh;
+ (void)premature; /* not used */
+
+ Curl_safefree(sftp->path);
+ sftp->path = NULL;
+
+ Curl_safefree(sftp->homedir);
+ sftp->homedir = NULL;
+
+ if (sftp->sftp_handle) {
+ if (libssh2_sftp_close(sftp->sftp_handle) < 0) {
+ infof(conn->data, "Failed to close libssh2 file\n");
+ }
+ }
+
+ if (sftp->sftp_session) {
+ if (libssh2_sftp_shutdown(sftp->sftp_session) < 0) {
+ infof(conn->data, "Failed to stop libssh2 sftp subsystem\n");
+ }
+ }
+
+ if (sftp->ssh_channel) {
+ if (libssh2_channel_close(sftp->ssh_channel) < 0) {
+ infof(conn->data, "Failed to stop libssh2 channel subsystem\n");
+ }
+ }
+
+ if (sftp->ssh_session) {
+ libssh2_session_disconnect(sftp->ssh_session, "Shutdown");
+ libssh2_session_free(sftp->ssh_session);
+ sftp->ssh_session = NULL;
+ }
+
+ free(conn->data->reqdata.proto.ssh);
+ conn->data->reqdata.proto.ssh = NULL;
+ Curl_pgrsDone(conn);
+
+ (void)status; /* unused */
+
+ return CURLE_OK;
+}
+
+/* return number of received (decrypted) bytes */
+ssize_t Curl_sftp_send(struct connectdata *conn, int sockindex,
+ void *mem, size_t len)
+{
+ ssize_t nwrite;
+
+ /* libssh2_sftp_write() returns size_t !*/
+
+ nwrite = (ssize_t)
+ libssh2_sftp_write(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
+ (void)sockindex;
+ return nwrite;
+}
+
+/*
+ * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
+ * a regular CURLcode value.
+ */
+ssize_t Curl_sftp_recv(struct connectdata *conn, int sockindex,
+ char *mem, size_t len)
+{
+ ssize_t nread;
+
+ /* libssh2_sftp_read() returns size_t !*/
+
+ nread = (ssize_t)
+ libssh2_sftp_read(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
+ (void)sockindex;
+ return nread;
+}
+
+#endif /* USE_LIBSSH2 */
diff --git a/Utilities/cmcurl/ssh.h b/Utilities/cmcurl/ssh.h
new file mode 100644
index 000000000..d9144903b
--- /dev/null
+++ b/Utilities/cmcurl/ssh.h
@@ -0,0 +1,49 @@
+#ifndef __SSH_H
+#define __SSH_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#ifdef USE_LIBSSH2
+
+CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done);
+
+CURLcode Curl_scp_do(struct connectdata *conn, bool *done);
+CURLcode Curl_scp_done(struct connectdata *conn, CURLcode, bool premature);
+
+ssize_t Curl_scp_send(struct connectdata *conn, int sockindex,
+ void *mem, size_t len);
+ssize_t Curl_scp_recv(struct connectdata *conn, int sockindex,
+ char *mem, size_t len);
+
+CURLcode Curl_sftp_do(struct connectdata *conn, bool *done);
+CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode, bool premature);
+
+ssize_t Curl_sftp_send(struct connectdata *conn, int sockindex,
+ void *mem, size_t len);
+ssize_t Curl_sftp_recv(struct connectdata *conn, int sockindex,
+ char *mem, size_t len);
+
+#endif /* USE_LIBSSH2 */
+
+#endif /* __SSH_H */
diff --git a/Utilities/cmcurl/sslgen.c b/Utilities/cmcurl/sslgen.c
new file mode 100644
index 000000000..f110a51ea
--- /dev/null
+++ b/Utilities/cmcurl/sslgen.c
@@ -0,0 +1,618 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/* This file is for "generic" SSL functions that all libcurl internals should
+ use. It is responsible for calling the proper 'ossl' function in ssluse.c
+ (OpenSSL based) or the 'gtls' function in gtls.c (GnuTLS based).
+
+ SSL-functions in libcurl should call functions in this source file, and not
+ to any specific SSL-layer.
+
+ Curl_ssl_ - prefix for generic ones
+ Curl_ossl_ - prefix for OpenSSL ones
+ Curl_gtls_ - prefix for GnuTLS ones
+
+ "SSL/TLS Strong Encryption: An Introduction"
+ http://httpd.apache.org/docs-2.0/ssl/ssl_intro.html
+*/
+
+#include "setup.h"
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "urldata.h"
+#define SSLGEN_C
+#include "sslgen.h" /* generic SSL protos etc */
+#include "ssluse.h" /* OpenSSL versions */
+#include "gtls.h" /* GnuTLS versions */
+#include "sendf.h"
+#include "strequal.h"
+#include "url.h"
+#include "memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* "global" init done? */
+static bool init_ssl=FALSE;
+
+static bool safe_strequal(char* str1, char* str2);
+
+static bool safe_strequal(char* str1, char* str2)
+{
+ if(str1 && str2)
+ /* both pointers point to something then compare them */
+ return (bool)(0 != strequal(str1, str2));
+ else
+ /* if both pointers are NULL then treat them as equal */
+ return (bool)(!str1 && !str2);
+}
+
+bool
+Curl_ssl_config_matches(struct ssl_config_data* data,
+ struct ssl_config_data* needle)
+{
+ if((data->version == needle->version) &&
+ (data->verifypeer == needle->verifypeer) &&
+ (data->verifyhost == needle->verifyhost) &&
+ safe_strequal(data->CApath, needle->CApath) &&
+ safe_strequal(data->CAfile, needle->CAfile) &&
+ safe_strequal(data->random_file, needle->random_file) &&
+ safe_strequal(data->egdsocket, needle->egdsocket) &&
+ safe_strequal(data->cipher_list, needle->cipher_list))
+ return TRUE;
+
+ return FALSE;
+}
+
+bool
+Curl_clone_ssl_config(struct ssl_config_data *source,
+ struct ssl_config_data *dest)
+{
+ dest->verifyhost = source->verifyhost;
+ dest->verifypeer = source->verifypeer;
+ dest->version = source->version;
+
+ if(source->CAfile) {
+ dest->CAfile = strdup(source->CAfile);
+ if(!dest->CAfile)
+ return FALSE;
+ }
+
+ if(source->CApath) {
+ dest->CApath = strdup(source->CApath);
+ if(!dest->CApath)
+ return FALSE;
+ }
+
+ if(source->cipher_list) {
+ dest->cipher_list = strdup(source->cipher_list);
+ if(!dest->cipher_list)
+ return FALSE;
+ }
+
+ if(source->egdsocket) {
+ dest->egdsocket = strdup(source->egdsocket);
+ if(!dest->egdsocket)
+ return FALSE;
+ }
+
+ if(source->random_file) {
+ dest->random_file = strdup(source->random_file);
+ if(!dest->random_file)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void Curl_free_ssl_config(struct ssl_config_data* sslc)
+{
+ if(sslc->CAfile)
+ free(sslc->CAfile);
+
+ if(sslc->CApath)
+ free(sslc->CApath);
+
+ if(sslc->cipher_list)
+ free(sslc->cipher_list);
+
+ if(sslc->egdsocket)
+ free(sslc->egdsocket);
+
+ if(sslc->random_file)
+ free(sslc->random_file);
+}
+
+/**
+ * Global SSL init
+ *
+ * @retval 0 error initializing SSL
+ * @retval 1 SSL initialized successfully
+ */
+int Curl_ssl_init(void)
+{
+ /* make sure this is only done once */
+ if(init_ssl)
+ return 1;
+ init_ssl = TRUE; /* never again */
+
+#ifdef USE_SSLEAY
+ return Curl_ossl_init();
+#else
+#ifdef USE_GNUTLS
+ return Curl_gtls_init();
+#else
+ /* no SSL support */
+ return 1;
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+}
+
+
+/* Global cleanup */
+void Curl_ssl_cleanup(void)
+{
+ if(init_ssl) {
+ /* only cleanup if we did a previous init */
+#ifdef USE_SSLEAY
+ Curl_ossl_cleanup();
+#else
+#ifdef USE_GNUTLS
+ Curl_gtls_cleanup();
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+ init_ssl = FALSE;
+ }
+}
+
+CURLcode
+Curl_ssl_connect(struct connectdata *conn, int sockindex)
+{
+#ifdef USE_SSL
+ /* mark this is being ssl enabled from here on. */
+ conn->ssl[sockindex].use = TRUE;
+
+#ifdef USE_SSLEAY
+ return Curl_ossl_connect(conn, sockindex);
+#else
+#ifdef USE_GNUTLS
+ return Curl_gtls_connect(conn, sockindex);
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+
+#else
+ /* without SSL */
+ (void)conn;
+ (void)sockindex;
+ return CURLE_OK;
+#endif /* USE_SSL */
+}
+
+CURLcode
+Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
+ bool *done)
+{
+#if defined(USE_SSL) && defined(USE_SSLEAY)
+ /* mark this is being ssl enabled from here on. */
+ conn->ssl[sockindex].use = TRUE;
+ return Curl_ossl_connect_nonblocking(conn, sockindex, done);
+
+#else
+ /* not implemented!
+ fallback to BLOCKING call. */
+ *done = TRUE;
+ return Curl_ssl_connect(conn, sockindex);
+#endif
+}
+
+#ifdef USE_SSL
+
+/*
+ * Check if there's a session ID for the given connection in the cache, and if
+ * there's one suitable, it is provided. Returns TRUE when no entry matched.
+ */
+int Curl_ssl_getsessionid(struct connectdata *conn,
+ void **ssl_sessionid,
+ size_t *idsize) /* set 0 if unknown */
+{
+ struct curl_ssl_session *check;
+ struct SessionHandle *data = conn->data;
+ long i;
+
+ if(!conn->ssl_config.sessionid)
+ /* session ID re-use is disabled */
+ return TRUE;
+
+ 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(curl_strequal(conn->host.name, check->name) &&
+ (conn->remote_port == check->remote_port) &&
+ Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) {
+ /* yes, we have a session ID! */
+ data->state.sessionage++; /* increase general age */
+ check->age = data->state.sessionage; /* set this as used in this age */
+ *ssl_sessionid = check->sessionid;
+ if(idsize)
+ *idsize = check->idsize;
+ return FALSE;
+ }
+ }
+ *ssl_sessionid = NULL;
+ return TRUE;
+}
+
+/*
+ * Kill a single session ID entry in the cache.
+ */
+static int kill_session(struct curl_ssl_session *session)
+{
+ if(session->sessionid) {
+ /* defensive check */
+
+ /* free the ID the SSL-layer specific way */
+#ifdef USE_SSLEAY
+ Curl_ossl_session_free(session->sessionid);
+#else
+ Curl_gtls_session_free(session->sessionid);
+#endif
+ session->sessionid=NULL;
+ session->age = 0; /* fresh */
+
+ Curl_free_ssl_config(&session->ssl_config);
+
+ Curl_safefree(session->name);
+ session->name = NULL; /* no name */
+
+ return 0; /* ok */
+ }
+ else
+ return 1;
+}
+
+/*
+ * Store session id in the session cache. The ID passed on to this function
+ * must already have been extracted and allocated the proper way for the SSL
+ * layer. Curl_XXXX_session_free() will be called to free/kill the session ID
+ * later on.
+ */
+CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
+ void *ssl_sessionid,
+ size_t idsize)
+{
+ int i;
+ struct SessionHandle *data=conn->data; /* the mother of all structs */
+ struct curl_ssl_session *store = &data->state.session[0];
+ long oldest_age=data->state.session[0].age; /* zero if unused */
+ char *clone_host;
+
+ /* Even though session ID re-use might be disabled, that only disables USING
+ IT. We still store it here in case the re-using is again enabled for an
+ upcoming transfer */
+
+ clone_host = strdup(conn->host.name);
+ if(!clone_host)
+ return CURLE_OUT_OF_MEMORY; /* bail out */
+
+ /* Now we should add the session ID and the host name to the cache, (remove
+ the oldest if necessary) */
+
+ /* 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_session(store);
+ else
+ store = &data->state.session[i]; /* use this slot */
+
+ /* now init the session struct wisely */
+ store->sessionid = ssl_sessionid;
+ store->idsize = idsize;
+ store->age = data->state.sessionage; /* set current age */
+ store->name = clone_host; /* clone host name */
+ store->remote_port = conn->remote_port; /* port number */
+
+ if (!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config))
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
+
+#endif
+
+void Curl_ssl_close_all(struct SessionHandle *data)
+{
+#ifdef USE_SSL
+ int i;
+ /* kill the session ID cache */
+ if(data->state.session) {
+ for(i=0; i< data->set.ssl.numsessions; i++)
+ /* the single-killer function handles empty table slots */
+ kill_session(&data->state.session[i]);
+
+ /* free the cache data */
+ free(data->state.session);
+ data->state.session = NULL;
+ }
+#ifdef USE_SSLEAY
+ Curl_ossl_close_all(data);
+#else
+#ifdef USE_GNUTLS
+ Curl_gtls_close_all(data);
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+#else /* USE_SSL */
+ (void)data;
+#endif /* USE_SSL */
+}
+
+void Curl_ssl_close(struct connectdata *conn)
+{
+ if(conn->ssl[FIRSTSOCKET].use) {
+#ifdef USE_SSLEAY
+ Curl_ossl_close(conn);
+#else
+#ifdef USE_GNUTLS
+ Curl_gtls_close(conn);
+#else
+ (void)conn;
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+ }
+}
+
+CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex)
+{
+ if(conn->ssl[sockindex].use) {
+#ifdef USE_SSLEAY
+ if(Curl_ossl_shutdown(conn, sockindex))
+ return CURLE_SSL_SHUTDOWN_FAILED;
+#else
+#ifdef USE_GNUTLS
+ if(Curl_gtls_shutdown(conn, sockindex))
+ return CURLE_SSL_SHUTDOWN_FAILED;
+#else
+ (void)conn;
+ (void)sockindex;
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+ }
+ return CURLE_OK;
+}
+
+/* Selects an (Open)SSL crypto engine
+ */
+CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine)
+{
+#ifdef USE_SSLEAY
+ return Curl_ossl_set_engine(data, engine);
+#else
+#ifdef USE_GNUTLS
+ /* FIX: add code here */
+ (void)data;
+ (void)engine;
+ return CURLE_FAILED_INIT;
+#else
+ /* no SSL layer */
+ (void)data;
+ (void)engine;
+ return CURLE_FAILED_INIT;
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+}
+
+/* Selects an (Open?)SSL crypto engine
+ */
+CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data)
+{
+#ifdef USE_SSLEAY
+ return Curl_ossl_set_engine_default(data);
+#else
+#ifdef USE_GNUTLS
+ /* FIX: add code here */
+ (void)data;
+ return CURLE_FAILED_INIT;
+#else
+ /* No SSL layer */
+ (void)data;
+ return CURLE_FAILED_INIT;
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+}
+
+/* Return list of OpenSSL crypto engine names. */
+struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data)
+{
+#ifdef USE_SSLEAY
+ return Curl_ossl_engines_list(data);
+#else
+#ifdef USE_GNUTLS
+ /* FIX: add code here? */
+ (void)data;
+ return NULL;
+#else
+ (void)data;
+ return NULL;
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+}
+
+/* return number of sent (non-SSL) bytes */
+ssize_t Curl_ssl_send(struct connectdata *conn,
+ int sockindex,
+ void *mem,
+ size_t len)
+{
+#ifdef USE_SSLEAY
+ return Curl_ossl_send(conn, sockindex, mem, len);
+#else
+#ifdef USE_GNUTLS
+ return Curl_gtls_send(conn, sockindex, mem, len);
+#else
+ (void)conn;
+ (void)sockindex;
+ (void)mem;
+ (void)len;
+ return 0;
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+}
+
+/* return number of received (decrypted) bytes */
+
+/*
+ * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
+ * a regular CURLcode value.
+ */
+ssize_t Curl_ssl_recv(struct connectdata *conn, /* connection data */
+ int sockindex, /* socketindex */
+ char *mem, /* store read data here */
+ size_t len) /* max amount to read */
+{
+#ifdef USE_SSL
+ ssize_t nread;
+ bool block = FALSE;
+
+#ifdef USE_SSLEAY
+ nread = Curl_ossl_recv(conn, sockindex, mem, len, &block);
+#else
+#ifdef USE_GNUTLS
+ nread = Curl_gtls_recv(conn, sockindex, mem, len, &block);
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+ if(nread == -1) {
+ if(!block)
+ return 0; /* this is a true error, not EWOULDBLOCK */
+ else
+ return -1;
+ }
+
+ return (int)nread;
+
+#else /* USE_SSL */
+ (void)conn;
+ (void)sockindex;
+ (void)mem;
+ (void)len;
+ return 0;
+#endif /* USE_SSL */
+}
+
+
+/*
+ * This sets up a session ID cache to the specified size. Make sure this code
+ * is agnostic to what underlying SSL technology we use.
+ */
+CURLcode Curl_ssl_initsessions(struct SessionHandle *data, long amount)
+{
+#ifdef USE_SSL
+ 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 *)
+ calloc(sizeof(struct curl_ssl_session), amount);
+ if(!session)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* store the info in the SSL section */
+ data->set.ssl.numsessions = amount;
+ data->state.session = session;
+ data->state.sessionage = 1; /* this is brand new */
+#else
+ /* without SSL, do nothing */
+ (void)data;
+ (void)amount;
+#endif
+
+ return CURLE_OK;
+}
+
+size_t Curl_ssl_version(char *buffer, size_t size)
+{
+#ifdef USE_SSLEAY
+ return Curl_ossl_version(buffer, size);
+#else
+#ifdef USE_GNUTLS
+ return Curl_gtls_version(buffer, size);
+#else
+ (void)buffer;
+ (void)size;
+ return 0; /* no SSL support */
+#endif /* USE_GNUTLS */
+#endif /* USE_SSLEAY */
+}
+
+
+/*
+ * This function tries to determine connection status.
+ *
+ * Return codes:
+ * 1 means the connection is still in place
+ * 0 means the connection has been closed
+ * -1 means the connection status is unknown
+ */
+int Curl_ssl_check_cxn(struct connectdata *conn)
+{
+#ifdef USE_SSLEAY
+ return Curl_ossl_check_cxn(conn);
+#else
+ (void)conn;
+ /* TODO: we lack implementation of this for GnuTLS */
+ return -1; /* connection status unknown */
+#endif /* USE_SSLEAY */
+}
+
+bool Curl_ssl_data_pending(struct connectdata *conn,
+ int connindex)
+{
+#ifdef USE_SSLEAY
+ /* OpenSSL-specific */
+ if(conn->ssl[connindex].handle)
+ /* SSL is in use */
+ return SSL_pending(conn->ssl[connindex].handle);
+#else
+ (void)conn;
+ (void)connindex;
+#endif
+ return FALSE; /* nothing pending */
+
+}
diff --git a/Utilities/cmcurl/sslgen.h b/Utilities/cmcurl/sslgen.h
new file mode 100644
index 000000000..c24d46bf3
--- /dev/null
+++ b/Utilities/cmcurl/sslgen.h
@@ -0,0 +1,84 @@
+#ifndef __SSLGEN_H
+#define __SSLGEN_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+bool Curl_ssl_config_matches(struct ssl_config_data* data,
+ struct ssl_config_data* needle);
+bool Curl_clone_ssl_config(struct ssl_config_data* source,
+ struct ssl_config_data* dest);
+void Curl_free_ssl_config(struct ssl_config_data* sslc);
+
+int Curl_ssl_init(void);
+void Curl_ssl_cleanup(void);
+CURLcode Curl_ssl_connect(struct connectdata *conn, int sockindex);
+CURLcode Curl_ssl_connect_nonblocking(struct connectdata *conn,
+ int sockindex,
+ bool *done);
+void Curl_ssl_close(struct connectdata *conn);
+/* tell the SSL stuff to close down all open information regarding
+ connections (and thus session ID caching etc) */
+void Curl_ssl_close_all(struct SessionHandle *data);
+CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine);
+/* Sets engine as default for all SSL operations */
+CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data);
+ssize_t Curl_ssl_send(struct connectdata *conn,
+ int sockindex,
+ void *mem,
+ size_t len);
+ssize_t Curl_ssl_recv(struct connectdata *conn, /* connection data */
+ int sockindex, /* socketindex */
+ char *mem, /* store read data here */
+ size_t len); /* max amount to read */
+
+/* init the SSL session ID cache */
+CURLcode Curl_ssl_initsessions(struct SessionHandle *, long);
+/* extract a session ID */
+int Curl_ssl_getsessionid(struct connectdata *conn,
+ void **ssl_sessionid,
+ size_t *idsize) /* set 0 if unknown */;
+/* add a new session ID */
+CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
+ void *ssl_sessionid,
+ size_t idsize);
+
+
+struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data);
+
+size_t Curl_ssl_version(char *buffer, size_t size);
+
+int Curl_ssl_check_cxn(struct connectdata *conn);
+
+CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex);
+
+bool Curl_ssl_data_pending(struct connectdata *conn,
+ int connindex);
+
+#if !defined(USE_SSL) && !defined(SSLGEN_C)
+/* set up blank macros for none-SSL builds */
+#define Curl_ssl_close_all(x)
+#endif
+
+#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
+
+#endif
diff --git a/Utilities/cmcurl/ssluse.c b/Utilities/cmcurl/ssluse.c
new file mode 100644
index 000000000..55afb2446
--- /dev/null
+++ b/Utilities/cmcurl/ssluse.c
@@ -0,0 +1,1945 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code
+ * but sslgen.c should ever call or use these functions.
+ */
+
+/*
+ * The original SSLeay-using code for curl was written by Linas Vepstas and
+ * Sampo Kellomaki 1998.
+ */
+
+#include "setup.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "formdata.h" /* for the boundary function */
+#include "url.h" /* for the ssl config check function */
+#include "inet_pton.h"
+#include "ssluse.h"
+#include "connect.h" /* Curl_sockerrno() proto */
+#include "strequal.h"
+#include "select.h"
+#include "sslgen.h"
+
+#define _MPRINTF_REPLACE /* use the internal *printf() functions */
+#include <curl/mprintf.h>
+
+#ifdef USE_SSLEAY
+
+#ifdef USE_OPENSSL
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#else
+#include <rand.h>
+#include <x509v3.h>
+#endif
+
+#include "memory.h"
+#include "easyif.h" /* for Curl_convert_from_utf8 prototype */
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090581fL
+#define HAVE_SSL_GET1_SESSION 1
+#else
+#undef HAVE_SSL_GET1_SESSION
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00904100L
+#define HAVE_USERDATA_IN_PWD_CALLBACK 1
+#else
+#undef HAVE_USERDATA_IN_PWD_CALLBACK
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00907001L
+/* ENGINE_load_private_key() takes four arguments */
+#define HAVE_ENGINE_LOAD_FOUR_ARGS
+#else
+/* ENGINE_load_private_key() takes three arguments */
+#undef HAVE_ENGINE_LOAD_FOUR_ARGS
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x00903001L) && defined(HAVE_OPENSSL_PKCS12_H)
+/* OpenSSL has PKCS 12 support */
+#define HAVE_PKCS12_SUPPORT
+#else
+/* OpenSSL/SSLEay does not have PKCS12 support */
+#undef HAVE_PKCS12_SUPPORT
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00906001L
+#define HAVE_ERR_ERROR_STRING_N 1
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+#define SSL_METHOD_QUAL const
+#else
+#define SSL_METHOD_QUAL
+#endif
+
+/*
+ * Number of bytes to read from the random number seed file. This must be
+ * a finite value (because some entropy "files" like /dev/urandom have
+ * an infinite length), but must be large enough to provide enough
+ * entopy to properly seed OpenSSL's PRNG.
+ */
+#define RAND_LOAD_LENGTH 1024
+
+#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
+static char global_passwd[64];
+#endif
+
+static int passwd_callback(char *buf, int num, int 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 (int)strlen(buf);
+ }
+ }
+ return 0;
+}
+
+/*
+ * rand_enough() is a function that returns TRUE if we have seeded the random
+ * engine properly. We use some preprocessor magic to provide a seed_enough()
+ * macro to use, just to prevent a compiler warning on this function if we
+ * pass in an argument that is never used.
+ */
+
+#ifdef HAVE_RAND_STATUS
+#define seed_enough(x) rand_enough()
+static bool rand_enough(void)
+{
+ return (bool)(0 != RAND_status());
+}
+#else
+#define seed_enough(x) rand_enough(x)
+static bool rand_enough(int nread)
+{
+ /* this is a very silly decision to make */
+ return (bool)(nread > 500);
+}
+#endif
+
+static int ossl_seed(struct SessionHandle *data)
+{
+ char *buf = data->state.buffer; /* point to the big buffer */
+ int nread=0;
+
+ /* Q: should we add support for a random file name as a libcurl option?
+ A: Yes, it is here */
+
+#ifndef RANDOM_FILE
+ /* if RANDOM_FILE isn't defined, we only perform this if an option tells
+ us to! */
+ if(data->set.ssl.random_file)
+#define RANDOM_FILE "" /* doesn't matter won't be used */
+#endif
+ {
+ /* let the option override the define */
+ nread += RAND_load_file((data->set.ssl.random_file?
+ data->set.ssl.random_file:RANDOM_FILE),
+ RAND_LOAD_LENGTH);
+ if(seed_enough(nread))
+ return nread;
+ }
+
+#if defined(HAVE_RAND_EGD)
+ /* only available in OpenSSL 0.9.5 and later */
+ /* EGD_SOCKET is set at configure time or not at all */
+#ifndef EGD_SOCKET
+ /* If we don't have the define set, we only do this if the egd-option
+ is set */
+ if(data->set.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;
+
+ /* Changed call to RAND_seed to use the underlying RAND_add implementation
+ * directly. Do this in a loop, with the amount of additional entropy
+ * being dependent upon the algorithm used by Curl_FormBoundary(): N bytes
+ * of a 7-bit ascii set. -- Richard Gorton, March 11 2003.
+ */
+
+ do {
+ area = Curl_FormBoundary();
+ if(!area)
+ return 3; /* out of memory */
+
+ len = (int)strlen(area);
+ RAND_add(area, len, (len >> 1));
+
+ free(area); /* now remove the random junk */
+ } while (!RAND_status());
+ }
+#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, RAND_LOAD_LENGTH);
+ if(seed_enough(nread))
+ return nread;
+ }
+
+ infof(data, "libcurl is now using a weak random seed!\n");
+ return nread;
+}
+
+int Curl_ossl_seed(struct SessionHandle *data)
+{
+ /* we have the "SSL is seeded" boolean static to prevent multiple
+ time-consuming seedings in vain */
+ static bool ssl_seeded = FALSE;
+
+ if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) {
+ ossl_seed(data);
+ ssl_seeded = TRUE;
+ }
+ return 0;
+}
+
+
+#ifndef SSL_FILETYPE_ENGINE
+#define SSL_FILETYPE_ENGINE 42
+#endif
+#ifndef SSL_FILETYPE_PKCS12
+#define SSL_FILETYPE_PKCS12 43
+#endif
+static int do_file_type(const char *type)
+{
+ if(!type || !type[0])
+ return SSL_FILETYPE_PEM;
+ if(curl_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;
+ if(curl_strequal(type, "P12"))
+ return SSL_FILETYPE_PKCS12;
+ return -1;
+}
+
+static
+int cert_stuff(struct connectdata *conn,
+ SSL_CTX* ctx,
+ char *cert_file,
+ const char *cert_type,
+ char *key_file,
+ const char *key_type)
+{
+ struct SessionHandle *data = conn->data;
+ int file_type;
+
+ if(cert_file != NULL) {
+ SSL *ssl;
+ X509 *x509;
+ int cert_done = 0;
+
+ 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:
+ */
+ size_t len = strlen(data->set.key_passwd);
+ if(len < sizeof(global_passwd))
+ memcpy(global_passwd, data->set.key_passwd, len+1);
+#else
+ /*
+ * We set the password in the callback userdata
+ */
+ SSL_CTX_set_default_passwd_cb_userdata(ctx,
+ data->set.key_passwd);
+#endif
+ /* Set passwd callback: */
+ SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
+ }
+
+ file_type = do_file_type(cert_type);
+
+#define SSL_CLIENT_CERT_ERR \
+ "unable to use client certificate (no key found or wrong pass phrase?)"
+
+ switch(file_type) {
+ case SSL_FILETYPE_PEM:
+ /* SSL_CTX_use_certificate_chain_file() only works on PEM files */
+ if(SSL_CTX_use_certificate_chain_file(ctx,
+ cert_file) != 1) {
+ failf(data, SSL_CLIENT_CERT_ERR);
+ return 0;
+ }
+ break;
+
+ case SSL_FILETYPE_ASN1:
+ /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but
+ we use the case above for PEM so this can only be performed with
+ ASN1 files. */
+ if(SSL_CTX_use_certificate_file(ctx,
+ cert_file,
+ file_type) != 1) {
+ failf(data, SSL_CLIENT_CERT_ERR);
+ return 0;
+ }
+ break;
+ case SSL_FILETYPE_ENGINE:
+ failf(data, "file type ENG for certificate not implemented");
+ return 0;
+
+ case SSL_FILETYPE_PKCS12:
+ {
+#ifdef HAVE_PKCS12_SUPPORT
+ FILE *f;
+ PKCS12 *p12;
+ EVP_PKEY *pri;
+
+ f = fopen(cert_file,"rb");
+ if (!f) {
+ failf(data, "could not open PKCS12 file '%s'", cert_file);
+ return 0;
+ }
+ p12 = d2i_PKCS12_fp(f, NULL);
+ fclose(f);
+
+ PKCS12_PBE_add();
+
+ if (!PKCS12_parse(p12, data->set.key_passwd, &pri, &x509, NULL)) {
+ failf(data,
+ "could not parse PKCS12 file, check password, OpenSSL error %s",
+ ERR_error_string(ERR_get_error(), NULL) );
+ return 0;
+ }
+
+ PKCS12_free(p12);
+
+ if(SSL_CTX_use_certificate(ctx, x509) != 1) {
+ failf(data, SSL_CLIENT_CERT_ERR);
+ EVP_PKEY_free(pri);
+ X509_free(x509);
+ return 0;
+ }
+
+ if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) {
+ failf(data, "unable to use private key from PKCS12 file '%s'",
+ cert_file);
+ EVP_PKEY_free(pri);
+ X509_free(x509);
+ return 0;
+ }
+
+ EVP_PKEY_free(pri);
+ X509_free(x509);
+ cert_done = 1;
+ break;
+#else
+ failf(data, "file type P12 for certificate not supported");
+ return 0;
+#endif
+ }
+ default:
+ failf(data, "not supported file type '%s' for certificate", cert_type);
+ return 0;
+ }
+
+ file_type = do_file_type(key_type);
+
+ switch(file_type) {
+ case SSL_FILETYPE_PEM:
+ if(cert_done)
+ break;
+ if(key_file == NULL)
+ /* cert & key can only be in PEM case in the same file */
+ key_file=cert_file;
+ case SSL_FILETYPE_ASN1:
+ if(SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type) != 1) {
+ failf(data, "unable to set private key file: '%s' type %s\n",
+ key_file, key_type?key_type:"PEM");
+ return 0;
+ }
+ break;
+ case SSL_FILETYPE_ENGINE:
+#ifdef HAVE_OPENSSL_ENGINE_H
+ { /* XXXX still needs some work */
+ EVP_PKEY *priv_key = NULL;
+ if(conn && conn->data && conn->data->state.engine) {
+#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
+ UI_METHOD *ui_method = UI_OpenSSL();
+#endif
+ if(!key_file || !key_file[0]) {
+ failf(data, "no key set to load from crypto engine\n");
+ return 0;
+ }
+ /* the typecast below was added to please mingw32 */
+ priv_key = (EVP_PKEY *)
+ ENGINE_load_private_key(conn->data->state.engine,key_file,
+#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
+ ui_method,
+#endif
+ 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(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;
+ }
+ }
+ break;
+#else
+ failf(data, "file type ENG for private key not supported\n");
+ return 0;
+#endif
+ case SSL_FILETYPE_PKCS12:
+ if(!cert_done) {
+ failf(data, "file type P12 for private key not supported\n");
+ return 0;
+ }
+ break;
+ default:
+ failf(data, "not supported file type for private key\n");
+ return 0;
+ }
+
+ ssl=SSL_new(ctx);
+ if (NULL == ssl) {
+ failf(data,"unable to create an SSL structure\n");
+ return 0;
+ }
+
+ x509=SSL_get_certificate(ssl);
+
+ /* This version was provided by Evan Jordan and is supposed to not
+ leak memory as the previous version: */
+ if(x509 != NULL) {
+ EVP_PKEY *pktmp = X509_get_pubkey(x509);
+ EVP_PKEY_copy_parameters(pktmp,SSL_get_privatekey(ssl));
+ EVP_PKEY_free(pktmp);
+ }
+
+ SSL_free(ssl);
+
+ /* If we are using DSA, we can copy the parameters from
+ * the private key */
+
+
+ /* Now we know that a key and cert have been set against
+ * the SSL context */
+ if(!SSL_CTX_check_private_key(ctx)) {
+ failf(data, "Private key does not match the certificate public key");
+ return(0);
+ }
+#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
+ /* erase it now */
+ memset(global_passwd, 0, sizeof(global_passwd));
+#endif
+ }
+ return(1);
+}
+
+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, sizeof(buf));
+ return ok;
+}
+
+/* Return error string for last OpenSSL error
+ */
+static char *SSL_strerror(unsigned long error, char *buf, size_t size)
+{
+#ifdef HAVE_ERR_ERROR_STRING_N
+ /* OpenSSL 0.9.6 and later has a function named
+ ERRO_error_string_n() that takes the size of the buffer as a
+ third argument */
+ ERR_error_string_n(error, buf, size);
+#else
+ (void) size;
+ ERR_error_string(error, buf);
+#endif
+ return (buf);
+}
+
+#endif /* USE_SSLEAY */
+
+#ifdef USE_SSLEAY
+/**
+ * Global SSL init
+ *
+ * @retval 0 error initializing SSL
+ * @retval 1 SSL initialized successfully
+ */
+int Curl_ossl_init(void)
+{
+#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+ ENGINE_load_builtin_engines();
+#endif
+
+ /* Lets get nice error messages */
+ SSL_load_error_strings();
+
+ /* Setup all the global SSL stuff */
+ if (!SSLeay_add_ssl_algorithms())
+ return 0;
+
+ return 1;
+}
+
+#endif /* USE_SSLEAY */
+
+#ifdef USE_SSLEAY
+
+/* Global cleanup */
+void Curl_ossl_cleanup(void)
+{
+ /* 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
+
+#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
+ /* this function was not present in 0.9.6b, but was added sometimes
+ later */
+ CRYPTO_cleanup_all_ex_data();
+#endif
+}
+
+/*
+ * This function uses SSL_peek to determine connection status.
+ *
+ * Return codes:
+ * 1 means the connection is still in place
+ * 0 means the connection has been closed
+ * -1 means the connection status is unknown
+ */
+int Curl_ossl_check_cxn(struct connectdata *conn)
+{
+ int rc;
+ char buf;
+
+ rc = SSL_peek(conn->ssl[FIRSTSOCKET].handle, (void*)&buf, 1);
+ if (rc > 0)
+ return 1; /* connection still in place */
+
+ if (rc == 0)
+ return 0; /* connection has been closed */
+
+ return -1; /* connection status unknown */
+}
+
+#endif /* USE_SSLEAY */
+
+/* Selects an OpenSSL crypto engine
+ */
+CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine)
+{
+#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
+ ENGINE *e = ENGINE_by_id(engine);
+
+ if (!e) {
+ failf(data, "SSL Engine '%s' not found", engine);
+ return (CURLE_SSL_ENGINE_NOTFOUND);
+ }
+
+ if (data->state.engine) {
+ ENGINE_finish(data->state.engine);
+ ENGINE_free(data->state.engine);
+ data->state.engine = NULL;
+ }
+ if (!ENGINE_init(e)) {
+ char buf[256];
+
+ ENGINE_free(e);
+ failf(data, "Failed to initialise SSL Engine '%s':\n%s",
+ engine, SSL_strerror(ERR_get_error(), buf, sizeof(buf)));
+ return (CURLE_SSL_ENGINE_INITFAILED);
+ }
+ data->state.engine = e;
+ return (CURLE_OK);
+#else
+ (void)engine;
+ failf(data, "SSL Engine not supported");
+ return (CURLE_SSL_ENGINE_NOTFOUND);
+#endif
+}
+
+#ifdef USE_SSLEAY
+/* Sets engine as default for all SSL operations
+ */
+CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data)
+{
+#ifdef HAVE_OPENSSL_ENGINE_H
+ if (data->state.engine) {
+ if (ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) {
+ infof(data,"set default crypto engine '%s'\n", ENGINE_get_id(data->state.engine));
+ }
+ else {
+ failf(data, "set default crypto engine '%s' failed", ENGINE_get_id(data->state.engine));
+ return CURLE_SSL_ENGINE_SETFAILED;
+ }
+ }
+#else
+ (void) data;
+#endif
+ return CURLE_OK;
+}
+#endif /* USE_SSLEAY */
+
+/* Return list of OpenSSL crypto engine names.
+ */
+struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data)
+{
+ struct curl_slist *list = NULL;
+#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
+ ENGINE *e;
+
+ for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+ list = curl_slist_append(list, ENGINE_get_id(e));
+#endif
+ (void) data;
+ return (list);
+}
+
+
+#ifdef USE_SSLEAY
+
+/*
+ * This function is called when an SSL connection is closed.
+ */
+void Curl_ossl_close(struct connectdata *conn)
+{
+ int i;
+ /*
+ 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);
+
+ for(i=0; i<2; i++) {
+ struct ssl_connect_data *connssl = &conn->ssl[i];
+
+ if(connssl->handle) {
+ (void)SSL_shutdown(connssl->handle);
+ SSL_set_connect_state(connssl->handle);
+
+ SSL_free (connssl->handle);
+ connssl->handle = NULL;
+ }
+ if(connssl->ctx) {
+ SSL_CTX_free (connssl->ctx);
+ connssl->ctx = NULL;
+ }
+ connssl->use = FALSE; /* get back to ordinary socket usage */
+ }
+}
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+int Curl_ossl_shutdown(struct connectdata *conn, int sockindex)
+{
+ int retval = 0;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct SessionHandle *data = conn->data;
+ char buf[120]; /* We will use this for the OpenSSL error buffer, so it has
+ to be at least 120 bytes long. */
+ unsigned long sslerror;
+ ssize_t nread;
+ int err;
+ int done = 0;
+
+ /* This has only been tested on the proftpd server, and the mod_tls code
+ sends a close notify alert without waiting for a close notify alert in
+ response. Thus we wait for a close notify alert from the server, but
+ we do not send one. Let's hope other servers do the same... */
+
+ if(connssl->handle) {
+ while(!done) {
+ int what = Curl_select(conn->sock[sockindex],
+ CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
+ if(what > 0) {
+ /* Something to read, let's do it and hope that it is the close
+ notify alert from the server */
+ nread = (ssize_t)SSL_read(conn->ssl[sockindex].handle, buf,
+ sizeof(buf));
+ err = SSL_get_error(conn->ssl[sockindex].handle, (int)nread);
+
+ switch(err) {
+ case SSL_ERROR_NONE: /* this is not an error */
+ case SSL_ERROR_ZERO_RETURN: /* no more data */
+ /* This is the expected response. There was no data but only
+ the close notify alert */
+ done = 1;
+ break;
+ case SSL_ERROR_WANT_READ:
+ /* there's data pending, re-invoke SSL_read() */
+ infof(data, "SSL_ERROR_WANT_READ\n");
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ /* SSL wants a write. Really odd. Let's bail out. */
+ infof(data, "SSL_ERROR_WANT_WRITE\n");
+ done = 1;
+ break;
+ default:
+ /* openssl/ssl.h says "look at error stack/return value/errno" */
+ sslerror = ERR_get_error();
+ failf(conn->data, "SSL read: %s, errno %d",
+ ERR_error_string(sslerror, buf),
+ Curl_sockerrno() );
+ done = 1;
+ break;
+ }
+ }
+ else if(0 == what) {
+ /* timeout */
+ failf(data, "SSL shutdown timeout");
+ done = 1;
+ break;
+ }
+ else {
+ /* anything that gets here is fatally bad */
+ failf(data, "select on SSL socket, errno: %d", Curl_sockerrno());
+ retval = -1;
+ done = 1;
+ }
+ } /* while()-loop for the select() */
+
+ if(data->set.verbose) {
+ switch(SSL_get_shutdown(connssl->handle)) {
+ case SSL_SENT_SHUTDOWN:
+ infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN\n");
+ break;
+ case SSL_RECEIVED_SHUTDOWN:
+ infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN\n");
+ break;
+ case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN:
+ infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|"
+ "SSL_RECEIVED__SHUTDOWN\n");
+ break;
+ }
+ }
+
+ connssl->use = FALSE; /* get back to ordinary socket usage */
+
+ SSL_free (connssl->handle);
+ connssl->handle = NULL;
+ }
+ return retval;
+}
+
+void Curl_ossl_session_free(void *ptr)
+{
+ /* free the ID */
+ SSL_SESSION_free(ptr);
+}
+
+/*
+ * This function is called when the 'data' struct is going away. Close
+ * down everything and free all resources!
+ */
+int Curl_ossl_close_all(struct SessionHandle *data)
+{
+#ifdef HAVE_OPENSSL_ENGINE_H
+ if(data->state.engine) {
+ ENGINE_finish(data->state.engine);
+ ENGINE_free(data->state.engine);
+ data->state.engine = NULL;
+ }
+#else
+ (void)data;
+#endif
+ return 0;
+}
+
+static int 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
+
+/* ====================================================== */
+#ifdef USE_SSLEAY
+
+/*
+ * Match a hostname against a wildcard pattern.
+ * E.g.
+ * "foo.host.com" matches "*.host.com".
+ *
+ * We are a bit more liberal than RFC2818 describes in that we
+ * accept multiple "*" in pattern (similar to what some other browsers do).
+ * E.g.
+ * "abc.def.domain.com" should strickly not match "*.domain.com", but we
+ * don't consider "." to be important in CERT checking.
+ */
+#define HOST_NOMATCH 0
+#define HOST_MATCH 1
+
+static int hostmatch(const char *hostname, const char *pattern)
+{
+ while (1) {
+ int c = *pattern++;
+
+ if (c == '\0')
+ return (*hostname ? HOST_NOMATCH : HOST_MATCH);
+
+ if (c == '*') {
+ c = *pattern;
+ if (c == '\0') /* "*\0" matches anything remaining */
+ return HOST_MATCH;
+
+ while (*hostname) {
+ /* The only recursive function in libcurl! */
+ if (hostmatch(hostname++,pattern) == HOST_MATCH)
+ return HOST_MATCH;
+ }
+ break;
+ }
+
+ if (toupper(c) != toupper(*hostname++))
+ break;
+ }
+ return HOST_NOMATCH;
+}
+
+static int
+cert_hostcheck(const char *match_pattern, const char *hostname)
+{
+ if (!match_pattern || !*match_pattern ||
+ !hostname || !*hostname) /* sanity check */
+ return 0;
+
+ if(curl_strequal(hostname,match_pattern)) /* trivial case */
+ return 1;
+
+ if (hostmatch(hostname,match_pattern) == HOST_MATCH)
+ return 1;
+ return 0;
+}
+
+/* Quote from RFC2818 section 3.1 "Server Identity"
+
+ If a subjectAltName extension of type dNSName is present, that MUST
+ be used as the identity. Otherwise, the (most specific) Common Name
+ field in the Subject field of the certificate MUST be used. Although
+ the use of the Common Name is existing practice, it is deprecated and
+ Certification Authorities are encouraged to use the dNSName instead.
+
+ Matching is performed using the matching rules specified by
+ [RFC2459]. If more than one identity of a given type is present in
+ the certificate (e.g., more than one dNSName name, a match in any one
+ of the set is considered acceptable.) Names may contain the wildcard
+ character * which is considered to match any single domain name
+ component or component fragment. E.g., *.a.com matches foo.a.com but
+ not bar.foo.a.com. f*.com matches foo.com but not bar.com.
+
+ In some cases, the URI is specified as an IP address rather than a
+ hostname. In this case, the iPAddress subjectAltName must be present
+ in the certificate and must exactly match the IP in the URI.
+
+*/
+static CURLcode verifyhost(struct connectdata *conn,
+ X509 *server_cert)
+{
+ bool matched = FALSE; /* no alternative match yet */
+ int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
+ int addrlen = 0;
+ struct SessionHandle *data = conn->data;
+ STACK_OF(GENERAL_NAME) *altnames;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+ CURLcode res = CURLE_OK;
+
+#ifdef ENABLE_IPV6
+ if(conn->bits.ipv6_ip &&
+ Curl_inet_pton(AF_INET6, conn->host.name, &addr)) {
+ target = GEN_IPADD;
+ addrlen = sizeof(struct in6_addr);
+ }
+ else
+#endif
+ if(Curl_inet_pton(AF_INET, conn->host.name, &addr)) {
+ target = GEN_IPADD;
+ addrlen = sizeof(struct in_addr);
+ }
+
+ /* get a "list" of alternative names */
+ altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
+
+ if(altnames) {
+ int numalts;
+ int i;
+
+ /* get amount of alternatives, RFC2459 claims there MUST be at least
+ one, but we don't depend on it... */
+ numalts = sk_GENERAL_NAME_num(altnames);
+
+ /* loop through all alternatives while none has matched */
+ for (i=0; (i<numalts) && !matched; i++) {
+ /* get a handle to alternative name number i */
+ const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);
+
+ /* only check alternatives of the same type the target is */
+ if(check->type == target) {
+ /* get data and length */
+ const char *altptr = (char *)ASN1_STRING_data(check->d.ia5);
+ int altlen;
+
+ switch(target) {
+ case GEN_DNS: /* name/pattern comparison */
+ /* The OpenSSL man page explicitly says: "In general it cannot be
+ assumed that the data returned by ASN1_STRING_data() is null
+ terminated or does not contain embedded nulls." But also that
+ "The actual format of the data will depend on the actual string
+ type itself: for example for and IA5String the data will be ASCII"
+
+ Gisle researched the OpenSSL sources:
+ "I checked the 0.9.6 and 0.9.8 sources before my patch and
+ it always 0-terminates an IA5String."
+ */
+ if (cert_hostcheck(altptr, conn->host.name))
+ matched = TRUE;
+ break;
+
+ case GEN_IPADD: /* IP address comparison */
+ /* compare alternative IP address if the data chunk is the same size
+ our server IP address is */
+ altlen = ASN1_STRING_length(check->d.ia5);
+ if((altlen == addrlen) && !memcmp(altptr, &addr, altlen))
+ matched = TRUE;
+ break;
+ }
+ }
+ }
+ GENERAL_NAMES_free(altnames);
+ }
+
+ if(matched)
+ /* an alternative name matched the server hostname */
+ infof(data, "\t subjectAltName: %s matched\n", conn->host.dispname);
+ else {
+ /* we have to look to the last occurence of a commonName in the
+ distinguished one to get the most significant one. */
+ int j,i=-1 ;
+
+/* The following is done because of a bug in 0.9.6b */
+
+ unsigned char *nulstr = (unsigned char *)"";
+ unsigned char *peer_CN = nulstr;
+
+ X509_NAME *name = X509_get_subject_name(server_cert) ;
+ if (name)
+ while ((j=X509_NAME_get_index_by_NID(name,NID_commonName,i))>=0)
+ i=j;
+
+ /* we have the name entry and we will now convert this to a string
+ that we can use for comparison. Doing this we support BMPstring,
+ UTF8 etc. */
+
+ if (i>=0) {
+ ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i));
+
+ /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
+ is already UTF-8 encoded. We check for this case and copy the raw
+ string manually to avoid the problem. This code can be made
+ conditional in the future when OpenSSL has been fixed. Work-around
+ brought by Alexis S. L. Carvalho. */
+ if (tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
+ j = ASN1_STRING_length(tmp);
+ if (j >= 0) {
+ peer_CN = OPENSSL_malloc(j+1);
+ if (peer_CN) {
+ memcpy(peer_CN, ASN1_STRING_data(tmp), j);
+ peer_CN[j] = '\0';
+ }
+ }
+ }
+ else /* not a UTF8 name */
+ j = ASN1_STRING_to_UTF8(&peer_CN, tmp);
+ }
+
+ if (peer_CN == nulstr)
+ peer_CN = NULL;
+#ifdef CURL_DOES_CONVERSIONS
+ else {
+ /* convert peer_CN from UTF8 */
+ size_t rc;
+ rc = Curl_convert_from_utf8(data, peer_CN, strlen(peer_CN));
+ /* Curl_convert_from_utf8 calls failf if unsuccessful */
+ if (rc != CURLE_OK) {
+ return(rc);
+ }
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+ if (!peer_CN) {
+ if(data->set.ssl.verifyhost > 1) {
+ failf(data,
+ "SSL: unable to obtain common name from peer certificate");
+ return CURLE_SSL_PEER_CERTIFICATE;
+ }
+ else {
+ /* Consider verifyhost == 1 as an "OK" for a missing CN field, but we
+ output a note about the situation */
+ infof(data, "\t common name: WARNING couldn't obtain\n");
+ }
+ }
+ else if(!cert_hostcheck((const char *)peer_CN, conn->host.name)) {
+ if(data->set.ssl.verifyhost > 1) {
+ failf(data, "SSL: certificate subject name '%s' does not match "
+ "target host name '%s'", peer_CN, conn->host.dispname);
+ res = CURLE_SSL_PEER_CERTIFICATE;
+ }
+ else
+ infof(data, "\t common name: %s (does not match '%s')\n",
+ peer_CN, conn->host.dispname);
+ }
+ else {
+ infof(data, "\t common name: %s (matched)\n", peer_CN);
+ }
+ if(peer_CN)
+ OPENSSL_free(peer_CN);
+ }
+ return res;
+}
+#endif
+
+/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions
+ and thus this cannot be done there. */
+#ifdef SSL_CTRL_SET_MSG_CALLBACK
+
+static const char *ssl_msg_type(int ssl_ver, int msg)
+{
+ if (ssl_ver == SSL2_VERSION_MAJOR) {
+ switch (msg) {
+ case SSL2_MT_ERROR:
+ return "Error";
+ case SSL2_MT_CLIENT_HELLO:
+ return "Client hello";
+ case SSL2_MT_CLIENT_MASTER_KEY:
+ return "Client key";
+ case SSL2_MT_CLIENT_FINISHED:
+ return "Client finished";
+ case SSL2_MT_SERVER_HELLO:
+ return "Server hello";
+ case SSL2_MT_SERVER_VERIFY:
+ return "Server verify";
+ case SSL2_MT_SERVER_FINISHED:
+ return "Server finished";
+ case SSL2_MT_REQUEST_CERTIFICATE:
+ return "Request CERT";
+ case SSL2_MT_CLIENT_CERTIFICATE:
+ return "Client CERT";
+ }
+ }
+ else if (ssl_ver == SSL3_VERSION_MAJOR) {
+ switch (msg) {
+ case SSL3_MT_HELLO_REQUEST:
+ return "Hello request";
+ case SSL3_MT_CLIENT_HELLO:
+ return "Client hello";
+ case SSL3_MT_SERVER_HELLO:
+ return "Server hello";
+ case SSL3_MT_CERTIFICATE:
+ return "CERT";
+ case SSL3_MT_SERVER_KEY_EXCHANGE:
+ return "Server key exchange";
+ case SSL3_MT_CLIENT_KEY_EXCHANGE:
+ return "Client key exchange";
+ case SSL3_MT_CERTIFICATE_REQUEST:
+ return "Request CERT";
+ case SSL3_MT_SERVER_DONE:
+ return "Server finished";
+ case SSL3_MT_CERTIFICATE_VERIFY:
+ return "CERT verify";
+ case SSL3_MT_FINISHED:
+ return "Finished";
+ }
+ }
+ return "Unknown";
+}
+
+static const char *tls_rt_type(int type)
+{
+ return (
+ type == SSL3_RT_CHANGE_CIPHER_SPEC ? "TLS change cipher, " :
+ type == SSL3_RT_ALERT ? "TLS alert, " :
+ type == SSL3_RT_HANDSHAKE ? "TLS handshake, " :
+ type == SSL3_RT_APPLICATION_DATA ? "TLS app data, " :
+ "TLS Unknown, ");
+}
+
+
+/*
+ * Our callback from the SSL/TLS layers.
+ */
+static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
+ const void *buf, size_t len, const SSL *ssl,
+ struct connectdata *conn)
+{
+ struct SessionHandle *data;
+ const char *msg_name, *tls_rt_name;
+ char ssl_buf[1024];
+ int ver, msg_type, txt_len;
+
+ if (!conn || !conn->data || !conn->data->set.fdebug ||
+ (direction != 0 && direction != 1))
+ return;
+
+ data = conn->data;
+ ssl_ver >>= 8;
+ ver = (ssl_ver == SSL2_VERSION_MAJOR ? '2' :
+ ssl_ver == SSL3_VERSION_MAJOR ? '3' : '?');
+
+ /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
+ * always pass-up content-type as 0. But the interesting message-type
+ * is at 'buf[0]'.
+ */
+ if (ssl_ver == SSL3_VERSION_MAJOR && content_type != 0)
+ tls_rt_name = tls_rt_type(content_type);
+ else
+ tls_rt_name = "";
+
+ msg_type = *(char*)buf;
+ msg_name = ssl_msg_type(ssl_ver, msg_type);
+
+ txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "SSLv%c, %s%s (%d):\n",
+ ver, tls_rt_name, msg_name, msg_type);
+ Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL);
+
+ Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
+ CURLINFO_SSL_DATA_IN, (char *)buf, len, NULL);
+ (void) ssl;
+}
+#endif
+
+#ifdef USE_SSLEAY
+/* ====================================================== */
+
+static CURLcode
+Curl_ossl_connect_step1(struct connectdata *conn,
+ int sockindex)
+{
+ CURLcode retcode = CURLE_OK;
+
+ struct SessionHandle *data = conn->data;
+ SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
+ void *ssl_sessionid=NULL;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+ curlassert(ssl_connect_1 == connssl->connecting_state);
+
+ /* Make funny stuff to get random input */
+ Curl_ossl_seed(data);
+
+ /* check to see if we've been told to use an explicit SSL/TLS version */
+ switch(data->set.ssl.version) {
+ default:
+ case CURL_SSLVERSION_DEFAULT:
+ /* 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;
+ }
+
+ if (connssl->ctx)
+ SSL_CTX_free(connssl->ctx);
+ connssl->ctx = SSL_CTX_new(req_method);
+
+ if(!connssl->ctx) {
+ failf(data, "SSL: couldn't create a context!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+#ifdef SSL_CTRL_SET_MSG_CALLBACK
+ if (data->set.fdebug && data->set.verbose) {
+ /* the SSL trace callback is only used for verbose logging so we only
+ inform about failures of setting it */
+ if (!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK,
+ (void (*)(void))ssl_tls_trace)) {
+ infof(data, "SSL: couldn't set callback!\n");
+ }
+ else if (!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0,
+ conn)) {
+ infof(data, "SSL: couldn't set callback argument!\n");
+ }
+ }
+#endif
+
+ /* OpenSSL contains code to work-around lots of bugs and flaws in various
+ SSL-implementations. SSL_CTX_set_options() is used to enabled those
+ work-arounds. The man page for this option states that SSL_OP_ALL enables
+ all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
+ enable the bug workaround options if compatibility with somewhat broken
+ implementations is desired."
+
+ */
+ SSL_CTX_set_options(connssl->ctx, SSL_OP_ALL);
+
+#if 0
+ /*
+ * Not sure it's needed to tell SSL_connect() that socket is
+ * non-blocking. It doesn't seem to care, but just return with
+ * SSL_ERROR_WANT_x.
+ */
+ if (data->state.used_interface == Curl_if_multi)
+ SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL);
+#endif
+
+ if(data->set.cert) {
+ if(!cert_stuff(conn,
+ connssl->ctx,
+ data->set.cert,
+ data->set.cert_type,
+ data->set.key,
+ data->set.key_type)) {
+ /* failf() is already done in cert_stuff() */
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+
+ if(data->set.ssl.cipher_list) {
+ if(!SSL_CTX_set_cipher_list(connssl->ctx,
+ data->set.ssl.cipher_list)) {
+ failf(data, "failed setting cipher list");
+ return CURLE_SSL_CIPHER;
+ }
+ }
+
+ if (data->set.ssl.CAfile || data->set.ssl.CApath) {
+ /* tell SSL where to find CA certificates that are used to verify
+ the servers certificate. */
+ if (!SSL_CTX_load_verify_locations(connssl->ctx, data->set.ssl.CAfile,
+ data->set.ssl.CApath)) {
+ if (data->set.ssl.verifypeer) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data,"error setting certificate verify locations:\n"
+ " CAfile: %s\n CApath: %s\n",
+ data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
+ data->set.ssl.CApath ? data->set.ssl.CApath : "none");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else {
+ /* Just continue with a warning if no strict certificate verification
+ is required. */
+ infof(data, "error setting certificate verify locations,"
+ " continuing anyway:\n");
+ }
+ }
+ else {
+ /* Everything is fine. */
+ infof(data, "successfully set certificate verify locations:\n");
+ }
+ infof(data,
+ " CAfile: %s\n"
+ " CApath: %s\n",
+ data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
+ data->set.ssl.CApath ? data->set.ssl.CApath : "none");
+ }
+ /* SSL always tries to verify the peer, this only says whether it should
+ * fail to connect if the verification fails, or if it should continue
+ * anyway. In the latter case the result of the verification is checked with
+ * SSL_get_verify_result() below. */
+ SSL_CTX_set_verify(connssl->ctx,
+ data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
+ cert_verify_callback);
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx,
+ data->set.ssl.fsslctxp);
+ if(retcode) {
+ failf(data,"error signaled by ssl ctx callback");
+ return retcode;
+ }
+ }
+
+ /* Lets make an SSL structure */
+ if (connssl->handle)
+ SSL_free(connssl->handle);
+ connssl->handle = SSL_new(connssl->ctx);
+ if (!connssl->handle) {
+ failf(data, "SSL: couldn't create a context (handle)!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ SSL_set_connect_state(connssl->handle);
+
+ connssl->server_cert = 0x0;
+
+ /* Check if there's a cached ID we can/should use here! */
+ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
+ /* we got a session id, use it! */
+ if (!SSL_set_session(connssl->handle, ssl_sessionid)) {
+ failf(data, "SSL: SSL_set_session failed: %s",
+ ERR_error_string(ERR_get_error(),NULL));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ /* Informational message */
+ infof (data, "SSL re-using session ID\n");
+ }
+
+ /* pass the raw socket into the SSL layers */
+ if (!SSL_set_fd(connssl->handle, sockfd)) {
+ failf(data, "SSL: SSL_set_fd failed: %s",
+ ERR_error_string(ERR_get_error(),NULL));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+}
+
+static CURLcode
+Curl_ossl_connect_step2(struct connectdata *conn,
+ int sockindex, long *timeout_ms)
+{
+ struct SessionHandle *data = conn->data;
+ int err;
+ long has_passed;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+ curlassert(ssl_connect_2 == connssl->connecting_state
+ || ssl_connect_2_reading == connssl->connecting_state
+ || ssl_connect_2_writing == connssl->connecting_state);
+
+ /* 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) {
+ /* get the most strict timeout of the ones converted to milliseconds */
+ 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 if(data->set.connecttimeout)
+ *timeout_ms = data->set.connecttimeout*1000;
+ else
+ /* no particular time-out has been set */
+ *timeout_ms= DEFAULT_CONNECT_TIMEOUT;
+
+ /* Evaluate in milliseconds how much time that has passed */
+ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
+
+ /* subtract the passed time */
+ *timeout_ms -= 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;
+ }
+
+ err = SSL_connect(connssl->handle);
+
+ /* 1 is fine
+ 0 is "not successful but was shut down controlled"
+ <0 is "handshake was not successful, because a fatal error occurred" */
+ if(1 != err) {
+ int detail = SSL_get_error(connssl->handle, err);
+
+ if(SSL_ERROR_WANT_READ == detail) {
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_OK;
+ }
+ else if(SSL_ERROR_WANT_WRITE == detail) {
+ connssl->connecting_state = ssl_connect_2_writing;
+ return CURLE_OK;
+ }
+ else {
+ /* untreated error */
+ unsigned long errdetail;
+ char error_buffer[256]; /* OpenSSL documents that this must be at least
+ 256 bytes long. */
+ CURLcode rc;
+ const char *cert_problem = NULL;
+
+ connssl->connecting_state = ssl_connect_2; /* the connection failed,
+ we're not waiting for
+ anything else. */
+
+ errdetail = ERR_get_error(); /* Gets the earliest error code from the
+ thread's error queue and removes the
+ entry. */
+
+ switch(errdetail) {
+ case 0x1407E086:
+ /* 1407E086:
+ SSL routines:
+ SSL2_SET_CERTIFICATE:
+ certificate verify failed */
+ /* fall-through */
+ case 0x14090086:
+ /* 14090086:
+ SSL routines:
+ SSL3_GET_SERVER_CERTIFICATE:
+ certificate verify failed */
+ cert_problem = "SSL certificate problem, verify that the CA cert is"
+ " OK. Details:\n";
+ rc = CURLE_SSL_CACERT;
+ break;
+ default:
+ rc = CURLE_SSL_CONNECT_ERROR;
+ break;
+ }
+
+ /* detail is already set to the SSL error above */
+
+ /* If we e.g. use SSLv2 request-method and the server doesn't like us
+ * (RST connection etc.), OpenSSL gives no explanation whatsoever and
+ * the SO_ERROR is also lost.
+ */
+ if (CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) {
+ failf(data, "Unknown SSL protocol error in connection to %s:%d ",
+ conn->host.name, conn->port);
+ return rc;
+ }
+ /* Could be a CERT problem */
+
+ SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
+ failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
+ return rc;
+ }
+ }
+ else {
+ /* we have been connected fine, we're not waiting for anything else. */
+ connssl->connecting_state = ssl_connect_3;
+
+ /* Informational message */
+ infof (data, "SSL connection using %s\n",
+ SSL_get_cipher(connssl->handle));
+
+ return CURLE_OK;
+ }
+}
+
+static CURLcode
+Curl_ossl_connect_step3(struct connectdata *conn,
+ int sockindex)
+{
+ CURLcode retcode = CURLE_OK;
+ char * str;
+ long lerr;
+ ASN1_TIME *certdate;
+ void *ssl_sessionid=NULL;
+ struct SessionHandle *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+ curlassert(ssl_connect_3 == connssl->connecting_state);
+
+ if(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
+ /* Since this is not a cached session ID, then we want to stach this one
+ in the cache! */
+ SSL_SESSION *our_ssl_sessionid;
+#ifdef HAVE_SSL_GET1_SESSION
+ our_ssl_sessionid = SSL_get1_session(connssl->handle);
+
+ /* SSL_get1_session() will increment the reference
+ count and the session will stay in memory until explicitly freed with
+ SSL_SESSION_free(3), regardless of its state.
+ This function was introduced in openssl 0.9.5a. */
+#else
+ our_ssl_sessionid = SSL_get_session(connssl->handle);
+
+ /* if SSL_get1_session() is unavailable, use SSL_get_session().
+ This is an inferior option because the session can be flushed
+ at any time by openssl. It is included only so curl compiles
+ under versions of openssl < 0.9.5a.
+
+ WARNING: How curl behaves if it's session is flushed is
+ untested.
+ */
+#endif
+ retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
+ 0 /* unknown size */);
+ if(retcode) {
+ failf(data, "failed to store ssl session");
+ return retcode;
+ }
+ }
+
+
+ /* 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
+ */
+
+ connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
+ if(!connssl->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(connssl->server_cert),
+ NULL, 0);
+ if(!str) {
+ failf(data, "SSL: couldn't get X509-subject!");
+ X509_free(connssl->server_cert);
+ connssl->server_cert = NULL;
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ infof(data, "\t subject: %s\n", str);
+ CRYPTO_free(str);
+
+ certdate = X509_get_notBefore(connssl->server_cert);
+ Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate);
+
+ certdate = X509_get_notAfter(connssl->server_cert);
+ Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate);
+
+ if(data->set.ssl.verifyhost) {
+ retcode = verifyhost(conn, connssl->server_cert);
+ if(retcode) {
+ X509_free(connssl->server_cert);
+ connssl->server_cert = NULL;
+ return retcode;
+ }
+ }
+
+ str = X509_NAME_oneline(X509_get_issuer_name(connssl->server_cert),
+ NULL, 0);
+ if(!str) {
+ failf(data, "SSL: couldn't get X509-issuer name!");
+ retcode = CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ infof(data, "\t issuer: %s\n", str);
+ CRYPTO_free(str);
+
+ /* We could do all sorts of certificate verification stuff here before
+ deallocating the certificate. */
+
+ lerr = data->set.ssl.certverifyresult=
+ SSL_get_verify_result(connssl->handle);
+ if(data->set.ssl.certverifyresult != X509_V_OK) {
+ if(data->set.ssl.verifypeer) {
+ /* We probably never reach this, because SSL_connect() will fail
+ and we return earlyer if verifypeer is set? */
+ failf(data, "SSL certificate verify result: %s (%ld)",
+ X509_verify_cert_error_string(lerr), lerr);
+ retcode = CURLE_SSL_PEER_CERTIFICATE;
+ }
+ else
+ infof(data, "SSL certificate verify result: %s (%ld),"
+ " continuing anyway.\n",
+ X509_verify_cert_error_string(lerr), lerr);
+ }
+ else
+ infof(data, "SSL certificate verify ok.\n");
+ }
+
+ X509_free(connssl->server_cert);
+ connssl->server_cert = NULL;
+ connssl->connecting_state = ssl_connect_done;
+ return retcode;
+}
+
+static CURLcode
+Curl_ossl_connect_common(struct connectdata *conn,
+ int sockindex,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode retcode;
+ struct SessionHandle *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ curl_socket_t sockfd = conn->sock[sockindex];
+ long timeout_ms;
+
+ if (ssl_connect_1==connssl->connecting_state) {
+ retcode = Curl_ossl_connect_step1(conn, sockindex);
+ if (retcode)
+ return retcode;
+ }
+
+ timeout_ms = 0;
+ while (ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state) {
+
+ /* if ssl is expecting something, check if it's available. */
+ if (connssl->connecting_state == ssl_connect_2_reading
+ || connssl->connecting_state == ssl_connect_2_writing) {
+
+ int writefd = ssl_connect_2_writing==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+ int readfd = ssl_connect_2_reading==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+ while(1) {
+ int what = Curl_select(readfd, writefd, nonblocking?0:(int)timeout_ms);
+ if(what > 0)
+ /* readable or writable, go loop in the outer loop */
+ break;
+ else if(0 == what) {
+ if (nonblocking) {
+ *done = FALSE;
+ return CURLE_OK;
+ }
+ else {
+ /* timeout */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+ else {
+ /* anything that gets here is fatally bad */
+ failf(data, "select on SSL socket, errno: %d", Curl_sockerrno());
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ } /* while()-loop for the select() */
+ }
+
+ /* get the timeout from step2 to avoid computing it twice. */
+ retcode = Curl_ossl_connect_step2(conn, sockindex, &timeout_ms);
+ if (retcode)
+ return retcode;
+
+ } /* repeat step2 until all transactions are done. */
+
+
+ if (ssl_connect_3==connssl->connecting_state) {
+ retcode = Curl_ossl_connect_step3(conn, sockindex);
+ if (retcode)
+ return retcode;
+ }
+
+ if (ssl_connect_done==connssl->connecting_state) {
+ *done = TRUE;
+ }
+ else {
+ *done = FALSE;
+ }
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+CURLcode
+Curl_ossl_connect_nonblocking(struct connectdata *conn,
+ int sockindex,
+ bool *done)
+{
+ return Curl_ossl_connect_common(conn, sockindex, TRUE, done);
+}
+
+CURLcode
+Curl_ossl_connect(struct connectdata *conn,
+ int sockindex)
+{
+ CURLcode retcode;
+ bool done = FALSE;
+
+ retcode = Curl_ossl_connect_common(conn, sockindex, FALSE, &done);
+ if (retcode)
+ return retcode;
+
+ curlassert(done);
+
+ return CURLE_OK;
+}
+
+/* return number of sent (non-SSL) bytes */
+ssize_t Curl_ossl_send(struct connectdata *conn,
+ int sockindex,
+ void *mem,
+ size_t len)
+{
+ /* SSL_write() is said to return 'int' while write() and send() returns
+ 'size_t' */
+ int err;
+ char error_buffer[120]; /* OpenSSL documents that this must be at least 120
+ bytes long. */
+ unsigned long sslerror;
+ int rc = SSL_write(conn->ssl[sockindex].handle, mem, (int)len);
+
+ if(rc < 0) {
+ err = SSL_get_error(conn->ssl[sockindex].handle, rc);
+
+ switch(err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* The operation did not complete; the same TLS/SSL I/O function
+ should be called again later. This is basicly an EWOULDBLOCK
+ equivalent. */
+ return 0;
+ case SSL_ERROR_SYSCALL:
+ failf(conn->data, "SSL_write() returned SYSCALL, errno = %d\n",
+ Curl_sockerrno());
+ return -1;
+ case SSL_ERROR_SSL:
+ /* A failure in the SSL library occurred, usually a protocol error.
+ The OpenSSL error queue contains more information on the error. */
+ sslerror = ERR_get_error();
+ failf(conn->data, "SSL_write() error: %s\n",
+ ERR_error_string(sslerror, error_buffer));
+ return -1;
+ }
+ /* a true error */
+ failf(conn->data, "SSL_write() return error %d\n", err);
+ return -1;
+ }
+ return (ssize_t)rc; /* number of bytes */
+}
+
+/*
+ * If the read would block we return -1 and set 'wouldblock' to TRUE.
+ * Otherwise we return the amount of data read. Other errors should return -1
+ * and set 'wouldblock' to FALSE.
+ */
+ssize_t Curl_ossl_recv(struct connectdata *conn, /* connection data */
+ int num, /* socketindex */
+ char *buf, /* store read data here */
+ size_t buffersize, /* max amount to read */
+ bool *wouldblock)
+{
+ char error_buffer[120]; /* OpenSSL documents that this must be at
+ least 120 bytes long. */
+ unsigned long sslerror;
+ ssize_t nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf,
+ (int)buffersize);
+ *wouldblock = FALSE;
+ if(nread < 0) {
+ /* failed SSL_read */
+ int err = SSL_get_error(conn->ssl[num].handle, (int)nread);
+
+ switch(err) {
+ case SSL_ERROR_NONE: /* this is not an error */
+ case SSL_ERROR_ZERO_RETURN: /* no more data */
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* there's data pending, re-invoke SSL_read() */
+ *wouldblock = TRUE;
+ return -1; /* basically EWOULDBLOCK */
+ default:
+ /* openssl/ssl.h says "look at error stack/return value/errno" */
+ sslerror = ERR_get_error();
+ failf(conn->data, "SSL read: %s, errno %d",
+ ERR_error_string(sslerror, error_buffer),
+ Curl_sockerrno() );
+ return -1;
+ }
+ }
+ return nread;
+}
+
+size_t Curl_ossl_version(char *buffer, size_t size)
+{
+#ifdef YASSL_VERSION
+ /* yassl provides an OpenSSL API compatiblity layer so it looks identical
+ to OpenSSL in all other aspects */
+ return snprintf(buffer, size, " yassl/%s", YASSL_VERSION);
+#else /* YASSL_VERSION */
+
+#if (SSLEAY_VERSION_NUMBER >= 0x905000)
+ {
+ char sub[2];
+ unsigned long ssleay_value;
+ sub[1]='\0';
+ ssleay_value=SSLeay();
+ if(ssleay_value < 0x906000) {
+ ssleay_value=SSLEAY_VERSION_NUMBER;
+ sub[0]='\0';
+ }
+ else {
+ if(ssleay_value&0xff0) {
+ sub[0]=(char)(((ssleay_value>>4)&0xff) + 'a' -1);
+ }
+ else
+ sub[0]='\0';
+ }
+
+ return snprintf(buffer, size, " OpenSSL/%lx.%lx.%lx%s",
+ (ssleay_value>>28)&0xf,
+ (ssleay_value>>20)&0xff,
+ (ssleay_value>>12)&0xff,
+ sub);
+ }
+
+#else /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
+
+#if (SSLEAY_VERSION_NUMBER >= 0x900000)
+ return snprintf(buffer, size, " OpenSSL/%lx.%lx.%lx",
+ (SSLEAY_VERSION_NUMBER>>28)&0xff,
+ (SSLEAY_VERSION_NUMBER>>20)&0xff,
+ (SSLEAY_VERSION_NUMBER>>12)&0xf);
+
+#else /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
+ {
+ char sub[2];
+ sub[1]='\0';
+ if(SSLEAY_VERSION_NUMBER&0x0f) {
+ sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1;
+ }
+ else
+ sub[0]='\0';
+
+ return snprintf(buffer, size, " SSL/%x.%x.%x%s",
+ (SSLEAY_VERSION_NUMBER>>12)&0xff,
+ (SSLEAY_VERSION_NUMBER>>8)&0xf,
+ (SSLEAY_VERSION_NUMBER>>4)&0xf, sub);
+ }
+#endif /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
+#endif /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
+
+#endif /* YASSL_VERSION */
+}
+#endif /* USE_SSLEAY */
diff --git a/Utilities/cmcurl/ssluse.h b/Utilities/cmcurl/ssluse.h
new file mode 100644
index 000000000..5bb7090c5
--- /dev/null
+++ b/Utilities/cmcurl/ssluse.h
@@ -0,0 +1,71 @@
+#ifndef __SSLUSE_H
+#define __SSLUSE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ * This header should only be needed to get included by sslgen.c and ssluse.c
+ */
+
+#include "urldata.h"
+CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex);
+CURLcode Curl_ossl_connect_nonblocking(struct connectdata *conn,
+ int sockindex,
+ bool *done);
+void Curl_ossl_close(struct connectdata *conn); /* close a SSL connection */
+/* tell OpenSSL to close down all open information regarding connections (and
+ thus session ID caching etc) */
+int Curl_ossl_close_all(struct SessionHandle *data);
+/* Sets an OpenSSL engine */
+CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine);
+
+/* function provided for the generic SSL-layer, called when a session id
+ should be freed */
+void Curl_ossl_session_free(void *ptr);
+
+/* Sets engine as default for all SSL operations */
+CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data);
+
+/* Build list of OpenSSL engines */
+struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data);
+
+int Curl_ossl_init(void);
+void Curl_ossl_cleanup(void);
+
+ssize_t Curl_ossl_send(struct connectdata *conn,
+ int sockindex,
+ void *mem,
+ size_t len);
+ssize_t Curl_ossl_recv(struct connectdata *conn, /* connection data */
+ int num, /* socketindex */
+ char *buf, /* store read data here */
+ size_t buffersize, /* max amount to read */
+ bool *wouldblock);
+
+size_t Curl_ossl_version(char *buffer, size_t size);
+int Curl_ossl_check_cxn(struct connectdata *cxn);
+int Curl_ossl_seed(struct SessionHandle *data);
+
+int Curl_ossl_shutdown(struct connectdata *conn, int sockindex);
+
+#endif
diff --git a/Utilities/cmcurl/strdup.c b/Utilities/cmcurl/strdup.c
new file mode 100644
index 000000000..e16e08a72
--- /dev/null
+++ b/Utilities/cmcurl/strdup.c
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+#include "strdup.h"
+
+#ifndef HAVE_STRDUP
+char *curlx_strdup(const char *str)
+{
+ int len;
+ char *newstr;
+
+ if (!str)
+ return (char *)NULL;
+
+ len = strlen(str);
+ newstr = (char *) malloc((len+1)*sizeof(char));
+ if (!newstr)
+ return (char *)NULL;
+
+ strcpy(newstr,str);
+
+ return newstr;
+
+}
+#endif
diff --git a/Utilities/cmcurl/strdup.h b/Utilities/cmcurl/strdup.h
new file mode 100644
index 000000000..3206db3cf
--- /dev/null
+++ b/Utilities/cmcurl/strdup.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#ifndef _CURL_STRDUP_H
+#define _CURL_STRDUP_H
+
+#include "setup.h"
+
+#ifndef HAVE_STRDUP
+extern char *curlx_strdup(const char *str);
+#endif
+
+#endif
+
diff --git a/Utilities/cmcurl/strequal.c b/Utilities/cmcurl/strequal.c
new file mode 100644
index 000000000..76ad5241f
--- /dev/null
+++ b/Utilities/cmcurl/strequal.c
@@ -0,0 +1,143 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <ctype.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#include "strequal.h"
+
+#if defined(HAVE_STRCASECMP) && defined(__STRICT_ANSI__)
+/* 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);
+#endif
+
+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
+}
+
+/*
+ * Curl_strcasestr() finds the first occurrence of the substring needle in the
+ * string haystack. The terminating `\0' characters are not compared. The
+ * matching is done CASE INSENSITIVE, which thus is the difference between
+ * this and strstr().
+ */
+char *Curl_strcasestr(const char *haystack, const char *needle)
+{
+ size_t nlen = strlen(needle);
+ size_t hlen = strlen(haystack);
+
+ while(hlen-- >= nlen) {
+ if(curl_strnequal(haystack, needle, nlen))
+ return (char *)haystack;
+ haystack++;
+ }
+ return NULL;
+}
+
+#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
diff --git a/Utilities/cmcurl/strequal.h b/Utilities/cmcurl/strequal.h
new file mode 100644
index 000000000..b3caa7345
--- /dev/null
+++ b/Utilities/cmcurl/strequal.h
@@ -0,0 +1,43 @@
+#ifndef __STREQUAL_H
+#define __STREQUAL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include <curl/curl.h>
+
+#define strequal(a,b) curl_strequal(a,b)
+#define strnequal(a,b,c) curl_strnequal(a,b,c)
+
+/* checkprefix() is a shorter version of the above, used when the first
+ argument is zero-byte terminated */
+#define checkprefix(a,b) strnequal(a,b,strlen(a))
+
+/* case insensitive strstr() */
+char *Curl_strcasestr(const char *haystack, const char *needle);
+
+#ifndef HAVE_STRLCAT
+#define strlcat(x,y,z) Curl_strlcat(x,y,z)
+#endif
+size_t strlcat(char *dst, const char *src, size_t siz);
+
+#endif
diff --git a/Utilities/cmcurl/strerror.c b/Utilities/cmcurl/strerror.c
new file mode 100644
index 000000000..6304fe89d
--- /dev/null
+++ b/Utilities/cmcurl/strerror.c
@@ -0,0 +1,748 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2004 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifdef HAVE_STRERROR_R
+#if !defined(HAVE_POSIX_STRERROR_R) && !defined(HAVE_GLIBC_STRERROR_R)
+#error "you MUST have either POSIX or glibc strerror_r if strerror_r is found"
+#endif /* !POSIX && !glibc */
+#endif /* HAVE_STRERROR_R */
+
+#include <curl/curl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef USE_LIBIDN
+#include <idna.h>
+#endif
+
+#include "strerror.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#if defined(HAVE_STRERROR_R) && defined(HAVE_NO_STRERROR_R_DECL)
+#ifdef HAVE_POSIX_STRERROR_R
+/* seen on AIX 5100-02 gcc 2.9 */
+extern int strerror_r(int errnum, char *strerrbuf, size_t buflen);
+#else
+extern char *strerror_r(int errnum, char *buf, size_t buflen);
+#endif
+#endif
+
+const char *
+curl_easy_strerror(CURLcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ switch (error) {
+ case CURLE_OK:
+ return "no error";
+
+ case CURLE_UNSUPPORTED_PROTOCOL:
+ return "unsupported protocol";
+
+ case CURLE_FAILED_INIT:
+ return "failed init";
+
+ case CURLE_URL_MALFORMAT:
+ return "URL using bad/illegal format or missing URL";
+
+ case CURLE_COULDNT_RESOLVE_PROXY:
+ return "couldn't resolve proxy name";
+
+ case CURLE_COULDNT_RESOLVE_HOST:
+ return "couldn't resolve host name";
+
+ case CURLE_COULDNT_CONNECT:
+ return "couldn't connect to server";
+
+ case CURLE_FTP_WEIRD_SERVER_REPLY:
+ return "FTP: weird server reply";
+
+ case CURLE_FTP_ACCESS_DENIED:
+ return "FTP: access denied";
+
+ case CURLE_FTP_WEIRD_PASS_REPLY:
+ return "FTP: unknown PASS reply";
+
+ case CURLE_FTP_WEIRD_USER_REPLY:
+ return "FTP: unknown USER reply";
+
+ case CURLE_FTP_WEIRD_PASV_REPLY:
+ return "FTP: unknown PASV reply";
+
+ case CURLE_FTP_WEIRD_227_FORMAT:
+ return "FTP: unknown 227 response format";
+
+ case CURLE_FTP_CANT_GET_HOST:
+ return "FTP: can't figure out the host in the PASV response";
+
+ case CURLE_FTP_CANT_RECONNECT:
+ return "FTP: can't connect to server the response code is unknown";
+
+ case CURLE_FTP_COULDNT_SET_BINARY:
+ return "FTP: couldn't set binary mode";
+
+ case CURLE_PARTIAL_FILE:
+ return "Transferred a partial file";
+
+ case CURLE_FTP_COULDNT_RETR_FILE:
+ return "FTP: couldn't retrieve (RETR failed) the specified file";
+
+ case CURLE_FTP_WRITE_ERROR:
+ return "FTP: the post-transfer acknowledge response was not OK";
+
+ case CURLE_FTP_QUOTE_ERROR:
+ return "FTP: a quote command returned error";
+
+ case CURLE_HTTP_RETURNED_ERROR:
+ return "HTTP response code said error";
+
+ case CURLE_WRITE_ERROR:
+ return "failed writing received data to disk/application";
+
+ case CURLE_FTP_COULDNT_STOR_FILE:
+ return "failed FTP upload (the STOR command)";
+
+ case CURLE_READ_ERROR:
+ return "failed to open/read local data from file/application";
+
+ case CURLE_OUT_OF_MEMORY:
+#ifdef CURL_DOES_CONVERSIONS
+ return "conversion failed -or- out of memory";
+#else
+ return "out of memory";
+#endif /* CURL_DOES_CONVERSIONS */
+
+ case CURLE_OPERATION_TIMEOUTED:
+ return "a timeout was reached";
+
+ case CURLE_FTP_COULDNT_SET_ASCII:
+ return "FTP could not set ASCII mode (TYPE A)";
+
+ case CURLE_FTP_PORT_FAILED:
+ return "FTP command PORT failed";
+
+ case CURLE_FTP_COULDNT_USE_REST:
+ return "FTP command REST failed";
+
+ case CURLE_FTP_COULDNT_GET_SIZE:
+ return "FTP command SIZE failed";
+
+ case CURLE_HTTP_RANGE_ERROR:
+ return "a range was requested but the server did not deliver it";
+
+ case CURLE_HTTP_POST_ERROR:
+ return "internal problem setting up the POST";
+
+ case CURLE_SSL_CONNECT_ERROR:
+ return "SSL connect error";
+
+ case CURLE_BAD_DOWNLOAD_RESUME:
+ return "couldn't resume download";
+
+ case CURLE_FILE_COULDNT_READ_FILE:
+ return "couldn't read a file:// file";
+
+ case CURLE_LDAP_CANNOT_BIND:
+ return "LDAP: cannot bind";
+
+ case CURLE_LDAP_SEARCH_FAILED:
+ return "LDAP: search failed";
+
+ case CURLE_LIBRARY_NOT_FOUND:
+ return "a required shared library was not found";
+
+ case CURLE_FUNCTION_NOT_FOUND:
+ return "a required function in the shared library was not found";
+
+ case CURLE_ABORTED_BY_CALLBACK:
+ return "the operation was aborted by an application callback";
+
+ case CURLE_BAD_FUNCTION_ARGUMENT:
+ return "a libcurl function was given a bad argument";
+
+ case CURLE_INTERFACE_FAILED:
+ return "failed binding local connection end";
+
+ case CURLE_TOO_MANY_REDIRECTS :
+ return "number of redirects hit maximum amount";
+
+ case CURLE_UNKNOWN_TELNET_OPTION:
+ return "User specified an unknown option";
+
+ case CURLE_TELNET_OPTION_SYNTAX :
+ return "Malformed telnet option";
+
+ case CURLE_SSL_PEER_CERTIFICATE:
+ return "SSL peer certificate was not ok";
+
+ case CURLE_GOT_NOTHING:
+ return "server returned nothing (no headers, no data)";
+
+ case CURLE_SSL_ENGINE_NOTFOUND:
+ return "SSL crypto engine not found";
+
+ case CURLE_SSL_ENGINE_SETFAILED:
+ return "can not set SSL crypto engine as default";
+
+ case CURLE_SSL_ENGINE_INITFAILED:
+ return "failed to initialise SSL crypto engine";
+
+ case CURLE_SEND_ERROR:
+ return "failed sending data to the peer";
+
+ case CURLE_RECV_ERROR:
+ return "failure when receiving data from the peer";
+
+ case CURLE_SHARE_IN_USE:
+ return "share is already in use";
+
+ case CURLE_SSL_CERTPROBLEM:
+ return "problem with the local SSL certificate";
+
+ case CURLE_SSL_CIPHER:
+ return "couldn't use specified SSL cipher";
+
+ case CURLE_SSL_CACERT:
+ return "peer certificate cannot be authenticated with known CA certificates";
+
+ case CURLE_SSL_CACERT_BADFILE:
+ return "problem with the SSL CA cert (path? access rights?)";
+
+ case CURLE_BAD_CONTENT_ENCODING:
+ return "Unrecognized HTTP Content-Encoding";
+
+ case CURLE_LDAP_INVALID_URL:
+ return "Invalid LDAP URL";
+
+ case CURLE_FILESIZE_EXCEEDED:
+ return "Maximum file size exceeded";
+
+ case CURLE_FTP_SSL_FAILED:
+ return "Requested FTP SSL level failed";
+
+ case CURLE_SSL_SHUTDOWN_FAILED:
+ return "Failed to shut down the SSL connection";
+
+ case CURLE_SEND_FAIL_REWIND:
+ return "Send failed since rewinding of the data stream failed";
+
+ case CURLE_LOGIN_DENIED:
+ return "FTP: login denied";
+
+ case CURLE_TFTP_NOTFOUND:
+ return "TFTP: File Not Found";
+
+ case CURLE_TFTP_PERM:
+ return "TFTP: Access Violation";
+
+ case CURLE_TFTP_DISKFULL:
+ return "TFTP: Disk full or allocation exceeded";
+
+ case CURLE_TFTP_ILLEGAL:
+ return "TFTP: Illegal operation";
+
+ case CURLE_TFTP_UNKNOWNID:
+ return "TFTP: Unknown transfer ID";
+
+ case CURLE_TFTP_EXISTS:
+ return "TFTP: File already exists";
+
+ case CURLE_TFTP_NOSUCHUSER:
+ return "TFTP: No such user";
+
+ case CURLE_CONV_FAILED:
+ return "conversion failed";
+
+ case CURLE_CONV_REQD:
+ return "caller must register CURLOPT_CONV_ callback options";
+
+ case CURLE_REMOTE_FILE_NOT_FOUND:
+ return "Remote file not found";
+
+ case CURLE_SSH:
+ return "Error in the SSH layer";
+
+ /* error codes not used by current libcurl */
+ case CURLE_URL_MALFORMAT_USER:
+ case CURLE_FTP_USER_PASSWORD_INCORRECT:
+ case CURLE_MALFORMAT_USER:
+ case CURLE_BAD_CALLING_ORDER:
+ case CURLE_BAD_PASSWORD_ENTERED:
+ case CURLE_OBSOLETE:
+ case CURL_LAST:
+ break;
+ }
+ /*
+ * By using a switch, gcc -Wall will complain about enum values
+ * which do not appear, helping keep this function up-to-date.
+ * By using gcc -Wall -Werror, you can't forget.
+ *
+ * A table would not have the same benefit. Most compilers will
+ * generate code very similar to a table in any case, so there
+ * is little performance gain from a table. And something is broken
+ * for the user's application, anyways, so does it matter how fast
+ * it _doesn't_ work?
+ *
+ * The line number for the error will be near this comment, which
+ * is why it is here, and not at the start of the switch.
+ */
+ return "unknown error";
+#else
+ if (error == CURLE_OK)
+ return "no error";
+ else
+ return "error";
+#endif
+}
+
+const char *
+curl_multi_strerror(CURLMcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ switch (error) {
+ case CURLM_CALL_MULTI_PERFORM:
+ return "please call curl_multi_perform() soon";
+
+ case CURLM_OK:
+ return "no error";
+
+ case CURLM_BAD_HANDLE:
+ return "invalid multi handle";
+
+ case CURLM_BAD_EASY_HANDLE:
+ return "invalid easy handle";
+
+ case CURLM_OUT_OF_MEMORY:
+ return "out of memory";
+
+ case CURLM_INTERNAL_ERROR:
+ return "internal error";
+
+ case CURLM_BAD_SOCKET:
+ return "invalid socket argument";
+
+ case CURLM_UNKNOWN_OPTION:
+ return "unknown option";
+
+ case CURLM_LAST:
+ break;
+ }
+
+ return "unknown error";
+#else
+ if (error == CURLM_OK)
+ return "no error";
+ else
+ return "error";
+#endif
+}
+
+const char *
+curl_share_strerror(CURLSHcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ switch (error) {
+ case CURLSHE_OK:
+ return "no error";
+
+ case CURLSHE_BAD_OPTION:
+ return "unknown share option";
+
+ case CURLSHE_IN_USE:
+ return "share currently in use";
+
+ case CURLSHE_INVALID:
+ return "invalid share handle";
+
+ case CURLSHE_NOMEM:
+ return "out of memory";
+
+ case CURLSHE_LAST:
+ break;
+ }
+
+ return "CURLSH unknown";
+#else
+ if (error == CURLSHE_OK)
+ return "no error";
+ else
+ return "error";
+#endif
+}
+
+#ifdef USE_WINSOCK
+
+/* This function handles most / all (?) Winsock errors cURL is able to produce.
+ */
+static const char *
+get_winsock_error (int err, char *buf, size_t len)
+{
+ const char *p;
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ switch (err) {
+ case WSAEINTR:
+ p = "Call interrupted.";
+ break;
+ case WSAEBADF:
+ p = "Bad file";
+ break;
+ case WSAEACCES:
+ p = "Bad access";
+ break;
+ case WSAEFAULT:
+ p = "Bad argument";
+ break;
+ case WSAEINVAL:
+ p = "Invalid arguments";
+ break;
+ case WSAEMFILE:
+ p = "Out of file descriptors";
+ break;
+ case WSAEWOULDBLOCK:
+ p = "Call would block";
+ break;
+ case WSAEINPROGRESS:
+ case WSAEALREADY:
+ p = "Blocking call in progress";
+ break;
+ case WSAENOTSOCK:
+ p = "Descriptor is not a socket.";
+ break;
+ case WSAEDESTADDRREQ:
+ p = "Need destination address";
+ break;
+ case WSAEMSGSIZE:
+ p = "Bad message size";
+ break;
+ case WSAEPROTOTYPE:
+ p = "Bad protocol";
+ break;
+ case WSAENOPROTOOPT:
+ p = "Protocol option is unsupported";
+ break;
+ case WSAEPROTONOSUPPORT:
+ p = "Protocol is unsupported";
+ break;
+ case WSAESOCKTNOSUPPORT:
+ p = "Socket is unsupported";
+ break;
+ case WSAEOPNOTSUPP:
+ p = "Operation not supported";
+ break;
+ case WSAEAFNOSUPPORT:
+ p = "Address family not supported";
+ break;
+ case WSAEPFNOSUPPORT:
+ p = "Protocol family not supported";
+ break;
+ case WSAEADDRINUSE:
+ p = "Address already in use";
+ break;
+ case WSAEADDRNOTAVAIL:
+ p = "Address not available";
+ break;
+ case WSAENETDOWN:
+ p = "Network down";
+ break;
+ case WSAENETUNREACH:
+ p = "Network unreachable";
+ break;
+ case WSAENETRESET:
+ p = "Network has been reset";
+ break;
+ case WSAECONNABORTED:
+ p = "Connection was aborted";
+ break;
+ case WSAECONNRESET:
+ p = "Connection was reset";
+ break;
+ case WSAENOBUFS:
+ p = "No buffer space";
+ break;
+ case WSAEISCONN:
+ p = "Socket is already connected";
+ break;
+ case WSAENOTCONN:
+ p = "Socket is not connected";
+ break;
+ case WSAESHUTDOWN:
+ p = "Socket has been shut down";
+ break;
+ case WSAETOOMANYREFS:
+ p = "Too many references";
+ break;
+ case WSAETIMEDOUT:
+ p = "Timed out";
+ break;
+ case WSAECONNREFUSED:
+ p = "Connection refused";
+ break;
+ case WSAELOOP:
+ p = "Loop??";
+ break;
+ case WSAENAMETOOLONG:
+ p = "Name too long";
+ break;
+ case WSAEHOSTDOWN:
+ p = "Host down";
+ break;
+ case WSAEHOSTUNREACH:
+ p = "Host unreachable";
+ break;
+ case WSAENOTEMPTY:
+ p = "Not empty";
+ break;
+ case WSAEPROCLIM:
+ p = "Process limit reached";
+ break;
+ case WSAEUSERS:
+ p = "Too many users";
+ break;
+ case WSAEDQUOT:
+ p = "Bad quota";
+ break;
+ case WSAESTALE:
+ p = "Something is stale";
+ break;
+ case WSAEREMOTE:
+ p = "Remote error";
+ break;
+#ifdef WSAEDISCON /* missing in SalfordC! */
+ case WSAEDISCON:
+ p = "Disconnected";
+ break;
+#endif
+ /* Extended Winsock errors */
+ case WSASYSNOTREADY:
+ p = "Winsock library is not ready";
+ break;
+ case WSANOTINITIALISED:
+ p = "Winsock library not initialised";
+ break;
+ case WSAVERNOTSUPPORTED:
+ p = "Winsock version not supported.";
+ break;
+
+ /* getXbyY() errors (already handled in herrmsg):
+ * Authoritative Answer: Host not found */
+ case WSAHOST_NOT_FOUND:
+ p = "Host not found";
+ break;
+
+ /* Non-Authoritative: Host not found, or SERVERFAIL */
+ case WSATRY_AGAIN:
+ p = "Host not found, try again";
+ break;
+
+ /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
+ case WSANO_RECOVERY:
+ p = "Unrecoverable error in call to nameserver";
+ break;
+
+ /* Valid name, no data record of requested type */
+ case WSANO_DATA:
+ p = "No data record of requested type";
+ break;
+
+ default:
+ return NULL;
+ }
+#else
+ if (error == CURLE_OK)
+ return NULL;
+ else
+ p = "error";
+#endif
+ strncpy (buf, p, len);
+ buf [len-1] = '\0';
+ return buf;
+}
+#endif /* USE_WINSOCK */
+
+/*
+ * Our thread-safe and smart strerror() replacement.
+ *
+ * The 'err' argument passed in to this function MUST be a true errno number
+ * as reported on this system. We do no range checking on the number before
+ * we pass it to the "number-to-message" convertion function and there might
+ * be systems that don't do proper range checking in there themselves.
+ *
+ * We don't do range checking (on systems other than Windows) since there is
+ * no good reliable and portable way to do it.
+ */
+const char *Curl_strerror(struct connectdata *conn, int err)
+{
+ char *buf, *p;
+ size_t max;
+
+ curlassert(conn);
+ curlassert(err >= 0);
+
+ buf = conn->syserr_buf;
+ max = sizeof(conn->syserr_buf)-1;
+ *buf = '\0';
+
+#ifdef USE_WINSOCK
+
+#ifdef _WIN32_WCE
+ buf[0]=0;
+ {
+ wchar_t wbuf[256];
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
+ LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL);
+ wcstombs(buf,wbuf,max);
+ }
+
+#else
+
+ /* 'sys_nerr' is the maximum errno number, it is not widely portable */
+ if (err >= 0 && err < sys_nerr)
+ strncpy(buf, strerror(err), max);
+ else {
+ if (!get_winsock_error(err, buf, max) &&
+ !FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
+ LANG_NEUTRAL, buf, (DWORD)max, NULL))
+ snprintf(buf, max, "Unknown error %d (%#x)", err, err);
+ }
+#endif
+#else /* not USE_WINSOCK coming up */
+
+ /* These should be atomic and hopefully thread-safe */
+#ifdef HAVE_STRERROR_R
+ /* There are two different APIs for strerror_r(). The POSIX and the GLIBC
+ versions. */
+#ifdef HAVE_POSIX_STRERROR_R
+ strerror_r(err, buf, max);
+ /* this may set errno to ERANGE if insufficient storage was supplied via
+ 'strerrbuf' and 'buflen' to contain the generated message string, or
+ EINVAL if the value of 'errnum' is not a valid error number.*/
+#else
+ {
+ /* HAVE_GLIBC_STRERROR_R */
+ char buffer[256];
+ char *msg = strerror_r(err, buffer, sizeof(buffer));
+ /* this version of strerror_r() only *might* use the buffer we pass to
+ the function, but it always returns the error message as a pointer,
+ so we must copy that string unconditionally (if non-NULL) */
+ if(msg)
+ strncpy(buf, msg, max);
+ else
+ snprintf(buf, max, "Unknown error %d", err);
+ }
+#endif /* end of HAVE_GLIBC_STRERROR_R */
+#else /* HAVE_STRERROR_R */
+ strncpy(buf, strerror(err), max);
+#endif /* end of HAVE_STRERROR_R */
+#endif /* end of ! USE_WINSOCK */
+
+ buf[max] = '\0'; /* make sure the string is zero terminated */
+
+ /* strip trailing '\r\n' or '\n'. */
+ if ((p = strrchr(buf,'\n')) != NULL && (p - buf) >= 2)
+ *p = '\0';
+ if ((p = strrchr(buf,'\r')) != NULL && (p - buf) >= 1)
+ *p = '\0';
+ return buf;
+}
+
+#ifdef USE_LIBIDN
+/*
+ * Return error-string for libidn status as returned from idna_to_ascii_lz().
+ */
+const char *Curl_idn_strerror (struct connectdata *conn, int err)
+{
+#ifdef HAVE_IDNA_STRERROR
+ (void)conn;
+ return idna_strerror((Idna_rc) err);
+#else
+ const char *str;
+ char *buf;
+ size_t max;
+
+ curlassert(conn);
+
+ buf = conn->syserr_buf;
+ max = sizeof(conn->syserr_buf)-1;
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ switch ((Idna_rc)err) {
+ case IDNA_SUCCESS:
+ str = "No error";
+ break;
+ case IDNA_STRINGPREP_ERROR:
+ str = "Error in string preparation";
+ break;
+ case IDNA_PUNYCODE_ERROR:
+ str = "Error in Punycode operation";
+ break;
+ case IDNA_CONTAINS_NON_LDH:
+ str = "Illegal ASCII characters";
+ break;
+ case IDNA_CONTAINS_MINUS:
+ str = "Contains minus";
+ break;
+ case IDNA_INVALID_LENGTH:
+ str = "Invalid output length";
+ break;
+ case IDNA_NO_ACE_PREFIX:
+ str = "No ACE prefix (\"xn--\")";
+ break;
+ case IDNA_ROUNDTRIP_VERIFY_ERROR:
+ str = "Roundtrip verify error";
+ break;
+ case IDNA_CONTAINS_ACE_PREFIX:
+ str = "Already have ACE prefix (\"xn--\")";
+ break;
+ case IDNA_ICONV_ERROR:
+ str = "Locale conversion failed";
+ break;
+ case IDNA_MALLOC_ERROR:
+ str = "Allocation failed";
+ break;
+ case IDNA_DLOPEN_ERROR:
+ str = "dlopen() error";
+ break;
+ default:
+ snprintf(buf, max, "error %d", (int)err);
+ str = NULL;
+ break;
+ }
+#else
+ if ((Idna_rc)err == IDNA_SUCCESS)
+ str = "No error";
+ else
+ str = "error";
+#endif
+ if (str)
+ strncpy(buf, str, max);
+ buf[max] = '\0';
+ return (buf);
+#endif
+}
+#endif /* USE_LIBIDN */
diff --git a/Utilities/cmcurl/strerror.h b/Utilities/cmcurl/strerror.h
new file mode 100644
index 000000000..b28050437
--- /dev/null
+++ b/Utilities/cmcurl/strerror.h
@@ -0,0 +1,34 @@
+#ifndef __CURL_STRERROR_H
+#define __CURL_STRERROR_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "urldata.h"
+
+const char *Curl_strerror (struct connectdata *conn, int err);
+
+#ifdef USE_LIBIDN
+const char *Curl_idn_strerror (struct connectdata *conn, int err);
+#endif
+
+#endif
diff --git a/Utilities/cmcurl/strtok.c b/Utilities/cmcurl/strtok.c
new file mode 100644
index 000000000..630e4e029
--- /dev/null
+++ b/Utilities/cmcurl/strtok.c
@@ -0,0 +1,68 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef HAVE_STRTOK_R
+#include <stddef.h>
+#include <string.h>
+
+#include "strtok.h"
+
+char *
+Curl_strtok_r(char *ptr, const char *sep, char **end)
+{
+ if (!ptr)
+ /* we got NULL input so then we get our last position instead */
+ ptr = *end;
+
+ /* pass all letters that are including in the separator string */
+ while (*ptr && strchr(sep, *ptr))
+ ++ptr;
+
+ if (*ptr) {
+ /* so this is where the next piece of string starts */
+ char *start = ptr;
+
+ /* set the end pointer to the first byte after the start */
+ *end = start + 1;
+
+ /* scan through the string to find where it ends, it ends on a
+ null byte or a character that exists in the separator string */
+ while (**end && !strchr(sep, **end))
+ ++*end;
+
+ if (**end) {
+ /* the end is not a null byte */
+ **end = '\0'; /* zero terminate it! */
+ ++*end; /* advance the last pointer to beyond the null byte */
+ }
+
+ return start; /* return the position where the string starts */
+ }
+
+ /* we ended up on a null byte, there are no more strings to find! */
+ return NULL;
+}
+
+#endif /* this was only compiled if strtok_r wasn't present */
diff --git a/Utilities/cmcurl/strtok.h b/Utilities/cmcurl/strtok.h
new file mode 100644
index 000000000..cf9fac8c7
--- /dev/null
+++ b/Utilities/cmcurl/strtok.h
@@ -0,0 +1,38 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $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
+
diff --git a/Utilities/cmcurl/strtoofft.c b/Utilities/cmcurl/strtoofft.c
new file mode 100644
index 000000000..3ab1bfdff
--- /dev/null
+++ b/Utilities/cmcurl/strtoofft.c
@@ -0,0 +1,165 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+#include "strtoofft.h"
+
+/*
+ * NOTE:
+ *
+ * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we
+ * could use in case strtoll() doesn't exist... See
+ * http://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html
+ */
+
+#ifdef NEED_CURL_STRTOLL
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+static int get_char(char c, int base);
+
+/**
+ * Emulated version of the strtoll function. This extracts a long long
+ * value from the given input string and returns it.
+ */
+curl_off_t
+curlx_strtoll(const char *nptr, char **endptr, int base)
+{
+ char *end;
+ int is_negative = 0;
+ int overflow;
+ int i;
+ curl_off_t value = 0;
+ curl_off_t newval;
+
+ /* Skip leading whitespace. */
+ end = (char *)nptr;
+ while (ISSPACE(end[0])) {
+ end++;
+ }
+
+ /* Handle the sign, if any. */
+ if (end[0] == '-') {
+ is_negative = 1;
+ end++;
+ }
+ else if (end[0] == '+') {
+ end++;
+ }
+ else if (end[0] == '\0') {
+ /* We had nothing but perhaps some whitespace -- there was no number. */
+ if (endptr) {
+ *endptr = end;
+ }
+ return 0;
+ }
+
+ /* Handle special beginnings, if present and allowed. */
+ if (end[0] == '0' && end[1] == 'x') {
+ if (base == 16 || base == 0) {
+ end += 2;
+ base = 16;
+ }
+ }
+ else if (end[0] == '0') {
+ if (base == 8 || base == 0) {
+ end++;
+ base = 8;
+ }
+ }
+
+ /* Matching strtol, if the base is 0 and it doesn't look like
+ * the number is octal or hex, we assume it's base 10.
+ */
+ if (base == 0) {
+ base = 10;
+ }
+
+ /* Loop handling digits. */
+ value = 0;
+ overflow = 0;
+ for (i = get_char(end[0], base);
+ i != -1;
+ end++, i = get_char(end[0], base)) {
+ newval = base * value + i;
+ if (newval < value) {
+ /* We've overflowed. */
+ overflow = 1;
+ break;
+ }
+ else
+ value = newval;
+ }
+
+ if (!overflow) {
+ if (is_negative) {
+ /* Fix the sign. */
+ value *= -1;
+ }
+ }
+ else {
+ if (is_negative)
+ value = CURL_LLONG_MIN;
+ else
+ value = CURL_LLONG_MAX;
+
+ errno = ERANGE;
+ }
+
+ if (endptr)
+ *endptr = end;
+
+ return value;
+}
+
+/**
+ * Returns the value of c in the given base, or -1 if c cannot
+ * be interpreted properly in that base (i.e., is out of range,
+ * is a null, etc.).
+ *
+ * @param c the character to interpret according to base
+ * @param base the base in which to interpret c
+ *
+ * @return the value of c in base, or -1 if c isn't in range
+ */
+static int get_char(char c, int base)
+{
+ int value = -1;
+ if (c <= '9' && c >= '0') {
+ value = c - '0';
+ }
+ else if (c <= 'Z' && c >= 'A') {
+ value = c - 'A' + 10;
+ }
+ else if (c <= 'z' && c >= 'a') {
+ value = c - 'a' + 10;
+ }
+
+ if (value >= base) {
+ value = -1;
+ }
+
+ return value;
+}
+#endif /* Only present if we need strtoll, but don't have it. */
diff --git a/Utilities/cmcurl/strtoofft.h b/Utilities/cmcurl/strtoofft.h
new file mode 100644
index 000000000..e27b43261
--- /dev/null
+++ b/Utilities/cmcurl/strtoofft.h
@@ -0,0 +1,73 @@
+#ifndef _CURL_STRTOOFFT_H
+#define _CURL_STRTOOFFT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ * CAUTION: this header is designed to work when included by the app-side
+ * as well as the library. Do not mix with library internals!
+ */
+
+#include "setup.h"
+#include <stddef.h>
+#include <curl/curl.h> /* for the curl_off_t type */
+
+/* Determine what type of file offset conversion handling we wish to use. For
+ * systems with a 32-bit curl_off_t type, we should use strtol. For systems
+ * with a 64-bit curl_off_t type, we should use strtoll if it exists, and if
+ * not, should try to emulate its functionality. At any rate, we define
+ * 'strtoofft' such that it can be used to work with curl_off_t's regardless.
+ */
+#if (SIZEOF_CURL_OFF_T > 4) && (SIZEOF_LONG < 8)
+#if HAVE_STRTOLL
+#define curlx_strtoofft strtoll
+#else /* HAVE_STRTOLL */
+
+/* For MSVC7 we can use _strtoi64() which seems to be a strtoll() clone */
+#if defined(_MSC_VER) && (_MSC_VER >= 1300)
+#define curlx_strtoofft _strtoi64
+#else /* MSVC7 or later */
+curl_off_t curlx_strtoll(const char *nptr, char **endptr, int base);
+#define curlx_strtoofft curlx_strtoll
+#define NEED_CURL_STRTOLL
+#endif /* MSVC7 or later */
+
+#endif /* HAVE_STRTOLL */
+#else /* (SIZEOF_CURL_OFF_T > 4) && (SIZEOF_LONG < 8) */
+/* simply use strtol() to get numbers, either 32 or 64 bit */
+#define curlx_strtoofft strtol
+#endif
+
+#if defined(_MSC_VER) || defined(__WATCOMC__)
+#define CURL_LLONG_MIN 0x8000000000000000i64
+#define CURL_LLONG_MAX 0x7FFFFFFFFFFFFFFFi64
+#elif defined(HAVE_LL)
+#define CURL_LLONG_MIN 0x8000000000000000LL
+#define CURL_LLONG_MAX 0x7FFFFFFFFFFFFFFFLL
+#else
+#define CURL_LLONG_MIN 0x8000000000000000L
+#define CURL_LLONG_MAX 0x7FFFFFFFFFFFFFFFL
+#endif
+
+#endif
+
diff --git a/Utilities/cmcurl/telnet.c b/Utilities/cmcurl/telnet.c
new file mode 100644
index 000000000..97d22b788
--- /dev/null
+++ b/Utilities/cmcurl/telnet.c
@@ -0,0 +1,1403 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef CURL_DISABLE_TELNET
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if defined(WIN32)
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#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
+
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "telnet.h"
+#include "connect.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#define TELOPTS
+#define TELCMDS
+
+#include "arpa_telnet.h"
+#include "memory.h"
+#include "select.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#define SUBBUFSIZE 512
+
+#define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer;
+#define CURL_SB_TERM(x) { x->subend = x->subpointer; CURL_SB_CLEAR(x); }
+#define CURL_SB_ACCUM(x,c) \
+ if (x->subpointer < (x->subbuffer+sizeof x->subbuffer)) { \
+ *x->subpointer++ = (c); \
+ }
+
+#define CURL_SB_GET(x) ((*x->subpointer++)&0xff)
+#define CURL_SB_PEEK(x) ((*x->subpointer)&0xff)
+#define CURL_SB_EOF(x) (x->subpointer >= x->subend)
+#define CURL_SB_LEN(x) (x->subend - x->subpointer)
+
+#ifdef USE_WINSOCK
+typedef FARPROC WSOCK2_FUNC;
+static CURLcode check_wsock2 ( struct SessionHandle *data );
+#endif
+
+static
+void telrcv(struct connectdata *,
+ unsigned char *inbuf, /* Data received from socket */
+ ssize_t 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,
+ size_t length);
+static void suboption(struct connectdata *);
+
+/* For negotiation compliant to RFC 1143 */
+#define CURL_NO 0
+#define CURL_YES 1
+#define CURL_WANTYES 2
+#define CURL_WANTNO 3
+
+#define CURL_EMPTY 0
+#define CURL_OPPOSITE 1
+
+/*
+ * Telnet receiver states for fsm
+ */
+typedef enum
+{
+ CURL_TS_DATA = 0,
+ CURL_TS_IAC,
+ CURL_TS_WILL,
+ CURL_TS_WONT,
+ CURL_TS_DO,
+ CURL_TS_DONT,
+ CURL_TS_CR,
+ CURL_TS_SB, /* sub-option collection */
+ CURL_TS_SE /* looking for sub-option end */
+} TelnetReceive;
+
+struct TELNET {
+ int please_negotiate;
+ int already_negotiated;
+ int us[256];
+ int usq[256];
+ int us_preferred[256];
+ int him[256];
+ int himq[256];
+ int him_preferred[256];
+ char subopt_ttype[32]; /* Set with suboption TTYPE */
+ char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */
+ struct curl_slist *telnet_vars; /* Environment variables */
+
+ /* suboptions */
+ unsigned char subbuffer[SUBBUFSIZE];
+ unsigned char *subpointer, *subend; /* buffer for sub-options */
+
+ TelnetReceive telrcv_state;
+};
+
+#ifdef USE_WINSOCK
+static CURLcode
+check_wsock2 ( struct SessionHandle *data )
+{
+ int err;
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ curlassert(data);
+
+ /* telnet requires at least WinSock 2.0 so ask for it. */
+ wVersionRequested = MAKEWORD(2, 0);
+
+ err = WSAStartup(wVersionRequested, &wsaData);
+
+ /* We must've called this once already, so this call */
+ /* should always succeed. But, just in case... */
+ if (err != 0) {
+ failf(data,"WSAStartup failed (%d)",err);
+ return CURLE_FAILED_INIT;
+ }
+
+ /* We have to have a WSACleanup call for every successful */
+ /* WSAStartup call. */
+ WSACleanup();
+
+ /* Check that our version is supported */
+ if (LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) ||
+ HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)) {
+ /* Our version isn't supported */
+ failf(data,"insufficient winsock version to support "
+ "telnet");
+ return CURLE_FAILED_INIT;
+ }
+
+ /* Our version is supported */
+ return CURLE_OK;
+}
+#endif
+
+static
+CURLcode init_telnet(struct connectdata *conn)
+{
+ struct TELNET *tn;
+
+ tn = (struct TELNET *)calloc(1, sizeof(struct TELNET));
+ if(!tn)
+ return CURLE_OUT_OF_MEMORY;
+
+ conn->data->reqdata.proto.telnet = (void *)tn; /* make us known */
+
+ tn->telrcv_state = CURL_TS_DATA;
+
+ /* Init suboptions */
+ CURL_SB_CLEAR(tn);
+
+ /* Set the options we want by default */
+ tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
+ tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
+ tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
+ tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
+
+ return CURLE_OK;
+}
+
+static void negotiate(struct connectdata *conn)
+{
+ int i;
+ struct TELNET *tn = (struct TELNET *) conn->data->reqdata.proto.telnet;
+
+ for(i = 0;i < CURL_NTELOPTS;i++)
+ {
+ if(tn->us_preferred[i] == CURL_YES)
+ set_local_option(conn, i, CURL_YES);
+
+ if(tn->him_preferred[i] == CURL_YES)
+ set_remote_option(conn, i, CURL_YES);
+ }
+}
+
+static void printoption(struct SessionHandle *data,
+ const char *direction, int cmd, int option)
+{
+ const char *fmt;
+ const char *opt;
+
+ if (data->set.verbose)
+ {
+ if (cmd == CURL_IAC)
+ {
+ if (CURL_TELCMD_OK(option))
+ infof(data, "%s IAC %s\n", direction, CURL_TELCMD(option));
+ else
+ infof(data, "%s IAC %d\n", direction, option);
+ }
+ else
+ {
+ fmt = (cmd == CURL_WILL) ? "WILL" : (cmd == CURL_WONT) ? "WONT" :
+ (cmd == CURL_DO) ? "DO" : (cmd == CURL_DONT) ? "DONT" : 0;
+ if (fmt)
+ {
+ if (CURL_TELOPT_OK(option))
+ opt = CURL_TELOPT(option);
+ else if (option == CURL_TELOPT_EXOPL)
+ opt = "EXOPL";
+ else
+ opt = NULL;
+
+ if(opt)
+ infof(data, "%s %s %s\n", direction, fmt, opt);
+ else
+ infof(data, "%s %s %d\n", direction, fmt, option);
+ }
+ else
+ infof(data, "%s %d %d\n", direction, cmd, option);
+ }
+ }
+}
+
+static void send_negotiation(struct connectdata *conn, int cmd, int option)
+{
+ unsigned char buf[3];
+ ssize_t bytes_written;
+ int err;
+ struct SessionHandle *data = conn->data;
+
+ buf[0] = CURL_IAC;
+ buf[1] = (unsigned char)cmd;
+ buf[2] = (unsigned char)option;
+
+ bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
+ if(bytes_written < 0) {
+ err = Curl_sockerrno();
+ failf(data,"Sending data failed (%d)",err);
+ }
+
+ printoption(conn->data, "SENT", cmd, option);
+}
+
+static
+void set_remote_option(struct connectdata *conn, int option, int newstate)
+{
+ struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet;
+ if(newstate == CURL_YES)
+ {
+ switch(tn->him[option])
+ {
+ case CURL_NO:
+ tn->him[option] = CURL_WANTYES;
+ send_negotiation(conn, CURL_DO, option);
+ break;
+
+ case CURL_YES:
+ /* Already enabled */
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->himq[option])
+ {
+ case CURL_EMPTY:
+ /* Already negotiating for CURL_YES, queue the request */
+ tn->himq[option] = CURL_OPPOSITE;
+ break;
+ case CURL_OPPOSITE:
+ /* Error: already queued an enable request */
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->himq[option])
+ {
+ case CURL_EMPTY:
+ /* Error: already negotiating for enable */
+ break;
+ case CURL_OPPOSITE:
+ tn->himq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+ }
+ }
+ else /* NO */
+ {
+ switch(tn->him[option])
+ {
+ case CURL_NO:
+ /* Already disabled */
+ break;
+
+ case CURL_YES:
+ tn->him[option] = CURL_WANTNO;
+ send_negotiation(conn, CURL_DONT, option);
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->himq[option])
+ {
+ case CURL_EMPTY:
+ /* Already negotiating for NO */
+ break;
+ case CURL_OPPOSITE:
+ tn->himq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->himq[option])
+ {
+ case CURL_EMPTY:
+ tn->himq[option] = CURL_OPPOSITE;
+ break;
+ case CURL_OPPOSITE:
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static
+void rec_will(struct connectdata *conn, int option)
+{
+ struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet;
+ switch(tn->him[option])
+ {
+ case CURL_NO:
+ if(tn->him_preferred[option] == CURL_YES)
+ {
+ tn->him[option] = CURL_YES;
+ send_negotiation(conn, CURL_DO, option);
+ }
+ else
+ {
+ send_negotiation(conn, CURL_DONT, option);
+ }
+ break;
+
+ case CURL_YES:
+ /* Already enabled */
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->himq[option])
+ {
+ case CURL_EMPTY:
+ /* Error: DONT answered by WILL */
+ tn->him[option] = CURL_NO;
+ break;
+ case CURL_OPPOSITE:
+ /* Error: DONT answered by WILL */
+ tn->him[option] = CURL_YES;
+ tn->himq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->himq[option])
+ {
+ case CURL_EMPTY:
+ tn->him[option] = CURL_YES;
+ break;
+ case CURL_OPPOSITE:
+ tn->him[option] = CURL_WANTNO;
+ tn->himq[option] = CURL_EMPTY;
+ send_negotiation(conn, CURL_DONT, option);
+ break;
+ }
+ break;
+ }
+}
+
+static
+void rec_wont(struct connectdata *conn, int option)
+{
+ struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet;
+ switch(tn->him[option])
+ {
+ case CURL_NO:
+ /* Already disabled */
+ break;
+
+ case CURL_YES:
+ tn->him[option] = CURL_NO;
+ send_negotiation(conn, CURL_DONT, option);
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->himq[option])
+ {
+ case CURL_EMPTY:
+ tn->him[option] = CURL_NO;
+ break;
+
+ case CURL_OPPOSITE:
+ tn->him[option] = CURL_WANTYES;
+ tn->himq[option] = CURL_EMPTY;
+ send_negotiation(conn, CURL_DO, option);
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->himq[option])
+ {
+ case CURL_EMPTY:
+ tn->him[option] = CURL_NO;
+ break;
+ case CURL_OPPOSITE:
+ tn->him[option] = CURL_NO;
+ tn->himq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+ }
+}
+
+static void
+set_local_option(struct connectdata *conn, int option, int newstate)
+{
+ struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet;
+ if(newstate == CURL_YES)
+ {
+ switch(tn->us[option])
+ {
+ case CURL_NO:
+ tn->us[option] = CURL_WANTYES;
+ send_negotiation(conn, CURL_WILL, option);
+ break;
+
+ case CURL_YES:
+ /* Already enabled */
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->usq[option])
+ {
+ case CURL_EMPTY:
+ /* Already negotiating for CURL_YES, queue the request */
+ tn->usq[option] = CURL_OPPOSITE;
+ break;
+ case CURL_OPPOSITE:
+ /* Error: already queued an enable request */
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->usq[option])
+ {
+ case CURL_EMPTY:
+ /* Error: already negotiating for enable */
+ break;
+ case CURL_OPPOSITE:
+ tn->usq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+ }
+ }
+ else /* NO */
+ {
+ switch(tn->us[option])
+ {
+ case CURL_NO:
+ /* Already disabled */
+ break;
+
+ case CURL_YES:
+ tn->us[option] = CURL_WANTNO;
+ send_negotiation(conn, CURL_WONT, option);
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->usq[option])
+ {
+ case CURL_EMPTY:
+ /* Already negotiating for NO */
+ break;
+ case CURL_OPPOSITE:
+ tn->usq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->usq[option])
+ {
+ case CURL_EMPTY:
+ tn->usq[option] = CURL_OPPOSITE;
+ break;
+ case CURL_OPPOSITE:
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static
+void rec_do(struct connectdata *conn, int option)
+{
+ struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet;
+ switch(tn->us[option])
+ {
+ case CURL_NO:
+ if(tn->us_preferred[option] == CURL_YES)
+ {
+ tn->us[option] = CURL_YES;
+ send_negotiation(conn, CURL_WILL, option);
+ }
+ else
+ {
+ send_negotiation(conn, CURL_WONT, option);
+ }
+ break;
+
+ case CURL_YES:
+ /* Already enabled */
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->usq[option])
+ {
+ case CURL_EMPTY:
+ /* Error: DONT answered by WILL */
+ tn->us[option] = CURL_NO;
+ break;
+ case CURL_OPPOSITE:
+ /* Error: DONT answered by WILL */
+ tn->us[option] = CURL_YES;
+ tn->usq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->usq[option])
+ {
+ case CURL_EMPTY:
+ tn->us[option] = CURL_YES;
+ break;
+ case CURL_OPPOSITE:
+ tn->us[option] = CURL_WANTNO;
+ tn->himq[option] = CURL_EMPTY;
+ send_negotiation(conn, CURL_WONT, option);
+ break;
+ }
+ break;
+ }
+}
+
+static
+void rec_dont(struct connectdata *conn, int option)
+{
+ struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet;
+ switch(tn->us[option])
+ {
+ case CURL_NO:
+ /* Already disabled */
+ break;
+
+ case CURL_YES:
+ tn->us[option] = CURL_NO;
+ send_negotiation(conn, CURL_WONT, option);
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->usq[option])
+ {
+ case CURL_EMPTY:
+ tn->us[option] = CURL_NO;
+ break;
+
+ case CURL_OPPOSITE:
+ tn->us[option] = CURL_WANTYES;
+ tn->usq[option] = CURL_EMPTY;
+ send_negotiation(conn, CURL_WILL, option);
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->usq[option])
+ {
+ case CURL_EMPTY:
+ tn->us[option] = CURL_NO;
+ break;
+ case CURL_OPPOSITE:
+ tn->us[option] = CURL_NO;
+ tn->usq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+ }
+}
+
+
+static void printsub(struct SessionHandle *data,
+ int direction, /* '<' or '>' */
+ unsigned char *pointer, /* where suboption data is */
+ size_t length) /* length of suboption data */
+{
+ unsigned int i = 0;
+
+ if (data->set.verbose)
+ {
+ if (direction)
+ {
+ infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT");
+ if (length >= 3)
+ {
+ int j;
+
+ i = pointer[length-2];
+ j = pointer[length-1];
+
+ if (i != CURL_IAC || j != CURL_SE)
+ {
+ infof(data, "(terminated by ");
+ if (CURL_TELOPT_OK(i))
+ infof(data, "%s ", CURL_TELOPT(i));
+ else if (CURL_TELCMD_OK(i))
+ infof(data, "%s ", CURL_TELCMD(i));
+ else
+ infof(data, "%d ", i);
+ if (CURL_TELOPT_OK(j))
+ infof(data, "%s", CURL_TELOPT(j));
+ else if (CURL_TELCMD_OK(j))
+ infof(data, "%s", CURL_TELCMD(j));
+ else
+ infof(data, "%d", j);
+ infof(data, ", not IAC SE!) ");
+ }
+ }
+ length -= 2;
+ }
+ if (length < 1)
+ {
+ infof(data, "(Empty suboption?)");
+ return;
+ }
+
+ if (CURL_TELOPT_OK(pointer[0])) {
+ switch(pointer[0]) {
+ case CURL_TELOPT_TTYPE:
+ case CURL_TELOPT_XDISPLOC:
+ case CURL_TELOPT_NEW_ENVIRON:
+ infof(data, "%s", CURL_TELOPT(pointer[0]));
+ break;
+ default:
+ infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
+ break;
+ }
+ }
+ else
+ infof(data, "%d (unknown)", pointer[i]);
+
+ switch(pointer[1]) {
+ case CURL_TELQUAL_IS:
+ infof(data, " IS");
+ break;
+ case CURL_TELQUAL_SEND:
+ infof(data, " SEND");
+ break;
+ case CURL_TELQUAL_INFO:
+ infof(data, " INFO/REPLY");
+ break;
+ case CURL_TELQUAL_NAME:
+ infof(data, " NAME");
+ break;
+ }
+
+ switch(pointer[0]) {
+ case CURL_TELOPT_TTYPE:
+ case CURL_TELOPT_XDISPLOC:
+ pointer[length] = 0;
+ infof(data, " \"%s\"", &pointer[2]);
+ break;
+ case CURL_TELOPT_NEW_ENVIRON:
+ if(pointer[1] == CURL_TELQUAL_IS) {
+ infof(data, " ");
+ for(i = 3;i < length;i++) {
+ switch(pointer[i]) {
+ case CURL_NEW_ENV_VAR:
+ infof(data, ", ");
+ break;
+ case CURL_NEW_ENV_VALUE:
+ infof(data, " = ");
+ break;
+ default:
+ infof(data, "%c", pointer[i]);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ for (i = 2; i < length; i++)
+ infof(data, " %.2x", pointer[i]);
+ break;
+ }
+
+ if (direction)
+ {
+ infof(data, "\n");
+ }
+ }
+}
+
+static CURLcode check_telnet_options(struct connectdata *conn)
+{
+ struct curl_slist *head;
+ char option_keyword[128];
+ char option_arg[256];
+ char *buf;
+ struct SessionHandle *data = conn->data;
+ struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet;
+
+ /* Add the user name as an environment variable if it
+ was given on the command line */
+ if(conn->bits.user_passwd)
+ {
+ snprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user);
+ tn->telnet_vars = curl_slist_append(tn->telnet_vars, option_arg);
+
+ tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
+ }
+
+ for(head = data->set.telnet_options; head; head=head->next) {
+ if(sscanf(head->data, "%127[^= ]%*[ =]%255s",
+ option_keyword, option_arg) == 2) {
+
+ /* Terminal type */
+ if(curl_strequal(option_keyword, "TTYPE")) {
+ strncpy(tn->subopt_ttype, option_arg, 31);
+ tn->subopt_ttype[31] = 0; /* String termination */
+ tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
+ continue;
+ }
+
+ /* Display variable */
+ if(curl_strequal(option_keyword, "XDISPLOC")) {
+ strncpy(tn->subopt_xdisploc, option_arg, 127);
+ tn->subopt_xdisploc[127] = 0; /* String termination */
+ tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
+ continue;
+ }
+
+ /* Environment variable */
+ if(curl_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[CURL_TELOPT_NEW_ENVIRON] = CURL_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 temp[2048];
+ ssize_t bytes_written;
+ size_t len;
+ size_t tmplen;
+ int err;
+ char varname[128];
+ char varval[128];
+ struct SessionHandle *data = conn->data;
+ struct TELNET *tn = (struct TELNET *)data->reqdata.proto.telnet;
+
+ printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn)+2);
+ switch (CURL_SB_GET(tn)) {
+ case CURL_TELOPT_TTYPE:
+ len = strlen(tn->subopt_ttype) + 4 + 2;
+ snprintf((char *)temp, sizeof(temp),
+ "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
+ CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
+ bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
+ if(bytes_written < 0) {
+ err = Curl_sockerrno();
+ failf(data,"Sending data failed (%d)",err);
+ }
+ printsub(data, '>', &temp[2], len-2);
+ break;
+ case CURL_TELOPT_XDISPLOC:
+ len = strlen(tn->subopt_xdisploc) + 4 + 2;
+ snprintf((char *)temp, sizeof(temp),
+ "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
+ CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
+ bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
+ if(bytes_written < 0) {
+ err = Curl_sockerrno();
+ failf(data,"Sending data failed (%d)",err);
+ }
+ printsub(data, '>', &temp[2], len-2);
+ break;
+ case CURL_TELOPT_NEW_ENVIRON:
+ snprintf((char *)temp, sizeof(temp),
+ "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
+ CURL_TELQUAL_IS);
+ len = 4;
+
+ for(v = tn->telnet_vars;v;v = v->next) {
+ tmplen = (strlen(v->data) + 1);
+ /* Add the variable only if it fits */
+ if(len + tmplen < (int)sizeof(temp)-6) {
+ sscanf(v->data, "%127[^,],%127s", varname, varval);
+ snprintf((char *)&temp[len], sizeof(temp) - len,
+ "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
+ CURL_NEW_ENV_VALUE, varval);
+ len += tmplen;
+ }
+ }
+ snprintf((char *)&temp[len], sizeof(temp) - len,
+ "%c%c", CURL_IAC, CURL_SE);
+ len += 2;
+ bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
+ if(bytes_written < 0) {
+ err = Curl_sockerrno();
+ failf(data,"Sending data failed (%d)",err);
+ }
+ printsub(data, '>', &temp[2], len-2);
+ break;
+ }
+ return;
+}
+
+static
+void telrcv(struct connectdata *conn,
+ unsigned char *inbuf, /* Data received from socket */
+ ssize_t count) /* Number of bytes received */
+{
+ unsigned char c;
+ int in = 0;
+ struct SessionHandle *data = conn->data;
+ struct TELNET *tn = (struct TELNET *)data->reqdata.proto.telnet;
+
+ while(count--)
+ {
+ c = inbuf[in++];
+
+ switch (tn->telrcv_state)
+ {
+ case CURL_TS_CR:
+ tn->telrcv_state = CURL_TS_DATA;
+ if (c == '\0')
+ {
+ break; /* Ignore \0 after CR */
+ }
+
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *)&c, 1);
+ continue;
+
+ case CURL_TS_DATA:
+ if (c == CURL_IAC)
+ {
+ tn->telrcv_state = CURL_TS_IAC;
+ break;
+ }
+ else if(c == '\r')
+ {
+ tn->telrcv_state = CURL_TS_CR;
+ }
+
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *)&c, 1);
+ continue;
+
+ case CURL_TS_IAC:
+ process_iac:
+ switch (c)
+ {
+ case CURL_WILL:
+ tn->telrcv_state = CURL_TS_WILL;
+ continue;
+ case CURL_WONT:
+ tn->telrcv_state = CURL_TS_WONT;
+ continue;
+ case CURL_DO:
+ tn->telrcv_state = CURL_TS_DO;
+ continue;
+ case CURL_DONT:
+ tn->telrcv_state = CURL_TS_DONT;
+ continue;
+ case CURL_SB:
+ CURL_SB_CLEAR(tn);
+ tn->telrcv_state = CURL_TS_SB;
+ continue;
+ case CURL_IAC:
+ Curl_client_write(conn, CLIENTWRITE_BODY, (char *)&c, 1);
+ break;
+ case CURL_DM:
+ case CURL_NOP:
+ case CURL_GA:
+ default:
+ printoption(data, "RCVD", CURL_IAC, c);
+ break;
+ }
+ tn->telrcv_state = CURL_TS_DATA;
+ continue;
+
+ case CURL_TS_WILL:
+ printoption(data, "RCVD", CURL_WILL, c);
+ tn->please_negotiate = 1;
+ rec_will(conn, c);
+ tn->telrcv_state = CURL_TS_DATA;
+ continue;
+
+ case CURL_TS_WONT:
+ printoption(data, "RCVD", CURL_WONT, c);
+ tn->please_negotiate = 1;
+ rec_wont(conn, c);
+ tn->telrcv_state = CURL_TS_DATA;
+ continue;
+
+ case CURL_TS_DO:
+ printoption(data, "RCVD", CURL_DO, c);
+ tn->please_negotiate = 1;
+ rec_do(conn, c);
+ tn->telrcv_state = CURL_TS_DATA;
+ continue;
+
+ case CURL_TS_DONT:
+ printoption(data, "RCVD", CURL_DONT, c);
+ tn->please_negotiate = 1;
+ rec_dont(conn, c);
+ tn->telrcv_state = CURL_TS_DATA;
+ continue;
+
+ case CURL_TS_SB:
+ if (c == CURL_IAC)
+ {
+ tn->telrcv_state = CURL_TS_SE;
+ }
+ else
+ {
+ CURL_SB_ACCUM(tn,c);
+ }
+ continue;
+
+ case CURL_TS_SE:
+ if (c != CURL_SE)
+ {
+ if (c != CURL_IAC)
+ {
+ /*
+ * This is an error. We only expect to get "IAC IAC" or "IAC SE".
+ * Several things may have 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.
+ */
+ CURL_SB_ACCUM(tn, CURL_IAC);
+ CURL_SB_ACCUM(tn, c);
+ tn->subpointer -= 2;
+ CURL_SB_TERM(tn);
+
+ printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
+ suboption(conn); /* handle sub-option */
+ tn->telrcv_state = CURL_TS_IAC;
+ goto process_iac;
+ }
+ CURL_SB_ACCUM(tn,c);
+ tn->telrcv_state = CURL_TS_SB;
+ }
+ else
+ {
+ CURL_SB_ACCUM(tn, CURL_IAC);
+ CURL_SB_ACCUM(tn, CURL_SE);
+ tn->subpointer -= 2;
+ CURL_SB_TERM(tn);
+ suboption(conn); /* handle sub-option */
+ tn->telrcv_state = CURL_TS_DATA;
+ }
+ break;
+ }
+ }
+}
+
+CURLcode Curl_telnet_done(struct connectdata *conn, CURLcode status, bool premature)
+{
+ struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet;
+ (void)status; /* unused */
+ (void)premature; /* not used */
+
+ curl_slist_free_all(tn->telnet_vars);
+
+ free(conn->data->reqdata.proto.telnet);
+ conn->data->reqdata.proto.telnet = NULL;
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_telnet(struct connectdata *conn, bool *done)
+{
+ CURLcode code;
+ struct SessionHandle *data = conn->data;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+#ifdef USE_WINSOCK
+ HMODULE wsock2;
+ WSOCK2_FUNC close_event_func;
+ WSOCK2_FUNC create_event_func;
+ WSOCK2_FUNC event_select_func;
+ WSOCK2_FUNC enum_netevents_func;
+ WSAEVENT event_handle;
+ WSANETWORKEVENTS events;
+ HANDLE stdin_handle;
+ HANDLE objs[2];
+ DWORD obj_count;
+ DWORD wait_timeout;
+ DWORD waitret;
+ DWORD readfile_read;
+#else
+ int interval_ms;
+ struct pollfd pfd[2];
+#endif
+ ssize_t nread;
+ bool keepon = TRUE;
+ char *buf = data->state.buffer;
+ struct TELNET *tn;
+
+ *done = TRUE; /* uncontionally */
+
+ code = init_telnet(conn);
+ if(code)
+ return code;
+
+ tn = (struct TELNET *)data->reqdata.proto.telnet;
+
+ code = check_telnet_options(conn);
+ if(code)
+ return code;
+
+#ifdef USE_WINSOCK
+ /*
+ ** This functionality only works with WinSock >= 2.0. So,
+ ** make sure have it.
+ */
+ code = check_wsock2(data);
+ if (code)
+ return code;
+
+ /* OK, so we have WinSock 2.0. We need to dynamically */
+ /* load ws2_32.dll and get the function pointers we need. */
+ wsock2 = LoadLibrary("WS2_32.DLL");
+ if (wsock2 == NULL) {
+ failf(data,"failed to load WS2_32.DLL (%d)",GetLastError());
+ return CURLE_FAILED_INIT;
+ }
+
+ /* Grab a pointer to WSACreateEvent */
+ create_event_func = GetProcAddress(wsock2,"WSACreateEvent");
+ if (create_event_func == NULL) {
+ failf(data,"failed to find WSACreateEvent function (%d)",
+ GetLastError());
+ FreeLibrary(wsock2);
+ return CURLE_FAILED_INIT;
+ }
+
+ /* And WSACloseEvent */
+ close_event_func = GetProcAddress(wsock2,"WSACloseEvent");
+ if (close_event_func == NULL) {
+ failf(data,"failed to find WSACloseEvent function (%d)",
+ GetLastError());
+ FreeLibrary(wsock2);
+ return CURLE_FAILED_INIT;
+ }
+
+ /* And WSAEventSelect */
+ event_select_func = GetProcAddress(wsock2,"WSAEventSelect");
+ if (event_select_func == NULL) {
+ failf(data,"failed to find WSAEventSelect function (%d)",
+ GetLastError());
+ FreeLibrary(wsock2);
+ return CURLE_FAILED_INIT;
+ }
+
+ /* And WSAEnumNetworkEvents */
+ enum_netevents_func = GetProcAddress(wsock2,"WSAEnumNetworkEvents");
+ if (enum_netevents_func == NULL) {
+ failf(data,"failed to find WSAEnumNetworkEvents function (%d)",
+ GetLastError());
+ FreeLibrary(wsock2);
+ return CURLE_FAILED_INIT;
+ }
+
+ /* We want to wait for both stdin and the socket. Since
+ ** the select() function in winsock only works on sockets
+ ** we have to use the WaitForMultipleObjects() call.
+ */
+
+ /* First, create a sockets event object */
+ event_handle = (WSAEVENT)create_event_func();
+ if (event_handle == WSA_INVALID_EVENT) {
+ failf(data,"WSACreateEvent failed (%d)",WSAGetLastError());
+ FreeLibrary(wsock2);
+ return CURLE_FAILED_INIT;
+ }
+
+ /* The get the Windows file handle for stdin */
+ stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
+
+ /* Create the list of objects to wait for */
+ objs[0] = event_handle;
+ objs[1] = stdin_handle;
+
+ /* Tell winsock what events we want to listen to */
+ if(event_select_func(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
+ close_event_func(event_handle);
+ FreeLibrary(wsock2);
+ return 0;
+ }
+
+ /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
+ else use the old WaitForMultipleObjects() way */
+ if(GetFileType(stdin_handle) == FILE_TYPE_PIPE) {
+ /* Don't wait for stdin_handle, just wait for event_handle */
+ obj_count = 1;
+ /* Check stdin_handle per 100 milliseconds */
+ wait_timeout = 100;
+ } else {
+ obj_count = 2;
+ wait_timeout = INFINITE;
+ }
+
+ /* Keep on listening and act on events */
+ while(keepon) {
+ waitret = WaitForMultipleObjects(obj_count, objs, FALSE, wait_timeout);
+ switch(waitret) {
+ case WAIT_TIMEOUT:
+ {
+ unsigned char outbuf[2];
+ int out_count = 0;
+ ssize_t bytes_written;
+ char *buffer = buf;
+
+ while(1) {
+ if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL, &readfile_read, NULL)) {
+ keepon = FALSE;
+ break;
+ }
+ nread = readfile_read;
+
+ if(!nread)
+ break;
+
+ if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
+ &readfile_read, NULL)) {
+ keepon = FALSE;
+ break;
+ }
+ nread = readfile_read;
+
+ while(nread--) {
+ outbuf[0] = *buffer++;
+ out_count = 1;
+ if(outbuf[0] == CURL_IAC)
+ outbuf[out_count++] = CURL_IAC;
+
+ Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf,
+ out_count, &bytes_written);
+ }
+ }
+ }
+ break;
+
+ case WAIT_OBJECT_0 + 1:
+ {
+ unsigned char outbuf[2];
+ int out_count = 0;
+ ssize_t bytes_written;
+ char *buffer = buf;
+
+ if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
+ &readfile_read, NULL)) {
+ keepon = FALSE;
+ break;
+ }
+ nread = readfile_read;
+
+ while(nread--) {
+ outbuf[0] = *buffer++;
+ out_count = 1;
+ if(outbuf[0] == CURL_IAC)
+ outbuf[out_count++] = CURL_IAC;
+
+ Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf,
+ out_count, &bytes_written);
+ }
+ }
+ break;
+
+ case WAIT_OBJECT_0:
+ if(enum_netevents_func(sockfd, event_handle, &events)
+ != SOCKET_ERROR) {
+ if(events.lNetworkEvents & FD_READ) {
+ /* This reallu OUGHT to check its return code. */
+ (void)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;
+ }
+ }
+
+ /* We called WSACreateEvent, so call WSACloseEvent */
+ if (close_event_func(event_handle) == FALSE) {
+ infof(data,"WSACloseEvent failed (%d)",WSAGetLastError());
+ }
+
+ /* "Forget" pointers into the library we're about to free */
+ create_event_func = NULL;
+ close_event_func = NULL;
+ event_select_func = NULL;
+ enum_netevents_func = NULL;
+
+ /* We called LoadLibrary, so call FreeLibrary */
+ if (!FreeLibrary(wsock2))
+ infof(data,"FreeLibrary(wsock2) failed (%d)",GetLastError());
+#else
+ pfd[0].fd = sockfd;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = 0;
+ pfd[1].events = POLLIN;
+ interval_ms = 1 * 1000;
+
+ while (keepon) {
+ switch (Curl_poll(pfd, 2, interval_ms)) {
+ case -1: /* error, stop reading */
+ keepon = FALSE;
+ continue;
+ case 0: /* timeout */
+ break;
+ default: /* read! */
+ if(pfd[1].revents & POLLIN) { /* 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] == CURL_IAC)
+ outbuf[out_count++] = CURL_IAC;
+
+ Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf,
+ out_count, &bytes_written);
+ }
+ }
+
+ if(pfd[0].revents & POLLIN) {
+ /* This OUGHT to check the return code... */
+ (void)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;
+ }
+ }
+ }
+ if(data->set.timeout) {
+ struct timeval now; /* current time */
+ now = Curl_tvnow();
+ if(Curl_tvdiff(now, conn->created)/1000 >= data->set.timeout) {
+ failf(data, "Time-out");
+ code = CURLE_OPERATION_TIMEOUTED;
+ keepon = FALSE;
+ }
+ }
+ }
+#endif
+ /* mark this as "no further transfer wanted" */
+ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+
+ return code;
+}
+#endif
diff --git a/Utilities/cmcurl/telnet.h b/Utilities/cmcurl/telnet.h
new file mode 100644
index 000000000..693abf3f8
--- /dev/null
+++ b/Utilities/cmcurl/telnet.h
@@ -0,0 +1,30 @@
+#ifndef __TELNET_H
+#define __TELNET_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#ifndef CURL_DISABLE_TELNET
+CURLcode Curl_telnet(struct connectdata *conn, bool *done);
+CURLcode Curl_telnet_done(struct connectdata *conn, CURLcode, bool premature);
+#endif
+#endif
diff --git a/Utilities/cmcurl/tftp.c b/Utilities/cmcurl/tftp.c
new file mode 100644
index 000000000..0e2e7ab45
--- /dev/null
+++ b/Utilities/cmcurl/tftp.c
@@ -0,0 +1,816 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef CURL_DISABLE_TFTP
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if defined(WIN32)
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#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
+
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "tftp.h"
+#include "progress.h"
+#include "connect.h"
+#include "strerror.h"
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "url.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "memory.h"
+#include "select.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* RFC2348 allows the block size to be negotiated, but we don't support that */
+#define TFTP_BLOCKSIZE 512
+
+typedef enum {
+ TFTP_MODE_NETASCII=0,
+ TFTP_MODE_OCTET
+} tftp_mode_t;
+
+typedef enum {
+ TFTP_STATE_START=0,
+ TFTP_STATE_RX,
+ TFTP_STATE_TX,
+ TFTP_STATE_FIN
+} tftp_state_t;
+
+typedef enum {
+ TFTP_EVENT_INIT=0,
+ TFTP_EVENT_RRQ = 1,
+ TFTP_EVENT_WRQ = 2,
+ TFTP_EVENT_DATA = 3,
+ TFTP_EVENT_ACK = 4,
+ TFTP_EVENT_ERROR = 5,
+ TFTP_EVENT_TIMEOUT
+} tftp_event_t;
+
+typedef enum {
+ TFTP_ERR_UNDEF=0,
+ TFTP_ERR_NOTFOUND,
+ TFTP_ERR_PERM,
+ TFTP_ERR_DISKFULL,
+ TFTP_ERR_ILLEGAL,
+ TFTP_ERR_UNKNOWNID,
+ TFTP_ERR_EXISTS,
+ TFTP_ERR_NOSUCHUSER,
+ TFTP_ERR_TIMEOUT,
+ TFTP_ERR_NORESPONSE
+} tftp_error_t;
+
+typedef struct tftp_packet {
+ unsigned char data[2 + 2 + TFTP_BLOCKSIZE];
+} tftp_packet_t;
+
+typedef struct tftp_state_data {
+ tftp_state_t state;
+ tftp_mode_t mode;
+ tftp_error_t error;
+ struct connectdata *conn;
+ curl_socket_t sockfd;
+ int retries;
+ int retry_time;
+ int retry_max;
+ time_t start_time;
+ time_t max_time;
+ unsigned short block;
+ struct Curl_sockaddr_storage local_addr;
+ socklen_t local_addrlen;
+ struct Curl_sockaddr_storage remote_addr;
+ socklen_t remote_addrlen;
+ int rbytes;
+ int sbytes;
+ tftp_packet_t rpacket;
+ tftp_packet_t spacket;
+} tftp_state_data_t;
+
+
+/* Forward declarations */
+static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) ;
+static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) ;
+void tftp_set_timeouts(tftp_state_data_t *state) ;
+
+/**********************************************************
+ *
+ * tftp_set_timeouts -
+ *
+ * Set timeouts based on state machine state.
+ * Use user provided connect timeouts until DATA or ACK
+ * packet is received, then use user-provided transfer timeouts
+ *
+ *
+ **********************************************************/
+void tftp_set_timeouts(tftp_state_data_t *state)
+{
+
+ struct SessionHandle *data = state->conn->data;
+ time_t maxtime, timeout;
+
+ time(&state->start_time);
+ if(state->state == TFTP_STATE_START) {
+ /* Compute drop-dead time */
+ maxtime = (time_t)(data->set.connecttimeout?data->set.connecttimeout:30);
+ state->max_time = state->start_time+maxtime;
+
+ /* Set per-block timeout to total */
+ timeout = maxtime ;
+
+ /* Average restart after 5 seconds */
+ state->retry_max = (int)(timeout/5);
+
+ /* Compute the re-start interval to suit the timeout */
+ state->retry_time = (int)(timeout/state->retry_max);
+ if(state->retry_time<1)
+ state->retry_time=1;
+
+ }
+ else {
+
+ /* Compute drop-dead time */
+ maxtime = (time_t)(data->set.timeout?data->set.timeout:3600);
+ state->max_time = state->start_time+maxtime;
+
+ /* Set per-block timeout to 10% of total */
+ timeout = maxtime/10 ;
+
+ /* Average reposting an ACK after 15 seconds */
+ state->retry_max = (int)(timeout/15);
+ }
+ /* But bound the total number */
+ if(state->retry_max<3)
+ state->retry_max=3;
+
+ if(state->retry_max>50)
+ state->retry_max=50;
+
+ /* Compute the re-ACK interval to suit the timeout */
+ state->retry_time = (int)(timeout/state->retry_max);
+ if(state->retry_time<1)
+ state->retry_time=1;
+
+ infof(data, "set timeouts for state %d; Total %d, retry %d maxtry %d\n",
+ state->state, (state->max_time-state->start_time),
+ state->retry_time, state->retry_max);
+}
+
+/**********************************************************
+ *
+ * tftp_set_send_first
+ *
+ * Event handler for the START state
+ *
+ **********************************************************/
+
+static void setpacketevent(tftp_packet_t *packet, unsigned short num)
+{
+ packet->data[0] = (unsigned char)(num >> 8);
+ packet->data[1] = (unsigned char)(num & 0xff);
+}
+
+
+static void setpacketblock(tftp_packet_t *packet, unsigned short num)
+{
+ packet->data[2] = (unsigned char)(num >> 8);
+ packet->data[3] = (unsigned char)(num & 0xff);
+}
+
+static unsigned short getrpacketevent(tftp_packet_t *packet)
+{
+ return (unsigned short)((packet->data[0] << 8) | packet->data[1]);
+}
+
+static unsigned short getrpacketblock(tftp_packet_t *packet)
+{
+ return (unsigned short)((packet->data[2] << 8) | packet->data[3]);
+}
+
+static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
+{
+ int sbytes;
+ const char *mode = "octet";
+ char *filename;
+ struct SessionHandle *data = state->conn->data;
+ CURLcode res = CURLE_OK;
+
+ /* Set ascii mode if -B flag was used */
+ if(data->set.prefer_ascii)
+ mode = "netascii";
+
+ switch(event) {
+
+ case TFTP_EVENT_INIT: /* Send the first packet out */
+ case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */
+ /* Increment the retry counter, quit if over the limit */
+ state->retries++;
+ if(state->retries>state->retry_max) {
+ state->error = TFTP_ERR_NORESPONSE;
+ state->state = TFTP_STATE_FIN;
+ return res;
+ }
+
+ if(data->set.upload) {
+ /* If we are uploading, send an WRQ */
+ setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
+ state->conn->data->reqdata.upload_fromhere = (char *)&state->spacket.data[4];
+ if(data->set.infilesize != -1)
+ Curl_pgrsSetUploadSize(data, data->set.infilesize);
+ }
+ else {
+ /* If we are downloading, send an RRQ */
+ setpacketevent(&state->spacket, TFTP_EVENT_RRQ);
+ }
+ /* As RFC3617 describes the separator slash is not actually part of the
+ file name so we skip the always-present first letter of the path string. */
+ filename = curl_easy_unescape(data, &state->conn->data->reqdata.path[1], 0,
+ NULL);
+ snprintf((char *)&state->spacket.data[2],
+ TFTP_BLOCKSIZE,
+ "%s%c%s%c", filename, '\0', mode, '\0');
+ sbytes = 4 + (int)strlen(filename) + (int)strlen(mode);
+ sbytes = sendto(state->sockfd, (void *)&state->spacket,
+ sbytes, 0,
+ state->conn->ip_addr->ai_addr,
+ state->conn->ip_addr->ai_addrlen);
+ if(sbytes < 0) {
+ failf(data, "%s\n", Curl_strerror(state->conn, Curl_sockerrno()));
+ }
+ Curl_safefree(filename);
+ break;
+
+ case TFTP_EVENT_ACK: /* Connected for transmit */
+ infof(data, "%s\n", "Connected for transmit");
+ state->state = TFTP_STATE_TX;
+ tftp_set_timeouts(state);
+ return tftp_tx(state, event);
+
+ case TFTP_EVENT_DATA: /* connected for receive */
+ infof(data, "%s\n", "Connected for receive");
+ state->state = TFTP_STATE_RX;
+ tftp_set_timeouts(state);
+ return tftp_rx(state, event);
+
+ case TFTP_EVENT_ERROR:
+ state->state = TFTP_STATE_FIN;
+ break;
+
+ default:
+ failf(state->conn->data, "tftp_send_first: internal error\n");
+ break;
+ }
+ return res;
+}
+
+/**********************************************************
+ *
+ * tftp_rx
+ *
+ * Event handler for the RX state
+ *
+ **********************************************************/
+static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
+{
+ int sbytes;
+ int rblock;
+ struct SessionHandle *data = state->conn->data;
+
+ switch(event) {
+
+ case TFTP_EVENT_DATA:
+
+ /* Is this the block we expect? */
+ rblock = getrpacketblock(&state->rpacket);
+ if ((state->block+1) != rblock) {
+ /* No, log it, up the retry count and fail if over the limit */
+ infof(data,
+ "Received unexpected DATA packet block %d\n", rblock);
+ state->retries++;
+ if (state->retries>state->retry_max) {
+ failf(data, "tftp_rx: giving up waiting for block %d\n",
+ state->block+1);
+ return CURLE_TFTP_ILLEGAL;
+ }
+ }
+ /* This is the expected block. Reset counters and ACK it. */
+ state->block = (unsigned short)rblock;
+ state->retries = 0;
+ setpacketevent(&state->spacket, TFTP_EVENT_ACK);
+ setpacketblock(&state->spacket, state->block);
+ sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+ 4, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ if(sbytes < 0) {
+ failf(data, "%s\n", Curl_strerror(state->conn, Curl_sockerrno()));
+ }
+
+ /* Check if completed (That is, a less than full packet is received) */
+ if (state->rbytes < (int)sizeof(state->spacket)){
+ state->state = TFTP_STATE_FIN;
+ }
+ else {
+ state->state = TFTP_STATE_RX;
+ }
+ break;
+
+ case TFTP_EVENT_TIMEOUT:
+ /* Increment the retry count and fail if over the limit */
+ state->retries++;
+ infof(data,
+ "Timeout waiting for block %d ACK. Retries = %d\n", state->retries);
+ if(state->retries > state->retry_max) {
+ state->error = TFTP_ERR_TIMEOUT;
+ state->state = TFTP_STATE_FIN;
+ }
+ else {
+ /* Resend the previous ACK */
+ sbytes = sendto(state->sockfd, (void *)&state->spacket,
+ 4, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ /* Check all sbytes were sent */
+ if(sbytes<0) {
+ failf(data, "%s\n", Curl_strerror(state->conn, Curl_sockerrno()));
+ }
+ }
+ break;
+
+ case TFTP_EVENT_ERROR:
+ state->state = TFTP_STATE_FIN;
+ break;
+
+ default:
+ failf(data, "%s\n", "tftp_rx: internal error");
+ break;
+ }
+ Curl_pgrsSetDownloadCounter(data,
+ (curl_off_t) state->block*TFTP_BLOCKSIZE);
+ return CURLE_OK;
+}
+
+/**********************************************************
+ *
+ * tftp_tx
+ *
+ * Event handler for the TX state
+ *
+ **********************************************************/
+static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
+{
+ struct SessionHandle *data = state->conn->data;
+ int sbytes;
+ int rblock;
+ CURLcode res = CURLE_OK;
+
+ switch(event) {
+
+ case TFTP_EVENT_ACK:
+ /* Ack the packet */
+ rblock = getrpacketblock(&state->rpacket);
+
+ if(rblock != state->block) {
+ /* This isn't the expected block. Log it and up the retry counter */
+ infof(data, "Received ACK for block %d, expecting %d\n",
+ rblock, state->block);
+ state->retries++;
+ /* Bail out if over the maximum */
+ if(state->retries>state->retry_max) {
+ failf(data, "tftp_tx: giving up waiting for block %d ack",
+ state->block);
+ res = CURLE_SEND_ERROR;
+ }
+ else {
+ /* Re-send the data packet */
+ sbytes = sendto(state->sockfd, (void *)&state->spacket,
+ 4+state->sbytes, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ /* Check all sbytes were sent */
+ if(sbytes<0) {
+ failf(data, "%s\n", Curl_strerror(state->conn, Curl_sockerrno()));
+ res = CURLE_SEND_ERROR;
+ }
+ }
+ return res;
+ }
+ /* This is the expected packet. Reset the counters and send the next
+ block */
+ state->block++;
+ state->retries = 0;
+ setpacketevent(&state->spacket, TFTP_EVENT_DATA);
+ setpacketblock(&state->spacket, state->block);
+ if(state->block > 1 && state->sbytes < TFTP_BLOCKSIZE) {
+ state->state = TFTP_STATE_FIN;
+ return CURLE_OK;
+ }
+ res = Curl_fillreadbuffer(state->conn, TFTP_BLOCKSIZE, &state->sbytes);
+ if(res)
+ return res;
+ sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+ 4+state->sbytes, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ /* Check all sbytes were sent */
+ if(sbytes<0) {
+ failf(data, "%s\n", Curl_strerror(state->conn, Curl_sockerrno()));
+ }
+ break;
+
+ case TFTP_EVENT_TIMEOUT:
+ /* Increment the retry counter and log the timeout */
+ state->retries++;
+ infof(data, "Timeout waiting for block %d ACK. "
+ " Retries = %d\n", state->retries);
+ /* Decide if we've had enough */
+ if(state->retries > state->retry_max) {
+ state->error = TFTP_ERR_TIMEOUT;
+ state->state = TFTP_STATE_FIN;
+ } else {
+ /* Re-send the data packet */
+ sbytes = sendto(state->sockfd, (void *)&state->spacket,
+ 4+state->sbytes, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ /* Check all sbytes were sent */
+ if(sbytes<0) {
+ failf(data, "%s\n", Curl_strerror(state->conn, Curl_sockerrno()));
+ }
+ }
+ break;
+
+ case TFTP_EVENT_ERROR:
+ state->state = TFTP_STATE_FIN;
+ break;
+
+ default:
+ failf(data, "%s\n", "tftp_tx: internal error");
+ break;
+ }
+
+ /* Update the progress meter */
+ Curl_pgrsSetUploadCounter(data, (curl_off_t) state->block*TFTP_BLOCKSIZE);
+
+ return res;
+}
+
+/**********************************************************
+ *
+ * tftp_state_machine
+ *
+ * The tftp state machine event dispatcher
+ *
+ **********************************************************/
+static CURLcode tftp_state_machine(tftp_state_data_t *state,
+ tftp_event_t event)
+{
+ CURLcode res = CURLE_OK;
+ struct SessionHandle *data = state->conn->data;
+ switch(state->state) {
+ case TFTP_STATE_START:
+ DEBUGF(infof(data, "TFTP_STATE_START\n"));
+ res = tftp_send_first(state, event);
+ break;
+ case TFTP_STATE_RX:
+ DEBUGF(infof(data, "TFTP_STATE_RX\n"));
+ res = tftp_rx(state, event);
+ break;
+ case TFTP_STATE_TX:
+ DEBUGF(infof(data, "TFTP_STATE_TX\n"));
+ res = tftp_tx(state, event);
+ break;
+ case TFTP_STATE_FIN:
+ infof(data, "%s\n", "TFTP finished");
+ break;
+ default:
+ DEBUGF(infof(data, "STATE: %d\n", state->state));
+ failf(data, "%s\n", "Internal state machine error");
+ res = CURLE_TFTP_ILLEGAL;
+ break;
+ }
+ return res;
+}
+
+
+/**********************************************************
+ *
+ * Curl_tftp_connect
+ *
+ * The connect callback
+ *
+ **********************************************************/
+CURLcode Curl_tftp_connect(struct connectdata *conn, bool *done)
+{
+ CURLcode code;
+ tftp_state_data_t *state;
+ int rc;
+
+ state = conn->data->reqdata.proto.tftp = calloc(sizeof(tftp_state_data_t),
+ 1);
+ if(!state)
+ return CURLE_OUT_OF_MEMORY;
+
+ conn->bits.close = FALSE; /* keep it open if possible */
+
+ state->conn = conn;
+ state->sockfd = state->conn->sock[FIRSTSOCKET];
+ state->state = TFTP_STATE_START;
+
+ ((struct sockaddr *)&state->local_addr)->sa_family =
+ conn->ip_addr->ai_family;
+
+ tftp_set_timeouts(state);
+
+ if(!conn->bits.reuse) {
+ /* If not reused, bind to any interface, random UDP port. If it is reused,
+ * this has already been done!
+ *
+ * We once used the size of the local_addr struct as the third argument for
+ * bind() to better work with IPv6 or whatever size the struct could have,
+ * but we learned that at least Tru64, AIX and IRIX *requires* the size of
+ * that argument to match the exact size of a 'sockaddr_in' struct when
+ * running IPv4-only.
+ *
+ * Therefore we use the size from the address we connected to, which we
+ * assume uses the same IP version and thus hopefully this works for both
+ * IPv4 and IPv6...
+ */
+ rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr,
+ conn->ip_addr->ai_addrlen);
+ if(rc) {
+ failf(conn->data, "bind() failed; %s\n",
+ Curl_strerror(conn, Curl_sockerrno()));
+ return CURLE_COULDNT_CONNECT;
+ }
+ }
+
+ Curl_pgrsStartNow(conn->data);
+
+ *done = TRUE;
+ code = CURLE_OK;
+ return(code);
+}
+
+/**********************************************************
+ *
+ * Curl_tftp_done
+ *
+ * The done callback
+ *
+ **********************************************************/
+CURLcode Curl_tftp_done(struct connectdata *conn, CURLcode status,
+ bool premature)
+{
+ (void)status; /* unused */
+ (void)premature; /* not used */
+
+#if 0
+ free(conn->data->reqdata.proto.tftp);
+ conn->data->reqdata.proto.tftp = NULL;
+#endif
+ Curl_pgrsDone(conn);
+
+ return CURLE_OK;
+}
+
+
+/**********************************************************
+ *
+ * Curl_tftp
+ *
+ * The do callback
+ *
+ * This callback handles the entire TFTP transfer
+ *
+ **********************************************************/
+
+CURLcode Curl_tftp(struct connectdata *conn, bool *done)
+{
+ struct SessionHandle *data = conn->data;
+ tftp_state_data_t *state =
+ (tftp_state_data_t *) conn->data->reqdata.proto.tftp;
+ tftp_event_t event;
+ CURLcode code;
+ int rc;
+ struct Curl_sockaddr_storage fromaddr;
+ socklen_t fromlen;
+ int check_time = 0;
+
+ *done = TRUE;
+
+ /*
+ Since connections can be re-used between SessionHandles, this might be a
+ connection already existing but on a fresh SessionHandle struct so we must
+ make sure we have a good 'struct TFTP' to play with. For new connections,
+ the struct TFTP is allocated and setup in the Curl_tftp_connect() function.
+ */
+ if(!state) {
+ code = Curl_tftp_connect(conn, done);
+ if(code)
+ return code;
+ state = (tftp_state_data_t *)conn->data->reqdata.proto.tftp;
+ }
+
+ /* Run the TFTP State Machine */
+ for(tftp_state_machine(state, TFTP_EVENT_INIT);
+ state->state != TFTP_STATE_FIN;
+ tftp_state_machine(state, event) ) {
+
+ /* Wait until ready to read or timeout occurs */
+ rc=Curl_select(state->sockfd, CURL_SOCKET_BAD, state->retry_time * 1000);
+
+ if(rc == -1) {
+ /* bail out */
+ int error = Curl_sockerrno();
+ failf(data, "%s\n", Curl_strerror(conn, error));
+ event = TFTP_EVENT_ERROR;
+ }
+ else if (rc==0) {
+ /* A timeout occured */
+ event = TFTP_EVENT_TIMEOUT;
+
+ /* Force a look at transfer timeouts */
+ check_time = 0;
+
+ }
+ else {
+
+ /* Receive the packet */
+ fromlen=sizeof(fromaddr);
+ state->rbytes = recvfrom(state->sockfd,
+ (void *)&state->rpacket, sizeof(state->rpacket),
+ 0, (struct sockaddr *)&fromaddr, &fromlen);
+ if(state->remote_addrlen==0) {
+ memcpy(&state->remote_addr, &fromaddr, fromlen);
+ state->remote_addrlen = fromlen;
+ }
+
+ /* Sanity check packet length */
+ if (state->rbytes < 4) {
+ failf(conn->data, "Received too short packet\n");
+ /* Not a timeout, but how best to handle it? */
+ event = TFTP_EVENT_TIMEOUT;
+ }
+ else {
+
+ /* The event is given by the TFTP packet time */
+ event = (tftp_event_t)getrpacketevent(&state->rpacket);
+
+ switch(event) {
+ case TFTP_EVENT_DATA:
+ /* Don't pass to the client empty or retransmitted packets */
+ if (state->rbytes > 4 &&
+ ((state->block+1) == getrpacketblock(&state->rpacket))) {
+ code = Curl_client_write(conn, CLIENTWRITE_BODY,
+ (char *)&state->rpacket.data[4],
+ state->rbytes-4);
+ if(code)
+ return code;
+ }
+ break;
+ case TFTP_EVENT_ERROR:
+ state->error = (tftp_error_t)getrpacketblock(&state->rpacket);
+ infof(conn->data, "%s\n", (char *)&state->rpacket.data[4]);
+ break;
+ case TFTP_EVENT_ACK:
+ break;
+ case TFTP_EVENT_RRQ:
+ case TFTP_EVENT_WRQ:
+ default:
+ failf(conn->data, "%s\n", "Internal error: Unexpected packet");
+ break;
+ }
+
+ /* Update the progress meter */
+ if(Curl_pgrsUpdate(conn))
+ return CURLE_ABORTED_BY_CALLBACK;
+ }
+ }
+
+ /* Check for transfer timeout every 10 blocks, or after timeout */
+ if(check_time%10==0) {
+ time_t current;
+ time(&current);
+ if(current>state->max_time) {
+ DEBUGF(infof(data, "timeout: %d > %d\n",
+ current, state->max_time));
+ state->error = TFTP_ERR_TIMEOUT;
+ state->state = TFTP_STATE_FIN;
+ }
+ }
+
+ }
+
+ /* Tell curl we're done */
+ code = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ if(code)
+ return code;
+
+ /* If we have encountered an error */
+ if(state->error) {
+
+ /* Translate internal error codes to curl error codes */
+ switch(state->error) {
+ case TFTP_ERR_NOTFOUND:
+ code = CURLE_TFTP_NOTFOUND;
+ break;
+ case TFTP_ERR_PERM:
+ code = CURLE_TFTP_PERM;
+ break;
+ case TFTP_ERR_DISKFULL:
+ code = CURLE_TFTP_DISKFULL;
+ break;
+ case TFTP_ERR_ILLEGAL:
+ code = CURLE_TFTP_ILLEGAL;
+ break;
+ case TFTP_ERR_UNKNOWNID:
+ code = CURLE_TFTP_UNKNOWNID;
+ break;
+ case TFTP_ERR_EXISTS:
+ code = CURLE_TFTP_EXISTS;
+ break;
+ case TFTP_ERR_NOSUCHUSER:
+ code = CURLE_TFTP_NOSUCHUSER;
+ break;
+ case TFTP_ERR_TIMEOUT:
+ code = CURLE_OPERATION_TIMEOUTED;
+ break;
+ case TFTP_ERR_NORESPONSE:
+ code = CURLE_COULDNT_CONNECT;
+ break;
+ default:
+ code= CURLE_ABORTED_BY_CALLBACK;
+ break;
+ }
+ }
+ else
+ code = CURLE_OK;
+ return code;
+}
+#endif
diff --git a/Utilities/cmcurl/tftp.h b/Utilities/cmcurl/tftp.h
new file mode 100644
index 000000000..c7125417b
--- /dev/null
+++ b/Utilities/cmcurl/tftp.h
@@ -0,0 +1,31 @@
+#ifndef __TFTP_H
+#define __TFTP_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#ifndef CURL_DISABLE_TFTP
+CURLcode Curl_tftp_connect(struct connectdata *conn, bool *done);
+CURLcode Curl_tftp(struct connectdata *conn, bool *done);
+CURLcode Curl_tftp_done(struct connectdata *conn, CURLcode, bool premature);
+#endif
+#endif
diff --git a/Utilities/cmcurl/timeval.c b/Utilities/cmcurl/timeval.c
new file mode 100644
index 000000000..bb9c0a174
--- /dev/null
+++ b/Utilities/cmcurl/timeval.c
@@ -0,0 +1,116 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "timeval.h"
+
+#ifndef HAVE_GETTIMEOFDAY
+
+#ifdef WIN32
+#include <mmsystem.h>
+
+static int gettimeofday(struct timeval *tp, void *nothing)
+{
+#ifdef WITHOUT_MM_LIB
+ 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;
+#else
+ /**
+ ** The earlier time calculations using GetLocalTime
+ ** had a time resolution of 10ms.The timeGetTime, part
+ ** of multimedia apis offer a better time resolution
+ ** of 1ms.Need to link against winmm.lib for this
+ **/
+ unsigned long Ticks = 0;
+ unsigned long Sec =0;
+ unsigned long Usec = 0;
+ Ticks = timeGetTime();
+
+ Sec = Ticks/1000;
+ Usec = (Ticks - (Sec*1000))*1000;
+ tp->tv_sec = Sec;
+ tp->tv_usec = Usec;
+#endif /* WITHOUT_MM_LIB */
+ (void)nothing;
+ return 0;
+}
+#else /* WIN32 */
+/* non-win32 version of Curl_gettimeofday() */
+static int gettimeofday(struct timeval *tp, void *nothing)
+{
+ (void)nothing; /* we don't support specific time-zones */
+ tp->tv_sec = (long)time(NULL);
+ tp->tv_usec = 0;
+ return 0;
+}
+#endif /* WIN32 */
+#endif /* HAVE_GETTIMEOFDAY */
+
+/* Return the current time in a timeval struct */
+struct timeval curlx_tvnow(void)
+{
+ struct timeval now;
+ (void)gettimeofday(&now, NULL);
+ return now;
+}
+
+/*
+ * Make sure that the first argument is the more recent time, as otherwise
+ * we'll get a weird negative time-diff back...
+ *
+ * Returns: the time difference in number of milliseconds.
+ */
+long curlx_tvdiff(struct timeval newer, struct timeval older)
+{
+ return (newer.tv_sec-older.tv_sec)*1000+
+ (newer.tv_usec-older.tv_usec)/1000;
+}
+
+/*
+ * Same as curlx_tvdiff but with full usec resolution.
+ *
+ * Returns: the time difference in seconds with subsecond resolution.
+ */
+double curlx_tvdiff_secs(struct timeval newer, struct timeval older)
+{
+ return (double)(newer.tv_sec-older.tv_sec)+
+ (double)(newer.tv_usec-older.tv_usec)/1000000.0;
+}
+
+/* return the number of seconds in the given input timeval struct */
+long Curl_tvlong(struct timeval t1)
+{
+ return t1.tv_sec;
+}
diff --git a/Utilities/cmcurl/timeval.h b/Utilities/cmcurl/timeval.h
new file mode 100644
index 000000000..effd741da
--- /dev/null
+++ b/Utilities/cmcurl/timeval.h
@@ -0,0 +1,76 @@
+#ifndef __TIMEVAL_H
+#define __TIMEVAL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/*
+ * CAUTION: this header is designed to work when included by the app-side
+ * as well as the library. Do not mix with library internals!
+ */
+
+#include "setup.h"
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#endif
+#else
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#endif
+
+#ifndef HAVE_STRUCT_TIMEVAL
+struct timeval {
+ long tv_sec;
+ long tv_usec;
+};
+#endif
+
+struct timeval curlx_tvnow(void);
+
+/*
+ * Make sure that the first argument (t1) is the more recent time and t2 is
+ * the older time, as otherwise you get a weird negative time-diff back...
+ *
+ * Returns: the time difference in number of milliseconds.
+ */
+long curlx_tvdiff(struct timeval t1, struct timeval t2);
+
+/*
+ * Same as curlx_tvdiff but with full usec resolution.
+ *
+ * Returns: the time difference in seconds with subsecond resolution.
+ */
+double curlx_tvdiff_secs(struct timeval t1, struct timeval t2);
+
+long Curl_tvlong(struct timeval t1);
+
+/* These two defines below exist to provide the older API for library
+ internals only. */
+#define Curl_tvnow() curlx_tvnow()
+#define Curl_tvdiff(x,y) curlx_tvdiff(x,y)
+#define Curl_tvdiff_secs(x,y) curlx_tvdiff_secs(x,y)
+
+#endif
diff --git a/Utilities/cmcurl/transfer.c b/Utilities/cmcurl/transfer.c
new file mode 100644
index 000000000..5cb3361d2
--- /dev/null
+++ b/Utilities/cmcurl/transfer.c
@@ -0,0 +1,2494 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <errno.h>
+
+#include "strtoofft.h"
+#include "strequal.h"
+
+#ifdef WIN32
+#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
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#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
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#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_SOCKET
+#error "We can't compile without socket() support!"
+#endif
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "netrc.h"
+
+#include "content_encoding.h"
+#include "hostip.h"
+#include "transfer.h"
+#include "sendf.h"
+#include "speedcheck.h"
+#include "progress.h"
+#include "http.h"
+#include "url.h"
+#include "getinfo.h"
+#include "sslgen.h"
+#include "http_digest.h"
+#include "http_ntlm.h"
+#include "http_negotiate.h"
+#include "share.h"
+#include "memory.h"
+#include "select.h"
+#include "multiif.h"
+#include "easyif.h" /* for Curl_convert_to_network prototype */
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#define CURL_TIMEOUT_EXPECT_100 1000 /* counting ms here */
+
+/*
+ * This function will call the read callback to fill our buffer with data
+ * to upload.
+ */
+CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
+{
+ struct SessionHandle *data = conn->data;
+ size_t buffersize = (size_t)bytes;
+ int nread;
+
+ if(conn->bits.upload_chunky) {
+ /* if chunked Transfer-Encoding */
+ buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */
+ data->reqdata.upload_fromhere += 10; /* 32bit hex + CRLF */
+ }
+
+ /* this function returns a size_t, so we typecast to int to prevent warnings
+ with picky compilers */
+ nread = (int)conn->fread(data->reqdata.upload_fromhere, 1,
+ buffersize, conn->fread_in);
+
+ if(nread == CURL_READFUNC_ABORT) {
+ failf(data, "operation aborted by callback\n");
+ return CURLE_ABORTED_BY_CALLBACK;
+ }
+
+ if(!conn->bits.forbidchunk && conn->bits.upload_chunky) {
+ /* if chunked Transfer-Encoding */
+ char hexbuffer[11];
+ int hexlen = snprintf(hexbuffer, sizeof(hexbuffer),
+ "%x\r\n", nread);
+ /* move buffer pointer */
+ data->reqdata.upload_fromhere -= hexlen;
+ nread += hexlen;
+
+ /* copy the prefix to the buffer */
+ memcpy(data->reqdata.upload_fromhere, hexbuffer, hexlen);
+
+ /* always append CRLF to the data */
+ memcpy(data->reqdata.upload_fromhere + nread, "\r\n", 2);
+
+ if((nread - hexlen) == 0) {
+ /* mark this as done once this chunk is transfered */
+ data->reqdata.keep.upload_done = TRUE;
+ }
+
+ nread+=2; /* for the added CRLF */
+ }
+
+ *nreadp = nread;
+
+#ifdef CURL_DOES_CONVERSIONS
+ if(data->set.prefer_ascii) {
+ CURLcode res;
+ res = Curl_convert_to_network(data, data->reqdata.upload_fromhere, nread);
+ /* Curl_convert_to_network calls failf if unsuccessful */
+ if(res != CURLE_OK) {
+ return(res);
+ }
+ }
+#endif /* CURL_DOES_CONVERSIONS */
+
+ return CURLE_OK;
+}
+
+/*
+ * checkhttpprefix()
+ *
+ * Returns TRUE if member of the list matches prefix of string
+ */
+static bool
+checkhttpprefix(struct SessionHandle *data,
+ const char *s)
+{
+ struct curl_slist *head = data->set.http200aliases;
+ bool rc = FALSE;
+#ifdef CURL_DOES_CONVERSIONS
+ /* convert from the network encoding using a scratch area */
+ char *scratch = calloc(1, strlen(s)+1);
+ if (NULL == scratch) {
+ failf (data, "Failed to calloc memory for conversion!");
+ return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
+ }
+ strcpy(scratch, s);
+ if (CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
+ /* Curl_convert_from_network calls failf if unsuccessful */
+ free(scratch);
+ return FALSE; /* can't return CURLE_foobar so return FALSE */
+ }
+ s = scratch;
+#endif /* CURL_DOES_CONVERSIONS */
+
+ while (head) {
+ if (checkprefix(head->data, s)) {
+ rc = TRUE;
+ break;
+ }
+ head = head->next;
+ }
+
+ if ((rc != TRUE) && (checkprefix("HTTP/", s))) {
+ rc = TRUE;
+ }
+
+#ifdef CURL_DOES_CONVERSIONS
+ free(scratch);
+#endif /* CURL_DOES_CONVERSIONS */
+ return rc;
+}
+
+/*
+ * Curl_readrewind() rewinds the read stream. This typically (so far) only
+ * used for HTTP POST/PUT with multi-pass authentication when a sending was
+ * denied and a resend is necessary.
+ */
+CURLcode Curl_readrewind(struct connectdata *conn)
+{
+ struct SessionHandle *data = conn->data;
+
+ conn->bits.rewindaftersend = FALSE; /* we rewind now */
+
+ /* We have sent away data. If not using CURLOPT_POSTFIELDS or
+ CURLOPT_HTTPPOST, call app to rewind
+ */
+ if(data->set.postfields ||
+ (data->set.httpreq == HTTPREQ_POST_FORM))
+ ; /* do nothing */
+ else {
+ if(data->set.ioctl) {
+ curlioerr err;
+
+ err = (data->set.ioctl) (data, CURLIOCMD_RESTARTREAD,
+ data->set.ioctl_client);
+ infof(data, "the ioctl callback returned %d\n", (int)err);
+
+ if(err) {
+ /* FIXME: convert to a human readable error message */
+ failf(data, "ioctl callback returned error %d\n", (int)err);
+ return CURLE_SEND_FAIL_REWIND;
+ }
+ }
+ else {
+ /* If no CURLOPT_READFUNCTION is used, we know that we operate on a
+ given FILE * stream and we can actually attempt to rewind that
+ ourself with fseek() */
+ if(data->set.fread == (curl_read_callback)fread) {
+ if(-1 != fseek(data->set.in, 0, SEEK_SET))
+ /* successful rewind */
+ return CURLE_OK;
+ }
+
+ /* no callback set or failure aboe, makes us fail at once */
+ failf(data, "necessary data rewind wasn't possible\n");
+ return CURLE_SEND_FAIL_REWIND;
+ }
+ }
+ return CURLE_OK;
+}
+
+static int data_pending(struct connectdata *conn)
+{
+ return Curl_ssl_data_pending(conn, FIRSTSOCKET);
+}
+
+#ifndef MIN
+#define MIN(a,b) (a < b ? a : b)
+#endif
+
+static void read_rewind(struct connectdata *conn,
+ size_t thismuch)
+{
+ conn->read_pos -= thismuch;
+ conn->bits.stream_was_rewound = TRUE;
+
+#ifdef CURLDEBUG
+ {
+ char buf[512 + 1];
+ size_t show;
+
+ show = MIN(conn->buf_len - conn->read_pos, sizeof(buf)-1);
+ memcpy(buf, conn->master_buffer + conn->read_pos, show);
+ buf[show] = '\0';
+
+ DEBUGF(infof(conn->data,
+ "Buffer after stream rewind (read_pos = %d): [%s]",
+ conn->read_pos, buf));
+ }
+#endif
+}
+
+/*
+ * Curl_readwrite() is the low-level function to be called when data is to
+ * be read and written to/from the connection.
+ */
+CURLcode Curl_readwrite(struct connectdata *conn,
+ bool *done)
+{
+ struct SessionHandle *data = conn->data;
+ struct Curl_transfer_keeper *k = &data->reqdata.keep;
+ CURLcode result;
+ ssize_t nread; /* number of bytes read */
+ int didwhat=0;
+
+ curl_socket_t fd_read;
+ curl_socket_t fd_write;
+ int select_res;
+
+ curl_off_t contentlength;
+
+ /* only use the proper socket if the *_HOLD bit is not set simultaneously as
+ then we are in rate limiting state in that transfer direction */
+
+ if((k->keepon & (KEEP_READ|KEEP_READ_HOLD)) == KEEP_READ)
+ fd_read = conn->sockfd;
+ else
+ fd_read = CURL_SOCKET_BAD;
+
+ if((k->keepon & (KEEP_WRITE|KEEP_WRITE_HOLD)) == KEEP_WRITE)
+ fd_write = conn->writesockfd;
+ else
+ fd_write = CURL_SOCKET_BAD;
+
+ select_res = Curl_select(fd_read, fd_write, 0);
+ if(select_res == CSELECT_ERR) {
+ failf(data, "select/poll returned error");
+ return CURLE_SEND_ERROR;
+ }
+
+ do {
+ /* We go ahead and do a read if we have a readable socket or if
+ the stream was rewound (in which case we have data in a
+ buffer) */
+ if((k->keepon & KEEP_READ) &&
+ ((select_res & CSELECT_IN) || conn->bits.stream_was_rewound)) {
+ /* read */
+ bool is_empty_data = FALSE;
+
+ /* This is where we loop until we have read everything there is to
+ read or we get a EWOULDBLOCK */
+ do {
+ size_t buffersize = data->set.buffer_size?
+ data->set.buffer_size : BUFSIZE;
+ size_t bytestoread = buffersize;
+ int readrc;
+
+ if (k->size != -1 && !k->header) {
+ /* make sure we don't read "too much" if we can help it since we
+ might be pipelining and then someone else might want to read what
+ follows! */
+ curl_off_t totalleft = k->size - k->bytecount;
+ if(totalleft < (curl_off_t)bytestoread)
+ bytestoread = (size_t)totalleft;
+ }
+
+ /* receive data from the network! */
+ readrc = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread);
+
+ /* subzero, this would've blocked */
+ if(0 > readrc)
+ break; /* get out of loop */
+
+ /* get the CURLcode from the int */
+ result = (CURLcode)readrc;
+
+ if(result>0)
+ return result;
+
+ if ((k->bytecount == 0) && (k->writebytecount == 0)) {
+ Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+ if(k->wait100_after_headers)
+ /* set time stamp to compare with when waiting for the 100 */
+ k->start100 = Curl_tvnow();
+ }
+
+ didwhat |= KEEP_READ;
+ /* indicates data of zero size, i.e. empty file */
+ is_empty_data = (bool)((nread == 0) && (k->bodywrites == 0));
+
+ /* NULL terminate, allowing string ops to be used */
+ if (0 < nread || is_empty_data) {
+ k->buf[nread] = 0;
+ }
+ else if (0 >= nread) {
+ /* if we receive 0 or less here, the server closed the connection
+ and we bail out from this! */
+
+ k->keepon &= ~KEEP_READ;
+ 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 */
+ bool stop_reading = FALSE;
+
+ /* header line within buffer loop */
+ do {
+ size_t hbufp_index;
+ size_t rest_length;
+ size_t full_length;
+ int writetype;
+
+ /* str_start is start of line within buf */
+ k->str_start = k->str;
+
+ /* data is in network encoding so use 0x0a instead of '\n' */
+ k->end_ptr = memchr(k->str_start, 0x0a, nread);
+
+ if (!k->end_ptr) {
+ /* Not a complete header line within buffer, append the data to
+ the end of the headerbuff. */
+
+ if (k->hbuflen + nread >= data->state.headersize) {
+ /* We enlarge the header buffer as it is too small */
+ char *newbuff;
+ size_t newsize=CURLMAX((k->hbuflen+nread)*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_OUT_OF_MEMORY;
+ }
+ data->state.headersize=newsize;
+ data->state.headerbuff = newbuff;
+ k->hbufp = data->state.headerbuff + hbufp_index;
+ }
+ memcpy(k->hbufp, k->str, nread);
+ k->hbufp += nread;
+ k->hbuflen += nread;
+ if (!k->headerline && (k->hbuflen>5)) {
+ /* make a first check that this looks like a HTTP header */
+ if(!checkhttpprefix(data, data->state.headerbuff)) {
+ /* this is not the beginning of a HTTP first header line */
+ k->header = FALSE;
+ k->badheader = HEADER_ALLBAD;
+ break;
+ }
+ }
+
+ break; /* read more and try again */
+ }
+
+ /* decrease the size of the remaining (supposed) header line */
+ rest_length = (k->end_ptr - k->str)+1;
+ nread -= (ssize_t)rest_length;
+
+ k->str = k->end_ptr + 1; /* move past new line */
+
+ full_length = k->str - k->str_start;
+
+ /*
+ * 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 + full_length >=
+ data->state.headersize) {
+ char *newbuff;
+ size_t newsize=CURLMAX((k->hbuflen+full_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_OUT_OF_MEMORY;
+ }
+ data->state.headersize= newsize;
+ data->state.headerbuff = newbuff;
+ k->hbufp = data->state.headerbuff + hbufp_index;
+ }
+
+ /* copy to end of line */
+ memcpy(k->hbufp, k->str_start, full_length);
+ k->hbufp += full_length;
+ k->hbuflen += full_length;
+ *k->hbufp = 0;
+ k->end_ptr = k->hbufp;
+
+ k->p = data->state.headerbuff;
+
+ /****
+ * We now have a FULL header line that p points to
+ *****/
+
+ if(!k->headerline) {
+ /* the first read header */
+ if((k->hbuflen>5) &&
+ !checkhttpprefix(data, data->state.headerbuff)) {
+ /* this is not the beginning of a HTTP first header line */
+ k->header = FALSE;
+ if(nread)
+ /* since there's more, this is a partial bad header */
+ k->badheader = HEADER_PARTHEADER;
+ else {
+ /* this was all we read so its all a bad header */
+ k->badheader = HEADER_ALLBAD;
+ nread = (ssize_t)rest_length;
+ }
+ break;
+ }
+ }
+
+ /* headers are in network encoding so
+ use 0x0a and 0x0d instead of '\n' and '\r' */
+ if ((0x0a == *k->p) || (0x0d == *k->p)) {
+ size_t headerlen;
+ /* Zero-length header line means end of headers! */
+
+#ifdef CURL_DOES_CONVERSIONS
+ if (0x0d == *k->p) {
+ *k->p = '\r'; /* replace with CR in host encoding */
+ k->p++; /* pass the CR byte */
+ }
+ if (0x0a == *k->p) {
+ *k->p = '\n'; /* replace with LF in host encoding */
+ k->p++; /* pass the LF byte */
+ }
+#else
+ if ('\r' == *k->p)
+ k->p++; /* pass the \r byte */
+ if ('\n' == *k->p)
+ k->p++; /* pass the \n byte */
+#endif /* CURL_DOES_CONVERSIONS */
+
+ if(100 == k->httpcode) {
+ /*
+ * We have made a HTTP PUT or POST and this is 1.1-lingo
+ * that tells us that the server is OK with this and ready
+ * to receive the data.
+ * However, we'll get more headers now so we must get
+ * back into the header-parsing state!
+ */
+ k->header = TRUE;
+ k->headerline = 0; /* restart the header line counter */
+ /* if we did wait for this do enable write now! */
+ if (k->write_after_100_header) {
+
+ k->write_after_100_header = FALSE;
+ k->keepon |= KEEP_WRITE;
+ }
+ }
+ else {
+ k->header = FALSE; /* no more header to parse! */
+
+ if((k->size == -1) && !conn->bits.chunk && !conn->bits.close)
+ /* When connection is not to get closed, but no
+ Content-Length nor Content-Encoding chunked have been
+ received, there is no body in this response. We don't set
+ stop_reading TRUE since that would also prevent necessary
+ authentication actions to take place. */
+ conn->bits.no_body = TRUE;
+
+ }
+
+ 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;
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ /*
+ * When all the headers have been parsed, see if we should give
+ * up and return an error.
+ */
+ if (Curl_http_should_fail(conn)) {
+ failf (data, "The requested URL returned error: %d",
+ k->httpcode);
+ return CURLE_HTTP_RETURNED_ERROR;
+ }
+#endif /* CURL_DISABLE_HTTP */
+
+ /* now, only output this if the header AND body are requested:
+ */
+ writetype = CLIENTWRITE_HEADER;
+ if (data->set.include_header)
+ writetype |= CLIENTWRITE_BODY;
+
+ headerlen = k->p - data->state.headerbuff;
+
+ result = Curl_client_write(conn, writetype,
+ data->state.headerbuff,
+ headerlen);
+ if(result)
+ return result;
+
+ data->info.header_size += (long)headerlen;
+ conn->headerbytecount += (long)headerlen;
+
+ conn->deductheadercount =
+ (100 == k->httpcode)?conn->headerbytecount:0;
+
+ if (data->reqdata.resume_from &&
+ (data->set.httpreq==HTTPREQ_GET) &&
+ (k->httpcode == 416)) {
+ /* "Requested Range Not Satisfiable" */
+ stop_reading = TRUE;
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ if(!stop_reading) {
+ /* Curl_http_auth_act() checks what authentication methods
+ * that are available and decides which one (if any) to
+ * use. It will set 'newurl' if an auth metod was picked. */
+ result = Curl_http_auth_act(conn);
+
+ if(result)
+ return result;
+
+ if(conn->bits.rewindaftersend) {
+ /* We rewind after a complete send, so thus we continue
+ sending now */
+ infof(data, "Keep sending data to get tossed away!\n");
+ k->keepon |= KEEP_WRITE;
+ }
+ }
+#endif /* CURL_DISABLE_HTTP */
+
+ if(!k->header) {
+ /*
+ * really end-of-headers.
+ *
+ * If we requested a "no body", this is a good time to get
+ * out and return home.
+ */
+ if(conn->bits.no_body)
+ stop_reading = TRUE;
+ else {
+ /* If we know the expected size of this document, we set the
+ maximum download size to the size of the expected
+ document or else, we won't know when to stop reading!
+
+ Note that we set the download maximum even if we read a
+ "Connection: close" header, to make sure that
+ "Content-Length: 0" still prevents us from attempting to
+ read the (missing) response-body.
+ */
+ /* According to RFC2616 section 4.4, we MUST ignore
+ Content-Length: headers if we are now receiving data
+ using chunked Transfer-Encoding.
+ */
+ if(conn->bits.chunk)
+ k->size=-1;
+
+ }
+ if(-1 != k->size) {
+ /* We do this operation even if no_body is true, since this
+ data might be retrieved later with curl_easy_getinfo()
+ and its CURLINFO_CONTENT_LENGTH_DOWNLOAD option. */
+
+ Curl_pgrsSetDownloadSize(data, k->size);
+ k->maxdownload = k->size;
+ }
+ /* If max download size is *zero* (nothing) we already
+ have nothing and can safely return ok now! */
+ if(0 == k->maxdownload)
+ stop_reading = TRUE;
+
+ if(stop_reading) {
+ /* we make sure that this socket isn't read more now */
+ k->keepon &= ~KEEP_READ;
+ }
+
+ break; /* exit header line loop */
+ }
+
+ /* We continue reading headers, so reset the line-based
+ header parsing variables hbufp && hbuflen */
+ k->hbufp = data->state.headerbuff;
+ k->hbuflen = 0;
+ continue;
+ }
+
+ /*
+ * Checks for special headers coming up.
+ */
+
+ if (!k->headerline++) {
+ /* This is the first header, it MUST be the error code line
+ or else we consider this to be the body right away! */
+ int httpversion_major;
+ int nc;
+#ifdef CURL_DOES_CONVERSIONS
+#define HEADER1 scratch
+#define SCRATCHSIZE 21
+ CURLcode res;
+ char scratch[SCRATCHSIZE+1]; /* "HTTP/major.minor 123" */
+ /* We can't really convert this yet because we
+ don't know if it's the 1st header line or the body.
+ So we do a partial conversion into a scratch area,
+ leaving the data at k->p as-is.
+ */
+ strncpy(&scratch[0], k->p, SCRATCHSIZE);
+ scratch[SCRATCHSIZE] = 0; /* null terminate */
+ res = Curl_convert_from_network(data,
+ &scratch[0],
+ SCRATCHSIZE);
+ if (CURLE_OK != res) {
+ /* Curl_convert_from_network calls failf if unsuccessful */
+ return res;
+ }
+#else
+#define HEADER1 k->p /* no conversion needed, just use k->p */
+#endif /* CURL_DOES_CONVERSIONS */
+
+ nc = sscanf(HEADER1,
+ " 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(HEADER1, " HTTP %3d", &k->httpcode);
+ k->httpversion = 10;
+
+ /* If user has set option HTTP200ALIASES,
+ compare header line against list of aliases
+ */
+ if (!nc) {
+ if (checkhttpprefix(data, k->p)) {
+ nc = 1;
+ k->httpcode = 200;
+ k->httpversion =
+ (data->set.httpversion==CURL_HTTP_VERSION_1_0)? 10 : 11;
+ }
+ }
+ }
+
+ if (nc) {
+ data->info.httpcode = k->httpcode;
+ data->info.httpversion = k->httpversion;
+
+ /*
+ * This code executes as part of processing the header. As a
+ * result, it's not totally clear how to interpret the
+ * response code yet as that depends on what other headers may
+ * be present. 401 and 407 may be errors, but may be OK
+ * depending on how authentication is working. Other codes
+ * are definitely errors, so give up here.
+ */
+ if (data->set.http_fail_on_error && (k->httpcode >= 400) &&
+ ((k->httpcode != 401) || !data->set.userpwd) &&
+ ((k->httpcode != 407) || !data->set.proxyuserpwd) ) {
+
+ if (data->reqdata.resume_from &&
+ (data->set.httpreq==HTTPREQ_GET) &&
+ (k->httpcode == 416)) {
+ /* "Requested Range Not Satisfiable", just proceed and
+ pretend this is no error */
+ }
+ else {
+ /* serious error, go home! */
+ failf (data, "The requested URL returned error: %d",
+ k->httpcode);
+ return CURLE_HTTP_RETURNED_ERROR;
+ }
+ }
+
+ 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 416: /* Requested Range Not Satisfiable, it has the
+ Content-Length: set as the "real" document but no
+ actual response is sent. */
+ 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. */
+ k->size=0;
+ k->maxdownload=0;
+ k->ignorecl = TRUE; /* ignore Content-Length headers */
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+ }
+ else {
+ k->header = FALSE; /* this is not a header line */
+ break;
+ }
+ }
+
+#ifdef CURL_DOES_CONVERSIONS
+ /* convert from the network encoding */
+ result = Curl_convert_from_network(data, k->p, strlen(k->p));
+ if (CURLE_OK != result) {
+ return(result);
+ }
+ /* Curl_convert_from_network calls failf if unsuccessful */
+#endif /* CURL_DOES_CONVERSIONS */
+
+ /* Check for Content-Length: header lines to get size. Ignore
+ the header completely if we get a 416 response as then we're
+ resuming a document that we don't get, and this header contains
+ info about the true size of the document we didn't get now. */
+ if (!k->ignorecl && !data->set.ignorecl &&
+ checkprefix("Content-Length:", k->p)) {
+ contentlength = curlx_strtoofft(k->p+15, NULL, 10);
+ if (data->set.max_filesize &&
+ contentlength > data->set.max_filesize) {
+ failf(data, "Maximum file size exceeded");
+ return CURLE_FILESIZE_EXCEEDED;
+ }
+ if(contentlength >= 0) {
+ k->size = contentlength;
+ k->maxdownload = k->size;
+ }
+ else {
+ /* Negative Content-Length is really odd, and we know it
+ happens for example when older Apache servers send large
+ files */
+ conn->bits.close = TRUE;
+ infof(data, "Negative content-length: %" FORMAT_OFF_T
+ ", closing after transfer\n", contentlength);
+ }
+ }
+ /* check for Content-Type: header lines to get the mime-type */
+ else if (checkprefix("Content-Type:", k->p)) {
+ char *start;
+ char *end;
+ size_t len;
+
+ /* Find the first non-space letter */
+ for(start=k->p+13;
+ *start && ISSPACE(*start);
+ start++)
+ ; /* empty loop */
+
+ /* data is now in the host encoding so
+ use '\r' and '\n' instead of 0x0d and 0x0a */
+ end = strchr(start, '\r');
+ if(!end)
+ end = strchr(start, '\n');
+
+ if(end) {
+ /* skip all trailing space letters */
+ for(; ISSPACE(*end) && (end > start); end--)
+ ; /* empty loop */
+
+ /* get length of the type */
+ len = end-start+1;
+
+ /* allocate memory of a cloned copy */
+ Curl_safefree(data->info.contenttype);
+
+ 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 */
+ }
+ }
+#ifndef CURL_DISABLE_HTTP
+ else if((k->httpversion == 10) &&
+ conn->bits.httpproxy &&
+ Curl_compareheader(k->p,
+ "Proxy-Connection:", "keep-alive")) {
+ /*
+ * When a HTTP/1.0 reply comes when using a proxy, the
+ * 'Proxy-Connection: keep-alive' line tells us the
+ * connection will be kept alive for our pleasure.
+ * Default action for 1.0 is to close.
+ */
+ conn->bits.close = FALSE; /* don't close when done */
+ infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
+ }
+ else if((k->httpversion == 11) &&
+ conn->bits.httpproxy &&
+ Curl_compareheader(k->p,
+ "Proxy-Connection:", "close")) {
+ /*
+ * We get a HTTP/1.1 response from a proxy and it says it'll
+ * close down after this transfer.
+ */
+ conn->bits.close = TRUE; /* close when done */
+ infof(data, "HTTP/1.1 proxy connection set close!\n");
+ }
+ else if((k->httpversion == 10) &&
+ Curl_compareheader(k->p, "Connection:", "keep-alive")) {
+ /*
+ * A HTTP/1.0 reply with the 'Connection: keep-alive' line
+ * tells us the connection will be kept alive for our
+ * pleasure. Default action for 1.0 is to close.
+ *
+ * [RFC2068, section 19.7.1] */
+ conn->bits.close = FALSE; /* don't close when done */
+ infof(data, "HTTP/1.0 connection set to keep alive!\n");
+ }
+ else if (Curl_compareheader(k->p, "Connection:", "close")) {
+ /*
+ * [RFC 2616, section 8.1.2.1]
+ * "Connection: close" is HTTP/1.1 language and means that
+ * the connection will close when this request has been
+ * served.
+ */
+ conn->bits.close = TRUE; /* close when done */
+ }
+ else if (Curl_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 (checkprefix("Trailer:", k->p) ||
+ checkprefix("Trailers:", k->p)) {
+ /*
+ * This test helps Curl_httpchunk_read() to determine to look
+ * for well formed trailers after the zero chunksize record. In
+ * this case a CRLF is required after the zero chunksize record
+ * when no trailers are sent, or after the last trailer record.
+ *
+ * It seems both Trailer: and Trailers: occur in the wild.
+ */
+ conn->bits.trailerHdrPresent = TRUE;
+ }
+
+ else if (checkprefix("Content-Encoding:", k->p) &&
+ data->set.encoding) {
+ /*
+ * Process Content-Encoding. Look for the values: identity,
+ * gzip, deflate, compress, x-gzip and x-compress. x-gzip and
+ * x-compress are the same as gzip and compress. (Sec 3.5 RFC
+ * 2616). zlib cannot handle compress. However, errors are
+ * handled further down when the response body is processed
+ */
+ char *start;
+
+ /* Find the first non-space letter */
+ for(start=k->p+17;
+ *start && ISSPACE(*start);
+ start++)
+ ; /* empty loop */
+
+ /* Record the content-encoding for later use */
+ if (checkprefix("identity", start))
+ k->content_encoding = IDENTITY;
+ else if (checkprefix("deflate", start))
+ k->content_encoding = DEFLATE;
+ else if (checkprefix("gzip", start)
+ || checkprefix("x-gzip", start))
+ k->content_encoding = GZIP;
+ else if (checkprefix("compress", start)
+ || checkprefix("x-compress", start))
+ k->content_encoding = COMPRESS;
+ }
+ else if (checkprefix("Content-Range:", k->p)) {
+ /* Content-Range: bytes [num]-
+ Content-Range: bytes: [num]-
+ Content-Range: [num]-
+
+ The second format was added since Sun's webserver
+ JavaWebServer/1.1.1 obviously sends the header this way!
+ The third added since some servers use that!
+ */
+
+ char *ptr = k->p + 14;
+
+ /* Move forward until first digit */
+ while(*ptr && !ISDIGIT(*ptr))
+ ptr++;
+
+ k->offset = curlx_strtoofft(ptr, NULL, 10);
+
+ if (data->reqdata.resume_from == k->offset)
+ /* we asked for a resume and we got it */
+ k->content_range = TRUE;
+ }
+#if !defined(CURL_DISABLE_COOKIES)
+ else if(data->cookies &&
+ checkprefix("Set-Cookie:", k->p)) {
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
+ CURL_LOCK_ACCESS_SINGLE);
+ Curl_cookie_add(data,
+ data->cookies, TRUE, k->p+11,
+ /* If there is a custom-set Host: name, use it
+ here, or else use real peer host name. */
+ conn->allocptr.cookiehost?
+ conn->allocptr.cookiehost:conn->host.name,
+ data->reqdata.path);
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+ }
+#endif
+ else if(checkprefix("Last-Modified:", k->p) &&
+ (data->set.timecondition || data->set.get_filetime) ) {
+ time_t secs=time(NULL);
+ k->timeofdoc = curl_getdate(k->p+strlen("Last-Modified:"),
+ &secs);
+ if(data->set.get_filetime)
+ data->info.filetime = (long)k->timeofdoc;
+ }
+ else if((checkprefix("WWW-Authenticate:", k->p) &&
+ (401 == k->httpcode)) ||
+ (checkprefix("Proxy-authenticate:", k->p) &&
+ (407 == k->httpcode))) {
+ result = Curl_http_input_auth(conn, k->httpcode, k->p);
+ if(result)
+ return result;
+ }
+ else if ((k->httpcode >= 300 && k->httpcode < 400) &&
+ checkprefix("Location:", k->p)) {
+ if(data->set.http_follow_location) {
+ /* 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(*start ))
+ start++;
+
+ /* Scan through the string from the end to find the last
+ non-space. k->end_ptr points to the actual terminating zero
+ letter, move pointer one letter back and start from
+ there. This logic strips off trailing whitespace, but keeps
+ any embedded whitespace. */
+ ptr = k->end_ptr-1;
+ while((ptr>=start) && ISSPACE(*ptr))
+ ptr--;
+ ptr++;
+
+ backup = *ptr; /* store the ending letter */
+ if(ptr != start) {
+ *ptr = '\0'; /* zero terminate */
+ data->reqdata.newurl = strdup(start); /* clone string */
+ *ptr = backup; /* restore ending letter */
+ if(!data->reqdata.newurl)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ }
+#endif /* CURL_DISABLE_HTTP */
+
+ /*
+ * End of header-checks. Write them to the client.
+ */
+
+ writetype = CLIENTWRITE_HEADER;
+ if (data->set.include_header)
+ writetype |= CLIENTWRITE_BODY;
+
+ if(data->set.verbose)
+ Curl_debug(data, CURLINFO_HEADER_IN,
+ k->p, (size_t)k->hbuflen, conn);
+
+ result = Curl_client_write(conn, writetype, k->p, k->hbuflen);
+ if(result)
+ return result;
+
+ data->info.header_size += (long)k->hbuflen;
+ conn->headerbytecount += (long)k->hbuflen;
+
+ /* reset hbufp pointer && hbuflen */
+ k->hbufp = data->state.headerbuff;
+ k->hbuflen = 0;
+ }
+ while (!stop_reading && *k->str); /* header line within buffer */
+
+ if(stop_reading)
+ /* We've stopped dealing with input, get out of the do-while loop */
+ break;
+
+ /* 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. */
+
+ } /* 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 || is_empty_data)) {
+
+ if(0 == k->bodywrites && !is_empty_data) {
+ /* These checks are only made the first time we are about to
+ write a piece of the body */
+ if(conn->protocol&PROT_HTTP) {
+ /* HTTP-only checks */
+
+ if (data->reqdata.newurl) {
+ if(conn->bits.close) {
+ /* Abort after the headers if "follow Location" is set
+ and we're set to close anyway. */
+ k->keepon &= ~KEEP_READ;
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ /* We have a new url to load, but since we want to be able
+ to re-use this connection properly, we read the full
+ response in "ignore more" */
+ k->ignorebody = TRUE;
+ infof(data, "Ignoring the response-body\n");
+ }
+ if (data->reqdata.resume_from && !k->content_range &&
+ (data->set.httpreq==HTTPREQ_GET) &&
+ !k->ignorebody) {
+ /* we wanted to resume a download, although the server doesn't
+ * seem to support this and we did this with a GET (if it
+ * wasn't a GET we did a POST or PUT resume) */
+ failf(data, "HTTP server doesn't seem to support "
+ "byte ranges. Cannot resume.");
+ return CURLE_HTTP_RANGE_ERROR;
+ }
+
+ if(data->set.timecondition && !data->reqdata.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 CURL_TIMECOND_IFMODSINCE:
+ default:
+ if(k->timeofdoc < data->set.timevalue) {
+ infof(data,
+ "The requested document is not new enough\n");
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ break;
+ case CURL_TIMECOND_IFUNMODSINCE:
+ if(k->timeofdoc > data->set.timevalue) {
+ infof(data,
+ "The requested document is not old enough\n");
+ *done = TRUE;
+ 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++;
+
+ /* pass data to the debug function before it gets "dechunked" */
+ if(data->set.verbose) {
+ if(k->badheader) {
+ Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
+ (size_t)k->hbuflen, conn);
+ if(k->badheader == HEADER_PARTHEADER)
+ Curl_debug(data, CURLINFO_DATA_IN,
+ k->str, (size_t)nread, conn);
+ }
+ else
+ Curl_debug(data, CURLINFO_DATA_IN,
+ k->str, (size_t)nread, conn);
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ if(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) {
+ if(CHUNKE_WRITE_ERROR == res) {
+ failf(data, "Failed writing data");
+ return CURLE_WRITE_ERROR;
+ }
+ failf(data, "Received problem %d in the chunky parser", res);
+ return CURLE_RECV_ERROR;
+ }
+ else if(CHUNKE_STOP == res) {
+ /* we're done reading chunks! */
+ k->keepon &= ~KEEP_READ; /* read no more */
+
+ /* There are now possibly N number of bytes at the end of the
+ str buffer that weren't written to the client, but we don't
+ care about them right now. */
+ }
+ /* If it returned OK, we just keep going */
+ }
+#endif /* CURL_DISABLE_HTTP */
+
+ if((-1 != k->maxdownload) &&
+ (k->bytecount + nread >= k->maxdownload)) {
+ /* The 'excess' amount below can't be more than BUFSIZE which
+ always will fit in a size_t */
+ size_t excess = (size_t)(k->bytecount + nread - k->maxdownload);
+ if (excess > 0 && !k->ignorebody) {
+ infof(data,
+ "Rewinding stream by : %d"
+ " bytes on url %s (size = %" FORMAT_OFF_T
+ ", maxdownload = %" FORMAT_OFF_T
+ ", bytecount = %" FORMAT_OFF_T ", nread = %d)\n",
+ excess, conn->data->reqdata.path,
+ k->size, k->maxdownload, k->bytecount, nread);
+ read_rewind(conn, excess);
+ }
+
+ nread = (ssize_t) (k->maxdownload - k->bytecount);
+ if(nread < 0 ) /* this should be unusual */
+ nread = 0;
+
+ k->keepon &= ~KEEP_READ; /* we're done reading */
+ }
+
+ k->bytecount += nread;
+
+ Curl_pgrsSetDownloadCounter(data, k->bytecount);
+
+ if(!conn->bits.chunk && (nread || k->badheader || is_empty_data)) {
+ /* If this is chunky transfer, it was already written */
+
+ if(k->badheader && !k->ignorebody) {
+ /* we parsed a piece of data wrongly assuming it was a header
+ and now we output it as body instead */
+ result = Curl_client_write(conn, CLIENTWRITE_BODY,
+ data->state.headerbuff,
+ k->hbuflen);
+ if(result)
+ return result;
+ }
+ if(k->badheader < HEADER_ALLBAD) {
+ /* This switch handles various content encodings. If there's an
+ error here, be sure to check over the almost identical code
+ in http_chunks.c.
+ Make sure that ALL_CONTENT_ENCODINGS contains all the
+ encodings handled here. */
+#ifdef HAVE_LIBZ
+ switch (k->content_encoding) {
+ case IDENTITY:
+#endif
+ /* This is the default when the server sends no
+ Content-Encoding header. See Curl_readwrite_init; the
+ memset() call initializes k->content_encoding to zero. */
+ if(!k->ignorebody)
+ result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
+ nread);
+#ifdef HAVE_LIBZ
+ break;
+
+ case DEFLATE:
+ /* Assume CLIENTWRITE_BODY; headers are not encoded. */
+ if(!k->ignorebody)
+ result = Curl_unencode_deflate_write(conn, k, nread);
+ break;
+
+ case GZIP:
+ /* Assume CLIENTWRITE_BODY; headers are not encoded. */
+ if(!k->ignorebody)
+ result = Curl_unencode_gzip_write(conn, k, nread);
+ break;
+
+ case COMPRESS:
+ default:
+ failf (data, "Unrecognized content encoding type. "
+ "libcurl understands `identity', `deflate' and `gzip' "
+ "content encodings.");
+ result = CURLE_BAD_CONTENT_ENCODING;
+ break;
+ }
+#endif
+ }
+ k->badheader = HEADER_NORMAL; /* taken care of now */
+
+ if(result)
+ return result;
+ }
+
+ } /* if (! header and data to read ) */
+
+ if (is_empty_data) {
+ /* if we received nothing, the server closed the connection and we
+ are done */
+ k->keepon &= ~KEEP_READ;
+ }
+
+ } while(data_pending(conn));
+
+ } /* if( read from socket ) */
+
+ /* If we still have writing to do, we check if we have a writable
+ socket. */
+ if((k->keepon & KEEP_WRITE) && (select_res & CSELECT_OUT)) {
+ /* write */
+
+ int i, si;
+ ssize_t bytes_written;
+ bool writedone=TRUE;
+
+ if ((k->bytecount == 0) && (k->writebytecount == 0))
+ Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
+ didwhat |= KEEP_WRITE;
+
+ /*
+ * We loop here to do the READ and SEND loop until we run out of
+ * data to send or until we get EWOULDBLOCK back
+ */
+ do {
+
+ /* only read more data if there's no upload data already
+ present in the upload buffer */
+ if(0 == data->reqdata.upload_present) {
+ /* init the "upload from here" pointer */
+ data->reqdata.upload_fromhere = k->uploadbuf;
+
+ if(!k->upload_done) {
+ /* HTTP pollution, this should be written nicer to become more
+ protocol agnostic. */
+ int fillcount;
+
+ if(k->wait100_after_headers &&
+ (data->reqdata.proto.http->sending == HTTPSEND_BODY)) {
+ /* If this call is to send body data, we must take some action:
+ We have sent off the full HTTP 1.1 request, and we shall now
+ go into the Expect: 100 state and await such a header */
+ k->wait100_after_headers = FALSE; /* headers sent */
+ k->write_after_100_header = TRUE; /* wait for the header */
+ k->keepon &= ~KEEP_WRITE; /* disable writing */
+ k->start100 = Curl_tvnow(); /* timeout count starts now */
+ didwhat &= ~KEEP_WRITE; /* we didn't write anything actually */
+ break;
+ }
+
+ result = Curl_fillreadbuffer(conn, BUFSIZE, &fillcount);
+ if(result)
+ return result;
+
+ nread = (ssize_t)fillcount;
+ }
+ else
+ nread = 0; /* we're done uploading/reading */
+
+ /* 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 */
+ writedone = TRUE;
+
+ if(conn->bits.rewindaftersend) {
+ result = Curl_readrewind(conn);
+ if(result)
+ return result;
+ }
+ break;
+ }
+
+ /* store number of bytes available for upload */
+ data->reqdata.upload_present = nread;
+
+ /* convert LF to CRLF if so asked */
+#ifdef CURL_DO_LINEEND_CONV
+ /* always convert if we're FTPing in ASCII mode */
+ if ((data->set.crlf) || (data->set.prefer_ascii)) {
+#else
+ if (data->set.crlf) {
+#endif /* CURL_DO_LINEEND_CONV */
+ if(data->state.scratch == NULL)
+ data->state.scratch = malloc(2*BUFSIZE);
+ if(data->state.scratch == NULL) {
+ failf (data, "Failed to alloc scratch buffer!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ /*
+ * ASCII/EBCDIC Note: This is presumably a text (not binary)
+ * transfer so the data should already be in ASCII.
+ * That means the hex values for ASCII CR (0x0d) & LF (0x0a)
+ * must be used instead of the escape sequences \r & \n.
+ */
+ for(i = 0, si = 0; i < nread; i++, si++) {
+ if (data->reqdata.upload_fromhere[i] == 0x0a) {
+ data->state.scratch[si++] = 0x0d;
+ data->state.scratch[si] = 0x0a;
+ if (!data->set.crlf) {
+ /* we're here only because FTP is in ASCII mode...
+ bump infilesize for the LF we just added */
+ data->set.infilesize++;
+ }
+ }
+ else
+ data->state.scratch[si] = data->reqdata.upload_fromhere[i];
+ }
+ if(si != nread) {
+ /* only perform the special operation if we really did replace
+ anything */
+ nread = si;
+
+ /* upload from the new (replaced) buffer instead */
+ data->reqdata.upload_fromhere = data->state.scratch;
+
+ /* set the new amount too */
+ data->reqdata.upload_present = nread;
+ }
+ }
+ }
+ else {
+ /* We have a partial buffer left from a previous "round". Use
+ that instead of reading more data */
+ }
+
+ /* write to socket (send away data) */
+ result = Curl_write(conn,
+ conn->writesockfd, /* socket to send to */
+ data->reqdata.upload_fromhere, /* buffer pointer */
+ data->reqdata.upload_present, /* buffer size */
+ &bytes_written); /* actually send away */
+ if(result)
+ return result;
+
+ if(data->set.verbose)
+ /* show the data before we change the pointer upload_fromhere */
+ Curl_debug(data, CURLINFO_DATA_OUT, data->reqdata.upload_fromhere,
+ (size_t)bytes_written, conn);
+
+ if(data->reqdata.upload_present != bytes_written) {
+ /* we only wrote a part of the buffer (if anything), deal with it! */
+
+ /* store the amount of bytes left in the buffer to write */
+ data->reqdata.upload_present -= bytes_written;
+
+ /* advance the pointer where to find the buffer when the next send
+ is to happen */
+ data->reqdata.upload_fromhere += bytes_written;
+
+ writedone = TRUE; /* we are done, stop the loop */
+ }
+ else {
+ /* we've uploaded that buffer now */
+ data->reqdata.upload_fromhere = k->uploadbuf;
+ data->reqdata.upload_present = 0; /* no more bytes left */
+
+ if(k->upload_done) {
+ /* switch off writing, we're done! */
+ k->keepon &= ~KEEP_WRITE; /* we're done writing */
+ writedone = TRUE;
+ }
+ }
+
+ k->writebytecount += bytes_written;
+ Curl_pgrsSetUploadCounter(data, k->writebytecount);
+
+ } while(!writedone); /* loop until we're done writing! */
+
+ }
+
+ } while(0); /* just to break out from! */
+
+ k->now = Curl_tvnow();
+ if(didwhat) {
+ /* Update read/write counters */
+ if(k->bytecountp)
+ *k->bytecountp = k->bytecount; /* read count */
+ if(k->writebytecountp)
+ *k->writebytecountp = k->writebytecount; /* write count */
+ }
+ else {
+ /* no read no write, this is a timeout? */
+ if (k->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. */
+
+ /* Quoting RFC2616, section "8.2.3 Use of the 100 (Continue) Status":
+
+ Therefore, when a client sends this header field to an origin server
+ (possibly via a proxy) from which it has never seen a 100 (Continue)
+ status, the client SHOULD NOT wait for an indefinite period before
+ sending the request body.
+
+ */
+
+ long ms = Curl_tvdiff(k->now, k->start100);
+ if(ms > CURL_TIMEOUT_EXPECT_100) {
+ /* we've waited long enough, continue anyway */
+ k->write_after_100_header = FALSE;
+ k->keepon |= KEEP_WRITE;
+ }
+ }
+ }
+
+ 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)) {
+ if (k->size != -1) {
+ failf(data, "Operation timed out after %d seconds with %"
+ FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received",
+ data->set.timeout, k->bytecount, k->size);
+ } else {
+ failf(data, "Operation timed out after %d seconds with %"
+ FORMAT_OFF_T " bytes received",
+ data->set.timeout, k->bytecount);
+ }
+ return CURLE_OPERATION_TIMEOUTED;
+ }
+
+ if(!k->keepon) {
+ /*
+ * The transfer has been performed. Just make some general checks before
+ * returning.
+ */
+
+ if(!(conn->bits.no_body) && (k->size != -1) &&
+ (k->bytecount != k->size) &&
+#ifdef CURL_DO_LINEEND_CONV
+ /* Most FTP servers don't adjust their file SIZE response for CRLFs,
+ so we'll check to see if the discrepancy can be explained
+ by the number of CRLFs we've changed to LFs.
+ */
+ (k->bytecount != (k->size + data->state.crlf_conversions)) &&
+#endif /* CURL_DO_LINEEND_CONV */
+ !data->reqdata.newurl) {
+ failf(data, "transfer closed with %" FORMAT_OFF_T
+ " bytes remaining to read",
+ k->size - k->bytecount);
+ return CURLE_PARTIAL_FILE;
+ }
+ else if(!(conn->bits.no_body) &&
+ conn->bits.chunk &&
+ (data->reqdata.proto.http->chunk.state != CHUNK_STOP)) {
+ /*
+ * In chunked mode, return an error if the connection is closed prior to
+ * the empty (terminiating) chunk is read.
+ *
+ * The condition above used to check for
+ * conn->proto.http->chunk.datasize != 0 which is true after reading
+ * *any* chunk, not just the empty chunk.
+ *
+ */
+ failf(data, "transfer closed with outstanding read data remaining");
+ return CURLE_PARTIAL_FILE;
+ }
+ if(Curl_pgrsUpdate(conn))
+ return CURLE_ABORTED_BY_CALLBACK;
+ }
+
+ /* Now update the "done" boolean we return */
+ *done = (bool)(0 == (k->keepon&(KEEP_READ|KEEP_WRITE)));
+
+ return CURLE_OK;
+}
+
+
+/*
+ * Curl_readwrite_init() inits the readwrite session. This is inited each time for a
+ * transfer, sometimes multiple times on the same SessionHandle
+ */
+
+CURLcode Curl_readwrite_init(struct connectdata *conn)
+{
+ struct SessionHandle *data = conn->data;
+ struct Curl_transfer_keeper *k = &data->reqdata.keep;
+
+ /* NB: the content encoding software depends on this initialization of
+ Curl_transfer_keeper.*/
+ 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->size = data->reqdata.size;
+ k->maxdownload = data->reqdata.maxdownload;
+ k->bytecountp = data->reqdata.bytecountp;
+ k->writebytecountp = data->reqdata.writebytecountp;
+
+ k->bytecount = 0;
+
+ 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;
+ k->ignorebody=FALSE;
+
+ Curl_pgrsTime(data, TIMER_PRETRANSFER);
+ Curl_speedinit(data);
+
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+
+ if (!conn->bits.getheader) {
+ k->header = FALSE;
+ if(k->size > 0)
+ Curl_pgrsSetDownloadSize(data, k->size);
+ }
+ /* we want header and/or body, if neither then don't do this! */
+ if(conn->bits.getheader || !conn->bits.no_body) {
+
+ if(conn->sockfd != CURL_SOCKET_BAD) {
+ k->keepon |= KEEP_READ;
+ }
+
+ if(conn->writesockfd != CURL_SOCKET_BAD) {
+ /* HTTP 1.1 magic:
+
+ Even if we require a 100-return code before uploading data, we might
+ need to write data before that since the REQUEST may not have been
+ finished sent off just yet.
+
+ Thus, we must check if the request has been sent before we set the
+ state info where we wait for the 100-return code
+ */
+ if (data->state.expect100header &&
+ (data->reqdata.proto.http->sending == HTTPSEND_BODY)) {
+ /* wait with write until we either got 100-continue or a timeout */
+ k->write_after_100_header = TRUE;
+ k->start100 = k->start;
+ }
+ else {
+ if(data->state.expect100header)
+ /* when we've sent off the rest of the headers, we must await a
+ 100-continue */
+ k->wait100_after_headers = TRUE;
+ k->keepon |= KEEP_WRITE;
+ }
+ }
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_single_getsock() gets called by the multi interface code when the app
+ * has requested to get the sockets for the current connection. This function
+ * will then be called once for every connection that the multi interface
+ * keeps track of. This function will only be called for connections that are
+ * in the proper state to have this information available.
+ */
+int Curl_single_getsock(struct connectdata *conn,
+ curl_socket_t *sock, /* points to numsocks number
+ of sockets */
+ int numsocks)
+{
+ struct SessionHandle *data = conn->data;
+ int bitmap = GETSOCK_BLANK;
+ int index = 0;
+
+ if(numsocks < 2)
+ /* simple check but we might need two slots */
+ return GETSOCK_BLANK;
+
+ if(data->reqdata.keep.keepon & KEEP_READ) {
+ bitmap |= GETSOCK_READSOCK(index);
+ sock[index] = conn->sockfd;
+ }
+
+ if(data->reqdata.keep.keepon & KEEP_WRITE) {
+
+ if((conn->sockfd != conn->writesockfd) ||
+ !(data->reqdata.keep.keepon & KEEP_READ)) {
+ /* only if they are not the same socket or we didn't have a readable
+ one, we increase index */
+ if(data->reqdata.keep.keepon & KEEP_READ)
+ index++; /* increase index if we need two entries */
+ sock[index] = conn->writesockfd;
+ }
+
+ bitmap |= GETSOCK_WRITESOCK(index);
+ }
+
+ return bitmap;
+}
+
+
+/*
+ * Transfer()
+ *
+ * This function is what performs the actual transfer. It is capable of
+ * doing both ways simultaneously.
+ * The transfer must already have been setup by a call to Curl_setup_transfer().
+ *
+ * Note that headers are created in a preallocated buffer of a default size.
+ * That buffer can be enlarged on demand, but it is never shrunken again.
+ *
+ * Parts of this function was once written by the friendly Mark Butler
+ * <butlerm@xmission.com>.
+ */
+
+static CURLcode
+Transfer(struct connectdata *conn)
+{
+ CURLcode result;
+ struct SessionHandle *data = conn->data;
+ struct Curl_transfer_keeper *k = &data->reqdata.keep;
+ bool done=FALSE;
+
+ if(!(conn->protocol & PROT_FILE))
+ /* Only do this if we are not transferring FILE:, since the file: treatment
+ is different*/
+ Curl_readwrite_init(conn);
+
+ if((conn->sockfd == CURL_SOCKET_BAD) &&
+ (conn->writesockfd == CURL_SOCKET_BAD))
+ /* nothing to read, nothing to write, we're already OK! */
+ return CURLE_OK;
+
+ /* we want header and/or body, if neither then don't do this! */
+ if(!conn->bits.getheader && conn->bits.no_body)
+ return CURLE_OK;
+
+ while (!done) {
+ curl_socket_t fd_read;
+ curl_socket_t fd_write;
+
+ /* limit-rate logic: if speed exceeds threshold, then do not include fd in
+ select set. The current speed is recalculated in each Curl_readwrite()
+ call */
+ if ((k->keepon & KEEP_WRITE) &&
+ (!data->set.max_send_speed ||
+ (data->progress.ulspeed < data->set.max_send_speed) )) {
+ fd_write = conn->writesockfd;
+ k->keepon &= ~KEEP_WRITE_HOLD;
+ }
+ else {
+ fd_write = CURL_SOCKET_BAD;
+ if(k->keepon & KEEP_WRITE)
+ k->keepon |= KEEP_WRITE_HOLD; /* hold it */
+ }
+
+ if ((k->keepon & KEEP_READ) &&
+ (!data->set.max_recv_speed ||
+ (data->progress.dlspeed < data->set.max_recv_speed)) ) {
+ fd_read = conn->sockfd;
+ k->keepon &= ~KEEP_READ_HOLD;
+ }
+ else {
+ fd_read = CURL_SOCKET_BAD;
+ if(k->keepon & KEEP_READ)
+ k->keepon |= KEEP_READ_HOLD; /* hold it */
+ }
+
+ /* The *_HOLD logic is necessary since even though there might be no
+ traffic during the select interval, we still call Curl_readwrite() for
+ the timeout case and if we limit transfer speed we must make sure that
+ this function doesn't transfer anything while in HOLD status. */
+
+ switch (Curl_select(fd_read, fd_write, 1000)) {
+ 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 */
+ 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;
+}
+
+/*
+ * Curl_pretransfer() is called immediately before a transfer starts.
+ */
+CURLcode Curl_pretransfer(struct SessionHandle *data)
+{
+ CURLcode res;
+ if(!data->change.url) {
+ /* we can't do anything wihout URL */
+ failf(data, "No URL set!\n");
+ return CURLE_URL_MALFORMAT;
+ }
+
+ /* Init the SSL session ID cache here. We do it here since we want to do it
+ after the *_setopt() calls (that could change the size of the cache) but
+ before any transfer takes place. */
+ res = Curl_ssl_initsessions(data, data->set.ssl.numsessions);
+ if(res)
+ return res;
+
+ data->set.followlocation=0; /* reset the location-follow counter */
+ data->state.this_is_a_follow = FALSE; /* reset this */
+ data->state.errorbuf = FALSE; /* no error has occurred */
+
+ data->state.authproblem = FALSE;
+ data->state.authhost.want = data->set.httpauth;
+ data->state.authproxy.want = data->set.proxyauth;
+
+ /* If there is a list of cookie files to read, do it now! */
+ if(data->change.cookielist) {
+ Curl_cookie_loadfiles(data);
+ }
+
+ /* Allow data->set.use_port to set which port to use. This needs to be
+ * disabled for example when we follow Location: headers to URLs using
+ * different ports! */
+ data->state.allow_port = TRUE;
+
+#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
+ /*************************************************************
+ * Tell signal handler to ignore SIGPIPE
+ *************************************************************/
+ if(!data->set.no_signal)
+ data->state.prev_signal = signal(SIGPIPE, SIG_IGN);
+#endif
+
+ Curl_initinfo(data); /* reset session-specific information "variables" */
+ Curl_pgrsStartNow(data);
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_posttransfer() is called immediately after a transfer ends
+ */
+CURLcode Curl_posttransfer(struct SessionHandle *data)
+{
+#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
+ /* restore the signal handler for SIGPIPE before we get back */
+ if(!data->set.no_signal)
+ signal(SIGPIPE, data->state.prev_signal);
+#else
+ (void)data; /* unused parameter */
+#endif
+
+ if(!(data->progress.flags & PGRS_HIDE) &&
+ !data->progress.callback)
+ /* only output if we don't use a progress callback and we're not hidden */
+ fprintf(data->set.err, "\n");
+
+ return CURLE_OK;
+}
+
+/*
+ * strlen_url() returns the length of the given URL if the spaces within the
+ * URL were properly URL encoded.
+ */
+static int strlen_url(char *url)
+{
+ char *ptr;
+ int newlen=0;
+ bool left=TRUE; /* left side of the ? */
+
+ for(ptr=url; *ptr; ptr++) {
+ switch(*ptr) {
+ case '?':
+ left=FALSE;
+ default:
+ newlen++;
+ break;
+ case ' ':
+ if(left)
+ newlen+=3;
+ else
+ newlen++;
+ break;
+ }
+ }
+ return newlen;
+}
+
+/* strcpy_url() copies a url to a output buffer and URL-encodes the spaces in
+ * the source URL accordingly.
+ */
+static void strcpy_url(char *output, char *url)
+{
+ /* we must add this with whitespace-replacing */
+ bool left=TRUE;
+ char *iptr;
+ char *optr = output;
+ for(iptr = url; /* read from here */
+ *iptr; /* until zero byte */
+ iptr++) {
+ switch(*iptr) {
+ case '?':
+ left=FALSE;
+ default:
+ *optr++=*iptr;
+ break;
+ case ' ':
+ if(left) {
+ *optr++='%'; /* add a '%' */
+ *optr++='2'; /* add a '2' */
+ *optr++='0'; /* add a '0' */
+ }
+ else
+ *optr++='+'; /* add a '+' here */
+ break;
+ }
+ }
+ *optr=0; /* zero terminate output buffer */
+
+}
+
+/*
+ * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string
+ * as given by the remote server and set up the new URL to request.
+ */
+CURLcode Curl_follow(struct SessionHandle *data,
+ char *newurl, /* this 'newurl' is the Location: string,
+ and it must be malloc()ed before passed
+ here */
+ bool retry) /* set TRUE if this is a request retry as
+ opposed to a real redirect following */
+{
+ /* Location: redirect */
+ char prot[16]; /* URL protocol string storage */
+ char letter; /* used for a silly sscanf */
+ size_t newlen;
+ char *newest;
+
+ if(!retry) {
+ if ((data->set.maxredirs != -1) &&
+ (data->set.followlocation >= data->set.maxredirs)) {
+ failf(data,"Maximum (%d) redirects followed", data->set.maxredirs);
+ return CURLE_TOO_MANY_REDIRECTS;
+ }
+
+ /* mark the next request as a followed location: */
+ data->state.this_is_a_follow = TRUE;
+
+ data->set.followlocation++; /* count location-followers */
+ }
+
+ if(data->set.http_auto_referer) {
+ /* We are asked to automatically set the previous URL as the
+ referer when we get the next URL. We pick the ->url field,
+ which may or may not be 100% correct */
+
+ if(data->change.referer_alloc)
+ /* 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 *useurl = newurl;
+ size_t urllen;
+
+ /* we must make our own copy of the URL to play with, as it may
+ point to read-only data */
+ char *url_clone=strdup(data->change.url);
+
+ if(!url_clone)
+ return CURLE_OUT_OF_MEMORY; /* skip out of this NOW */
+
+ /* protsep points to the start of the host name */
+ protsep=strstr(url_clone, "//");
+ if(!protsep)
+ protsep=url_clone;
+ else
+ protsep+=2; /* pass the slashes */
+
+ if('/' != newurl[0]) {
+ int level=0;
+
+ /* First we need to find out if there's a ?-letter in the URL,
+ and cut it and the right-side of that off */
+ pathsep = strchr(protsep, '?');
+ if(pathsep)
+ *pathsep=0;
+
+ /* we have a relative path to append to the last slash if
+ there's one available */
+ pathsep = strrchr(protsep, '/');
+ if(pathsep)
+ *pathsep=0;
+
+ /* Check if there's any slash after the host name, and if so,
+ remember that position instead */
+ pathsep = strchr(protsep, '/');
+ if(pathsep)
+ protsep = pathsep+1;
+ else
+ protsep = NULL;
+
+ /* now deal with one "./" or any amount of "../" in the newurl
+ and act accordingly */
+
+ if((useurl[0] == '.') && (useurl[1] == '/'))
+ useurl+=2; /* just skip the "./" */
+
+ while((useurl[0] == '.') &&
+ (useurl[1] == '.') &&
+ (useurl[2] == '/')) {
+ level++;
+ useurl+=3; /* pass the "../" */
+ }
+
+ if(protsep) {
+ while(level--) {
+ /* cut off one more level from the right of the original URL */
+ pathsep = strrchr(protsep, '/');
+ if(pathsep)
+ *pathsep=0;
+ else {
+ *protsep=0;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ /* We got a new absolute path for this server, cut off from the
+ first slash */
+ pathsep = strchr(protsep, '/');
+ if(pathsep) {
+ /* When people use badly formatted URLs, such as
+ "http://www.url.com?dir=/home/daniel" we must not use the first
+ slash, if there's a ?-letter before it! */
+ char *sep = strchr(protsep, '?');
+ if(sep && (sep < pathsep))
+ pathsep = sep;
+ *pathsep=0;
+ }
+ else {
+ /* There was no slash. Now, since we might be operating on a badly
+ formatted URL, such as "http://www.url.com?id=2380" which doesn't
+ use a slash separator as it is supposed to, we need to check for a
+ ?-letter as well! */
+ pathsep = strchr(protsep, '?');
+ if(pathsep)
+ *pathsep=0;
+ }
+ }
+
+ /* If the new part contains a space, this is a mighty stupid redirect
+ but we still make an effort to do "right". To the left of a '?'
+ letter we replace each space with %20 while it is replaced with '+'
+ on the right side of the '?' letter.
+ */
+ newlen = strlen_url(useurl);
+
+ urllen = strlen(url_clone);
+
+ newest=(char *)malloc( urllen + 1 + /* possible slash */
+ newlen + 1 /* zero byte */);
+
+ if(!newest) {
+ free(url_clone); /* don't leak this */
+ return CURLE_OUT_OF_MEMORY; /* go out from this */
+ }
+
+ /* copy over the root url part */
+ memcpy(newest, url_clone, urllen);
+
+ /* check if we need to append a slash */
+ if(('/' == useurl[0]) || (protsep && !*protsep))
+ ;
+ else
+ newest[urllen++]='/';
+
+ /* then append the new piece on the right side */
+ strcpy_url(&newest[urllen], useurl);
+
+ free(newurl); /* newurl is the allocated pointer */
+ free(url_clone);
+ newurl = newest;
+ }
+ else {
+ /* This is an absolute URL, don't allow the custom port number */
+ data->state.allow_port = FALSE;
+
+ if(strchr(newurl, ' ')) {
+ /* This new URL contains at least one space, this is a mighty stupid
+ redirect but we still make an effort to do "right". */
+ newlen = strlen_url(newurl);
+
+ newest = malloc(newlen+1); /* get memory for this */
+ if(newest) {
+ strcpy_url(newest, newurl); /* create a space-free URL */
+
+ free(newurl); /* that was no good */
+ newurl = newest; /* use this instead now */
+ }
+ }
+
+ }
+
+ if(data->change.url_alloc)
+ free(data->change.url);
+ else
+ data->change.url_alloc = TRUE; /* the URL is allocated */
+
+ data->change.url = newurl;
+ newurl = NULL; /* don't free! */
+
+ infof(data, "Issue another request to this URL: '%s'\n", data->change.url);
+
+ /*
+ * We get here when the HTTP code is 300-399 (and 401). We need to perform
+ * differently based on exactly what return code there was.
+ *
+ * News from 7.10.6: we can also get here on a 401 or 407, in case we act on
+ * a HTTP (proxy-) authentication scheme other than Basic.
+ */
+ switch(data->info.httpcode) {
+ /* 401 - Act on a www-authentication, we keep on moving and do the
+ Authorization: XXXX header in the HTTP request code snippet */
+ /* 407 - Act on a proxy-authentication, we keep on moving and do the
+ Proxy-Authorization: XXXX header in the HTTP request code snippet */
+ /* 300 - Multiple Choices */
+ /* 306 - Not used */
+ /* 307 - Temporary Redirect */
+ default: /* for all above (and the unknown ones) */
+ /* Some codes are explicitly mentioned since I've checked RFC2616 and they
+ * seem to be OK to POST to.
+ */
+ break;
+ case 301: /* Moved Permanently */
+ /* (quote from RFC2616, section 10.3.2):
+ *
+ * Note: When automatically redirecting a POST request after receiving a
+ * 301 status code, some existing HTTP/1.0 user agents will erroneously
+ * change it into a GET request.
+ *
+ * ----
+ *
+ * Warning: Because most of importants user agents do this obvious RFC2616
+ * violation, many webservers expect this misbehavior. So these servers
+ * often answers to a POST request with an error page. To be sure that
+ * libcurl gets the page that most user agents would get, libcurl has to
+ * force GET:
+ */
+ if( data->set.httpreq == HTTPREQ_POST
+ || data->set.httpreq == HTTPREQ_POST_FORM) {
+ infof(data,
+ "Violate RFC 2616/10.3.2 and switch from POST to GET\n");
+ data->set.httpreq = HTTPREQ_GET;
+ }
+ break;
+ case 302: /* Found */
+ /* (From 10.3.3)
+
+ Note: RFC 1945 and RFC 2068 specify that the client is not allowed
+ to change the method on the redirected request. However, most
+ existing user agent implementations treat 302 as if it were a 303
+ response, performing a GET on the Location field-value regardless
+ of the original request method. The status codes 303 and 307 have
+ been added for servers that wish to make unambiguously clear which
+ kind of reaction is expected of the client.
+
+ (From 10.3.4)
+
+ Note: Many pre-HTTP/1.1 user agents do not understand the 303
+ status. When interoperability with such clients is a concern, the
+ 302 status code may be used instead, since most user agents react
+ to a 302 response as described here for 303.
+ */
+ case 303: /* See Other */
+ /* Disable both types of POSTs, since doing a second POST when
+ * following isn't what anyone would want! */
+ if(data->set.httpreq != HTTPREQ_GET) {
+ data->set.httpreq = HTTPREQ_GET; /* enforce GET request */
+ infof(data, "Disables POST, goes with %s\n",
+ data->set.opt_no_body?"HEAD":"GET");
+ }
+ break;
+ case 304: /* Not Modified */
+ /* 304 means we did a conditional request and it was "Not modified".
+ * We shouldn't get any Location: header in this response!
+ */
+ break;
+ case 305: /* Use Proxy */
+ /* (quote from RFC2616, section 10.3.6):
+ * "The requested resource MUST be accessed through the proxy given
+ * by the Location field. The Location field gives the URI of the
+ * proxy. The recipient is expected to repeat this single request
+ * via the proxy. 305 responses MUST only be generated by origin
+ * servers."
+ */
+ break;
+ }
+ Curl_pgrsTime(data, TIMER_REDIRECT);
+ Curl_pgrsResetTimes(data);
+
+ return CURLE_OK;
+}
+
+static CURLcode
+Curl_connect_host(struct SessionHandle *data,
+ struct connectdata **conn)
+{
+ CURLcode res = CURLE_OK;
+ int urlchanged = FALSE;
+
+ do {
+ bool async;
+ bool protocol_done=TRUE; /* will be TRUE always since this is only used
+ within the easy interface */
+ Curl_pgrsTime(data, TIMER_STARTSINGLE);
+ data->change.url_changed = FALSE;
+ res = Curl_connect(data, conn, &async, &protocol_done);
+
+ if((CURLE_OK == res) && async) {
+ /* Now, if async is TRUE here, we need to wait for the name
+ to resolve */
+ res = Curl_wait_for_resolv(*conn, NULL);
+ if(CURLE_OK == res)
+ /* Resolved, continue with the connection */
+ res = Curl_async_resolved(*conn, &protocol_done);
+ else
+ /* if we can't resolve, we kill this "connection" now */
+ (void)Curl_disconnect(*conn);
+ }
+ if(res)
+ break;
+
+ /* If a callback (or something) has altered the URL we should use within
+ the Curl_connect(), we detect it here and act as if we are redirected
+ to the new URL */
+ urlchanged = data->change.url_changed;
+ if ((CURLE_OK == res) && urlchanged) {
+ res = Curl_done(conn, res, FALSE);
+ if(CURLE_OK == res) {
+ char *gotourl = strdup(data->change.url);
+ res = Curl_follow(data, gotourl, FALSE);
+ if(res)
+ free(gotourl);
+ }
+ }
+ } while (urlchanged && res == CURLE_OK);
+
+ return res;
+}
+
+/* Returns TRUE and sets '*url' if a request retry is wanted */
+bool Curl_retry_request(struct connectdata *conn,
+ char **url)
+{
+ bool retry = FALSE;
+ struct SessionHandle *data = conn->data;
+
+ if((data->reqdata.keep.bytecount+conn->headerbytecount == 0) &&
+ conn->bits.reuse &&
+ !conn->bits.no_body) {
+ /* We got no data, we attempted to re-use a connection and yet we want a
+ "body". This might happen if the connection was left alive when we were
+ done using it before, but that was closed when we wanted to read from
+ it again. Bad luck. Retry the same request on a fresh connect! */
+ infof(conn->data, "Connection died, retrying a fresh connect\n");
+ *url = strdup(conn->data->change.url);
+
+ conn->bits.close = TRUE; /* close this connection */
+ conn->bits.retry = TRUE; /* mark this as a connection we're about
+ to retry. Marking it this way should
+ prevent i.e HTTP transfers to return
+ error just because nothing has been
+ transfered! */
+ retry = TRUE;
+ }
+
+ return retry;
+}
+
+/*
+ * Curl_perform() is the internal high-level function that gets called by the
+ * external curl_easy_perform() function. It inits, performs and cleans up a
+ * single file transfer.
+ */
+CURLcode Curl_perform(struct SessionHandle *data)
+{
+ CURLcode res;
+ CURLcode res2;
+ struct connectdata *conn=NULL;
+ char *newurl = NULL; /* possibly a new URL to follow to! */
+ bool retry = FALSE;
+
+ data->state.used_interface = Curl_if_easy;
+
+ res = Curl_pretransfer(data);
+ if(res)
+ return res;
+
+ /*
+ * It is important that there is NO 'return' from this function at any other
+ * place than falling down to the end of the function! This is because we
+ * have cleanup stuff that must be done before we get back, and that is only
+ * performed after this do-while loop.
+ */
+
+ do {
+ res = Curl_connect_host(data, &conn); /* primary connection */
+
+ if(res == CURLE_OK) {
+ bool do_done;
+ if(data->set.connect_only) {
+ /* keep connection open for application to use the socket */
+ conn->bits.close = FALSE;
+ res = Curl_done(&conn, CURLE_OK, FALSE);
+ break;
+ }
+ res = Curl_do(&conn, &do_done);
+
+ if(res == CURLE_OK) {
+ res = Transfer(conn); /* now fetch that URL please */
+ if(res == CURLE_OK) {
+ retry = Curl_retry_request(conn, &newurl);
+
+ if(!retry)
+ /*
+ * We must duplicate the new URL here as the connection data may
+ * be free()ed in the Curl_done() function.
+ */
+ newurl = data->reqdata.newurl?strdup(data->reqdata.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;
+
+ if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
+ /* if we failed anywhere, we must clean up the secondary socket if
+ it was used */
+ sclose(conn->sock[SECONDARYSOCKET]);
+ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+ }
+ }
+
+ /* Always run Curl_done(), even if some of the previous calls
+ failed, but return the previous (original) error code */
+ res2 = Curl_done(&conn, res, FALSE);
+
+ if(CURLE_OK == res)
+ res = res2;
+ }
+ else
+ /* Curl_do() failed, clean up left-overs in the done-call */
+ res2 = Curl_done(&conn, res, FALSE);
+
+ /*
+ * Important: 'conn' cannot be used here, since it may have been closed
+ * in 'Curl_done' or other functions.
+ */
+
+ if((res == CURLE_OK) && newurl) {
+ res = Curl_follow(data, newurl, retry);
+ if(CURLE_OK == res) {
+ newurl = NULL;
+ continue;
+ }
+ }
+ }
+ break; /* it only reaches here when this shouldn't loop */
+
+ } while(1); /* loop if Location: */
+
+ if(newurl)
+ free(newurl);
+
+ if(res && !data->state.errorbuf) {
+ /*
+ * As an extra precaution: if no error string has been set and there was
+ * an error, use the strerror() string or if things are so bad that not
+ * even that is good, set a bad string that mentions the error code.
+ */
+ const char *str = curl_easy_strerror(res);
+ if(!str)
+ failf(data, "unspecified error %d", (int)res);
+ else
+ failf(data, "%s", str);
+ }
+
+ /* run post-transfer 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;
+}
+
+/*
+ * Curl_setup_transfer() is called to setup some basic properties for the
+ * upcoming transfer.
+ */
+CURLcode
+Curl_setup_transfer(
+ struct connectdata *c_conn, /* connection data */
+ int sockindex, /* socket index to read from or -1 */
+ curl_off_t size, /* -1 if unknown at this point */
+ bool getheader, /* TRUE if header parsing is wanted */
+ curl_off_t *bytecountp, /* return number of bytes read or NULL */
+ int writesockindex, /* socket index to write to, it may very
+ well be the same we read from. -1
+ disables */
+ curl_off_t *writecountp /* return number of bytes written or
+ NULL */
+ )
+{
+ struct connectdata *conn = (struct connectdata *)c_conn;
+ struct SessionHandle *data = conn->data;
+
+ if(!conn)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ curlassert((sockindex <= 1) && (sockindex >= -1));
+
+ /* now copy all input parameters */
+ conn->sockfd = sockindex == -1 ?
+ CURL_SOCKET_BAD : conn->sock[sockindex];
+ conn->writesockfd = writesockindex == -1 ?
+ CURL_SOCKET_BAD:conn->sock[writesockindex];
+ conn->bits.getheader = getheader;
+
+ data->reqdata.size = size;
+ data->reqdata.bytecountp = bytecountp;
+ data->reqdata.writebytecountp = writecountp;
+
+ return CURLE_OK;
+}
diff --git a/Utilities/cmcurl/transfer.h b/Utilities/cmcurl/transfer.h
new file mode 100644
index 000000000..3c415cc72
--- /dev/null
+++ b/Utilities/cmcurl/transfer.h
@@ -0,0 +1,51 @@
+#ifndef __TRANSFER_H
+#define __TRANSFER_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+CURLcode Curl_perform(struct SessionHandle *data);
+CURLcode Curl_pretransfer(struct SessionHandle *data);
+CURLcode Curl_second_connect(struct connectdata *conn);
+CURLcode Curl_posttransfer(struct SessionHandle *data);
+CURLcode Curl_follow(struct SessionHandle *data, char *newurl, bool retry);
+CURLcode Curl_readwrite(struct connectdata *conn, bool *done);
+int Curl_single_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks);
+CURLcode Curl_readwrite_init(struct connectdata *conn);
+CURLcode Curl_readrewind(struct connectdata *conn);
+CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp);
+bool Curl_retry_request(struct connectdata *conn, char **url);
+
+/* This sets up a forthcoming transfer */
+CURLcode
+Curl_setup_transfer (struct connectdata *data,
+ int sockindex, /* socket index to read from or -1 */
+ curl_off_t size, /* -1 if unknown at this point */
+ bool getheader, /* TRUE if header parsing is wanted */
+ curl_off_t *bytecountp, /* return number of bytes read */
+ int writesockindex, /* socket index to write to, it may
+ very well be the same we read from.
+ -1 disables */
+ curl_off_t *writecountp /* return number of bytes written */
+);
+#endif
diff --git a/Utilities/cmcurl/url.c b/Utilities/cmcurl/url.c
new file mode 100644
index 000000000..da12231f1
--- /dev/null
+++ b/Utilities/cmcurl/url.c
@@ -0,0 +1,4252 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/* -- WIN32 approved -- */
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <errno.h>
+
+#ifdef WIN32
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#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 VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifndef HAVE_SOCKET
+#error "We can't compile without socket() support!"
+#endif
+#endif
+
+#ifdef USE_LIBIDN
+#include <idna.h>
+#include <tld.h>
+#include <stringprep.h>
+#ifdef HAVE_IDN_FREE_H
+#include <idn-free.h>
+#else
+void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
+ libidn 0.4.5's make install! */
+#endif
+#ifndef HAVE_IDN_FREE
+/* if idn_free() was not found in this version of libidn, use plain free()
+ instead */
+#define idn_free(x) (free)(x)
+#endif
+#endif /* USE_LIBIDN */
+
+#include "urldata.h"
+#include "netrc.h"
+
+#include "formdata.h"
+#include "base64.h"
+#include "sslgen.h"
+#include "hostip.h"
+#include "transfer.h"
+#include "sendf.h"
+#include "progress.h"
+#include "cookie.h"
+#include "strequal.h"
+#include "strerror.h"
+#include "escape.h"
+#include "strtok.h"
+#include "share.h"
+#include "content_encoding.h"
+#include "http_digest.h"
+#include "http_negotiate.h"
+#include "select.h"
+#include "multiif.h"
+#include "easyif.h"
+
+/* And now for the protocols */
+#include "ftp.h"
+#include "dict.h"
+#include "telnet.h"
+#include "tftp.h"
+#include "http.h"
+#include "file.h"
+#include "ldap.h"
+#include "ssh.h"
+#include "url.h"
+#include "connect.h"
+#include "inet_ntop.h"
+#include "http_ntlm.h"
+#include "socks.h"
+#include <ca-bundle.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 HAVE_KRB4
+#include "krb4.h"
+#endif
+#include "memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* Local static prototypes */
+static long ConnectionKillOne(struct SessionHandle *data);
+static bool ConnectionExists(struct SessionHandle *data,
+ struct connectdata *needle,
+ struct connectdata **usethis);
+static long ConnectionStore(struct SessionHandle *data,
+ struct connectdata *conn);
+static bool IsPipeliningPossible(struct SessionHandle *handle);
+static bool IsPipeliningEnabled(struct SessionHandle *handle);
+static void conn_free(struct connectdata *conn);
+
+static void signalPipeClose(struct curl_llist *pipe);
+
+#define MAX_PIPELINE_LENGTH 5
+
+/*
+ * We use this ZERO_NULL to avoid picky compiler warnings,
+ * when assigning a NULL pointer to a function pointer var.
+ */
+
+#define ZERO_NULL 0
+
+#ifndef USE_ARES
+/* not for ares builds */
+
+#ifndef WIN32
+/* not for WIN32 builds */
+
+#ifdef HAVE_SIGSETJMP
+extern sigjmp_buf curl_jmpenv;
+#endif
+
+#ifdef SIGALRM
+static
+RETSIGTYPE alarmfunc(int sig)
+{
+ /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
+ (void)sig;
+#ifdef HAVE_SIGSETJMP
+ siglongjmp(curl_jmpenv, 1);
+#endif
+ /*return;*/ /* not reahed, and has no effect anyway */
+}
+#endif /* SIGALRM */
+#endif /* WIN32 */
+#endif /* USE_ARES */
+
+void Curl_safefree(void *ptr)
+{
+ if(ptr)
+ free(ptr);
+}
+
+static void close_connections(struct SessionHandle *data)
+{
+ /* Loop through all open connections and kill them one by one */
+ while(-1 != ConnectionKillOne(data))
+ ; /* empty loop */
+}
+
+/*
+ * This is the internal function curl_easy_cleanup() calls. This should
+ * cleanup and free all resources associated with this sessionhandle.
+ *
+ * NOTE: if we ever add something that attempts to write to a socket or
+ * similar here, we must ignore SIGPIPE first. It is currently only done
+ * when curl_easy_perform() is invoked.
+ */
+
+CURLcode Curl_close(struct SessionHandle *data)
+{
+ struct Curl_multi *m = data->multi;
+
+#ifdef CURLDEBUG
+ /* only for debugging, scan through all connections and see if there's a
+ pipe reference still identifying this handle */
+
+ if(data->state.is_in_pipeline)
+ fprintf(stderr, "CLOSED when in pipeline!\n");
+
+ if(data->state.connc && data->state.connc->type == CONNCACHE_MULTI) {
+ struct conncache *c = data->state.connc;
+ int i;
+ struct curl_llist *pipe;
+ struct curl_llist_element *curr;
+ struct connectdata *connptr;
+
+ for(i=0; i< c->num; i++) {
+ connptr = c->connects[i];
+ if(!connptr)
+ continue;
+
+ pipe = connptr->send_pipe;
+ if(pipe) {
+ for (curr = pipe->head; curr; curr=curr->next) {
+ if(data == (struct SessionHandle *) curr->ptr) {
+ fprintf(stderr,
+ "MAJOR problem we %p are still in send pipe for %p done %d\n",
+ data, connptr, connptr->bits.done);
+ }
+ }
+ }
+ pipe = connptr->recv_pipe;
+ if(pipe) {
+ for (curr = pipe->head; curr; curr=curr->next) {
+ if(data == (struct SessionHandle *) curr->ptr) {
+ fprintf(stderr,
+ "MAJOR problem we %p are still in recv pipe for %p done %d\n",
+ data, connptr, connptr->bits.done);
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ if(m)
+ /* This handle is still part of a multi handle, take care of this first
+ and detach this handle from there. */
+ Curl_multi_rmeasy(data->multi, data);
+
+ data->magic = 0; /* force a clear AFTER the possibly enforced removal from
+ the multi handle, since that function uses the magic
+ field! */
+
+ if(data->state.connc) {
+
+ if(data->state.connc->type == CONNCACHE_PRIVATE) {
+ /* close all connections still alive that are in the private connection
+ cache, as we no longer have the pointer left to the shared one. */
+ close_connections(data);
+
+ /* free the connection cache if allocated privately */
+ Curl_rm_connc(data->state.connc);
+ }
+ }
+
+ if(data->state.shared_conn) {
+ /* marked to be used by a pending connection so we can't kill this handle
+ just yet */
+ data->state.closed = TRUE;
+ return CURLE_OK;
+ }
+
+ if ( ! (data->share && data->share->hostcache) ) {
+ if ( !Curl_global_host_cache_use(data)) {
+ Curl_hash_destroy(data->dns.hostcache);
+ }
+ }
+
+ /* Free the pathbuffer */
+ Curl_safefree(data->reqdata.pathbuffer);
+ Curl_safefree(data->reqdata.proto.generic);
+
+ /* Close down all open SSL info and sessions */
+ Curl_ssl_close_all(data);
+ Curl_safefree(data->state.first_host);
+ Curl_safefree(data->state.scratch);
+
+ if(data->change.referer_alloc)
+ free(data->change.referer);
+
+ if(data->change.url_alloc)
+ free(data->change.url);
+
+ Curl_safefree(data->state.headerbuff);
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+ if(data->set.cookiejar) {
+ if(data->change.cookielist) {
+ /* If there is a list of cookie files to read, do it first so that
+ we have all the told files read before we write the new jar */
+ Curl_cookie_loadfiles(data);
+ }
+
+ /* we have a "destination" for all the cookies to get dumped to */
+ if(Curl_cookie_output(data->cookies, data->set.cookiejar))
+ infof(data, "WARNING: failed to save cookies in %s\n",
+ data->set.cookiejar);
+ }
+ else {
+ if(data->change.cookielist)
+ /* since nothing is written, we can just free the list of cookie file
+ names */
+ curl_slist_free_all(data->change.cookielist); /* clean up list */
+ }
+
+ if( !data->share || (data->cookies != data->share->cookies) ) {
+ Curl_cookie_cleanup(data->cookies);
+ }
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+#endif
+
+ Curl_digest_cleanup(data);
+
+ Curl_safefree(data->info.contenttype);
+
+ /* this destroys the channel and we cannot use it anymore after this */
+ ares_destroy(data->state.areschannel);
+
+#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
+ /* close iconv conversion descriptors */
+ if (data->inbound_cd != (iconv_t)-1) {
+ iconv_close(data->inbound_cd);
+ }
+ if (data->outbound_cd != (iconv_t)-1) {
+ iconv_close(data->outbound_cd);
+ }
+ if (data->utf8_cd != (iconv_t)-1) {
+ iconv_close(data->utf8_cd);
+ }
+#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
+
+ /* No longer a dirty share, if it exists */
+ if (data->share)
+ data->share->dirty--;
+
+ free(data);
+ return CURLE_OK;
+}
+
+/* create a connection cache of a private or multi type */
+struct conncache *Curl_mk_connc(int type,
+ int amount) /* set -1 to use default */
+{
+ /* It is subject for debate how many default connections to have for a multi
+ connection cache... */
+ int default_amount = amount == -1?
+ ((type == CONNCACHE_PRIVATE)?5:10):amount;
+ struct conncache *c;
+
+ c= calloc(sizeof(struct conncache), 1);
+ if(!c)
+ return NULL;
+
+ c->connects = calloc(sizeof(struct connectdata *), default_amount);
+ if(!c->connects) {
+ free(c);
+ return NULL;
+ }
+
+ c->num = default_amount;
+
+ return c;
+}
+
+/* Change number of entries of a connection cache */
+CURLcode Curl_ch_connc(struct SessionHandle *data,
+ struct conncache *c,
+ long newamount)
+{
+ long i;
+ struct connectdata **newptr;
+
+ if(newamount < 1)
+ newamount = 1; /* we better have at least one entry */
+
+ if(!c) {
+ /* we get a NULL pointer passed in as connection cache, which means that
+ there is no cache created for this SessionHandle just yet, we create a
+ brand new with the requested size.
+ */
+ data->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, newamount);
+ if(!data->state.connc)
+ return CURLE_OUT_OF_MEMORY;
+ return CURLE_OK;
+ }
+
+ if(newamount < c->num) {
+ /* 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!
+
+ NOTE: for conncache_multi cases we must make sure that we only
+ close handles not in use.
+ */
+ for(i=newamount; i< c->num; i++)
+ Curl_disconnect(c->connects[i]);
+
+ /* If the most recent connection is no longer valid, mark it
+ invalid. */
+ if(data->state.lastconnect <= newamount)
+ data->state.lastconnect = -1;
+ }
+ if(newamount > 0) {
+ newptr= (struct connectdata **)
+ realloc(c->connects, sizeof(struct connectdata *) * newamount);
+ if(!newptr)
+ /* we closed a few connections in vain, but so what? */
+ return CURLE_OUT_OF_MEMORY;
+
+ /* nullify the newly added pointers */
+ for(i=c->num; i<newamount; i++)
+ newptr[i] = NULL;
+
+ c->connects = newptr;
+ c->num = newamount;
+ }
+ /* we no longer support less than 1 as size for the connection cache, and
+ I'm not sure it ever worked to set it to zero */
+ return CURLE_OK;
+}
+
+/* Free a connection cache. This is called from Curl_close() and
+ curl_multi_cleanup(). */
+void Curl_rm_connc(struct conncache *c)
+{
+ if(c->connects) {
+ int i;
+ for(i = 0; i < c->num; ++i)
+ conn_free(c->connects[i]);
+
+ free(c->connects);
+ }
+
+ free(c);
+}
+
+/**
+ * Curl_open()
+ *
+ * @param curl is a pointer to a sessionhandle pointer that gets set by this
+ * function.
+ * @return CURLcode
+ */
+
+CURLcode Curl_open(struct SessionHandle **curl)
+{
+ CURLcode res = CURLE_OK;
+ struct SessionHandle *data;
+
+ /* Very simple start-up: alloc the struct, init it with zeroes and return */
+ data = (struct SessionHandle *)calloc(1, sizeof(struct SessionHandle));
+ if(!data)
+ /* this is a very serious error */
+ return CURLE_OUT_OF_MEMORY;
+
+ data->magic = CURLEASY_MAGIC_NUMBER;
+
+#ifdef USE_ARES
+ if(ARES_SUCCESS != ares_init(&data->state.areschannel)) {
+ free(data);
+ return CURLE_FAILED_INIT;
+ }
+ /* make sure that all other returns from this function should destroy the
+ ares channel before returning error! */
+#endif
+
+ /* 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)
+ res = CURLE_OUT_OF_MEMORY;
+ else {
+ 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;
+
+ /* conversion callbacks for non-ASCII hosts */
+ data->set.convfromnetwork = (curl_conv_callback)ZERO_NULL;
+ data->set.convtonetwork = (curl_conv_callback)ZERO_NULL;
+ data->set.convfromutf8 = (curl_conv_callback)ZERO_NULL;
+
+#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
+ /* conversion descriptors for iconv calls */
+ data->outbound_cd = (iconv_t)-1;
+ data->inbound_cd = (iconv_t)-1;
+ data->utf8_cd = (iconv_t)-1;
+#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
+
+ data->set.infilesize = -1; /* we don't know any size */
+ data->set.postfieldsize = -1;
+ data->set.maxredirs = -1; /* allow any amount by default */
+ 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.ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */
+ data->set.ftp_filemethod = FTPFILE_MULTICWD;
+ 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;
+
+ data->set.proxyport = 1080;
+ data->set.proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
+ data->set.httpauth = CURLAUTH_BASIC; /* defaults to basic */
+ data->set.proxyauth = CURLAUTH_BASIC; /* defaults to basic */
+
+ /* This no longer creates a connection cache here. It is instead made on
+ the first call to curl_easy_perform() or when the handle is added to a
+ multi stack. */
+
+ data->set.ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth
+ type */
+
+ /* most recent connection is not yet defined */
+ data->state.lastconnect = -1;
+
+ Curl_easy_initHandleData(data);
+
+ /*
+ * libcurl 7.10 introduced SSL verification *by default*! This needs to be
+ * switched off unless wanted.
+ */
+ data->set.ssl.verifypeer = TRUE;
+ data->set.ssl.verifyhost = 2;
+ data->set.ssl.sessionid = TRUE; /* session ID caching enabled by default */
+#ifdef CURL_CA_BUNDLE
+ /* This is our preferred CA cert bundle since install time */
+ data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE;
+#endif
+ }
+
+ if(res) {
+ ares_destroy(data->state.areschannel);
+ if(data->state.headerbuff)
+ free(data->state.headerbuff);
+ free(data);
+ data = NULL;
+ }
+ else
+ *curl = data;
+
+ return res;
+}
+
+CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
+ va_list param)
+{
+ char *argptr;
+ CURLcode result = CURLE_OK;
+
+ 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 = (bool)(0 != 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.
+ */
+ result = Curl_ch_connc(data, data->state.connc, va_arg(param, long));
+ break;
+ case CURLOPT_FORBID_REUSE:
+ /*
+ * When this transfer is done, it must not be left to be reused by a
+ * subsequent transfer but shall be closed immediately.
+ */
+ data->set.reuse_forbid = (bool)(0 != va_arg(param, long));
+ 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 = (bool)(0 != va_arg(param, long));
+ 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 = (bool)(0 != va_arg(param, long));
+ break;
+ case CURLOPT_HEADER:
+ /*
+ * Set to include the header in the general data output stream.
+ */
+ data->set.include_header = (bool)(0 != va_arg(param, long));
+ break;
+ case CURLOPT_NOPROGRESS:
+ /*
+ * Shut off the internal supported progress meter
+ */
+ data->set.hide_progress = (bool)(0 != va_arg(param, long));
+ if(data->set.hide_progress)
+ data->progress.flags |= PGRS_HIDE;
+ else
+ data->progress.flags &= ~PGRS_HIDE;
+ break;
+ case CURLOPT_NOBODY:
+ /*
+ * Do not include the body part in the output data stream.
+ */
+ data->set.opt_no_body = (bool)(0 != va_arg(param, long));
+ if(data->set.opt_no_body)
+ /* in HTTP lingo, this means using the HEAD request */
+ data->set.httpreq = HTTPREQ_HEAD;
+ break;
+ case CURLOPT_FAILONERROR:
+ /*
+ * Don't output the >=300 error code HTML-page, but instead only
+ * return error.
+ */
+ data->set.http_fail_on_error = (bool)(0 != va_arg(param, long));
+ break;
+ case CURLOPT_UPLOAD:
+ case CURLOPT_PUT:
+ /*
+ * We want to sent data to the remote host. If this is HTTP, that equals
+ * using the PUT request.
+ */
+ data->set.upload = (bool)(0 != va_arg(param, long));
+ 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 = (bool)(0 != va_arg(param, long));
+ break;
+ case CURLOPT_FTP_CREATE_MISSING_DIRS:
+ /*
+ * An FTP option that modifies an upload to create missing directories on
+ * the server.
+ */
+ data->set.ftp_create_missing_dirs = (bool)(0 != va_arg(param, long));
+ break;
+ case CURLOPT_FTP_RESPONSE_TIMEOUT:
+ /*
+ * An FTP option that specifies how quickly an FTP response must be
+ * obtained before it is considered failure.
+ */
+ data->set.ftp_response_timeout = va_arg( param , long );
+ 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 = (bool)(0 != va_arg(param, long));
+ break;
+ case CURLOPT_FTPAPPEND:
+ /*
+ * We want to upload and append to an existing (FTP) file.
+ */
+ data->set.ftp_append = (bool)(0 != va_arg(param, long));
+ break;
+ case CURLOPT_FTP_FILEMETHOD:
+ /*
+ * How do access files over FTP.
+ */
+ data->set.ftp_filemethod = (curl_ftpfile)va_arg(param, long);
+ break;
+ case CURLOPT_NETRC:
+ /*
+ * Parse the $HOME/.netrc file
+ */
+ data->set.use_netrc = (enum CURL_NETRC_OPTION)va_arg(param, long);
+ break;
+ case CURLOPT_NETRC_FILE:
+ /*
+ * Use this file instead of the $HOME/.netrc file
+ */
+ data->set.netrc_file = va_arg(param, char *);
+ break;
+ case CURLOPT_TRANSFERTEXT:
+ /*
+ * This option was previously named 'FTPASCII'. Renamed to work with
+ * more protocols than merely FTP.
+ *
+ * Transfer using ASCII (instead of BINARY).
+ */
+ data->set.prefer_ascii = (bool)(0 != va_arg(param, long));
+ break;
+ case CURLOPT_TIMECONDITION:
+ /*
+ * Set HTTP time condition. This must be one of the defines in the
+ * curl/curl.h header file.
+ */
+ data->set.timecondition = (curl_TimeCond)va_arg(param, long);
+ break;
+ case CURLOPT_TIMEVALUE:
+ /*
+ * This is the value to compare with the remote document with the
+ * method set with CURLOPT_TIMECONDITION
+ */
+ data->set.timevalue = (time_t)va_arg(param, long);
+ break;
+ case CURLOPT_SSLVERSION:
+ /*
+ * Set explicit SSL version to try to connect with, as some SSL
+ * implementations are lame.
+ */
+ data->set.ssl.version = va_arg(param, long);
+ break;
+
+#ifndef CURL_DISABLE_HTTP
+ case CURLOPT_AUTOREFERER:
+ /*
+ * Switch on automatic referer that gets set if curl follows locations.
+ */
+ data->set.http_auto_referer = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_ENCODING:
+ /*
+ * String to use at the value of Accept-Encoding header.
+ *
+ * If the encoding is set to "" we use an Accept-Encoding header that
+ * encompasses all the encodings we support.
+ * If the encoding is set to NULL we don't send an Accept-Encoding header
+ * and ignore an received Content-Encoding header.
+ *
+ */
+ data->set.encoding = va_arg(param, char *);
+ if(data->set.encoding && !*data->set.encoding)
+ data->set.encoding = (char*)ALL_CONTENT_ENCODINGS;
+ break;
+
+ case CURLOPT_FOLLOWLOCATION:
+ /*
+ * Follow Location: header hints on a HTTP-server.
+ */
+ data->set.http_follow_location = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_UNRESTRICTED_AUTH:
+ /*
+ * Send authentication (user+password) when following locations, even when
+ * hostname changed.
+ */
+ data->set.http_disable_hostname_check_before_authentication =
+ (bool)(0 != 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_POST:
+ /* Does this option serve a purpose anymore? Yes it does, when
+ CURLOPT_POSTFIELDS isn't used and the POST data is read off the
+ callback! */
+ if(va_arg(param, long)) {
+ data->set.httpreq = HTTPREQ_POST;
+ data->set.opt_no_body = FALSE; /* this is implied */
+ }
+ else
+ data->set.httpreq = HTTPREQ_GET;
+ break;
+
+ case CURLOPT_POSTFIELDS:
+ /*
+ * A string with POST data. Makes curl HTTP POST. Even if it is NULL.
+ */
+ data->set.postfields = va_arg(param, char *);
+ data->set.httpreq = HTTPREQ_POST;
+ break;
+
+ case CURLOPT_POSTFIELDSIZE:
+ /*
+ * The size of the POSTFIELD data to prevent libcurl to do strlen() to
+ * figure it out. Enables binary posts.
+ */
+ data->set.postfieldsize = va_arg(param, long);
+ break;
+
+ case CURLOPT_POSTFIELDSIZE_LARGE:
+ /*
+ * The size of the POSTFIELD data to prevent libcurl to do strlen() to
+ * figure it out. Enables binary posts.
+ */
+ data->set.postfieldsize = va_arg(param, curl_off_t);
+ break;
+
+ case CURLOPT_HTTPPOST:
+ /*
+ * Set to make us do HTTP POST
+ */
+ data->set.httppost = va_arg(param, struct curl_httppost *);
+ data->set.httpreq = HTTPREQ_POST_FORM;
+ data->set.opt_no_body = FALSE; /* this is implied */
+ break;
+
+ case CURLOPT_REFERER:
+ /*
+ * String to set in the HTTP Referer: field.
+ */
+ if(data->change.referer_alloc) {
+ 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_USERAGENT:
+ /*
+ * String to use in the HTTP User-Agent field
+ */
+ data->set.useragent = va_arg(param, char *);
+ break;
+
+ case CURLOPT_HTTPHEADER:
+ /*
+ * Set a list with HTTP headers to use (or replace internals with)
+ */
+ data->set.headers = va_arg(param, struct curl_slist *);
+ break;
+
+ case CURLOPT_HTTP200ALIASES:
+ /*
+ * Set a list of aliases for HTTP 200 in response header
+ */
+ data->set.http200aliases = va_arg(param, struct curl_slist *);
+ break;
+
+#if !defined(CURL_DISABLE_COOKIES)
+ case CURLOPT_COOKIE:
+ /*
+ * Cookie string to send to the remote server in the request.
+ */
+ data->set.cookie = va_arg(param, char *);
+ break;
+
+ case CURLOPT_COOKIEFILE:
+ /*
+ * Set cookie file to read and parse. Can be used multiple times.
+ */
+ argptr = (char *)va_arg(param, void *);
+ if(argptr) {
+ struct curl_slist *cl;
+ /* append the cookie file name to the list of file names, and deal with
+ them later */
+ cl = curl_slist_append(data->change.cookielist, argptr);
+
+ if(!cl)
+ return CURLE_OUT_OF_MEMORY;
+
+ data->change.cookielist = cl;
+ }
+ 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(data, NULL, data->cookies,
+ data->set.cookiesession);
+ break;
+
+ case CURLOPT_COOKIESESSION:
+ /*
+ * Set this option to TRUE to start a new "cookie session". It will
+ * prevent the forthcoming read-cookies-from-file actions to accept
+ * cookies that are marked as being session cookies, as they belong to a
+ * previous session.
+ *
+ * In the original Netscape cookie spec, "session cookies" are cookies
+ * with no expire date set. RFC2109 describes the same action if no
+ * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds
+ * a 'Discard' action that can enforce the discard even for cookies that
+ * have a Max-Age.
+ *
+ * We run mostly with the original cookie spec, as hardly anyone implements
+ * anything else.
+ */
+ data->set.cookiesession = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_COOKIELIST:
+ argptr = va_arg(param, char *);
+
+ if(argptr == NULL)
+ break;
+
+ if(strequal(argptr, "ALL")) {
+ /* clear all cookies */
+ Curl_cookie_clearall(data->cookies);
+ break;
+ }
+ else if(strequal(argptr, "SESS")) {
+ /* clear session cookies */
+ Curl_cookie_clearsess(data->cookies);
+ break;
+ }
+
+ if(!data->cookies)
+ /* if cookie engine was not running, activate it */
+ data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE);
+
+ argptr = strdup(argptr);
+ if(!argptr) {
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ if(checkprefix("Set-Cookie:", argptr))
+ /* HTTP Header format line */
+ Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL);
+
+ else
+ /* Netscape format line */
+ Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL);
+
+ free(argptr);
+ break;
+#endif /* CURL_DISABLE_COOKIES */
+
+ case CURLOPT_HTTPGET:
+ /*
+ * Set to force us do HTTP GET
+ */
+ if(va_arg(param, long)) {
+ data->set.httpreq = HTTPREQ_GET;
+ data->set.upload = FALSE; /* switch off upload */
+ data->set.opt_no_body = FALSE; /* this is implied */
+ }
+ break;
+
+ case CURLOPT_HTTP_VERSION:
+ /*
+ * This sets a requested HTTP version to be used. The value is one of
+ * the listed enums in curl/curl.h.
+ */
+ data->set.httpversion = va_arg(param, long);
+ break;
+
+ case CURLOPT_HTTPPROXYTUNNEL:
+ /*
+ * Tunnel operations through the proxy instead of normal proxy use
+ */
+ data->set.tunnel_thru_httpproxy = (bool)(0 != va_arg(param, long));
+ 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_PROXYPORT:
+ /*
+ * Explicitly set HTTP proxy port number.
+ */
+ data->set.proxyport = va_arg(param, long);
+ break;
+
+ case CURLOPT_HTTPAUTH:
+ /*
+ * Set HTTP Authentication type BITMASK.
+ */
+ {
+ long auth = va_arg(param, long);
+ /* switch off bits we can't support */
+#ifndef USE_NTLM
+ auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
+#endif
+#ifndef HAVE_GSSAPI
+ auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */
+#endif
+ if(!auth)
+ return CURLE_FAILED_INIT; /* no supported types left! */
+
+ data->set.httpauth = auth;
+ }
+ break;
+
+ case CURLOPT_PROXYAUTH:
+ /*
+ * Set HTTP Authentication type BITMASK.
+ */
+ {
+ long auth = va_arg(param, long);
+ /* switch off bits we can't support */
+#ifndef USE_NTLM
+ auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
+#endif
+#ifndef HAVE_GSSAPI
+ auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */
+#endif
+ if(!auth)
+ return CURLE_FAILED_INIT; /* no supported types left! */
+
+ data->set.proxyauth = auth;
+ }
+ break;
+#endif /* CURL_DISABLE_HTTP */
+
+ case CURLOPT_PROXY:
+ /*
+ * Set proxy server:port to use as HTTP proxy.
+ *
+ * If the proxy is set to "" we explicitly say that we don't want to use a
+ * proxy (even though there might be environment variables saying so).
+ *
+ * Setting it to NULL, means no proxy but allows the environment variables
+ * to decide for us.
+ */
+ data->set.proxy = va_arg(param, char *);
+ break;
+
+ case CURLOPT_WRITEHEADER:
+ /*
+ * Custom pointer to pass the header write callback function
+ */
+ data->set.writeheader = (void *)va_arg(param, void *);
+ break;
+ case CURLOPT_ERRORBUFFER:
+ /*
+ * Error buffer provided by the caller to get the human readable
+ * error string in.
+ */
+ data->set.errorbuffer = va_arg(param, char *);
+ break;
+ case CURLOPT_FILE:
+ /*
+ * FILE pointer to write to or include in the data write callback
+ */
+ data->set.out = va_arg(param, FILE *);
+ break;
+ case CURLOPT_FTPPORT:
+ /*
+ * Use FTP PORT, this also specifies which IP address to use
+ */
+ data->set.ftpport = va_arg(param, char *);
+ data->set.ftp_use_port = (bool)(NULL != data->set.ftpport);
+ break;
+
+ case CURLOPT_FTP_USE_EPRT:
+ data->set.ftp_use_eprt = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_FTP_USE_EPSV:
+ data->set.ftp_use_epsv = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_FTP_SSL_CCC:
+ data->set.ftp_use_ccc = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_FTP_SKIP_PASV_IP:
+ /*
+ * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the
+ * bypass of the IP address in PASV responses.
+ */
+ data->set.ftp_skip_ip = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_INFILE:
+ /*
+ * FILE pointer to read the file to be uploaded from. Or possibly
+ * used as argument to the read callback.
+ */
+ data->set.in = va_arg(param, FILE *);
+ break;
+ case CURLOPT_INFILESIZE:
+ /*
+ * If known, this should inform curl about the file size of the
+ * to-be-uploaded file.
+ */
+ data->set.infilesize = va_arg(param, long);
+ break;
+ case CURLOPT_INFILESIZE_LARGE:
+ /*
+ * If known, this should inform curl about the file size of the
+ * to-be-uploaded file.
+ */
+ data->set.infilesize = va_arg(param, curl_off_t);
+ break;
+ case CURLOPT_LOW_SPEED_LIMIT:
+ /*
+ * The low speed limit that if transfers are below this for
+ * CURLOPT_LOW_SPEED_TIME, the transfer is aborted.
+ */
+ data->set.low_speed_limit=va_arg(param, long);
+ break;
+ case CURLOPT_MAX_SEND_SPEED_LARGE:
+ /*
+ * The max speed limit that sends transfer more than
+ * CURLOPT_MAX_SEND_PER_SECOND bytes per second the transfer is
+ * throttled..
+ */
+ data->set.max_send_speed=va_arg(param, curl_off_t);
+ break;
+ case CURLOPT_MAX_RECV_SPEED_LARGE:
+ /*
+ * The max speed limit that sends transfer more than
+ * CURLOPT_MAX_RECV_PER_SECOND bytes per second the transfer is
+ * throttled..
+ */
+ data->set.max_recv_speed=va_arg(param, curl_off_t);
+ break;
+ case CURLOPT_LOW_SPEED_TIME:
+ /*
+ * The low speed time that if transfers are below the set
+ * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted.
+ */
+ data->set.low_speed_time=va_arg(param, long);
+ break;
+ case CURLOPT_URL:
+ /*
+ * The URL to fetch.
+ */
+ if(data->change.url_alloc) {
+ /* the already set URL is allocated, free it first! */
+ free(data->change.url);
+ data->change.url_alloc=FALSE;
+ }
+ data->set.set_url = va_arg(param, char *);
+ data->change.url = data->set.set_url;
+ data->change.url_changed = TRUE;
+ break;
+ case CURLOPT_PORT:
+ /*
+ * The port number to use when getting the URL
+ */
+ data->set.use_port = va_arg(param, long);
+ break;
+ case CURLOPT_TIMEOUT:
+ /*
+ * The maximum time you allow curl to use for a single transfer
+ * operation.
+ */
+ data->set.timeout = va_arg(param, long);
+ break;
+ case CURLOPT_CONNECTTIMEOUT:
+ /*
+ * The maximum time you allow curl to use to connect.
+ */
+ data->set.connecttimeout = va_arg(param, long);
+ 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);
+ if(data->set.fprogress)
+ data->progress.callback = TRUE; /* no longer internal */
+ else
+ data->progress.callback = FALSE; /* NULL enforces internal */
+
+ break;
+ case CURLOPT_PROGRESSDATA:
+ /*
+ * Custom client data to pass to the progress callback
+ */
+ data->set.progress_client = va_arg(param, void *);
+ break;
+ 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_RESUME_FROM_LARGE:
+ /*
+ * Resume transfer at the give file position
+ */
+ data->set.set_resume_from = va_arg(param, curl_off_t);
+ break;
+ case CURLOPT_DEBUGFUNCTION:
+ /*
+ * stderr write callback.
+ */
+ data->set.fdebug = va_arg(param, curl_debug_callback);
+ /*
+ * if the callback provided is NULL, it'll use the default callback
+ */
+ break;
+ case CURLOPT_DEBUGDATA:
+ /*
+ * Set to a void * that should receive all error writes. This
+ * defaults to CURLOPT_STDERR for normal operations.
+ */
+ data->set.debugdata = va_arg(param, void *);
+ break;
+ case CURLOPT_STDERR:
+ /*
+ * Set to a FILE * that should receive all error writes. This
+ * defaults to stderr for normal operations.
+ */
+ data->set.err = va_arg(param, FILE *);
+ if(!data->set.err)
+ data->set.err = stderr;
+ break;
+ case CURLOPT_HEADERFUNCTION:
+ /*
+ * Set header write callback
+ */
+ data->set.fwrite_header = va_arg(param, curl_write_callback);
+ break;
+ case CURLOPT_WRITEFUNCTION:
+ /*
+ * Set data write callback
+ */
+ data->set.fwrite = va_arg(param, curl_write_callback);
+ if(!data->set.fwrite)
+ /* When set to NULL, reset to our internal default function */
+ data->set.fwrite = (curl_write_callback)fwrite;
+ break;
+ case CURLOPT_READFUNCTION:
+ /*
+ * Read data callback
+ */
+ data->set.fread = va_arg(param, curl_read_callback);
+ if(!data->set.fread)
+ /* When set to NULL, reset to our internal default function */
+ data->set.fread = (curl_read_callback)fread;
+ break;
+ case CURLOPT_CONV_FROM_NETWORK_FUNCTION:
+ /*
+ * "Convert from network encoding" callback
+ */
+ data->set.convfromnetwork = va_arg(param, curl_conv_callback);
+ break;
+ case CURLOPT_CONV_TO_NETWORK_FUNCTION:
+ /*
+ * "Convert to network encoding" callback
+ */
+ data->set.convtonetwork = va_arg(param, curl_conv_callback);
+ break;
+ case CURLOPT_CONV_FROM_UTF8_FUNCTION:
+ /*
+ * "Convert from UTF-8 encoding" callback
+ */
+ data->set.convfromutf8 = va_arg(param, curl_conv_callback);
+ break;
+ case CURLOPT_IOCTLFUNCTION:
+ /*
+ * I/O control callback. Might be NULL.
+ */
+ data->set.ioctl = va_arg(param, curl_ioctl_callback);
+ break;
+ case CURLOPT_IOCTLDATA:
+ /*
+ * I/O control data pointer. Might be NULL.
+ */
+ data->set.ioctl_client = va_arg(param, void *);
+ break;
+ case CURLOPT_SSLCERT:
+ /*
+ * String that holds file name of the SSL certificate to use
+ */
+ 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.
+ */
+ argptr = va_arg(param, char *);
+ if (argptr && argptr[0])
+ result = Curl_ssl_set_engine(data, argptr);
+ break;
+
+ case CURLOPT_SSLENGINE_DEFAULT:
+ /*
+ * flag to set engine as default.
+ */
+ result = Curl_ssl_set_engine_default(data);
+ break;
+ case CURLOPT_CRLF:
+ /*
+ * Kludgy option to enable CRLF conversions. Subject for removal.
+ */
+ data->set.crlf = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_INTERFACE:
+ /*
+ * Set what interface or address/hostname to bind the socket to when
+ * performing an operation and thus what from-IP your connection will use.
+ */
+ data->set.device = va_arg(param, char *);
+ break;
+ case CURLOPT_LOCALPORT:
+ /*
+ * Set what local port to bind the socket to when performing an operation.
+ */
+ data->set.localport = (unsigned short) va_arg(param, long);
+ break;
+ case CURLOPT_LOCALPORTRANGE:
+ /*
+ * Set number of local ports to try, starting with CURLOPT_LOCALPORT.
+ */
+ data->set.localportrange = (int) va_arg(param, long);
+ break;
+ case CURLOPT_KRB4LEVEL:
+ /*
+ * A string that defines the krb4 security level.
+ */
+ data->set.krb4_level = va_arg(param, char *);
+ data->set.krb4 = (bool)(NULL != data->set.krb4_level);
+ 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_SSL_CTX_FUNCTION:
+ /*
+ * Set a SSL_CTX callback
+ */
+ data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback);
+ break;
+ case CURLOPT_SSL_CTX_DATA:
+ /*
+ * Set a SSL_CTX callback parameter pointer
+ */
+ data->set.ssl.fsslctxp = va_arg(param, void *);
+ break;
+ case CURLOPT_CAINFO:
+ /*
+ * Set CA info for SSL connection. Specify file name of the CA certificate
+ */
+ data->set.ssl.CAfile = va_arg(param, char *);
+ break;
+ case CURLOPT_CAPATH:
+ /*
+ * Set CA path info for SSL connection. Specify directory name of the CA
+ * certificates which have been prepared using openssl c_rehash utility.
+ */
+ /* This does not work on windows. */
+ data->set.ssl.CApath = va_arg(param, char *);
+ break;
+ case CURLOPT_TELNETOPTIONS:
+ /*
+ * Set a linked list of telnet options
+ */
+ data->set.telnet_options = va_arg(param, struct curl_slist *);
+ break;
+
+ case CURLOPT_BUFFERSIZE:
+ /*
+ * The application kindly asks for a differently sized receive buffer.
+ * If it seems reasonable, we'll use it.
+ */
+ data->set.buffer_size = va_arg(param, long);
+
+ if((data->set.buffer_size> (BUFSIZE -1 )) ||
+ (data->set.buffer_size < 1))
+ data->set.buffer_size = 0; /* huge internal default */
+
+ break;
+
+ case CURLOPT_NOSIGNAL:
+ /*
+ * The application asks not to set any signal() or alarm() handlers,
+ * even when using a timeout.
+ */
+ data->set.no_signal = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_SHARE:
+ {
+ struct Curl_share *set;
+ set = va_arg(param, struct Curl_share *);
+
+ /* disconnect from old share, if any */
+ if(data->share) {
+ Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
+
+ if(data->dns.hostcachetype == HCACHE_SHARED) {
+ data->dns.hostcache = NULL;
+ data->dns.hostcachetype = HCACHE_NONE;
+ }
+
+ if(data->share->cookies == data->cookies)
+ data->cookies = NULL;
+
+ data->share->dirty--;
+
+ Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
+ data->share = NULL;
+ }
+
+ /* use new share if it set */
+ data->share = set;
+ if(data->share) {
+
+ Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
+
+ data->share->dirty++;
+
+ if(data->share->hostcache) {
+ /* use shared host cache, first free the private one if any */
+ if(data->dns.hostcachetype == HCACHE_PRIVATE)
+ Curl_hash_destroy(data->dns.hostcache);
+
+ data->dns.hostcache = data->share->hostcache;
+ data->dns.hostcachetype = HCACHE_SHARED;
+ }
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ if(data->share->cookies) {
+ /* use shared cookie list, first free own one if any */
+ if (data->cookies)
+ Curl_cookie_cleanup(data->cookies);
+ data->cookies = data->share->cookies;
+ }
+#endif /* CURL_DISABLE_HTTP */
+ Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
+
+ }
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ /* check cookie list is set */
+ if(!data->cookies)
+ data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE );
+#endif /* CURL_DISABLE_HTTP */
+ /* check for host cache not needed,
+ * it will be done by curl_easy_perform */
+ }
+ break;
+
+ case CURLOPT_PROXYTYPE:
+ /*
+ * Set proxy type. HTTP/SOCKS4/SOCKS5
+ */
+ data->set.proxytype = (curl_proxytype)va_arg(param, long);
+ break;
+
+ case CURLOPT_PRIVATE:
+ /*
+ * Set private data pointer.
+ */
+ data->set.private_data = va_arg(param, char *);
+ break;
+
+ case CURLOPT_MAXFILESIZE:
+ /*
+ * Set the maximum size of a file to download.
+ */
+ data->set.max_filesize = va_arg(param, long);
+ break;
+
+ case CURLOPT_FTP_SSL:
+ /*
+ * Make FTP transfers attempt to use SSL/TLS.
+ */
+ data->set.ftp_ssl = (curl_ftpssl)va_arg(param, long);
+ break;
+
+ case CURLOPT_FTPSSLAUTH:
+ /*
+ * Set a specific auth for FTP-SSL transfers.
+ */
+ data->set.ftpsslauth = (curl_ftpauth)va_arg(param, long);
+ break;
+
+ case CURLOPT_IPRESOLVE:
+ data->set.ip_version = va_arg(param, long);
+ break;
+
+ case CURLOPT_MAXFILESIZE_LARGE:
+ /*
+ * Set the maximum size of a file to download.
+ */
+ data->set.max_filesize = va_arg(param, curl_off_t);
+ break;
+
+ case CURLOPT_TCP_NODELAY:
+ /*
+ * Enable or disable TCP_NODELAY, which will disable/enable the Nagle
+ * algorithm
+ */
+ data->set.tcp_nodelay = (bool)(0 != va_arg(param, long));
+ break;
+
+ /*
+ case CURLOPT_SOURCE_URL:
+ case CURLOPT_SOURCE_USERPWD:
+ case CURLOPT_SOURCE_QUOTE:
+ case CURLOPT_SOURCE_PREQUOTE:
+ case CURLOPT_SOURCE_POSTQUOTE:
+ These former 3rd party transfer options are deprecated */
+
+ case CURLOPT_FTP_ACCOUNT:
+ data->set.ftp_account = va_arg(param, char *);
+ break;
+
+ case CURLOPT_IGNORE_CONTENT_LENGTH:
+ data->set.ignorecl = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_CONNECT_ONLY:
+ /*
+ * No data transfer, set up connection and let application use the socket
+ */
+ data->set.connect_only = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_FTP_ALTERNATIVE_TO_USER:
+ data->set.ftp_alternative_to_user = va_arg(param, char *);
+ break;
+
+ case CURLOPT_SOCKOPTFUNCTION:
+ /*
+ * socket callback function: called after socket() but before connect()
+ */
+ data->set.fsockopt = va_arg(param, curl_sockopt_callback);
+ break;
+
+ case CURLOPT_SOCKOPTDATA:
+ /*
+ * socket callback data pointer. Might be NULL.
+ */
+ data->set.sockopt_client = va_arg(param, void *);
+ break;
+
+ case CURLOPT_SSL_SESSIONID_CACHE:
+ data->set.ssl.sessionid = (bool)(0 != va_arg(param, long));
+ break;
+
+ case CURLOPT_SSH_AUTH_TYPES:
+ data->set.ssh_auth_types = va_arg(param, long);
+ break;
+
+ case CURLOPT_SSH_PUBLIC_KEYFILE:
+ /*
+ * Use this file instead of the $HOME/.ssh/id_dsa.pub file
+ */
+ data->set.ssh_public_key = va_arg(param, char *);
+ break;
+
+ case CURLOPT_SSH_PRIVATE_KEYFILE:
+ /*
+ * Use this file instead of the $HOME/.ssh/id_dsa file
+ */
+ data->set.ssh_private_key = va_arg(param, char *);
+ break;
+
+ default:
+ /* unknown tag and its companion, just ignore: */
+ result = CURLE_FAILED_INIT; /* correct this */
+ break;
+ }
+
+ return result;
+}
+
+static void conn_free(struct connectdata *conn)
+{
+ if (!conn)
+ return;
+
+ /* close possibly still open sockets */
+ if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
+ sclose(conn->sock[SECONDARYSOCKET]);
+ if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET])
+ sclose(conn->sock[FIRSTSOCKET]);
+
+ Curl_safefree(conn->user);
+ Curl_safefree(conn->passwd);
+ Curl_safefree(conn->proxyuser);
+ Curl_safefree(conn->proxypasswd);
+ Curl_safefree(conn->allocptr.proxyuserpwd);
+ Curl_safefree(conn->allocptr.uagent);
+ Curl_safefree(conn->allocptr.userpwd);
+ Curl_safefree(conn->allocptr.accept_encoding);
+ Curl_safefree(conn->allocptr.rangeline);
+ Curl_safefree(conn->allocptr.ref);
+ Curl_safefree(conn->allocptr.host);
+ Curl_safefree(conn->allocptr.cookiehost);
+ Curl_safefree(conn->ip_addr_str);
+ Curl_safefree(conn->trailer);
+ Curl_safefree(conn->host.rawalloc); /* host name buffer */
+ Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */
+
+ Curl_llist_destroy(conn->send_pipe, NULL);
+ Curl_llist_destroy(conn->recv_pipe, NULL);
+
+ /* possible left-overs from the async name resolvers */
+#if defined(USE_ARES)
+ Curl_safefree(conn->async.hostname);
+ Curl_safefree(conn->async.os_specific);
+#elif defined(CURLRES_THREADED)
+ Curl_destroy_thread_data(&conn->async);
+#endif
+
+ Curl_free_ssl_config(&conn->ssl_config);
+
+ free(conn); /* free all the connection oriented data */
+}
+
+CURLcode Curl_disconnect(struct connectdata *conn)
+{
+ struct SessionHandle *data;
+ if(!conn)
+ return CURLE_OK; /* this is closed and fine already */
+ data = conn->data;
+
+ if(!data) {
+ DEBUGF(infof(data, "DISCONNECT without easy handle, ignoring\n"));
+ return CURLE_OK;
+ }
+
+#if defined(CURLDEBUG) && defined(AGGRESIVE_TEST)
+ /* scan for DNS cache entries still marked as in use */
+ Curl_hash_apply(data->hostcache,
+ NULL, Curl_scan_cache_used);
+#endif
+
+ Curl_expire(data, 0); /* shut off timers */
+ Curl_hostcache_prune(data); /* kill old DNS cache entries */
+
+ /*
+ * 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(data->reqdata.rangestringalloc) {
+ free(data->reqdata.range);
+ data->reqdata.rangestringalloc = FALSE;
+ }
+
+ if((conn->ntlm.state != NTLMSTATE_NONE) ||
+ (conn->proxyntlm.state != NTLMSTATE_NONE)) {
+ /* Authentication data is a mix of connection-related and sessionhandle-
+ related stuff. NTLM is connection-related so when we close the shop
+ we shall forget. */
+ data->state.authhost.done = FALSE;
+ data->state.authhost.picked =
+ data->state.authhost.want;
+
+ data->state.authproxy.done = FALSE;
+ data->state.authproxy.picked =
+ data->state.authproxy.want;
+
+ data->state.authproblem = FALSE;
+
+ Curl_ntlm_cleanup(conn);
+ }
+
+ if(conn->curl_disconnect)
+ /* This is set if protocol-specific cleanups should be made */
+ conn->curl_disconnect(conn);
+
+ if(-1 != conn->connectindex) {
+ /* unlink ourselves! */
+ infof(data, "Closing connection #%ld\n", conn->connectindex);
+ if(data->state.connc)
+ /* only clear the table entry if we still know in which cache we
+ used to be in */
+ data->state.connc->connects[conn->connectindex] = NULL;
+ }
+
+#ifdef USE_LIBIDN
+ if(conn->host.encalloc)
+ idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed
+ with idn_free() since this was allocated
+ by libidn */
+ if(conn->proxy.encalloc)
+ idn_free(conn->proxy.encalloc); /* encoded proxy name buffer, must be
+ freed with idn_free() since this was
+ allocated by libidn */
+#endif
+
+ Curl_ssl_close(conn);
+
+ /* Indicate to all handles on the pipe that we're dead */
+ if (IsPipeliningEnabled(data)) {
+ signalPipeClose(conn->send_pipe);
+ signalPipeClose(conn->recv_pipe);
+ }
+
+ conn_free(conn);
+
+ return CURLE_OK;
+}
+
+/*
+ * This function should return TRUE if the socket is to be assumed to
+ * be dead. Most commonly this happens when the server has closed the
+ * connection due to inactivity.
+ */
+static bool SocketIsDead(curl_socket_t sock)
+{
+ int sval;
+ bool ret_val = TRUE;
+
+ sval = Curl_select(sock, CURL_SOCKET_BAD, 0);
+ if(sval == 0)
+ /* timeout */
+ ret_val = FALSE;
+
+ return ret_val;
+}
+
+static bool IsPipeliningPossible(struct SessionHandle *handle)
+{
+ if (handle->multi && Curl_multi_canPipeline(handle->multi) &&
+ (handle->set.httpreq == HTTPREQ_GET ||
+ handle->set.httpreq == HTTPREQ_HEAD) &&
+ handle->set.httpversion != CURL_HTTP_VERSION_1_0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static bool IsPipeliningEnabled(struct SessionHandle *handle)
+{
+ if (handle->multi && Curl_multi_canPipeline(handle->multi))
+ return TRUE;
+
+ return FALSE;
+}
+
+void Curl_addHandleToPipeline(struct SessionHandle *data,
+ struct curl_llist *pipe)
+{
+#ifdef CURLDEBUG
+ if(!IsPipeliningPossible(data)) {
+ /* when not pipelined, there MUST be no handle in the list already */
+ if(pipe->head)
+ infof(data, "PIPE when no PIPE supposed!\n");
+ }
+#endif
+ Curl_llist_insert_next(pipe, pipe->tail, data);
+}
+
+
+int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
+ struct curl_llist *pipe)
+{
+ struct curl_llist_element *curr;
+
+ curr = pipe->head;
+ while (curr) {
+ if (curr->ptr == handle) {
+ Curl_llist_remove(pipe, curr, NULL);
+ return 1; /* we removed a handle */
+ }
+ curr = curr->next;
+ }
+
+ return 0;
+}
+
+#if 0 /* this code is saved here as it is useful for debugging purposes */
+static void Curl_printPipeline(struct curl_llist *pipe)
+{
+ struct curl_llist_element *curr;
+
+ curr = pipe->head;
+ while (curr) {
+ struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
+ infof(data, "Handle in pipeline: %s\n",
+ data->reqdata.path);
+ curr = curr->next;
+ }
+}
+#endif
+
+bool Curl_isHandleAtHead(struct SessionHandle *handle,
+ struct curl_llist *pipe)
+{
+ struct curl_llist_element *curr = pipe->head;
+ if (curr) {
+ return (bool)(curr->ptr == handle);
+ }
+
+ return FALSE;
+}
+
+static void signalPipeClose(struct curl_llist *pipe)
+{
+ struct curl_llist_element *curr;
+
+ curr = pipe->head;
+ while (curr) {
+ struct curl_llist_element *next = curr->next;
+ struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
+
+#ifdef CURLDEBUG /* debug-only code */
+ if(data->magic != CURLEASY_MAGIC_NUMBER) {
+ /* MAJOR BADNESS */
+ fprintf(stderr, "signalPipeClose() found BAAD easy handle\n");
+ }
+ else
+#endif
+
+ data->state.pipe_broke = TRUE;
+ Curl_llist_remove(pipe, curr, NULL);
+ curr = next;
+ }
+}
+
+
+/*
+ * Given one filled in connection struct (named needle), this function should
+ * detect if there already is one that has all the significant details
+ * exactly the same and thus should be used instead.
+ *
+ * If there is a match, this function returns TRUE - and has marked the
+ * connection as 'in-use'. It must later be called with ConnectionDone() to
+ * return back to 'idle' (unused) state.
+ */
+static bool
+ConnectionExists(struct SessionHandle *data,
+ struct connectdata *needle,
+ struct connectdata **usethis)
+{
+ long i;
+ struct connectdata *check;
+ bool canPipeline = IsPipeliningPossible(data);
+
+ for(i=0; i< data->state.connc->num; i++) {
+ bool match = FALSE;
+ /*
+ * Note that if we use a HTTP proxy, we check connections to that
+ * proxy and not to the actual remote server.
+ */
+ check = data->state.connc->connects[i];
+ if(!check)
+ /* NULL pointer means not filled-in entry */
+ continue;
+
+ if (check->connectindex == -1) {
+ check->connectindex = i; /* Set this appropriately since it might have
+ been set to -1 when the easy was removed
+ from the multi */
+ }
+
+ infof(data, "Examining connection #%ld for reuse\n", check->connectindex);
+
+ if(check->inuse && !canPipeline) {
+ /* can only happen within multi handles, and means that another easy
+ handle is using this connection */
+ continue;
+ }
+
+#ifdef CURLRES_ASYNCH
+ /* ip_addr_str is NULL only if the resolving of the name hasn't completed
+ yet and until then we don't re-use this connection */
+ if (!check->ip_addr_str) {
+ infof(data,
+ "Connection #%ld has not finished name resolve, can't reuse\n",
+ check->connectindex);
+ continue;
+ }
+#endif
+
+ if (check->send_pipe->size +
+ check->recv_pipe->size >= MAX_PIPELINE_LENGTH) {
+ infof(data, "Connection #%ld has its pipeline full, can't reuse\n",
+ check->connectindex);
+ continue;
+ }
+
+ if (data->state.is_in_pipeline && check->bits.close) {
+ /* Don't pick a connection that is going to be closed */
+ infof(data, "Connection #%ld has been marked for close, can't reuse\n",
+ check->connectindex);
+ continue;
+ }
+
+ if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL))
+ /* don't do mixed SSL and non-SSL connections */
+ continue;
+
+ if(!needle->bits.httpproxy || needle->protocol&PROT_SSL) {
+ /* The requested connection does not use a HTTP proxy or it
+ uses SSL. */
+
+ if(!(needle->protocol&PROT_SSL) && check->bits.httpproxy)
+ /* we don't do SSL but the cached connection has a proxy,
+ then don't match this */
+ continue;
+
+ if(strequal(needle->protostr, check->protostr) &&
+ strequal(needle->host.name, check->host.name) &&
+ (needle->remote_port == check->remote_port) ) {
+ if(needle->protocol & PROT_SSL) {
+ /* This is SSL, verify that we're using the same
+ ssl options as well */
+ if(!Curl_ssl_config_matches(&needle->ssl_config,
+ &check->ssl_config)) {
+ infof(data,
+ "Connection #%ld has different SSL parameters, "
+ "can't reuse\n",
+ check->connectindex );
+ continue;
+ }
+ }
+ if((needle->protocol & PROT_FTP) ||
+ ((needle->protocol & PROT_HTTP) &&
+ (data->state.authhost.want==CURLAUTH_NTLM))) {
+ /* This is FTP or HTTP+NTLM, verify that we're using the same name
+ and password as well */
+ if(!strequal(needle->user, check->user) ||
+ !strequal(needle->passwd, check->passwd)) {
+ /* one of them was different */
+ continue;
+ }
+ }
+ match = TRUE;
+ }
+ }
+ else { /* The requested needle connection is using a proxy,
+ is the checked one using the same? */
+ if(check->bits.httpproxy &&
+ strequal(needle->proxy.name, check->proxy.name) &&
+ needle->port == check->port) {
+ /* This is the same proxy connection, use it! */
+ match = TRUE;
+ }
+ }
+
+ if(match) {
+ if (!IsPipeliningEnabled(data)) {
+ /* The check for a dead socket makes sense only in the
+ non-pipelining case */
+ bool dead = SocketIsDead(check->sock[FIRSTSOCKET]);
+ if(dead) {
+ check->data = data;
+ infof(data, "Connection #%d seems to be dead!\n", i);
+
+ Curl_disconnect(check); /* disconnect resources */
+ data->state.connc->connects[i]=NULL; /* nothing here */
+
+ return FALSE;
+ }
+ }
+
+ check->inuse = TRUE; /* mark this as being in use so that no other
+ handle in a multi stack may nick it */
+
+ if (canPipeline) {
+ /* Mark the connection as being in a pipeline */
+ check->is_in_pipeline = TRUE;
+ }
+
+ check->connectindex = i; /* Set this appropriately since it might have
+ been set to -1 when the easy was removed
+ from the multi */
+ *usethis = check;
+ return TRUE; /* yes, we found one to use! */
+ }
+ }
+
+ return FALSE; /* no matching connecting exists */
+}
+
+
+
+/*
+ * This function frees/closes a connection in the connection cache. This
+ * should take the previously set policy into account when deciding which
+ * of the connections to kill.
+ */
+static long
+ConnectionKillOne(struct SessionHandle *data)
+{
+ long i;
+ struct connectdata *conn;
+ long highscore=-1;
+ long connindex=-1;
+ long score;
+ struct timeval now;
+
+ now = Curl_tvnow();
+
+ for(i=0; data->state.connc && (i< data->state.connc->num); i++) {
+ conn = data->state.connc->connects[i];
+
+ if(!conn || conn->inuse)
+ continue;
+
+ /* Set higher score for the age passed since the connection was used */
+ score = Curl_tvdiff(now, conn->now);
+
+ if(score > highscore) {
+ highscore = score;
+ connindex = i;
+ }
+ }
+ if(connindex >= 0) {
+ /* Set the connection's owner correctly */
+ conn = data->state.connc->connects[connindex];
+ conn->data = data;
+
+ /* the winner gets the honour of being disconnected */
+ (void)Curl_disconnect(conn);
+
+ /* clean the array entry */
+ data->state.connc->connects[connindex] = NULL;
+ }
+
+ return connindex; /* return the available index or -1 */
+}
+
+/* this connection can now be marked 'idle' */
+static void
+ConnectionDone(struct connectdata *conn)
+{
+ conn->inuse = FALSE;
+ if (!conn->send_pipe && !conn->recv_pipe)
+ conn->is_in_pipeline = FALSE;
+}
+
+/*
+ * 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 long
+ConnectionStore(struct SessionHandle *data,
+ struct connectdata *conn)
+{
+ long i;
+ for(i=0; i< data->state.connc->num; i++) {
+ if(!data->state.connc->connects[i])
+ break;
+ }
+ if(i == data->state.connc->num) {
+ /* there was no room available, kill one */
+ i = ConnectionKillOne(data);
+ if(-1 != i)
+ infof(data, "Connection (#%d) was killed to make room (holds %d)\n",
+ i, data->state.connc->num);
+ else
+ infof(data, "This connection did not fit in the connection cache\n");
+ }
+
+ conn->connectindex = i; /* Make the child know where the pointer to this
+ particular data is stored. But note that this -1
+ if this is not within the cache and this is
+ probably not checked for everywhere (yet). */
+ conn->inuse = TRUE;
+ 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.
+
+ TODO: make sure we really can work with more handles than positions in
+ the cache, or possibly we should (allow to automatically) resize the
+ connection cache when we add more easy handles to a multi handle!
+ */
+ data->state.connc->connects[i] = conn; /* fill in this */
+ conn->data = data;
+ }
+
+ return i;
+}
+
+static CURLcode ConnectPlease(struct SessionHandle *data,
+ struct connectdata *conn,
+ struct Curl_dns_entry *hostaddr,
+ bool *connected)
+{
+ CURLcode result;
+ Curl_addrinfo *addr;
+ char *hostname = conn->bits.httpproxy?conn->proxy.name:conn->host.name;
+
+ infof(data, "About to connect() to %s%s port %d (#%d)\n",
+ conn->bits.httpproxy?"proxy ":"",
+ hostname, conn->port, conn->connectindex);
+
+ /*************************************************************
+ * Connect to server/proxy
+ *************************************************************/
+ result= Curl_connecthost(conn,
+ hostaddr,
+ &conn->sock[FIRSTSOCKET],
+ &addr,
+ connected);
+ if(CURLE_OK == result) {
+ /* All is cool, then we store the current information */
+ conn->dns_entry = hostaddr;
+ conn->ip_addr = addr;
+
+ Curl_store_ip_addr(conn);
+
+ switch(data->set.proxytype) {
+ case CURLPROXY_SOCKS5:
+ result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, conn);
+ break;
+ case CURLPROXY_HTTP:
+ /* do nothing here. handled later. */
+ break;
+ case CURLPROXY_SOCKS4:
+ result = Curl_SOCKS4(conn->proxyuser, conn);
+ break;
+ default:
+ failf(data, "unknown proxytype option given");
+ result = CURLE_COULDNT_CONNECT;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * verboseconnect() displays verbose information after a connect
+ */
+static void verboseconnect(struct connectdata *conn)
+{
+ infof(conn->data, "Connected to %s (%s) port %d (#%d)\n",
+ conn->bits.httpproxy ? conn->proxy.dispname : conn->host.dispname,
+ conn->ip_addr_str, conn->port, conn->connectindex);
+}
+
+int Curl_protocol_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks)
+{
+ if(conn->curl_proto_getsock)
+ return conn->curl_proto_getsock(conn, socks, numsocks);
+ return GETSOCK_BLANK;
+}
+
+int Curl_doing_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks)
+{
+ if(conn && conn->curl_doing_getsock)
+ return conn->curl_doing_getsock(conn, socks, numsocks);
+ return GETSOCK_BLANK;
+}
+
+/*
+ * We are doing protocol-specific connecting and this is being called over and
+ * over from the multi interface until the connection phase is done on
+ * protocol layer.
+ */
+
+CURLcode Curl_protocol_connecting(struct connectdata *conn,
+ bool *done)
+{
+ CURLcode result=CURLE_OK;
+
+ if(conn && conn->curl_connecting) {
+ *done = FALSE;
+ result = conn->curl_connecting(conn, done);
+ }
+ else
+ *done = TRUE;
+
+ return result;
+}
+
+/*
+ * We are DOING this is being called over and over from the multi interface
+ * until the DOING phase is done on protocol layer.
+ */
+
+CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done)
+{
+ CURLcode result=CURLE_OK;
+
+ if(conn && conn->curl_doing) {
+ *done = FALSE;
+ result = conn->curl_doing(conn, done);
+ }
+ else
+ *done = TRUE;
+
+ return result;
+}
+
+/*
+ * We have discovered that the TCP connection has been successful, we can now
+ * proceed with some action.
+ *
+ */
+CURLcode Curl_protocol_connect(struct connectdata *conn,
+ bool *protocol_done)
+{
+ CURLcode result=CURLE_OK;
+ struct SessionHandle *data = conn->data;
+
+ *protocol_done = FALSE;
+
+ if(conn->bits.tcpconnect && conn->bits.protoconnstart) {
+ /* We already are connected, get back. This may happen when the connect
+ worked fine in the first call, like when we connect to a local server
+ or proxy. Note that we don't know if the protocol is actually done.
+
+ Unless this protocol doesn't have any protocol-connect callback, as
+ then we know we're done. */
+ if(!conn->curl_connecting)
+ *protocol_done = TRUE;
+
+ return CURLE_OK;
+ }
+
+ if(!conn->bits.tcpconnect) {
+
+ Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
+
+ if(data->set.verbose)
+ verboseconnect(conn);
+ }
+
+ if(!conn->bits.protoconnstart) {
+ if(conn->curl_connect) {
+ /* is there a protocol-specific connect() procedure? */
+
+ /* Set start time here for timeout purposes in the connect procedure, it
+ is later set again for the progress meter purpose */
+ conn->now = Curl_tvnow();
+
+ /* Call the protocol-specific connect function */
+ result = conn->curl_connect(conn, protocol_done);
+ }
+ else
+ *protocol_done = TRUE;
+
+ /* it has started, possibly even completed but that knowledge isn't stored
+ in this bit! */
+ if (!result)
+ conn->bits.protoconnstart = TRUE;
+ }
+
+ return result; /* pass back status */
+}
+
+/*
+ * Helpers for IDNA convertions.
+ */
+#ifdef USE_LIBIDN
+static bool is_ASCII_name(const char *hostname)
+{
+ const unsigned char *ch = (const unsigned char*)hostname;
+
+ while (*ch) {
+ if (*ch++ & 0x80)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Check if characters in hostname is allowed in Top Level Domain.
+ */
+static bool tld_check_name(struct SessionHandle *data,
+ const char *ace_hostname)
+{
+ size_t err_pos;
+ char *uc_name = NULL;
+ int rc;
+
+ /* Convert (and downcase) ACE-name back into locale's character set */
+ rc = idna_to_unicode_lzlz(ace_hostname, &uc_name, 0);
+ if (rc != IDNA_SUCCESS)
+ return (FALSE);
+
+ rc = tld_check_lz(uc_name, &err_pos, NULL);
+ if (rc == TLD_INVALID)
+ infof(data, "WARNING: %s; pos %u = `%c'/0x%02X\n",
+#ifdef HAVE_TLD_STRERROR
+ tld_strerror((Tld_rc)rc),
+#else
+ "<no msg>",
+#endif
+ err_pos, uc_name[err_pos],
+ uc_name[err_pos] & 255);
+ else if (rc != TLD_SUCCESS)
+ infof(data, "WARNING: TLD check for %s failed; %s\n",
+ uc_name,
+#ifdef HAVE_TLD_STRERROR
+ tld_strerror((Tld_rc)rc)
+#else
+ "<no msg>"
+#endif
+ );
+ if (uc_name)
+ idn_free(uc_name);
+ return (bool)(rc == TLD_SUCCESS);
+}
+#endif
+
+static void fix_hostname(struct SessionHandle *data,
+ struct connectdata *conn, struct hostname *host)
+{
+ /* set the name we use to display the host name */
+ host->dispname = host->name;
+
+#ifdef USE_LIBIDN
+ /*************************************************************
+ * Check name for non-ASCII and convert hostname to ACE form.
+ *************************************************************/
+ if (!is_ASCII_name(host->name) &&
+ stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
+ char *ace_hostname = NULL;
+ int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0);
+ infof (data, "Input domain encoded as `%s'\n",
+ stringprep_locale_charset ());
+ if (rc != IDNA_SUCCESS)
+ infof(data, "Failed to convert %s to ACE; %s\n",
+ host->name, Curl_idn_strerror(conn,rc));
+ else {
+ /* tld_check_name() displays a warning if the host name contains
+ "illegal" characters for this TLD */
+ (void)tld_check_name(data, ace_hostname);
+
+ host->encalloc = ace_hostname;
+ /* change the name pointer to point to the encoded hostname */
+ host->name = host->encalloc;
+ }
+ }
+#else
+ (void)data; /* never used */
+ (void)conn; /* never used */
+#endif
+}
+
+/*
+ * Parse URL and fill in the relevant members of the connection struct.
+ */
+static CURLcode ParseURLAndFillConnection(struct SessionHandle *data,
+ struct connectdata *conn)
+{
+ char *at;
+ char *tmp;
+
+ char *path = data->reqdata.path;
+
+ /*************************************************************
+ * Parse the URL.
+ *
+ * We need to parse the url even when using the proxy, because we will need
+ * the hostname and port in case we are trying to SSL connect through the
+ * proxy -- and we don't know if we will need to use SSL until we parse the
+ * url ...
+ ************************************************************/
+ if((2 == sscanf(data->change.url, "%15[^:]:%[^\n]",
+ conn->protostr,
+ path)) && strequal(conn->protostr, "file")) {
+ if(path[0] == '/' && path[1] == '/') {
+ /* Allow omitted hostname (e.g. file:/<path>). This is not strictly
+ * speaking a valid file: URL by RFC 1738, but treating file:/<path> as
+ * file://localhost/<path> is similar to how other schemes treat missing
+ * hostnames. See RFC 1808. */
+
+ /* This cannot be done with strcpy() in a portable manner, since the
+ memory areas overlap! */
+ memmove(path, path + 2, strlen(path + 2)+1);
+ }
+ /*
+ * we deal with file://<host>/<path> differently since it supports no
+ * hostname other than "localhost" and "127.0.0.1", which is unique among
+ * the URL protocols specified in RFC 1738
+ */
+ if(path[0] != '/') {
+ /* the URL included a host name, we ignore host names in file:// URLs
+ as the standards don't define what to do with them */
+ char *ptr=strchr(path, '/');
+ if(ptr) {
+ /* there was a slash present
+
+ RFC1738 (section 3.1, page 5) says:
+
+ The rest of the locator consists of data specific to the scheme,
+ and is known as the "url-path". It supplies the details of how the
+ specified resource can be accessed. Note that the "/" between the
+ host (or port) and the url-path is NOT part of the url-path.
+
+ As most agents use file://localhost/foo to get '/foo' although the
+ slash preceding foo is a separator and not a slash for the path,
+ a URL as file://localhost//foo must be valid as well, to refer to
+ the same file with an absolute path.
+ */
+
+ if(ptr[1] && ('/' == ptr[1]))
+ /* if there was two slashes, we skip the first one as that is then
+ used truly as a separator */
+ ptr++;
+
+ /* This cannot be made with strcpy, as the memory chunks overlap! */
+ memmove(path, ptr, strlen(ptr)+1);
+ }
+ }
+
+ strcpy(conn->protostr, "file"); /* store protocol string lowercase */
+ }
+ else {
+ /* clear path */
+ path[0]=0;
+
+ if (2 > sscanf(data->change.url,
+ "%15[^\n:]://%[^\n/]%[^\n]",
+ conn->protostr,
+ conn->host.name, path)) {
+
+ /*
+ * The URL was badly formatted, let's try the browser-style _without_
+ * protocol specified like 'http://'.
+ */
+ if((1 > sscanf(data->change.url, "%[^\n/]%[^\n]",
+ conn->host.name, 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.
+ */
+
+ /* Note: if you add a new protocol, please update the list in
+ * lib/version.c too! */
+
+ if(checkprefix("FTP.", conn->host.name))
+ strcpy(conn->protostr, "ftp");
+ else if (checkprefix("DICT.", conn->host.name))
+ strcpy(conn->protostr, "DICT");
+ else if (checkprefix("LDAP.", conn->host.name))
+ strcpy(conn->protostr, "LDAP");
+ else {
+ strcpy(conn->protostr, "http");
+ }
+
+ conn->protocol |= PROT_MISSING; /* not given in URL */
+ }
+ }
+
+ /* We search for '?' in the host name (but only on the right side of a
+ * @-letter to allow ?-letters in username and password) to handle things
+ * like http://example.com?param= (notice the missing '/').
+ */
+ at = strchr(conn->host.name, '@');
+ if(at)
+ tmp = strchr(at+1, '?');
+ else
+ tmp = strchr(conn->host.name, '?');
+
+ if(tmp) {
+ /* We must insert a slash before the '?'-letter in the URL. If the URL had
+ a slash after the '?', that is where the path currently begins and the
+ '?string' is still part of the host name.
+
+ We must move the trailing part from the host name and put it first in
+ the path. And have it all prefixed with a slash.
+ */
+
+ size_t hostlen = strlen(tmp);
+ size_t pathlen = strlen(path);
+
+ /* move the existing path plus the zero byte forward, to make room for
+ the host-name part */
+ memmove(path+hostlen+1, path, pathlen+1);
+
+ /* now copy the trailing host part in front of the existing path */
+ memcpy(path+1, tmp, hostlen);
+
+ path[0]='/'; /* prepend the missing slash */
+
+ *tmp=0; /* now cut off the hostname at the ? */
+ }
+ else if(!path[0]) {
+ /* if there's no path set, use a single slash */
+ strcpy(path, "/");
+ }
+
+ /* If the URL is malformatted (missing a '/' after hostname before path) we
+ * insert a slash here. The only letter except '/' we accept to start a path
+ * is '?'.
+ */
+ if(path[0] == '?') {
+ /* We need this function to deal with overlapping memory areas. We know
+ that the memory area 'path' points to is 'urllen' bytes big and that
+ is bigger than the path. Use +1 to move the zero byte too. */
+ memmove(&path[1], path, strlen(path)+1);
+ path[0] = '/';
+ }
+
+ /*
+ * So if the URL was A://B/C,
+ * conn->protostr is A
+ * conn->host.name is B
+ * data->reqdata.path is /C
+ */
+
+ return CURLE_OK;
+}
+
+static void llist_dtor(void *user, void *element)
+{
+ (void)user;
+ (void)element;
+ /* Do nothing */
+}
+
+
+/**
+ * CreateConnection() sets up a new connectdata struct, or re-uses an already
+ * existing one, and resolves host name.
+ *
+ * if this function returns CURLE_OK and *async is set to TRUE, the resolve
+ * response will be coming asynchronously. If *async is FALSE, the name is
+ * already resolved.
+ *
+ * @param data The sessionhandle pointer
+ * @param in_connect is set to the next connection data pointer
+ * @param addr is set to the new dns entry for this connection. If this
+ * connection is re-used it will be NULL.
+ * @param async is set TRUE/FALSE depending on the nature of this lookup
+ * @return CURLcode
+ * @see SetupConnection()
+ *
+ * *NOTE* this function assigns the conn->data pointer!
+ */
+
+static CURLcode CreateConnection(struct SessionHandle *data,
+ struct connectdata **in_connect,
+ struct Curl_dns_entry **addr,
+ bool *async)
+{
+
+ char *tmp;
+ CURLcode result=CURLE_OK;
+ struct connectdata *conn;
+ struct connectdata *conn_temp = NULL;
+ size_t urllen;
+ struct Curl_dns_entry *hostaddr;
+#if defined(HAVE_ALARM) && !defined(USE_ARES)
+ unsigned int prev_alarm=0;
+#endif
+ char endbracket;
+ char user[MAX_CURL_USER_LENGTH];
+ char passwd[MAX_CURL_PASSWORD_LENGTH];
+ int rc;
+ bool reuse;
+ char *proxy;
+ bool proxy_alloc = FALSE;
+
+#ifndef USE_ARES
+#ifdef SIGALRM
+#ifdef HAVE_SIGACTION
+ struct sigaction keep_sigact; /* store the old struct here */
+ bool keep_copysig=FALSE; /* did copy it? */
+#else
+#ifdef HAVE_SIGNAL
+ void (*keep_sigact)(int); /* store the old handler here */
+#endif /* HAVE_SIGNAL */
+#endif /* HAVE_SIGACTION */
+#endif /* SIGALRM */
+#endif /* USE_ARES */
+
+ *addr = NULL; /* nothing yet */
+ *async = FALSE;
+
+ /*************************************************************
+ * Check input data
+ *************************************************************/
+
+ if(!data->change.url)
+ return CURLE_URL_MALFORMAT;
+
+ /* First, split up the current URL in parts so that we can use the
+ parts for checking against the already present connections. In order
+ to not have to modify everything at once, we allocate a temporary
+ connection data struct and fill in for comparison purposes. */
+
+ conn = (struct connectdata *)calloc(sizeof(struct connectdata), 1);
+ 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;
+
+ /* and we setup a few fields in case we end up actually using this struct */
+
+ conn->data = data; /* Setup the association between this connection
+ and the SessionHandle */
+
+ conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
+ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
+ conn->connectindex = -1; /* no index */
+
+ conn->bits.httpproxy = (bool)(data->set.proxy /* http proxy or not */
+ && *data->set.proxy
+ && (data->set.proxytype == CURLPROXY_HTTP));
+ proxy = data->set.proxy; /* if global proxy is set, this is it */
+
+ /* Default protocol-independent behavior doesn't support persistent
+ connections, so we set this to force-close. Protocols that support
+ this need to set this to FALSE in their "curl_do" functions. */
+ conn->bits.close = TRUE;
+
+ conn->readchannel_inuse = FALSE;
+ conn->writechannel_inuse = FALSE;
+
+ conn->read_pos = 0;
+ conn->buf_len = 0;
+
+ /* Initialize the pipeline lists */
+ conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
+ conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
+
+ /* Store creation time to help future close decision making */
+ conn->created = Curl_tvnow();
+
+ /* range status */
+ data->reqdata.use_range = (bool)(NULL != data->set.set_range);
+
+ data->reqdata.range = data->set.set_range; /* clone the range setting */
+ data->reqdata.resume_from = data->set.set_resume_from;
+
+ conn->bits.user_passwd = (bool)(NULL != data->set.userpwd);
+ conn->bits.proxy_user_passwd = (bool)(NULL != data->set.proxyuserpwd);
+ conn->bits.no_body = data->set.opt_no_body;
+ conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
+ conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
+ conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
+
+ /* This initing continues below, see the comment "Continue connectdata
+ * initialization here" */
+
+ /***********************************************************
+ * We need to allocate memory to store the path in. We get the size of the
+ * full URL to be sure, and we need to make it at least 256 bytes since
+ * other parts of the code will rely on this fact
+ ***********************************************************/
+#define LEAST_PATH_ALLOC 256
+ urllen=strlen(data->change.url);
+ if(urllen < LEAST_PATH_ALLOC)
+ urllen=LEAST_PATH_ALLOC;
+
+ if (!data->set.source_url /* 3rd party FTP */
+ && data->reqdata.pathbuffer) {
+ /* Free the old buffer */
+ free(data->reqdata.pathbuffer);
+ }
+
+ /*
+ * We malloc() the buffers below urllen+2 to make room for to possibilities:
+ * 1 - an extra terminating zero
+ * 2 - an extra slash (in case a syntax like "www.host.com?moo" is used)
+ */
+
+ data->reqdata.pathbuffer=(char *)malloc(urllen+2);
+ if(NULL == data->reqdata.pathbuffer)
+ return CURLE_OUT_OF_MEMORY; /* really bad error */
+ data->reqdata.path = data->reqdata.pathbuffer;
+
+ conn->host.rawalloc=(char *)malloc(urllen+2);
+ if(NULL == conn->host.rawalloc)
+ return CURLE_OUT_OF_MEMORY;
+
+ conn->host.name = conn->host.rawalloc;
+ conn->host.name[0] = 0;
+
+ result = ParseURLAndFillConnection(data, conn);
+ if (result != CURLE_OK) {
+ return result;
+ }
+
+ /*************************************************************
+ * Take care of proxy authentication stuff
+ *************************************************************/
+ if(conn->bits.proxy_user_passwd) {
+ char proxyuser[MAX_CURL_USER_LENGTH]="";
+ char proxypasswd[MAX_CURL_PASSWORD_LENGTH]="";
+
+ sscanf(data->set.proxyuserpwd,
+ "%" MAX_CURL_USER_LENGTH_TXT "[^:]:"
+ "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]",
+ proxyuser, proxypasswd);
+
+ conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);
+ if(!conn->proxyuser)
+ return CURLE_OUT_OF_MEMORY;
+
+ conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);
+ if(!conn->proxypasswd)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ /*************************************************************
+ * Detect what (if any) proxy to use
+ *************************************************************/
+ if(!conn->bits.httpproxy) {
+ /* If proxy was not specified, we check for default proxy environment
+ * variables, to enable i.e Lynx compliance:
+ *
+ * http_proxy=http://some.server.dom:port/
+ * https_proxy=http://some.server.dom:port/
+ * ftp_proxy=http://some.server.dom:port/
+ * no_proxy=domain1.dom,host.domain2.dom
+ * (a comma-separated list of hosts which should
+ * not be proxied, or an asterisk to override
+ * all proxy variables)
+ * all_proxy=http://some.server.dom:port/
+ * (seems to exist for the CERN www lib. Probably
+ * the first to check for.)
+ *
+ * For compatibility, the all-uppercase versions of these variables are
+ * checked if the lowercase versions don't exist.
+ */
+ char *no_proxy=NULL;
+ char *no_proxy_tok_buf;
+ 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) {
+ size_t namelen;
+ char *endptr = strchr(conn->host.name, ':');
+ if(endptr)
+ namelen=endptr-conn->host.name;
+ else
+ namelen=strlen(conn->host.name);
+
+ if(strlen(nope) <= namelen) {
+ char *checkn=
+ conn->host.name + namelen - strlen(nope);
+ if(checkprefix(nope, checkn)) {
+ /* 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++ = (char)tolower((int)*protop++);
+
+ /* append _proxy */
+ strcpy(envp, "_proxy");
+
+ /* read the protocol proxy: */
+ prox=curl_getenv(proxy_env);
+
+ /*
+ * We don't try the uppercase version of HTTP_PROXY because of
+ * security reasons:
+ *
+ * When curl is used in a webserver application
+ * environment (cgi or php), this environment variable can
+ * be controlled by the web server user by setting the
+ * http header 'Proxy:' to some value.
+ *
+ * This can cause 'internal' http/ftp requests to be
+ * arbitrarily redirected by any external attacker.
+ */
+ if(!prox && !strequal("http_proxy", proxy_env)) {
+ /* There was no lowercase variable, try the uppercase version: */
+ for(envp = proxy_env; *envp; envp++)
+ *envp = (char)toupper((int)*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) {
+ long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING);
+ /* force this to become HTTP */
+ conn->protocol = PROT_HTTP | bits;
+
+ 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 */
+#endif /* CURL_DISABLE_HTTP */
+
+ /*************************************************************
+ * 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 */
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ /************************************************************
+ * 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(data->reqdata.resume_from) {
+ if(!data->reqdata.use_range) {
+ /* if it already was in use, we just skip this */
+ data->reqdata.range = aprintf("%" FORMAT_OFF_T "-", data->reqdata.resume_from);
+ if(!data->reqdata.range)
+ return CURLE_OUT_OF_MEMORY;
+ data->reqdata.rangestringalloc = TRUE; /* mark as allocated */
+ data->reqdata.use_range = 1; /* switch on range usage */
+ }
+ }
+#endif
+ /*************************************************************
+ * Setup internals depending on protocol
+ *************************************************************/
+
+ conn->socktype = SOCK_STREAM; /* most of them are TCP streams */
+
+ if (strequal(conn->protostr, "HTTP")) {
+#ifndef CURL_DISABLE_HTTP
+ conn->port = PORT_HTTP;
+ conn->remote_port = PORT_HTTP;
+ conn->protocol |= PROT_HTTP;
+ conn->curl_do = Curl_http;
+ conn->curl_do_more = (Curl_do_more_func)ZERO_NULL;
+ conn->curl_done = Curl_http_done;
+ conn->curl_connect = Curl_http_connect;
+#else
+ failf(data, LIBCURL_NAME
+ " was built with HTTP disabled, http: not supported!");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+ }
+ else if (strequal(conn->protostr, "HTTPS")) {
+#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
+
+ conn->port = PORT_HTTPS;
+ conn->remote_port = PORT_HTTPS;
+ conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL;
+
+ conn->curl_do = Curl_http;
+ conn->curl_do_more = (Curl_do_more_func)ZERO_NULL;
+ conn->curl_done = Curl_http_done;
+ conn->curl_connect = Curl_http_connect;
+ conn->curl_connecting = Curl_https_connecting;
+ conn->curl_proto_getsock = Curl_https_getsock;
+
+#else /* USE_SSL */
+ failf(data, LIBCURL_NAME
+ " was built with SSL disabled, https: not supported!");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+#endif /* !USE_SSL */
+ }
+ else if(strequal(conn->protostr, "FTP") ||
+ strequal(conn->protostr, "FTPS")) {
+
+#ifndef CURL_DISABLE_FTP
+ char *type;
+ int port = PORT_FTP;
+
+ if(strequal(conn->protostr, "FTPS")) {
+#ifdef USE_SSL
+ conn->protocol |= PROT_FTPS|PROT_SSL;
+ conn->ssl[SECONDARYSOCKET].use = TRUE; /* send data securely */
+ port = PORT_FTPS;
+#else
+ failf(data, LIBCURL_NAME
+ " was built with SSL disabled, ftps: not supported!");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+#endif /* !USE_SSL */
+ }
+
+ conn->port = port;
+ conn->remote_port = (unsigned short)port;
+ conn->protocol |= PROT_FTP;
+
+ if(proxy && *proxy && !data->set.tunnel_thru_httpproxy) {
+ /* Unless we have asked to tunnel ftp operations through the proxy, we
+ switch and use HTTP operations only */
+#ifndef CURL_DISABLE_HTTP
+ conn->curl_do = Curl_http;
+ conn->curl_done = Curl_http_done;
+ conn->protocol = PROT_HTTP; /* switch to HTTP */
+#else
+ failf(data, "FTP over http proxy requires HTTP support built-in!");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+ }
+ else {
+ conn->curl_do = Curl_ftp;
+ conn->curl_do_more = Curl_ftp_nextconnect;
+ conn->curl_done = Curl_ftp_done;
+ conn->curl_connect = Curl_ftp_connect;
+ conn->curl_connecting = Curl_ftp_multi_statemach;
+ conn->curl_doing = Curl_ftp_doing;
+ conn->curl_proto_getsock = Curl_ftp_getsock;
+ conn->curl_doing_getsock = Curl_ftp_getsock;
+ conn->curl_disconnect = Curl_ftp_disconnect;
+ }
+
+ data->reqdata.path++; /* don't include the initial slash */
+
+ /* FTP URLs support an extension like ";type=<typecode>" that
+ * we'll try to get now! */
+ type=strstr(data->reqdata.path, ";type=");
+ if(!type) {
+ type=strstr(conn->host.rawalloc, ";type=");
+ }
+ if(type) {
+ char command;
+ *type=0; /* it was in the middle of the hostname */
+ command = (char)toupper((int)type[6]);
+ switch(command) {
+ case 'A': /* ASCII mode */
+ data->set.prefer_ascii = TRUE;
+ break;
+ case 'D': /* directory mode */
+ data->set.ftp_list_only = TRUE;
+ break;
+ case 'I': /* binary mode */
+ default:
+ /* switch off ASCII */
+ data->set.prefer_ascii = FALSE;
+ break;
+ }
+ }
+#else /* CURL_DISABLE_FTP */
+ failf(data, LIBCURL_NAME
+ " was built with FTP disabled, ftp/ftps: not supported!");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+ }
+ else if(strequal(conn->protostr, "TELNET")) {
+#ifndef CURL_DISABLE_TELNET
+ /* telnet testing factory */
+ conn->protocol |= PROT_TELNET;
+
+ conn->port = PORT_TELNET;
+ conn->remote_port = PORT_TELNET;
+ conn->curl_do = Curl_telnet;
+ conn->curl_done = Curl_telnet_done;
+#else
+ failf(data, LIBCURL_NAME
+ " was built with TELNET disabled!");
+#endif
+ }
+ else if (strequal(conn->protostr, "DICT")) {
+#ifndef CURL_DISABLE_DICT
+ conn->protocol |= PROT_DICT;
+ conn->port = PORT_DICT;
+ conn->remote_port = PORT_DICT;
+ conn->curl_do = Curl_dict;
+ /* no DICT-specific done */
+ conn->curl_done = (Curl_done_func)ZERO_NULL;
+#else
+ failf(data, LIBCURL_NAME
+ " was built with DICT disabled!");
+#endif
+ }
+ else if (strequal(conn->protostr, "LDAP")) {
+#ifndef CURL_DISABLE_LDAP
+ conn->protocol |= PROT_LDAP;
+ conn->port = PORT_LDAP;
+ conn->remote_port = PORT_LDAP;
+ conn->curl_do = Curl_ldap;
+ /* no LDAP-specific done */
+ conn->curl_done = (Curl_done_func)ZERO_NULL;
+#else
+ failf(data, LIBCURL_NAME
+ " was built with LDAP disabled!");
+#endif
+ }
+ else if (strequal(conn->protostr, "FILE")) {
+#ifndef CURL_DISABLE_FILE
+ conn->protocol |= PROT_FILE;
+
+ conn->curl_do = Curl_file;
+ conn->curl_done = Curl_file_done;
+
+ /* 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) {
+ conn->data = data;
+ conn->bits.tcpconnect = TRUE; /* we are "connected */
+ ConnectionStore(data, conn);
+
+ result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
+ -1, NULL); /* no upload */
+ }
+
+ return result;
+#else
+ failf(data, LIBCURL_NAME
+ " was built with FILE disabled!");
+#endif
+ }
+ else if (strequal(conn->protostr, "TFTP")) {
+#ifndef CURL_DISABLE_TFTP
+ char *type;
+ conn->socktype = SOCK_DGRAM; /* UDP datagram based */
+ conn->protocol |= PROT_TFTP;
+ conn->port = PORT_TFTP;
+ conn->remote_port = PORT_TFTP;
+ conn->curl_connect = Curl_tftp_connect;
+ conn->curl_do = Curl_tftp;
+ conn->curl_done = Curl_tftp_done;
+ /* TFTP URLs support an extension like ";mode=<typecode>" that
+ * we'll try to get now! */
+ type=strstr(data->reqdata.path, ";mode=");
+ if(!type) {
+ type=strstr(conn->host.rawalloc, ";mode=");
+ }
+ if(type) {
+ char command;
+ *type=0; /* it was in the middle of the hostname */
+ command = (char)toupper((int)type[6]);
+ switch(command) {
+ case 'A': /* ASCII mode */
+ case 'N': /* NETASCII mode */
+ data->set.prefer_ascii = TRUE;
+ break;
+ case 'O': /* octet mode */
+ case 'I': /* binary mode */
+ default:
+ /* switch off ASCII */
+ data->set.prefer_ascii = FALSE;
+ break;
+ }
+ }
+#else
+ failf(data, LIBCURL_NAME
+ " was built with TFTP disabled!");
+#endif
+ }
+ else if (strequal(conn->protostr, "SCP")) {
+#ifdef USE_LIBSSH2
+ conn->port = PORT_SSH;
+ conn->remote_port = PORT_SSH;
+ conn->protocol = PROT_SCP;
+ conn->curl_connect = Curl_ssh_connect; /* ssh_connect? */
+ conn->curl_do = Curl_scp_do;
+ conn->curl_done = Curl_scp_done;
+ conn->curl_do_more = (Curl_do_more_func)ZERO_NULL;
+#else
+ failf(data, LIBCURL_NAME
+ " was built without LIBSSH2, scp: not supported!");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+ }
+ else if (strequal(conn->protostr, "SFTP")) {
+#ifdef USE_LIBSSH2
+ conn->port = PORT_SSH;
+ conn->remote_port = PORT_SSH;
+ conn->protocol = PROT_SFTP;
+ conn->curl_connect = Curl_ssh_connect; /* ssh_connect? */
+ conn->curl_do = Curl_sftp_do;
+ conn->curl_done = Curl_sftp_done;
+ conn->curl_do_more = (Curl_do_more_func)NULL;
+#else
+ failf(data, LIBCURL_NAME
+ " was built without LIBSSH2, scp: not supported!");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+}
+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;
+ }
+
+ if(proxy && *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. If 'proxy_alloc' is TRUE, the string is already
+ allocated and we can treat it as duplicated. */
+ char *proxydup=proxy_alloc?proxy:strdup(proxy);
+
+ /* We use 'proxyptr' to point to the proxy name from now on... */
+ char *proxyptr=proxydup;
+ char *portptr;
+ char *atsign;
+
+ if(NULL == proxydup) {
+ failf(data, "memory shortage");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* 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.
+ */
+
+ /* Skip the protocol part if present */
+ endofprot=strstr(proxyptr, "://");
+ if(endofprot)
+ proxyptr = endofprot+3;
+
+ /* Is there a username and password given in this proxy url? */
+ atsign = strchr(proxyptr, '@');
+ if(atsign) {
+ char proxyuser[MAX_CURL_USER_LENGTH];
+ char proxypasswd[MAX_CURL_PASSWORD_LENGTH];
+ proxypasswd[0] = 0;
+
+ if(1 <= sscanf(proxyptr,
+ "%" MAX_CURL_USER_LENGTH_TXT"[^:]:"
+ "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
+ proxyuser, proxypasswd)) {
+ CURLcode res = CURLE_OK;
+
+ /* found user and password, rip them out. note that we are
+ unescaping them, as there is otherwise no way to have a
+ username or password with reserved characters like ':' in
+ them. */
+ Curl_safefree(conn->proxyuser);
+ conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);
+
+ if(!conn->proxyuser)
+ res = CURLE_OUT_OF_MEMORY;
+ else {
+ Curl_safefree(conn->proxypasswd);
+ conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);
+
+ if(!conn->proxypasswd)
+ res = CURLE_OUT_OF_MEMORY;
+ }
+
+ if(CURLE_OK == res) {
+ conn->bits.proxy_user_passwd = TRUE; /* enable it */
+ atsign = strdup(atsign+1); /* the right side of the @-letter */
+
+ if(atsign) {
+ free(proxydup); /* free the former proxy string */
+ proxydup = proxyptr = atsign; /* now use this instead */
+ }
+ else
+ res = CURLE_OUT_OF_MEMORY;
+ }
+
+ if(res) {
+ free(proxydup); /* free the allocated proxy string */
+ return res;
+ }
+ }
+ }
+
+ /* start scanning for port number at this point */
+ portptr = proxyptr;
+
+ /* detect and extract RFC2732-style IPv6-addresses */
+ if(*proxyptr == '[') {
+ char *ptr = ++proxyptr; /* advance beyond the initial bracket */
+ while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':')))
+ ptr++;
+ if(*ptr == ']') {
+ /* yeps, it ended nicely with a bracket as well */
+ *ptr = 0;
+ portptr = ptr+1;
+ }
+ /* Note that if this didn't end with a bracket, we still advanced the
+ * proxyptr first, but I can't see anything wrong with that as no host
+ * name nor a numeric can legally start with a bracket.
+ */
+ }
+
+ /* Get port number off proxy.server.com:1080 */
+ prox_portno = strchr(portptr, ':');
+ if (prox_portno) {
+ *prox_portno = 0x0; /* cut off number from host name */
+ prox_portno ++;
+ /* now set the local port number */
+ conn->port = 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->proxy.rawalloc = strdup(proxyptr);
+ conn->proxy.name = conn->proxy.rawalloc;
+
+ free(proxydup); /* free the duplicate pointer and not the modified */
+ proxy = NULL; /* this may have just been freed */
+ if(!conn->proxy.rawalloc)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /*************************************************************
+ * If the protocol is using SSL and HTTP proxy is used, we set
+ * the tunnel_proxy bit.
+ *************************************************************/
+ if((conn->protocol&PROT_SSL) && conn->bits.httpproxy)
+ conn->bits.tunnel_proxy = TRUE;
+
+ /*************************************************************
+ * Take care of user and password authentication stuff
+ *************************************************************/
+
+ /*
+ * Inputs: data->set.userpwd (CURLOPT_USERPWD)
+ * data->set.fpasswd (CURLOPT_PASSWDFUNCTION)
+ * data->set.use_netrc (CURLOPT_NETRC)
+ * conn->host.name
+ * netrc file
+ * hard-coded defaults
+ *
+ * Outputs: (almost :- all currently undefined)
+ * conn->bits.user_passwd - non-zero if non-default passwords exist
+ * conn->user - non-zero length if defined
+ * conn->passwd - ditto
+ * conn->host.name - remove user name and password
+ */
+
+ /* At this point, we're hoping all the other special cases have
+ * been taken care of, so conn->host.name is at most
+ * [user[:password]]@]hostname
+ *
+ * We need somewhere to put the embedded details, so do that first.
+ */
+
+ user[0] =0; /* to make everything well-defined */
+ passwd[0]=0;
+
+ if (conn->protocol & (PROT_FTP|PROT_HTTP|PROT_SCP|PROT_SFTP)) {
+ /* This is a FTP, HTTP, SCP or SFTP URL, we will now try to extract the
+ * possible user+password pair in a string like:
+ * ftp://user:password@ftp.my.site:8021/README */
+ char *ptr=strchr(conn->host.name, '@');
+ char *userpass = conn->host.name;
+ if(ptr != NULL) {
+ /* there's a user+password given here, to the left of the @ */
+
+ conn->host.name = ++ptr;
+
+ /* So the hostname is sane. Only bother interpreting the
+ * results if we could care. It could still be wasted
+ * work because it might be overtaken by the programmatically
+ * set user/passwd, but doing that first adds more cases here :-(
+ */
+
+ if (data->set.use_netrc != CURL_NETRC_REQUIRED) {
+ /* We could use the one in the URL */
+
+ conn->bits.user_passwd = 1; /* enable user+password */
+
+ if(*userpass != ':') {
+ /* the name is given, get user+password */
+ sscanf(userpass, "%" MAX_CURL_USER_LENGTH_TXT "[^:@]:"
+ "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
+ user, passwd);
+ }
+ else
+ /* no name given, get the password only */
+ sscanf(userpass, ":%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", passwd);
+
+ if(user[0]) {
+ char *newname=curl_easy_unescape(data, user, 0, NULL);
+ if(!newname)
+ return CURLE_OUT_OF_MEMORY;
+ if(strlen(newname) < sizeof(user))
+ strcpy(user, newname);
+
+ /* if the new name is longer than accepted, then just use
+ the unconverted name, it'll be wrong but what the heck */
+ free(newname);
+ }
+ if (passwd[0]) {
+ /* we have a password found in the URL, decode it! */
+ char *newpasswd=curl_easy_unescape(data, passwd, 0, NULL);
+ if(!newpasswd)
+ return CURLE_OUT_OF_MEMORY;
+ if(strlen(newpasswd) < sizeof(passwd))
+ strcpy(passwd, newpasswd);
+
+ free(newpasswd);
+ }
+ }
+ }
+ }
+
+ /*************************************************************
+ * 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)
+ *
+ * The conn->host.name is currently [user:passwd@]host[:port] where host
+ * could be a hostname, IPv4 address or IPv6 address.
+ *************************************************************/
+ if((1 == sscanf(conn->host.name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) &&
+ (']' == endbracket)) {
+ /* this is a RFC2732-style specified IP-address */
+ conn->bits.ipv6_ip = TRUE;
+
+ conn->host.name++; /* pass the starting bracket */
+ tmp = strchr(conn->host.name, ']');
+ *tmp = 0; /* zero terminate */
+ tmp++; /* pass the ending bracket */
+ if(':' != *tmp)
+ tmp = NULL; /* no port number available */
+ }
+ else
+ tmp = strrchr(conn->host.name, ':');
+
+ if(data->set.use_port && data->state.allow_port) {
+ /* if set, we use this and ignore the port possibly given in the URL */
+ conn->remote_port = (unsigned short)data->set.use_port;
+ if(tmp)
+ *tmp = '\0'; /* cut off the name there anyway - if there was a port
+ number - since the port number is to be ignored! */
+ if(conn->bits.httpproxy) {
+ /* we need to create new URL with the new port number */
+ char *url;
+
+ url = aprintf("http://%s:%d%s", conn->host.name, conn->remote_port,
+ data->reqdata.path);
+ if(!url)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(data->change.url_alloc)
+ free(data->change.url);
+
+ data->change.url = url;
+ data->change.url_alloc = TRUE;
+ }
+ }
+ else if (tmp) {
+ /* no CURLOPT_PORT given, extract the one from the URL */
+
+ char *rest;
+ unsigned long port;
+
+ port=strtoul(tmp+1, &rest, 10); /* Port number must be decimal */
+
+ if (rest != (tmp+1) && *rest == '\0') {
+ /* The colon really did have only digits after it,
+ * so it is either a port number or a mistake */
+
+ if (port > 0xffff) { /* Single unix standard says port numbers are
+ * 16 bits long */
+ failf(data, "Port number too large: %lu", port);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ *tmp = '\0'; /* cut off the name there */
+ conn->remote_port = (unsigned short)port;
+ }
+ }
+
+ /* Programmatically set password:
+ * - always applies, if available
+ * - takes precedence over the values we just set above
+ * so scribble it over the top.
+ * User-supplied passwords are assumed not to need unescaping.
+ *
+ * user_password is set in "inherit initial knowledge' above,
+ * so it doesn't have to be set in this block
+ */
+ if (data->set.userpwd != NULL) {
+ /* the name is given, get user+password */
+ sscanf(data->set.userpwd,
+ "%" MAX_CURL_USER_LENGTH_TXT "[^:]:"
+ "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]",
+ user, passwd);
+ }
+
+ conn->bits.netrc = FALSE;
+ if (data->set.use_netrc != CURL_NETRC_IGNORED) {
+ if(Curl_parsenetrc(conn->host.name,
+ user, passwd,
+ data->set.netrc_file)) {
+ infof(data, "Couldn't find host %s in the " DOT_CHAR
+ "netrc file, using defaults\n",
+ conn->host.name);
+ }
+ else {
+ /* set bits.netrc TRUE to remember that we got the name from a .netrc
+ file, so that it is safe to use even if we followed a Location: to a
+ different host or similar. */
+ conn->bits.netrc = TRUE;
+
+ conn->bits.user_passwd = 1; /* enable user+password */
+ }
+ }
+
+ /* If our protocol needs a password and we have none, use the defaults */
+ if ( (conn->protocol & PROT_FTP) &&
+ !conn->bits.user_passwd) {
+
+ conn->user = strdup(CURL_DEFAULT_USER);
+ conn->passwd = strdup(CURL_DEFAULT_PASSWORD);
+ /* This is the default password, so DON'T set conn->bits.user_passwd */
+ }
+ else {
+ /* store user + password, zero-length if not set */
+ conn->user = strdup(user);
+ conn->passwd = strdup(passwd);
+ }
+ if(!conn->user || !conn->passwd)
+ return CURLE_OUT_OF_MEMORY;
+
+ /*************************************************************
+ * Check the current list of connections to see if we can
+ * re-use an already existing one or if we have to create a
+ * new one.
+ *************************************************************/
+
+ /* get a cloned copy of the SSL config situation stored in the
+ connection struct */
+ if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config))
+ return CURLE_OUT_OF_MEMORY;
+
+ /* reuse_fresh is TRUE if we are told to use a new connection by force, but
+ we only acknowledge this option if this is not a re-used connection
+ already (which happens due to follow-location or during a HTTP
+ authentication phase). */
+ if(data->set.reuse_fresh && !data->state.this_is_a_follow)
+ reuse = FALSE;
+ else
+ reuse = ConnectionExists(data, conn, &conn_temp);
+
+ if(reuse) {
+ /*
+ * We already have a connection for this, we got the former connection
+ * in the conn_temp variable and thus we need to cleanup the one we
+ * just allocated before we can move along and use the previously
+ * existing one.
+ */
+ struct connectdata *old_conn = conn;
+
+ if(old_conn->proxy.rawalloc)
+ free(old_conn->proxy.rawalloc);
+
+ /* free the SSL config struct from this connection struct as this was
+ allocated in vain and is targeted for destruction */
+ Curl_free_ssl_config(&conn->ssl_config);
+
+ conn = conn_temp; /* use this connection from now on */
+
+ conn->data = old_conn->data;
+
+ /* get the user+password information from the old_conn struct since it may
+ * be new for this request even when we re-use an existing connection */
+ conn->bits.user_passwd = old_conn->bits.user_passwd;
+ if (conn->bits.user_passwd) {
+ /* use the new user namd and password though */
+ Curl_safefree(conn->user);
+ Curl_safefree(conn->passwd);
+ conn->user = old_conn->user;
+ conn->passwd = old_conn->passwd;
+ old_conn->user = NULL;
+ old_conn->passwd = NULL;
+ }
+
+ conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd;
+ if (conn->bits.proxy_user_passwd) {
+ /* use the new proxy user name and proxy password though */
+ Curl_safefree(conn->proxyuser);
+ Curl_safefree(conn->proxypasswd);
+ conn->proxyuser = old_conn->proxyuser;
+ conn->proxypasswd = old_conn->proxypasswd;
+ old_conn->proxyuser = NULL;
+ old_conn->proxypasswd = NULL;
+ }
+
+ /* host can change, when doing keepalive with a proxy ! */
+ if (conn->bits.httpproxy) {
+ free(conn->host.rawalloc);
+ conn->host=old_conn->host;
+ }
+
+ /* get the newly set value, not the old one */
+ conn->bits.no_body = old_conn->bits.no_body;
+
+ if (!conn->bits.httpproxy)
+ free(old_conn->host.rawalloc); /* free the newly allocated name buffer */
+
+ /* re-use init */
+ conn->bits.reuse = TRUE; /* yes, we're re-using here */
+ conn->bits.chunk = FALSE; /* always assume not chunked unless told
+ otherwise */
+
+ Curl_safefree(old_conn->user);
+ Curl_safefree(old_conn->passwd);
+ Curl_safefree(old_conn->proxyuser);
+ Curl_safefree(old_conn->proxypasswd);
+ Curl_llist_destroy(old_conn->send_pipe, NULL);
+ Curl_llist_destroy(old_conn->recv_pipe, NULL);
+
+ free(old_conn); /* we don't need this anymore */
+
+ /*
+ * If we're doing a resumed transfer, we need to setup our stuff
+ * properly.
+ */
+ data->reqdata.resume_from = data->set.set_resume_from;
+ if (data->reqdata.resume_from) {
+ if (data->reqdata.rangestringalloc == TRUE)
+ free(data->reqdata.range);
+ data->reqdata.range = aprintf("%" FORMAT_OFF_T "-",
+ data->reqdata.resume_from);
+ if(!data->reqdata.range)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* tell ourselves to fetch this range */
+ data->reqdata.use_range = TRUE; /* enable range download */
+ data->reqdata.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 */
+ data->reqdata.range = strdup(data->set.set_range);
+ if(!data->reqdata.range)
+ return CURLE_OUT_OF_MEMORY;
+ data->reqdata.rangestringalloc = TRUE; /* mark range string allocated */
+ data->reqdata.use_range = TRUE; /* enable range download */
+ }
+ else
+ data->reqdata.use_range = FALSE; /* disable range download */
+
+ *in_connect = conn; /* return this instead! */
+
+ infof(data, "Re-using existing connection! (#%ld) with host %s\n",
+ conn->connectindex,
+ conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
+ }
+ else {
+ /*
+ * This is a brand new connection, so let's store it in the connection
+ * cache of ours!
+ */
+ ConnectionStore(data, conn);
+ }
+
+ /* Continue connectdata initialization here. */
+
+ /*
+ *
+ * Inherit the proper values from the urldata struct AFTER we have arranged
+ * the persistent connection stuff */
+ conn->fread = data->set.fread;
+ conn->fread_in = data->set.in;
+
+ if ((conn->protocol&PROT_HTTP) &&
+ data->set.upload &&
+ (data->set.infilesize == -1) &&
+ (data->set.httpversion != CURL_HTTP_VERSION_1_0)) {
+ /* HTTP, upload, unknown file size and not HTTP 1.0 */
+ conn->bits.upload_chunky = TRUE;
+ }
+ else {
+ /* else, no chunky upload */
+ conn->bits.upload_chunky = FALSE;
+ }
+
+#ifndef USE_ARES
+ /*************************************************************
+ * Set timeout if that is being used, and we're not using an asynchronous
+ * name resolve.
+ *************************************************************/
+ if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
+ /*************************************************************
+ * Set signal handler to catch SIGALRM
+ * Store the old value to be able to set it back later!
+ *************************************************************/
+
+#ifdef SIGALRM
+#ifdef HAVE_ALARM
+ long shortest;
+#endif
+#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 /* HAVE_SIGACTION */
+ /* no sigaction(), revert to the much lamer signal() */
+#ifdef HAVE_SIGNAL
+ keep_sigact = signal(SIGALRM, alarmfunc);
+#endif
+#endif /* HAVE_SIGACTION */
+
+ /* 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
+ shortest = data->set.timeout; /* default to this timeout value */
+ if(shortest && data->set.connecttimeout &&
+ (data->set.connecttimeout < shortest))
+ /* if both are set, pick the shortest */
+ shortest = data->set.connecttimeout;
+ else if(!shortest)
+ /* if timeout is not set, use the connect timeout */
+ shortest = data->set.connecttimeout;
+
+ /* alarm() makes a signal get sent when the timeout fires off, and that
+ will abort system calls */
+ prev_alarm = alarm((unsigned int) shortest);
+ /* 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
+#endif /* SIGALRM */
+ }
+#endif /* USE_ARES */
+
+ /*************************************************************
+ * Resolve the name of the server or proxy
+ *************************************************************/
+ if(conn->bits.reuse) {
+ /* re-used connection, no resolving is necessary */
+ hostaddr = NULL;
+ /* we'll need to clear conn->dns_entry later in Curl_disconnect() */
+
+ if (conn->bits.httpproxy)
+ fix_hostname(data, conn, &conn->host);
+ }
+ else {
+ /* this is a fresh connect */
+
+ /* set a pointer to the hostname we display */
+ fix_hostname(data, conn, &conn->host);
+
+ if(!conn->proxy.name || !*conn->proxy.name) {
+ /* If not connecting via a proxy, extract the port from the URL, if it is
+ * there, thus overriding any defaults that might have been set above. */
+ conn->port = conn->remote_port; /* it is the same port */
+
+ /* Resolve target host right on */
+ rc = Curl_resolv(conn, conn->host.name, (int)conn->port, &hostaddr);
+ if(rc == CURLRESOLV_PENDING)
+ *async = TRUE;
+
+ else if(!hostaddr) {
+ failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
+ result = CURLE_COULDNT_RESOLVE_HOST;
+ /* don't return yet, we need to clean up the timeout first */
+ }
+ }
+ else {
+ /* This is a proxy that hasn't been resolved yet. */
+
+ /* IDN-fix the proxy name */
+ fix_hostname(data, conn, &conn->proxy);
+
+ /* resolve proxy */
+ rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &hostaddr);
+
+ if(rc == CURLRESOLV_PENDING)
+ *async = TRUE;
+
+ else if(!hostaddr) {
+ failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
+ result = CURLE_COULDNT_RESOLVE_PROXY;
+ /* don't return yet, we need to clean up the timeout first */
+ }
+ }
+ }
+ *addr = hostaddr;
+
+#if defined(HAVE_ALARM) && defined(SIGALRM) && !defined(USE_ARES)
+ if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
+#ifdef HAVE_SIGACTION
+ if(keep_copysig) {
+ /* we got a struct as it looked before, now put that one back nice
+ and clean */
+ sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
+ }
+#else
+#ifdef HAVE_SIGNAL
+ /* restore the previous SIGALRM handler */
+ signal(SIGALRM, keep_sigact);
+#endif
+#endif /* HAVE_SIGACTION */
+
+ /* switch back the alarm() to either zero or to what it was before minus
+ the time we spent until now! */
+ if(prev_alarm) {
+ /* there was an alarm() set before us, now put it back */
+ unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
+ unsigned long alarm_set;
+
+ /* the alarm period is counted in even number of seconds */
+ alarm_set = prev_alarm - elapsed_ms/1000;
+
+ if(!alarm_set ||
+ ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
+ /* if the alarm time-left reached zero or turned "negative" (counted
+ with unsigned values), we should fire off a SIGALRM here, but we
+ won't, and zero would be to switch it off so we never set it to
+ less than 1! */
+ alarm(1);
+ result = CURLE_OPERATION_TIMEOUTED;
+ failf(data, "Previous alarm fired off!");
+ }
+ else
+ alarm((unsigned int)alarm_set);
+ }
+ else
+ alarm(0); /* just shut it off */
+ }
+#endif
+
+ return result;
+}
+
+/* SetupConnection() is called after the name resolve initiated in
+ * CreateConnection() is all done.
+ *
+ * NOTE: the argument 'hostaddr' is NULL when this function is called for a
+ * re-used connection.
+ *
+ * conn->data MUST already have been setup fine (in CreateConnection)
+ */
+
+static CURLcode SetupConnection(struct connectdata *conn,
+ struct Curl_dns_entry *hostaddr,
+ bool *protocol_done)
+{
+ CURLcode result=CURLE_OK;
+ struct SessionHandle *data = conn->data;
+
+ Curl_pgrsTime(data, TIMER_NAMELOOKUP);
+
+ if(conn->protocol & PROT_FILE) {
+ /* There's nothing in this function to setup if we're only doing
+ a file:// transfer */
+ *protocol_done = TRUE;
+ return result;
+ }
+ *protocol_done = FALSE; /* default to not done */
+
+ /*************************************************************
+ * Send user-agent to HTTP proxies even if the target protocol
+ * isn't HTTP.
+ *************************************************************/
+ if((conn->protocol&PROT_HTTP) || conn->bits.httpproxy) {
+ if(data->set.useragent) {
+ Curl_safefree(conn->allocptr.uagent);
+ conn->allocptr.uagent =
+ aprintf("User-Agent: %s\r\n", data->set.useragent);
+ if(!conn->allocptr.uagent)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ conn->headerbytecount = 0;
+
+#ifdef CURL_DO_LINEEND_CONV
+ data->state.crlf_conversions = 0; /* reset CRLF conversion counter */
+#endif /* CURL_DO_LINEEND_CONV */
+
+ for(;;) {
+ /* loop for CURL_SERVER_CLOSED_CONNECTION */
+
+ if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
+ bool connected = FALSE;
+
+ /* Connect only if not already connected! */
+ result = ConnectPlease(data, conn, hostaddr, &connected);
+
+ if(connected) {
+ result = Curl_protocol_connect(conn, protocol_done);
+ if(CURLE_OK == result)
+ conn->bits.tcpconnect = TRUE;
+ }
+ else
+ conn->bits.tcpconnect = FALSE;
+
+ /* if the connection was closed by the server while exchanging
+ authentication information, retry with the new set
+ authentication information */
+ if(conn->bits.proxy_connect_closed) {
+ /* reset the error buffer */
+ if (data->set.errorbuffer)
+ data->set.errorbuffer[0] = '\0';
+ data->state.errorbuf = FALSE;
+ continue;
+ }
+
+ if(CURLE_OK != result)
+ return result;
+ }
+ else {
+ Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
+ conn->bits.tcpconnect = TRUE;
+ *protocol_done = TRUE;
+ if(data->set.verbose)
+ verboseconnect(conn);
+ }
+ /* Stop the loop now */
+ break;
+ }
+
+ conn->now = Curl_tvnow(); /* time this *after* the connect is done, we
+ set this here perhaps a second time */
+
+#ifdef __EMX__
+ /* 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,
+ bool *asyncp,
+ bool *protocol_done)
+{
+ CURLcode code;
+ struct Curl_dns_entry *dns;
+
+ *asyncp = FALSE; /* assume synchronous resolves by default */
+
+ /* call the stuff that needs to be called */
+ code = CreateConnection(data, in_connect, &dns, asyncp);
+
+ if(CURLE_OK == code) {
+ /* no error */
+ if(dns || !*asyncp)
+ /* If an address is available it means that we already have the name
+ resolved, OR it isn't async. if this is a re-used connection 'dns'
+ will be NULL here. Continue connecting from here */
+ code = SetupConnection(*in_connect, dns, protocol_done);
+ /* else
+ response will be received and treated async wise */
+ }
+
+ if(CURLE_OK != code) {
+ /* We're not allowed to return failure with memory left allocated
+ in the connectdata struct, free those here */
+ if(*in_connect) {
+ Curl_disconnect(*in_connect); /* close the connection */
+ *in_connect = NULL; /* return a NULL */
+ }
+ }
+ else {
+ if ((*in_connect)->is_in_pipeline)
+ data->state.is_in_pipeline = TRUE;
+ }
+
+ return code;
+}
+
+/* Call this function after Curl_connect() has returned async=TRUE and
+ then a successful name resolve has been received.
+
+ Note: this function disconnects and frees the conn data in case of
+ resolve failure */
+CURLcode Curl_async_resolved(struct connectdata *conn,
+ bool *protocol_done)
+{
+#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
+ defined(USE_THREADING_GETADDRINFO)
+ CURLcode code = SetupConnection(conn, conn->async.dns, protocol_done);
+
+ if(code)
+ /* We're not allowed to return failure with memory left allocated
+ in the connectdata struct, free those here */
+ Curl_disconnect(conn); /* close the connection */
+
+ return code;
+#else
+ (void)conn;
+ (void)protocol_done;
+ return CURLE_OK;
+#endif
+}
+
+
+CURLcode Curl_done(struct connectdata **connp,
+ CURLcode status, bool premature) /* an error if this is called after an
+ error was detected */
+{
+ CURLcode result;
+ struct connectdata *conn = *connp;
+ struct SessionHandle *data = conn->data;
+
+ Curl_expire(data, 0); /* stop timer */
+
+ if(conn->bits.done)
+ return CURLE_OK; /* Curl_done() has already been called */
+
+ conn->bits.done = TRUE; /* called just now! */
+
+ if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) &&
+ conn->readchannel_inuse)
+ conn->readchannel_inuse = FALSE;
+ if(Curl_removeHandleFromPipeline(data, conn->send_pipe) &&
+ conn->writechannel_inuse)
+ conn->writechannel_inuse = FALSE;
+
+ /* cleanups done even if the connection is re-used */
+ if(data->reqdata.rangestringalloc) {
+ free(data->reqdata.range);
+ data->reqdata.rangestringalloc = FALSE;
+ }
+
+ /* Cleanup possible redirect junk */
+ if(data->reqdata.newurl) {
+ free(data->reqdata.newurl);
+ data->reqdata.newurl = NULL;
+ }
+
+ if(conn->dns_entry) {
+ Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
+ conn->dns_entry = NULL;
+ }
+
+ /* this calls the protocol-specific function pointer previously set */
+ if(conn->curl_done)
+ result = conn->curl_done(conn, status, premature);
+ else
+ result = CURLE_OK;
+
+ Curl_pgrsDone(conn); /* done with the operation */
+
+ /* for ares-using, make sure all possible outstanding requests are properly
+ cancelled before we proceed */
+ ares_cancel(data->state.areschannel);
+
+ /* 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 || conn->bits.close) {
+ CURLcode res2 = Curl_disconnect(conn); /* close the connection */
+
+ *connp = NULL; /* to make the caller of this function better detect that
+ this was actually killed here */
+
+ /* If we had an error already, make sure we return that one. But
+ if we got a new error, return that. */
+ if(!result && res2)
+ result = res2;
+ }
+ else {
+ ConnectionDone(conn); /* the connection is no longer in use */
+
+ /* remember the most recently used connection */
+ data->state.lastconnect = conn->connectindex;
+
+ infof(data, "Connection #%ld to host %s left intact\n",
+ conn->connectindex,
+ conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
+ }
+
+ return result;
+}
+
+CURLcode Curl_do(struct connectdata **connp, bool *done)
+{
+ CURLcode result=CURLE_OK;
+ struct connectdata *conn = *connp;
+ struct SessionHandle *data = conn->data;
+
+ conn->bits.done = FALSE; /* Curl_done() is not called yet */
+ conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */
+
+ if(conn->curl_do) {
+ /* generic protocol-specific function pointer set in curl_connect() */
+ result = conn->curl_do(conn, done);
+
+ /* This was formerly done in transfer.c, but we better do it here */
+
+ if((CURLE_SEND_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 connection */
+ result = Curl_done(&conn, result, FALSE); /* we are so done with this */
+
+ /* conn may no longer be a good pointer */
+
+ /*
+ * According to bug report #1330310. We need to check for
+ * CURLE_SEND_ERROR here as well. I figure this could happen when the
+ * request failed on a FTP connection and thus Curl_done() itself tried
+ * to use the connection (again). Slight Lack of feedback in the report,
+ * but I don't think this extra check can do much harm.
+ */
+ if((CURLE_OK == result) || (CURLE_SEND_ERROR == result)) {
+ bool async;
+ bool protocol_done = TRUE;
+
+ /* Now, redo the connect and get a new connection */
+ result = Curl_connect(data, connp, &async, &protocol_done);
+ if(CURLE_OK == result) {
+ /* We have connected or sent away a name resolve query fine */
+
+ conn = *connp; /* setup conn to again point to something nice */
+ if(async) {
+ /* Now, if async is TRUE here, we need to wait for the name
+ to resolve */
+ result = Curl_wait_for_resolv(conn, NULL);
+ if(result)
+ return result;
+
+ /* Resolved, continue with the connection */
+ result = Curl_async_resolved(conn, &protocol_done);
+ if(result)
+ return result;
+ }
+
+ /* ... finally back to actually retry the DO phase */
+ result = conn->curl_do(conn, done);
+ }
+ }
+ }
+ }
+ return result;
+}
+
+CURLcode Curl_do_more(struct connectdata *conn)
+{
+ CURLcode result=CURLE_OK;
+
+ if(conn->curl_do_more)
+ result = conn->curl_do_more(conn);
+
+ return result;
+}
diff --git a/Utilities/cmcurl/url.h b/Utilities/cmcurl/url.h
new file mode 100644
index 000000000..446b3d91a
--- /dev/null
+++ b/Utilities/cmcurl/url.h
@@ -0,0 +1,85 @@
+#ifndef __URL_H
+#define __URL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include <stdarg.h> /* to make sure we have ap_list */
+
+/*
+ * Prototypes for library-wide functions provided by url.c
+ */
+
+CURLcode Curl_open(struct SessionHandle **curl);
+CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
+ va_list arg);
+CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */
+CURLcode Curl_connect(struct SessionHandle *, struct connectdata **,
+ bool *async, bool *protocol_connect);
+CURLcode Curl_async_resolved(struct connectdata *conn,
+ bool *protocol_connect);
+CURLcode Curl_do(struct connectdata **, bool *done);
+CURLcode Curl_do_more(struct connectdata *);
+CURLcode Curl_done(struct connectdata **, CURLcode, bool premature);
+CURLcode Curl_disconnect(struct connectdata *);
+CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done);
+CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done);
+CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done);
+void Curl_safefree(void *ptr);
+
+/* create a connection cache */
+struct conncache *Curl_mk_connc(int type, int amount);
+/* free a connection cache */
+void Curl_rm_connc(struct conncache *c);
+/* Change number of entries of a connection cache */
+CURLcode Curl_ch_connc(struct SessionHandle *data,
+ struct conncache *c,
+ long newamount);
+
+int Curl_protocol_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks);
+int Curl_doing_getsock(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks);
+
+void Curl_addHandleToPipeline(struct SessionHandle *handle,
+ struct curl_llist *pipe);
+int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
+ struct curl_llist *pipe);
+bool Curl_isHandleAtHead(struct SessionHandle *handle,
+ struct curl_llist *pipe);
+
+void Curl_close_connections(struct SessionHandle *data);
+
+#if 0
+CURLcode Curl_protocol_fdset(struct connectdata *conn,
+ fd_set *read_fd_set,
+ fd_set *write_fd_set,
+ int *max_fdp);
+CURLcode Curl_doing_fdset(struct connectdata *conn,
+ fd_set *read_fd_set,
+ fd_set *write_fd_set,
+ int *max_fdp);
+#endif
+
+#endif
diff --git a/Utilities/cmcurl/urldata.h b/Utilities/cmcurl/urldata.h
new file mode 100644
index 000000000..9537ce36b
--- /dev/null
+++ b/Utilities/cmcurl/urldata.h
@@ -0,0 +1,1340 @@
+#ifndef __URLDATA_H
+#define __URLDATA_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+/* This file is for lib internal stuff */
+
+#include "setup.h"
+
+#define PORT_FTP 21
+#define PORT_FTPS 990
+#define PORT_TELNET 23
+#define PORT_HTTP 80
+#define PORT_HTTPS 443
+#define PORT_DICT 2628
+#define PORT_LDAP 389
+#define PORT_TFTP 69
+#define PORT_SSH 22
+
+#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 "curl_by_daniel@haxx.se"
+
+#include "cookie.h"
+#include "formdata.h"
+
+#ifdef USE_SSLEAY
+#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
+#ifdef HAVE_OPENSSL_PKCS12_H
+#include <openssl/pkcs12.h>
+#endif
+#else /* SSLeay-style includes */
+#include "rsa.h"
+#include "crypto.h"
+#include "x509.h"
+#include "pem.h"
+#include "ssl.h"
+#include "err.h"
+#endif /* USE_OPENSSL */
+#endif /* USE_SSLEAY */
+
+#ifdef USE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include "timeval.h"
+
+#ifdef HAVE_ZLIB_H
+#include <zlib.h> /* for content-encoding */
+#endif
+
+#ifdef USE_ARES
+#include <ares.h>
+#endif
+
+#include <curl/curl.h>
+
+#include "http_chunks.h" /* for the structs and enum stuff */
+#include "hostip.h"
+#include "hash.h"
+#include "splay.h"
+
+#ifdef HAVE_GSSAPI
+# ifdef HAVE_GSSGNU
+# include <gss.h>
+# elif defined HAVE_GSSMIT
+# include <gssapi/gssapi.h>
+# include <gssapi/gssapi_generic.h>
+# else
+# include <gssapi.h>
+# endif
+#endif
+
+#ifdef HAVE_LIBSSH2_H
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+#endif /* HAVE_LIBSSH2_H */
+
+/* Download buffer size, keep it fairly big for speed reasons */
+#undef BUFSIZE
+#define BUFSIZE CURL_MAX_WRITE_SIZE
+
+/* Initial size of the buffer to store headers in, it'll be enlarged in case
+ of need. */
+#define HEADERSIZE 256
+
+#define CURLEASY_MAGIC_NUMBER 0xc0dedbad
+
+/* Just a convenience macro to get the larger value out of two given.
+ We prefix with CURL to prevent name collisions. */
+#define CURLMAX(x,y) ((x)>(y)?(x):(y))
+
+#ifdef HAVE_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
+
+/* enum for the nonblocking SSL connection state machine */
+typedef enum {
+ ssl_connect_1,
+ ssl_connect_2,
+ ssl_connect_2_reading,
+ ssl_connect_2_writing,
+ ssl_connect_3,
+ ssl_connect_done
+} ssl_connect_state;
+
+/* struct for data related to each SSL connection */
+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;
+ ssl_connect_state connecting_state;
+#endif /* USE_SSLEAY */
+#ifdef USE_GNUTLS
+ gnutls_session session;
+ gnutls_certificate_credentials cred;
+#endif /* USE_GNUTLS */
+};
+
+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 verify
+ 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 */
+ curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
+ void *fsslctxp; /* parameter for call back */
+ bool sessionid; /* cache session IDs or not */
+};
+
+/* information stored 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 */
+ size_t idsize; /* if known, otherwise 0 */
+ long age; /* just a number, the higher the more recent */
+ unsigned short remote_port; /* remote port to connect to */
+ struct ssl_config_data ssl_config; /* setup for this session */
+};
+
+/* Struct used for Digest challenge-response authentication */
+struct digestdata {
+ char *nonce;
+ char *cnonce;
+ char *realm;
+ int algo;
+ bool stale; /* set true for re-negotiation */
+ char *opaque;
+ char *qop;
+ char *algorithm;
+ int nc; /* nounce count */
+};
+
+typedef enum {
+ NTLMSTATE_NONE,
+ NTLMSTATE_TYPE1,
+ NTLMSTATE_TYPE2,
+ NTLMSTATE_TYPE3,
+ NTLMSTATE_LAST
+} curlntlm;
+
+#ifdef USE_WINDOWS_SSPI
+/* When including these headers, you must define either SECURITY_WIN32
+ * or SECURITY_KERNEL, indicating who is compiling the code.
+ */
+#define SECURITY_WIN32 1
+#include <security.h>
+#include <sspi.h>
+#include <rpc.h>
+#endif
+
+#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
+#include <iconv.h>
+#endif
+
+/* Struct used for NTLM challenge-response authentication */
+struct ntlmdata {
+ curlntlm state;
+#ifdef USE_WINDOWS_SSPI
+ CredHandle handle;
+ CtxtHandle c_handle;
+ SEC_WINNT_AUTH_IDENTITY identity;
+ SEC_WINNT_AUTH_IDENTITY *p_identity;
+ int has_handles;
+ void *type_2;
+ int n_type_2;
+#else
+ unsigned int flags;
+ unsigned char nonce[8];
+#endif
+};
+
+#ifdef HAVE_GSSAPI
+struct negotiatedata {
+ bool gss; /* Whether we're processing GSS-Negotiate or Negotiate */
+ const char* protocol; /* "GSS-Negotiate" or "Negotiate" */
+ OM_uint32 status;
+ gss_ctx_id_t context;
+ gss_name_t server_name;
+ gss_buffer_desc output_token;
+};
+#endif
+
+/****************************************************************************
+ * HTTP unique setup
+ ***************************************************************************/
+struct HTTP {
+ struct FormData *sendit;
+ curl_off_t postsize; /* off_t to handle large file sizes */
+ char *postdata;
+
+ const char *p_pragma; /* Pragma: string */
+ const char *p_accept; /* Accept: string */
+ curl_off_t readbytecount;
+ curl_off_t writebytecount;
+
+ /* For FORM posting */
+ struct Form form;
+ struct Curl_chunker chunk;
+
+ struct back {
+ curl_read_callback fread; /* backup storage for fread pointer */
+ void *fread_in; /* backup storage for fread_in pointer */
+ char *postdata;
+ curl_off_t postsize;
+ } backup;
+
+ enum {
+ HTTPSEND_NADA, /* init */
+ HTTPSEND_REQUEST, /* sending a request */
+ HTTPSEND_BODY, /* sending body */
+ HTTPSEND_LAST /* never use this */
+ } sending;
+
+ void *send_buffer; /* used if the request couldn't be sent in one chunk,
+ points to an allocated send_buffer struct */
+};
+
+/****************************************************************************
+ * FTP unique setup
+ ***************************************************************************/
+typedef enum {
+ FTP_STOP, /* do nothing state, stops the state machine */
+ FTP_WAIT220, /* waiting for the initial 220 response immediately after
+ a connect */
+ FTP_AUTH,
+ FTP_USER,
+ FTP_PASS,
+ FTP_ACCT,
+ FTP_PBSZ,
+ FTP_PROT,
+ FTP_CCC,
+ FTP_PWD,
+ FTP_QUOTE, /* waiting for a response to a command sent in a quote list */
+ FTP_RETR_PREQUOTE,
+ FTP_STOR_PREQUOTE,
+ FTP_POSTQUOTE,
+ FTP_CWD, /* change dir */
+ FTP_MKD, /* if the dir didn't exist */
+ FTP_MDTM, /* to figure out the datestamp */
+ FTP_TYPE, /* to set type when doing a head-like request */
+ FTP_LIST_TYPE, /* set type when about to do a dir list */
+ FTP_RETR_TYPE, /* set type when about to RETR a file */
+ FTP_STOR_TYPE, /* set type when about to STOR a file */
+ FTP_SIZE, /* get the remote file's size for head-like request */
+ FTP_RETR_SIZE, /* get the remote file's size for RETR */
+ FTP_STOR_SIZE, /* get the size for (resumed) STOR */
+ FTP_REST, /* when used to check if the server supports it in head-like */
+ FTP_RETR_REST, /* when asking for "resume" in for RETR */
+ FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */
+ FTP_PASV, /* generic state for PASV and EPSV, check count1 */
+ FTP_LIST, /* generic state for LIST, NLST or a custom list command */
+ FTP_RETR,
+ FTP_STOR, /* generic state for STOR and APPE */
+ FTP_QUIT,
+ FTP_LAST /* never used */
+} ftpstate;
+
+typedef enum {
+ FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */
+ FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */
+ FTPFILE_SINGLECWD = 3 /* make one CWD, then SIZE / RETR / STOR on the file */
+} curl_ftpfile;
+
+/* This FTP struct is used in the SessionHandle. All FTP data that is
+ connection-oriented must be in FTP_conn to properly deal with the fact that
+ perhaps the SessionHandle is changed between the times the connection is
+ used. */
+struct FTP {
+ curl_off_t *bytecountp;
+ char *user; /* user name string */
+ char *passwd; /* password string */
+ char *urlpath; /* the originally given path part of the URL */
+ char *file; /* decoded file */
+ bool no_transfer; /* nothing was transfered, (possibly because a resumed
+ transfer already was complete) */
+ curl_off_t downloadsize;
+};
+
+/* ftp_conn is used for striuct connection-oriented data in the connectdata
+ struct */
+struct ftp_conn {
+ char *entrypath; /* the PWD reply when we logged on */
+ char **dirs; /* realloc()ed array for path components */
+ int dirdepth; /* number of entries used in the 'dirs' array */
+ int diralloc; /* number of entries allocated for the 'dirs' array */
+ char *cache; /* data cache between getresponse()-calls */
+ curl_off_t cache_size; /* size of cache in bytes */
+ bool dont_check; /* Set to TRUE to prevent the final (post-transfer)
+ file size and 226/250 status check. It should still
+ read the line, just ignore the result. */
+ long response_time; /* When no timeout is given, this is the amount of
+ seconds we await for an FTP response. Initialized
+ in Curl_ftp_connect() */
+ bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do anything. If
+ the connection has timed out or been closed, this
+ should be FALSE when it gets to Curl_ftp_quit() */
+ bool cwddone; /* if it has been determined that the proper CWD combo
+ already has been done */
+ bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent
+ caching the current directory */
+ char *prevpath; /* conn->path from the previous transfer */
+ char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a
+ and others (A/I or zero) */
+ size_t nread_resp; /* number of bytes currently read of a server response */
+ char *linestart_resp; /* line start pointer for the FTP server response
+ reader function */
+
+ int count1; /* general purpose counter for the state machine */
+ int count2; /* general purpose counter for the state machine */
+ int count3; /* general purpose counter for the state machine */
+ char *sendthis; /* allocated pointer to a buffer that is to be sent to the
+ ftp server */
+ size_t sendleft; /* number of bytes left to send from the sendthis buffer */
+ size_t sendsize; /* total size of the sendthis buffer */
+ struct timeval response; /* set to Curl_tvnow() when a command has been sent
+ off, used to time-out response reading */
+ ftpstate state; /* always use ftp.c:state() to change state! */
+};
+
+struct SSHPROTO {
+ curl_off_t *bytecountp;
+ char *user;
+ char *passwd;
+ char *path; /* the path we operate on */
+ char *homedir;
+ char *errorstr;
+#ifdef USE_LIBSSH2
+ LIBSSH2_SESSION *ssh_session; /* Secure Shell session */
+ LIBSSH2_CHANNEL *ssh_channel; /* Secure Shell channel handle */
+ LIBSSH2_SFTP *sftp_session; /* SFTP handle */
+ LIBSSH2_SFTP_HANDLE *sftp_handle;
+#endif /* USE_LIBSSH2 */
+};
+
+
+/****************************************************************************
+ * FILE unique setup
+ ***************************************************************************/
+struct FILEPROTO {
+ char *path; /* the path we operate on */
+ char *freepath; /* pointer to the allocated block we must free, this might
+ differ from the 'path' pointer */
+ 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 ipv6_ip; /* we communicate with a remote site specified with pure IPv6
+ IP address */
+ bool ipv6; /* we communicate with a site using an IPv6 address */
+
+ bool do_more; /* this is set TRUE if the ->curl_do_more() function is
+ supposed to be called, after ->curl_do() */
+
+ bool upload_chunky; /* set TRUE if we are doing chunked transfer-encoding
+ on upload */
+ bool getheader; /* TRUE if header parsing is wanted */
+
+ bool forbidchunk; /* used only to explicitly forbid chunk-upload for
+ specific upload buffers. See readmoredata() in
+ http.c for details. */
+
+ bool tcpconnect; /* the TCP layer (or simimlar) is connected, this is set
+ the first time on the first connect function call */
+ bool protoconnstart;/* the protocol layer has STARTED its operation after
+ the TCP layer connect */
+
+ bool retry; /* this connection is about to get closed and then
+ re-attempted at another connection. */
+ bool no_body; /* CURLOPT_NO_BODY (or similar) was set */
+ bool tunnel_proxy; /* if CONNECT is used to "tunnel" through the proxy.
+ This is implicit when SSL-protocols are used through
+ proxies, but can also be enabled explicitly by
+ apps */
+ bool authneg; /* TRUE when the auth phase has started, which means
+ that we are creating a request with an auth header,
+ but it is not the final request in the auth
+ negotiation. */
+ bool rewindaftersend;/* TRUE when the sending couldn't be stopped even
+ though it will be discarded. When the whole send
+ operation is done, we must call the data rewind
+ callback. */
+ bool ftp_use_epsv; /* As set with CURLOPT_FTP_USE_EPSV, but if we find out
+ EPSV doesn't work we disable it for the forthcoming
+ requests */
+
+ bool ftp_use_eprt; /* As set with CURLOPT_FTP_USE_EPRT, but if we find out
+ EPRT doesn't work we disable it for the forthcoming
+ requests */
+ bool netrc; /* name+password provided by netrc */
+
+ bool trailerHdrPresent; /* Set when Trailer: header found in HTTP response.
+ Required to determine whether to look for trailers
+ in case of Transfer-Encoding: chunking */
+ bool done; /* set to FALSE when Curl_do() is called and set to TRUE
+ when Curl_done() is called, to prevent Curl_done() to
+ get invoked twice when the multi interface is
+ used. */
+ bool stream_was_rewound; /* Indicates that the stream was rewound after a
+ request read past the end of its response byte
+ boundary */
+ bool proxy_connect_closed; /* set true if a proxy disconnected the
+ connection in a CONNECT request with auth, so
+ that libcurl should reconnect and continue. */
+};
+
+struct hostname {
+ char *rawalloc; /* allocated "raw" version of the name */
+ char *encalloc; /* allocated IDN-encoded version of the name */
+ char *name; /* name to use internally, might be encoded, might be raw */
+ char *dispname; /* name to display, as 'name' might be encoded */
+};
+
+/*
+ * Flags on the keepon member of the Curl_transfer_keeper
+ */
+
+#define KEEP_NONE 0
+#define KEEP_READ 1 /* there is or may be data to read */
+#define KEEP_WRITE 2 /* there is or may be data to write */
+#define KEEP_READ_HOLD 4 /* when set, no reading should be done but there
+ might still be data to read */
+#define KEEP_WRITE_HOLD 8 /* when set, no writing should be done but there
+ might still be data to write */
+
+/*
+ * 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 {
+
+ /** Values copied over from the HandleData struct each time on init **/
+
+ curl_off_t size; /* -1 if unknown at this point */
+ curl_off_t *bytecountp; /* return number of bytes read or NULL */
+
+ curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch, 0
+ means unlimited */
+ curl_off_t *writebytecountp; /* return number of bytes written or NULL */
+
+ /** End of HandleData struct copies **/
+
+ curl_off_t bytecount; /* total number of bytes read */
+ curl_off_t writebytecount; /* number of bytes written */
+
+ struct timeval start; /* transfer started at this time */
+ struct timeval now; /* current time */
+ bool header; /* incoming data has HTTP header */
+ enum {
+ HEADER_NORMAL, /* no bad header at all */
+ HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest
+ is normal data */
+ HEADER_ALLBAD /* all was believed to be header */
+ } badheader; /* the header was deemed bad and will be
+ written as body */
+ int headerline; /* counts header lines to better track the
+ first one */
+ char *hbufp; /* points at *end* of header line */
+ size_t 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 */
+ curl_off_t 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 */
+ struct timeval start100; /* time stamp to wait for the 100 code from */
+ bool write_after_100_header; /* TRUE = we enable the write after we
+ received a 100-continue/timeout or
+ FALSE = directly */
+ bool wait100_after_headers; /* TRUE = after the request-headers have been
+ sent off properly, we go into the wait100
+ state, FALSE = don't */
+ int content_encoding; /* What content encoding. sec 3.5, RFC2616. */
+
+#define IDENTITY 0 /* No encoding */
+#define DEFLATE 1 /* zlib delfate [RFC 1950 & 1951] */
+#define GZIP 2 /* gzip algorithm [RFC 1952] */
+#define COMPRESS 3 /* Not handled, added for completeness */
+
+#ifdef HAVE_LIBZ
+ bool zlib_init; /* True if zlib already initialized;
+ undefined if Content-Encoding header. */
+ z_stream z; /* State structure for zlib. */
+#endif
+
+ time_t timeofdoc;
+ long bodywrites;
+
+ char *buf;
+ char *uploadbuf;
+ curl_socket_t maxfd;
+
+ int keepon;
+
+ bool upload_done; /* set to TRUE when doing chunked transfer-encoding upload
+ and we're uploading the last chunk */
+
+ bool ignorebody; /* we read a response-body but we ignore it! */
+ bool ignorecl; /* This HTTP response has no body so we ignore the Content-
+ Length: header */
+};
+
+#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
+ defined(USE_THREADING_GETADDRINFO)
+struct Curl_async {
+ char *hostname;
+ int port;
+ struct Curl_dns_entry *dns;
+ bool done; /* set TRUE when the lookup is complete */
+ int status; /* if done is TRUE, this is the status from the callback */
+ void *os_specific; /* 'struct thread_data' for Windows */
+};
+#endif
+
+#define FIRSTSOCKET 0
+#define SECONDARYSOCKET 1
+
+/* These function pointer types are here only to allow easier typecasting
+ within the source when we need to cast between data pointers (such as NULL)
+ and function pointers. */
+typedef CURLcode (*Curl_do_more_func)(struct connectdata *);
+typedef CURLcode (*Curl_done_func)(struct connectdata *, CURLcode, bool);
+
+
+/*
+ * Store's request specific data in the easy handle (SessionHandle).
+ * Previously, these members were on the connectdata struct but since
+ * a conn struct may now be shared between different SessionHandles,
+ * we store connection-specifc data here.
+ *
+ */
+struct HandleData {
+ char *pathbuffer;/* allocated buffer to store the URL's path part in */
+ char *path; /* path to use, points to somewhere within the pathbuffer
+ area */
+
+ char *newurl; /* This can only be set if a Location: was in the
+ document headers */
+
+ /* 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. */
+ ssize_t 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;
+
+ curl_off_t size; /* -1 if unknown at this point */
+ curl_off_t *bytecountp; /* return number of bytes read or NULL */
+
+ curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch, 0
+ means unlimited */
+ curl_off_t *writebytecountp; /* return number of bytes written or NULL */
+
+ bool use_range;
+ bool rangestringalloc; /* the range string is malloc()'ed */
+
+ char *range; /* range, if used. See README for detailed specification on
+ this syntax. */
+ curl_off_t resume_from; /* continue [ftp] transfer from here */
+
+ /* Protocol specific data */
+
+ union {
+ struct HTTP *http;
+ struct HTTP *https; /* alias, just for the sake of being more readable */
+ struct FTP *ftp;
+ void *tftp; /* private for tftp.c-eyes only */
+ struct FILEPROTO *file;
+ void *telnet; /* private for telnet.c-eyes only */
+ void *generic;
+ struct SSHPROTO *ssh;
+ } proto;
+};
+
+/*
+ * The connectdata struct contains all fields and variables that should be
+ * unique for an entire connection.
+ */
+struct connectdata {
+ /* 'data' is the CURRENT SessionHandle using this connection -- take great
+ caution that this might very well vary between different times this
+ connection is used! */
+ struct SessionHandle *data;
+
+ bool inuse; /* This is a marker for the connection cache logic. If this is
+ TRUE this handle is being used by an easy handle and cannot
+ be used by any other easy handle without careful
+ consideration (== only for pipelining). */
+
+ /**** Fields set when inited and not modified again */
+ long connectindex; /* what index in the connection cache connects index this
+ particular struct has */
+ long protocol; /* PROT_* flags concerning the protocol set */
+#define PROT_MISSING (1<<0)
+#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 */
+#define PROT_TFTP (1<<11)
+#define PROT_SCP (1<<12)
+#define PROT_SFTP (1<<13)
+
+#define PROT_CLOSEACTION PROT_FTP /* these ones need action before socket
+ close */
+
+ /* 'dns_entry' is the particular host we use. This points to an entry in the
+ DNS cache and it will not get pruned while locked. It gets unlocked in
+ Curl_done(). This entry will be NULL if the connection is re-used as then
+ there is no name resolve done. */
+ struct Curl_dns_entry *dns_entry;
+
+ /* 'ip_addr' is the particular IP we connected to. It points to a struct
+ within the DNS cache, so this pointer is only valid as long as the DNS
+ cache entry remains locked. It gets unlocked in Curl_done() */
+ Curl_addrinfo *ip_addr;
+
+ /* 'ip_addr_str' is the ip_addr data as a human readable malloc()ed string.
+ It remains available as long as the connection does, which is longer than
+ the ip_addr itself. Set with Curl_store_ip_addr() when ip_addr has been
+ set. */
+ char *ip_addr_str;
+
+ char protostr[16]; /* store the protocol string in this buffer */
+ int socktype; /* SOCK_STREAM or SOCK_DGRAM */
+
+ struct hostname host;
+ struct hostname proxy;
+
+ long port; /* which port to use locally */
+ unsigned short remote_port; /* what remote port to connect to,
+ not the proxy port! */
+
+ long headerbytecount; /* only count received headers */
+ long deductheadercount; /* this amount of bytes doesn't count when we check
+ if anything has been transfered at the end of
+ a connection. We use this counter to make only
+ a 100 reply (without a following second response
+ code) result in a CURLE_GOT_NOTHING error code */
+
+ char *user; /* user name string, allocated */
+ char *passwd; /* password string, allocated */
+
+ char *proxyuser; /* proxy user name string, allocated */
+ char *proxypasswd; /* proxy password string, allocated */
+
+ struct timeval now; /* "current" time */
+ struct timeval created; /* creation time */
+ curl_socket_t sock[2]; /* two sockets, the second is used for the data
+ transfer when doing FTP */
+
+ struct ssl_connect_data ssl[2]; /* this is for ssl-stuff */
+ struct ssl_config_data ssl_config;
+
+ 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 *, bool *done);
+ Curl_done_func curl_done;
+
+ /* If the curl_do() function is better made in two halves, this
+ * curl_do_more() function will be called afterwards, if set. For example
+ * for doing the FTP stuff after the PASV/PORT command.
+ */
+ Curl_do_more_func curl_do_more;
+
+ /* 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.
+ * The 'done' pointer points to a bool that should be set to TRUE if the
+ * function completes before return. If it doesn't complete, the caller
+ * should call the curl_connecting() function until it is.
+ */
+ CURLcode (*curl_connect)(struct connectdata *, bool *done);
+
+ /* See above. Currently only used for FTP. */
+ CURLcode (*curl_connecting)(struct connectdata *, bool *done);
+ CURLcode (*curl_doing)(struct connectdata *, bool *done);
+
+ /* Called from the multi interface during the PROTOCONNECT phase, and it
+ should then return a proper fd set */
+ int (*curl_proto_getsock)(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks);
+
+ /* Called from the multi interface during the DOING phase, and it should
+ then return a proper fd set */
+ int (*curl_doing_getsock)(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks);
+
+ /* 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 *);
+
+ /* 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 *);
+
+ /**** curl_get() phase fields */
+
+ curl_socket_t sockfd; /* socket to read from or CURL_SOCKET_BAD */
+ curl_socket_t writesockfd; /* socket to write to, it may very
+ well be the same we read from.
+ CURL_SOCKET_BAD disables */
+
+ /** 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 *accept_encoding; /* 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 *host; /* free later if not NULL */
+ char *cookiehost; /* free later if not NULL */
+ } allocptr;
+
+ int sec_complete; /* if krb4 is enabled for this connection */
+#ifdef HAVE_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;
+ void *app_data;
+ const struct Curl_sec_client_mech *mech;
+ struct sockaddr_in local_addr;
+#endif
+
+ bool readchannel_inuse; /* whether the read channel is in use by an easy
+ handle */
+ bool writechannel_inuse; /* whether the write channel is in use by an easy
+ handle */
+ bool is_in_pipeline; /* TRUE if this connection is in a pipeline */
+
+ struct curl_llist *send_pipe; /* List of handles waiting to
+ send on this pipeline */
+ struct curl_llist *recv_pipe; /* List of handles waiting to read
+ their responses on this pipeline */
+
+ char master_buffer[BUFSIZE]; /* The master buffer for this connection. */
+ size_t read_pos; /* Current read position in the master buffer */
+ size_t buf_len; /* Length of the buffer?? */
+
+
+ /*************** Request - specific items ************/
+
+ /* previously this was in the urldata struct */
+ curl_read_callback fread; /* function that reads the input */
+ void *fread_in; /* pointer to pass to the fread() above */
+
+ struct ntlmdata ntlm; /* NTLM differs from other authentication schemes
+ because it authenticates connections, not
+ single requests! */
+ struct ntlmdata proxyntlm; /* NTLM data for proxy */
+
+ char syserr_buf [256]; /* buffer for Curl_strerror() */
+
+#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
+ defined(USE_THREADING_GETADDRINFO)
+ /* data used for the asynch name resolve callback */
+ struct Curl_async async;
+#endif
+
+ /* These three are used for chunked-encoding trailer support */
+ char *trailer; /* allocated buffer to store trailer in */
+ int trlMax; /* allocated buffer size */
+ int trlPos; /* index of where to store data */
+
+ union {
+ struct ftp_conn ftpc;
+ } proto;
+};
+
+/* The end of connectdata. */
+
+/*
+ * Struct to keep statistical and informational data.
+ */
+struct PureInfo {
+ int httpcode; /* Recent HTTP or FTP response code */
+ int httpproxycode;
+ int httpversion;
+ long filetime; /* If requested, this is might get set. Set to -1 if the time
+ was unretrievable. We cannot have this of type time_t,
+ since time_t is unsigned on several platforms such as
+ OpenVMS. */
+ long header_size; /* size of read header(s) in bytes */
+ long request_size; /* the amount of bytes sent in the request(s) */
+
+ long proxyauthavail;
+ long httpauthavail;
+
+ long numconnects; /* how many new connection did libcurl created */
+
+ 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 */
+ curl_off_t size_dl; /* total expected size */
+ curl_off_t size_ul; /* total expected size */
+ curl_off_t downloaded; /* transfered so far */
+ curl_off_t uploaded; /* transfered so far */
+
+ curl_off_t 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;
+
+ curl_off_t dlspeed;
+ curl_off_t ulspeed;
+
+ double t_nslookup;
+ double t_connect;
+ double t_pretransfer;
+ double t_starttransfer;
+ double t_redirect;
+
+ struct timeval start;
+ struct timeval t_startsingle;
+#define CURR_TIME (5+1) /* 6 entries for 5 seconds */
+
+ curl_off_t 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_HEAD,
+ 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
+#define MAX_CURL_USER_LENGTH_TXT "255"
+#define MAX_CURL_PASSWORD_LENGTH_TXT "255"
+
+struct auth {
+ long want; /* Bitmask set to the authentication methods wanted by the app
+ (with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */
+ long picked;
+ long avail; /* bitmask for what the server reports to support for this
+ resource */
+ bool done; /* TRUE when the auth phase is done and ready to do the *actual*
+ request */
+ bool multi; /* TRUE if this is not yet authenticated but within the auth
+ multipass negotiation */
+
+};
+
+struct conncache {
+ /* 'connects' will be an allocated array with pointers. If the pointer is
+ set, it holds an allocated connection. */
+ struct connectdata **connects;
+ long num; /* number of entries of the 'connects' array */
+ enum {
+ CONNCACHE_PRIVATE, /* used for an easy handle alone */
+ CONNCACHE_MULTI /* shared within a multi handle */
+ } type;
+};
+
+
+struct UrlState {
+ enum {
+ Curl_if_none,
+ Curl_if_easy,
+ Curl_if_multi
+ } used_interface;
+
+ struct conncache *connc; /* points to the connection cache this handle
+ uses */
+
+ /* buffers to store authentication data in, as parsed from input options */
+ struct timeval keeps_speed; /* for the progress meter really */
+
+ long lastconnect; /* index of most recent connect or -1 if undefined */
+
+ char *headerbuff; /* allocated buffer to store headers in */
+ size_t headersize; /* size of the allocation */
+
+ char buffer[BUFSIZE+1]; /* download buffer */
+ char uploadbuffer[BUFSIZE+1]; /* upload buffer */
+ curl_off_t current_speed; /* the ProgressShow() funcion sets this,
+ bytes / second */
+ bool this_is_a_follow; /* this is a followed Location: request */
+
+ bool is_in_pipeline; /* Indicates whether this handle is part of a pipeline */
+
+ char *first_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; /* huge buffer[BUFSIZE*2] 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. */
+ int os_errno; /* filled in with errno whenever an error occurs */
+#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. */
+
+ struct digestdata digest;
+ struct digestdata proxydigest;
+
+#ifdef HAVE_GSSAPI
+ struct negotiatedata negotiate;
+#endif
+
+ struct auth authhost;
+ struct auth authproxy;
+
+ bool authproblem; /* TRUE if there's some problem authenticating */
+
+#ifdef USE_ARES
+ ares_channel areschannel; /* for name resolves */
+#endif
+
+#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
+ ENGINE *engine;
+#endif /* USE_SSLEAY */
+ struct timeval expiretime; /* set this with Curl_expire() only */
+ struct Curl_tree timenode; /* for the splay stuff */
+
+ /* a place to store the most recenlty set FTP entrypath */
+ char *most_recent_ftp_entrypath;
+
+ /* set after initial USER failure, to prevent an authentication loop */
+ bool ftp_trying_alternative;
+
+ bool expect100header; /* TRUE if we added Expect: 100-continue */
+
+ bool pipe_broke; /* TRUE if the connection we were pipelined on broke
+ and we need to restart from the beginning */
+ bool cancelled; /* TRUE if the request was cancelled */
+
+#ifndef WIN32
+/* do FTP line-end conversions on most platforms */
+#define CURL_DO_LINEEND_CONV
+ /* for FTP downloads: track CRLF sequences that span blocks */
+ bool prev_block_had_trailing_cr;
+ /* for FTP downloads: how many CRLFs did we converted to LFs? */
+ curl_off_t crlf_conversions;
+#endif
+ /* If set to non-NULL, there's a connection in a shared connection cache
+ that uses this handle so we can't kill this SessionHandle just yet but
+ must keep it around and add it to the list of handles to kill once all
+ its connections are gone */
+ void *shared_conn;
+ bool closed; /* set to TRUE when curl_easy_cleanup() has been called on this
+ handle, but it is kept around as mentioned for
+ shared_conn */
+};
+
+
+/*
+ * 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 */
+ bool url_changed; /* set on CURL_OPT_URL, used to detect if the URL was
+ changed after the connect phase, as we allow callback
+ to change it and if so, we reconnect to use the new
+ URL instead */
+ char *referer; /* referer string */
+ bool referer_alloc; /* referer sting is malloc()ed */
+ struct curl_slist *cookielist; /* list of cookie files set by
+ curl_easy_setopt(COOKIEFILE) calls */
+};
+
+/*
+ * 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 Curl_one_easy; /* declared and used only in multi.c */
+struct Curl_multi; /* declared and used only in multi.c */
+
+struct UserDefined {
+ FILE *err; /* the stderr user data goes here */
+ void *debugdata; /* the data that will be passed to fdebug */
+ 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 if non-NULL */
+ char *set_url; /* what original URL to work on */
+ char *proxy; /* proxy to use */
+ long use_port; /* which port to use (when not using default) */
+ char *userpwd; /* <user:password>, if used */
+ long httpauth; /* what kind of HTTP authentication to use (bitmask) */
+ long proxyauth; /* what kind of proxy authentication to use (bitmask) */
+ 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, set to -1
+ for infinity */
+ char *set_referer; /* custom string */
+ bool free_referer; /* set TRUE if 'referer' points to a string we
+ allocated */
+ char *useragent; /* User-Agent string */
+ char *encoding; /* Accept-Encoding string */
+ char *postfields; /* if POST, set the fields' values here */
+ curl_off_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; /* local network interface/address to use */
+ unsigned short localport; /* local port number to bind to */
+ int localportrange; /* number of additional port numbers to test in case the
+ 'localport' one can't be bind()ed */
+ 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 */
+ curl_debug_callback fdebug; /* function that write informational data */
+ curl_ioctl_callback ioctl; /* function for I/O control */
+ curl_sockopt_callback fsockopt; /* function for setting socket options */
+ void *sockopt_client; /* pointer to pass to the socket options callback */
+
+ /* the 3 curl_conv_callback functions below are used on non-ASCII hosts */
+ /* function to convert from the network encoding: */
+ curl_conv_callback convfromnetwork;
+ /* function to convert to the network encoding: */
+ curl_conv_callback convtonetwork;
+ /* function to convert from UTF-8 encoding: */
+ curl_conv_callback convfromutf8;
+
+ void *progress_client; /* pointer to pass to the progress callback */
+ void *ioctl_client; /* pointer to pass to the ioctl callback */
+ long timeout; /* in seconds, 0 means no timeout */
+ long connecttimeout; /* in seconds, 0 means no timeout */
+ long ftp_response_timeout; /* in seconds, 0 means no timeout */
+ curl_off_t infilesize; /* size of file to upload, -1 means unknown */
+ long low_speed_limit; /* bytes/second */
+ long low_speed_time; /* number of seconds */
+ curl_off_t max_send_speed; /* high speed limit in bytes/second for upload */
+ curl_off_t max_recv_speed; /* high speed limit in bytes/second for download */
+ curl_off_t 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 curl_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 *cookiejar; /* dump all cookies to this file */
+ bool cookiesession; /* new cookie session? */
+ bool crlf; /* convert crlf on ftp upload(?) */
+ char *ftp_account; /* ftp account data */
+ char *ftp_alternative_to_user; /* command to send if USER/PASS fails */
+ struct curl_slist *quote; /* after connection is established */
+ struct curl_slist *postquote; /* after the transfer */
+ struct curl_slist *prequote; /* before the transfer, after type */
+ struct curl_slist *source_quote; /* 3rd party quote */
+ struct curl_slist *source_prequote; /* in 3rd party transfer mode - before
+ the transfer on source host */
+ struct curl_slist *source_postquote; /* in 3rd party transfer mode - after
+ the transfer on source host */
+ 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_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 */
+
+ curl_proxytype proxytype; /* what kind of proxy that is in use */
+
+ int dns_cache_timeout; /* DNS cache timeout */
+ long buffer_size; /* size of receive buffer to use */
+
+ char *private_data; /* Private data */
+
+ struct Curl_one_easy *one_easy; /* When adding an easy handle to a multi
+ handle, an internal 'Curl_one_easy'
+ struct is created and this is a pointer
+ to the particular struct associated with
+ this SessionHandle */
+
+ struct curl_slist *http200aliases; /* linked list of aliases for http200 */
+
+ long ip_version;
+
+ curl_off_t max_filesize; /* Maximum file size to download */
+
+ char *source_url; /* for 3rd party transfer */
+ char *source_userpwd; /* for 3rd party transfer */
+
+ curl_ftpfile ftp_filemethod; /* how to get to a file when FTP is used */
+
+/* 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 printhost; /* printing host name in debug info */
+ bool get_filetime;
+ bool tunnel_thru_httpproxy;
+ bool prefer_ascii; /* ASCII rather than binary */
+ bool ftp_append;
+ bool ftp_list_only;
+ bool ftp_create_missing_dirs;
+ bool ftp_use_port;
+ bool hide_progress;
+ bool http_fail_on_error;
+ bool http_follow_location;
+ bool http_disable_hostname_check_before_authentication;
+ bool include_header; /* include received protocol headers in data output */
+ bool http_set_referer;
+ bool http_auto_referer; /* set "correct" referer when following location: */
+ bool opt_no_body; /* as set with CURLOPT_NO_BODY */
+ bool set_port;
+ bool upload;
+ enum CURL_NETRC_OPTION
+ use_netrc; /* defined in include/curl.h */
+ char *netrc_file; /* if not NULL, use this instead of trying to find
+ $HOME/.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 ftp_use_epsv; /* if EPSV is to be attempted or not */
+ bool ftp_use_eprt; /* if EPRT is to be attempted or not */
+ bool ftp_use_ccc; /* if CCC is to be attempted or not */
+
+ curl_ftpssl ftp_ssl; /* if AUTH TLS is to be attempted etc */
+ curl_ftpauth ftpsslauth; /* what AUTH XXX to be attempted */
+ bool no_signal; /* do not use any signal/alarm handler */
+ bool global_dns_cache; /* subject for future removal */
+ bool tcp_nodelay; /* whether to enable TCP_NODELAY or not */
+ bool ignorecl; /* ignore content length */
+ bool ftp_skip_ip; /* skip the IP address the FTP server passes on to
+ us */
+ bool connect_only; /* make connection, let application use the socket */
+ long ssh_auth_types; /* allowed SSH auth types */
+ char *ssh_public_key; /* the path to the public key file for
+ authentication */
+ char *ssh_private_key; /* the path to the private key file for
+ authentication */
+};
+
+struct Names {
+ struct curl_hash *hostcache;
+ enum {
+ HCACHE_NONE, /* not pointing to anything */
+ HCACHE_PRIVATE, /* points to our own */
+ HCACHE_GLOBAL, /* points to the (shrug) global one */
+ HCACHE_MULTI, /* points to a shared one in the multi handle */
+ HCACHE_SHARED /* points to a shared one in a shared object */
+ } hostcachetype;
+};
+
+/*
+ * The 'connectdata' struct MUST have all the connection oriented stuff as we
+ * may have several simultaneous connections and connection structs in memory.
+ *
+ * The 'struct UserDefined' 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 {
+ struct Names dns;
+ struct Curl_multi *multi; /* if non-NULL, points to the multi handle
+ struct to which this "belongs" */
+ struct Curl_share *share; /* Share, handles global variable mutexing */
+ struct HandleData reqdata; /* Request-specific data */
+ 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 */
+#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
+ iconv_t outbound_cd; /* for translating to the network encoding */
+ iconv_t inbound_cd; /* for translating from the network encoding */
+ iconv_t utf8_cd; /* for translating to UTF8 */
+#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
+ unsigned int magic; /* set to a CURLEASY_MAGIC_NUMBER */
+};
+
+#define LIBCURL_NAME "libcurl"
+
+#endif
diff --git a/Utilities/cmcurl/version.c b/Utilities/cmcurl/version.c
new file mode 100644
index 000000000..9085f7df8
--- /dev/null
+++ b/Utilities/cmcurl/version.c
@@ -0,0 +1,249 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sslgen.h"
+
+#define _MPRINTF_REPLACE /* use the internal *printf() functions */
+#include <curl/mprintf.h>
+
+#ifdef USE_ARES
+#include <ares_version.h>
+#endif
+
+#ifdef USE_LIBIDN
+#include <stringprep.h>
+#endif
+
+#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
+#include <iconv.h>
+#endif
+
+#ifdef USE_LIBSSH2
+#include <libssh2.h>
+#endif
+
+
+char *curl_version(void)
+{
+ static char version[200];
+ char *ptr=version;
+ size_t len;
+ size_t left = sizeof(version);
+ strcpy(ptr, LIBCURL_NAME "/" LIBCURL_VERSION );
+ ptr=strchr(ptr, '\0');
+ left -= strlen(ptr);
+
+ len = Curl_ssl_version(ptr, left);
+ left -= len;
+ ptr += len;
+
+#ifdef HAVE_LIBZ
+ len = snprintf(ptr, left, " zlib/%s", zlibVersion());
+ left -= len;
+ ptr += len;
+#endif
+#ifdef USE_ARES
+ /* this function is only present in c-ares, not in the original ares */
+ len = snprintf(ptr, left, " c-ares/%s", ares_version(NULL));
+ left -= len;
+ ptr += len;
+#endif
+#ifdef USE_LIBIDN
+ if(stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
+ len = snprintf(ptr, left, " libidn/%s", stringprep_check_version(NULL));
+ left -= len;
+ ptr += len;
+ }
+#endif
+#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
+#ifdef _LIBICONV_VERSION
+ len = snprintf(ptr, left, " iconv/%d.%d",
+ _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255);
+#else
+ /* version unknown */
+ len = snprintf(ptr, left, " iconv");
+#endif /* _LIBICONV_VERSION */
+ left -= len;
+ ptr += len;
+#endif
+#ifdef USE_LIBSSH2
+ len = snprintf(ptr, left, " libssh2/%s", LIBSSH2_VERSION);
+ left -= len;
+ ptr += len;
+#endif
+
+ return version;
+}
+
+/* data for curl_version_info */
+
+static const char * const protocols[] = {
+#ifndef CURL_DISABLE_TFTP
+ "tftp",
+#endif
+#ifndef CURL_DISABLE_FTP
+ "ftp",
+#endif
+#ifndef CURL_DISABLE_TELNET
+ "telnet",
+#endif
+#ifndef CURL_DISABLE_DICT
+ "dict",
+#endif
+#ifndef CURL_DISABLE_LDAP
+ "ldap",
+#endif
+#ifndef CURL_DISABLE_HTTP
+ "http",
+#endif
+#ifndef CURL_DISABLE_FILE
+ "file",
+#endif
+
+#ifdef USE_SSL
+#ifndef CURL_DISABLE_HTTP
+ "https",
+#endif
+#ifndef CURL_DISABLE_FTP
+ "ftps",
+#endif
+#endif
+
+#ifdef USE_LIBSSH2
+ "scp",
+ "sftp",
+#endif
+
+ NULL
+};
+
+static curl_version_info_data version_info = {
+ CURLVERSION_NOW,
+ LIBCURL_VERSION,
+ LIBCURL_VERSION_NUM,
+ OS, /* as found by configure or set by hand at build-time */
+ 0 /* features is 0 by default */
+#ifdef ENABLE_IPV6
+ | CURL_VERSION_IPV6
+#endif
+#ifdef HAVE_KRB4
+ | CURL_VERSION_KERBEROS4
+#endif
+#ifdef USE_SSL
+ | CURL_VERSION_SSL
+#endif
+#ifdef USE_NTLM
+ | CURL_VERSION_NTLM
+#endif
+#ifdef USE_WINDOWS_SSPI
+ | CURL_VERSION_SSPI
+#endif
+#ifdef HAVE_LIBZ
+ | CURL_VERSION_LIBZ
+#endif
+#ifdef HAVE_GSSAPI
+ | CURL_VERSION_GSSNEGOTIATE
+#endif
+#ifdef CURLDEBUG
+ | CURL_VERSION_DEBUG
+#endif
+#ifdef USE_ARES
+ | CURL_VERSION_ASYNCHDNS
+#endif
+#ifdef HAVE_SPNEGO
+ | CURL_VERSION_SPNEGO
+#endif
+#if defined(ENABLE_64BIT) && (SIZEOF_CURL_OFF_T > 4)
+ | CURL_VERSION_LARGEFILE
+#endif
+#if defined(CURL_DOES_CONVERSIONS)
+ | CURL_VERSION_CONV
+#endif
+ ,
+ NULL, /* ssl_version */
+ 0, /* ssl_version_num, this is kept at zero */
+ NULL, /* zlib_version */
+ protocols,
+ NULL, /* c-ares version */
+ 0, /* c-ares version numerical */
+ NULL, /* libidn version */
+ 0, /* iconv version */
+ NULL, /* ssh lib version */
+};
+
+curl_version_info_data *curl_version_info(CURLversion stamp)
+{
+#ifdef USE_LIBSSH2
+ static char ssh_buffer[80];
+#endif
+
+#ifdef USE_SSL
+ static char ssl_buffer[80];
+ Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer));
+ version_info.ssl_version = ssl_buffer;
+#endif
+
+#ifdef HAVE_LIBZ
+ version_info.libz_version = zlibVersion();
+ /* libz left NULL if non-existing */
+#endif
+#ifdef USE_ARES
+ {
+ int aresnum;
+ version_info.ares = ares_version(&aresnum);
+ version_info.ares_num = aresnum;
+ }
+#endif
+#ifdef USE_LIBIDN
+ /* This returns a version string if we use the given version or later,
+ otherwise it returns NULL */
+ version_info.libidn = stringprep_check_version(LIBIDN_REQUIRED_VERSION);
+ if(version_info.libidn)
+ version_info.features |= CURL_VERSION_IDN;
+#endif
+
+#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
+#ifdef _LIBICONV_VERSION
+ version_info.iconv_ver_num = _LIBICONV_VERSION;
+#else
+ /* version unknown */
+ version_info.iconv_ver_num = -1;
+#endif /* _LIBICONV_VERSION */
+#endif
+
+#ifdef USE_LIBSSH2
+ snprintf(ssh_buffer, sizeof(ssh_buffer), "libssh2/%s", LIBSSH2_VERSION);
+ version_info.libssh_version = ssh_buffer;
+#endif
+
+ (void)stamp; /* avoid compiler warnings, we don't use this */
+
+ return &version_info;
+}