diff options
author | Andy Cedilnik <andy.cedilnik@kitware.com> | 2005-06-24 13:02:17 (GMT) |
---|---|---|
committer | Andy Cedilnik <andy.cedilnik@kitware.com> | 2005-06-24 13:02:17 (GMT) |
commit | 7c730973bba620ba50e02fb1330e99c2230d3d8b (patch) | |
tree | 08e6f54fd0f1b53a52bbdbe29db25171ddf81526 /Utilities | |
parent | c4036bd94240d4850703bbe62dbdcf3ff34bb3cd (diff) | |
download | CMake-7c730973bba620ba50e02fb1330e99c2230d3d8b.zip CMake-7c730973bba620ba50e02fb1330e99c2230d3d8b.tar.gz CMake-7c730973bba620ba50e02fb1330e99c2230d3d8b.tar.bz2 |
ENH: Initial import
Diffstat (limited to 'Utilities')
135 files changed, 42825 insertions, 0 deletions
diff --git a/Utilities/cmcurl/CMake/CheckTypeSize.c.in b/Utilities/cmcurl/CMake/CheckTypeSize.c.in new file mode 100644 index 0000000..822d9c5 --- /dev/null +++ b/Utilities/cmcurl/CMake/CheckTypeSize.c.in @@ -0,0 +1,34 @@ +#ifdef CHECK_TYPE_SIZE_TYPE + +@CHECK_TYPE_SIZE_PREINCLUDE@ + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif /* HAVE_STDINT_H */ + +#ifdef HAVE_STDDEF_H +# include <stddef.h> +#endif /* HAVE_STDDEF_H */ + +@CHECK_TYPE_SIZE_PREMAIN@ + +#ifdef __CLASSIC_C__ +int main(){ + int ac; + char*av[]; +#else +int main(int ac, char*av[]){ +#endif + if(ac > 1000){return *av[0];} + return sizeof(CHECK_TYPE_SIZE_TYPE); +} + +#else /* CHECK_TYPE_SIZE_TYPE */ + +# error "CHECK_TYPE_SIZE_TYPE has to specify the type" + +#endif /* CHECK_TYPE_SIZE_TYPE */ diff --git a/Utilities/cmcurl/CMake/CheckTypeSize.cmake b/Utilities/cmcurl/CMake/CheckTypeSize.cmake new file mode 100644 index 0000000..ddc990a --- /dev/null +++ b/Utilities/cmcurl/CMake/CheckTypeSize.cmake @@ -0,0 +1,51 @@ +# +# Check if the type exists and determine size of type. if the type +# exists, the size will be stored to the variable. +# +# CHECK_TYPE_SIZE - macro which checks the size of type +# VARIABLE - variable to store size if the type exists. +# HAVE_${VARIABLE} - does the variable exists or not +# + +MACRO(CHECK_TYPE_SIZE TYPE VARIABLE) + SET(CMAKE_ALLOW_UNKNOWN_VARIABLE_READ_ACCESS 1) + IF("HAVE_${VARIABLE}" MATCHES "^HAVE_${VARIABLE}$") + SET(MACRO_CHECK_TYPE_SIZE_FLAGS + "-DCHECK_TYPE_SIZE_TYPE=\"${TYPE}\" ${CMAKE_REQUIRED_FLAGS}") + FOREACH(def HAVE_SYS_TYPES_H HAVE_STDINT_H HAVE_STDDEF_H) + IF("${def}") + SET(MACRO_CHECK_TYPE_SIZE_FLAGS + "${MACRO_CHECK_TYPE_SIZE_FLAGS} -D${def}") + ENDIF("${def}") + ENDFOREACH(def) + SET(CHECK_TYPE_SIZE_PREMAIN) + FOREACH(def ${CMAKE_EXTRA_INCLUDE_FILES}) + SET(CHECK_TYPE_SIZE_PREMAIN "${CHECK_TYPE_SIZE_PREMAIN}#include \"${def}\"\n") + ENDFOREACH(def) + CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/CMake/CheckTypeSize.c.in" + "${CMAKE_BINARY_DIR}/CMakeTmp/CheckTypeSize.c" IMMEDIATE @ONLY) + FILE(READ "${CMAKE_BINARY_DIR}/CMakeTmp/CheckTypeSize.c" + CHECK_TYPE_SIZE_FILE_CONTENT) + MESSAGE(STATUS "Check size of ${TYPE}") + IF(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_TYPE_SIZE_ADD_LIBRARIES + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") + ENDIF(CMAKE_REQUIRED_LIBRARIES) + TRY_RUN(${VARIABLE} HAVE_${VARIABLE} + ${CMAKE_BINARY_DIR} + "${CMAKE_BINARY_DIR}/CMakeTmp/CheckTypeSize.c" + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_TYPE_SIZE_FLAGS} + "${CHECK_TYPE_SIZE_ADD_LIBRARIES}" + OUTPUT_VARIABLE OUTPUT) + IF(HAVE_${VARIABLE}) + MESSAGE(STATUS "Check size of ${TYPE} - done") + FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeOutput.log + "Determining size of ${TYPE} passed with the following output:\n${OUTPUT}\n\n") + ELSE(HAVE_${VARIABLE}) + MESSAGE(STATUS "Check size of ${TYPE} - failed") + FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeError.log + "Determining size of ${TYPE} failed with the following output:\n${OUTPUT}\nCheckTypeSize.c:\n${CHECK_TYPE_SIZE_FILE_CONTENT}\n\n") + ENDIF(HAVE_${VARIABLE}) + ENDIF("HAVE_${VARIABLE}" MATCHES "^HAVE_${VARIABLE}$") + SET(CMAKE_ALLOW_UNKNOWN_VARIABLE_READ_ACCESS ) +ENDMACRO(CHECK_TYPE_SIZE) diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c new file mode 100644 index 0000000..61278ea --- /dev/null +++ b/Utilities/cmcurl/CMake/CurlTests.c @@ -0,0 +1,535 @@ +#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__) +#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 +#include <sys/types.h> +#include <sys/socket.h> + +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 +#ifdef HAVE_LONG_LONG_CONSTANT +int main() +{ + long long c = 0x8000000000000000LL; + long long k = 0x7FFFFFFFFFFFFFFFLL; + if ( c == 0x8000000000000000LL && c != k ) + { + return 0; + } + return 1; +} +#endif diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt new file mode 100644 index 0000000..afd027a --- /dev/null +++ b/Utilities/cmcurl/CMakeLists.txt @@ -0,0 +1,586 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.0) +PROJECT(LIBCURL C) + +INCLUDE_REGULAR_EXPRESSION("^.*\\.h$") + +# Setup package meta-data +SET(PACKAGE "curl") +SET(VERSION "7.12.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}) + +# 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 + base64.c + connect.c + content_encoding.c + cookie.c + dict.c + easy.c + escape.c + file.c + formdata.c + ftp.c + getdate.c + getenv.c + getinfo.c + hash.c + hostares.c + hostasyn.c + hostip.c + hostip4.c + hostip6.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 + llist.c + md5.c + memdebug.c + mprintf.c + multi.c + netrc.c + progress.c + sendf.c + share.c + speedcheck.c + ssluse.c + strequal.c + strerror.c + telnet.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) + INCLUDE(${LIBCURL_SOURCE_DIR}/Platforms/WindowsCache.cmake) +ENDIF(WIN32) + +# 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 +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) + +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. Experimental" OFF) +MARK_AS_ADVANCED(CMAKE_USE_OPENSSL) +IF(CMAKE_USE_OPENSSL) + CHECK_LIBRARY_EXISTS_CONCAT("crypto" CRYPTO_lock HAVE_LIBCRYPTO) + CHECK_LIBRARY_EXISTS_CONCAT("ssl" SSL_connect HAVE_LIBSSL) +ENDIF(CMAKE_USE_OPENSSL) + +# 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) + + +# 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 +CHECK_INCLUDE_FILE_CONCAT("stdio.h" HAVE_STDIO_H) +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) +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("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("winsock.h" HAVE_WINSOCK_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("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_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 double" SIZEOF_LONG_DOUBLE) +IF(NOT HAVE_SIZEOF_SSIZE_T) + SET(ssize_t int) +ENDIF(NOT HAVE_SIZEOF_SSIZE_T) +IF(HAVE_SIZEOF_LONG_LONG) + SET(HAVE_LONGLONG 1) +ENDIF(HAVE_SIZEOF_LONG_LONG) + +FIND_FILE(RANDOM_FILE urandom /dev) +MARK_AS_ADVANCED(RANDOM_FILE) + +# Check for some functions that are used +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) +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) +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) + +# 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) +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") + ELSE(${CURL_TEST}) + MESSAGE(STATUS "Performing Curl Test ${CURL_TEST} - Failed") + SET(${CURL_TEST} "" CACHE INTERNAL "Curl test ${FUNCTION}") + FILE(APPEND ${CMAKE_BINARY_DIR}/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) +MACRO(CURL_INTERNAL_TEST_RUN CURL_TEST) + IF("${CURL_TEST}_COMPILE" MATCHES "^${CURL_TEST}_COMPILE$") + 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_RUN(${CURL_TEST} ${CURL_TEST}_COMPILE + ${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}_COMPILE AND NOT ${CURL_TEST}) + SET(${CURL_TEST} 1 CACHE INTERNAL "Curl test ${FUNCTION}") + MESSAGE(STATUS "Performing Curl Test ${CURL_TEST} - Success") + ELSE(${CURL_TEST}_COMPILE AND NOT ${CURL_TEST}) + MESSAGE(STATUS "Performing Curl Test ${CURL_TEST} - Failed") + SET(${CURL_TEST} "" CACHE INTERNAL "Curl test ${FUNCTION}") + FILE(APPEND "${CMAKE_BINARY_DIR}/CMakeError.log" + "Performing Curl Test ${CURL_TEST} failed with the following output:\n" + "${OUTPUT}") + IF(${CURL_TEST}_COMPILE) + FILE(APPEND "${CMAKE_BINARY_DIR}/CMakeError.log" + "There was a running problem of this test\n") + ENDIF(${CURL_TEST}_COMPILE) + FILE(APPEND "${CMAKE_BINARY_DIR}/CMakeError.log" + "\n\n") + ENDIF(${CURL_TEST}_COMPILE AND NOT ${CURL_TEST}) + ENDIF("${CURL_TEST}_COMPILE" MATCHES "^${CURL_TEST}_COMPILE$") +ENDMACRO(CURL_INTERNAL_TEST_RUN) + +# 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) + +FOREACH(CURL_TEST + HAVE_GLIBC_STRERROR_R + HAVE_POSIX_STRERROR_R + HAVE_LONG_LONG_CONSTANT + ) + CURL_INTERNAL_TEST_RUN(${CURL_TEST}) +ENDFOREACH(CURL_TEST) + +# 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) + # 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(CMAKE_COMPILER_IS_GNUCC AND APPLE) + +# The rest of the build + +INCLUDE_DIRECTORIES(${LIBCURL_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${LIBCURL_BINARY_DIR}) +ADD_DEFINITIONS(-DHAVE_CONFIG_H) +CONFIGURE_FILE(${LIBCURL_SOURCE_DIR}/config.h.in + ${LIBCURL_BINARY_DIR}/config.h) + +ADD_LIBRARY(cmcurl ${libCurl_SRCS}) +TARGET_LINK_LIBRARIES(cmcurl ${CURL_LIBS}) + +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) +ADD_TEST(curl "${EXECUTABLE_OUTPUT_PATH}/LIBCURL") diff --git a/Utilities/cmcurl/Platforms/WindowsCache.cmake b/Utilities/cmcurl/Platforms/WindowsCache.cmake new file mode 100644 index 0000000..1a84b37 --- /dev/null +++ b/Utilities/cmcurl/Platforms/WindowsCache.cmake @@ -0,0 +1,123 @@ +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_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_WINSOCK_H 1) + SET(HAVE_X509_H 0) + SET(HAVE_ZLIB_H 0) + + SET(HAVE_SIZEOF_LONG_DOUBLE 1) + SET(HAVE_SIZEOF_SSIZE_T 0) + 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_SOCKLEN_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 0000000..86d1093 --- /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 0000000..214410f --- /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 0000000..7c9ce2a --- /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 0000000..c21774a --- /dev/null +++ b/Utilities/cmcurl/Testing/curltest.c @@ -0,0 +1,143 @@ +/* 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 GetWebFile(void) +{ + 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, "http://www.cmake.org/page1.html"); + res = curl_easy_perform(curl); + if ( res != 0 ) + { + printf("Error fetching: http://www.cmake.org/page1.html\n"); + retVal = 1; + } + + /* get another document from the same server using the same + connection */ + /* + curl_easy_setopt(curl, CURLOPT_URL, "http://www.cmake.org/page2.html"); + res = curl_easy_perform(curl); + if ( res != 0 ) + { + printf("Error fetching: http://www.cmake.org/page2.html\n"); + 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); + retVal += GetWebFile(); + + /* Do not check the output of FTP socks5 cannot handle FTP yet */ + GetFtpFile(); + curl_global_cleanup(); + return retVal; +} diff --git a/Utilities/cmcurl/Testing/ftpget.c b/Utilities/cmcurl/Testing/ftpget.c new file mode 100644 index 0000000..db3edfb --- /dev/null +++ b/Utilities/cmcurl/Testing/ftpget.c @@ -0,0 +1,83 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * $Id$ + */ + +#include "curl/curl.h" +#include "curl/types.h" +#include "curl/easy.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 0000000..d3f5d42 --- /dev/null +++ b/Utilities/cmcurl/Testing/ftpgetresp.c @@ -0,0 +1,63 @@ +/***************************************************************************** + * _ _ ____ _ + * 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; + + /* 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 0000000..bca0a56 --- /dev/null +++ b/Utilities/cmcurl/Testing/ftpupload.c @@ -0,0 +1,92 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * $Id$ + */ + +#include "curl/curl.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 0000000..a8872da --- /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 0000000..1b4154f --- /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 0000000..78275c4 --- /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 0000000..c3936ef --- /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 0000000..8534703 --- /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 0000000..9b7cda0 --- /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 0000000..e3ea7be --- /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 0000000..6dd6050 --- /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 0000000..9a53603 --- /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 0000000..faab462 --- /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 0000000..5f791c8 --- /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 0000000..16a7d5e --- /dev/null +++ b/Utilities/cmcurl/amigaos.c @@ -0,0 +1,49 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "amigaos.h" +#include <stdio.h> /* for stderr */ + +struct Library *SocketBase = NULL; + +void amiga_cleanup() +{ + if(SocketBase) + CloseLibrary(SocketBase); + + SocketBase = NULL; +} + +BOOL amiga_init() +{ + if(!SocketBase) + SocketBase = OpenLibrary("bsdsocket.library", 4); + + if(!SocketBase) { + fprintf(stderr, "No TCP/IP Stack running!\n\a"); + return FALSE; + } + + atexit(amiga_cleanup); + return TRUE; +} diff --git a/Utilities/cmcurl/amigaos.h b/Utilities/cmcurl/amigaos.h new file mode 100644 index 0000000..0196eec --- /dev/null +++ b/Utilities/cmcurl/amigaos.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 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 <bsdsocket.h> + +#include "config-amigaos.h" + +#define select(args...) WaitSelect( args, NULL) +#define inet_ntoa(x) Inet_NtoA( x ## .s_addr) +#define ioctl(a,b,c,d) IoctlSocket( (LONG)a, (ULONG)b, (char*)c) +#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 0000000..5359ff1 --- /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 *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 *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 0000000..7f8ae86 --- /dev/null +++ b/Utilities/cmcurl/base64.c @@ -0,0 +1,288 @@ +/*************************************************************************** + * _ _ ____ _ + * 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$ + ***************************************************************************/ + +/* Base64 encoding/decoding + * + * Test harnesses down the bottom - compile with -DTEST_ENCODE for + * a program that will read in raw data from stdin and write out + * a base64-encoded version to stdout, and the length returned by the + * encoding function to stderr. Compile with -DTEST_DECODE for a program that + * will go the other way. + * + * This code will break if int is smaller than 32 bits + */ + +#include "setup.h" + +#include <stdlib.h> +#include <string.h> + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#include "base64.h" +#include "curl_memory.h" + +/* include memdebug.h last */ +#include "memdebug.h" + + +static void decodeQuantum(unsigned char *dest, const char *src) +{ + unsigned int x = 0; + int i; + for(i = 0; i < 4; i++) { + if(src[i] >= 'A' && src[i] <= 'Z') + x = (x << 6) + (unsigned int)(src[i] - 'A' + 0); + else if(src[i] >= 'a' && src[i] <= 'z') + x = (x << 6) + (unsigned int)(src[i] - 'a' + 26); + else if(src[i] >= '0' && src[i] <= '9') + x = (x << 6) + (unsigned int)(src[i] - '0' + 52); + else if(src[i] == '+') + x = (x << 6) + 62; + else if(src[i] == '/') + x = (x << 6) + 63; + 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 into the memory pointed to by + * dest. Returns the length of the decoded data. + */ +size_t Curl_base64_decode(const char *src, char *dest) +{ + int length = 0; + int equalsTerm = 0; + int i; + int numQuantums; + unsigned char lastQuantum[3]; + size_t rawlen; + + while((src[length] != '=') && src[length]) + length++; + while(src[length+equalsTerm] == '=') + equalsTerm++; + + numQuantums = (length + equalsTerm) / 4; + + rawlen = (numQuantums * 3) - equalsTerm; + + for(i = 0; i < numQuantums - 1; i++) { + decodeQuantum((unsigned char *)dest, src); + dest += 3; src += 4; + } + + decodeQuantum(lastQuantum, src); + for(i = 0; i < 3 - equalsTerm; i++) + dest[i] = lastQuantum[i]; + + return rawlen; +} + +/* ---- Base64 Encoding --- */ +static char table64[]= + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* + * Curl_base64_encode() + * + * Returns the length of the newly created base64 string. The third argument + * is a pointer to an allocated area holding the base64 data. If something + * went wrong, -1 is returned. + * + */ +size_t Curl_base64_encode(const char *inp, size_t insize, char **outptr) +{ + unsigned char ibuf[3]; + unsigned char obuf[4]; + int i; + int inputparts; + char *output; + char *base64data; + + 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; + + 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] = (ibuf [0] & 0xFC) >> 2; + obuf [1] = ((ibuf [0] & 0x03) << 4) | ((ibuf [1] & 0xF0) >> 4); + obuf [2] = ((ibuf [1] & 0x0F) << 2) | ((ibuf [2] & 0xC0) >> 6); + obuf [3] = ibuf [2] & 0x3F; + + switch(inputparts) { + case 1: /* only one byte read */ + 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 */ + + 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; + + data = (unsigned char *)suck(&dataLen); + base64Len = Curl_base64_encode(data, dataLen, &base64); + + fprintf(stderr, "%d\n", base64Len); + fprintf(stdout, "%s", base64); + + free(base64); free(data); + return 0; +} +#endif + +#ifdef TEST_DECODE +/* decoding test harness. Read in a base64 string from stdin and write out the + * length returned by Curl_base64_decode, followed by the decoded data itself + * + * 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; + + base64 = (char *)suck(&base64Len); + data = (unsigned char *)malloc(base64Len * 3/4 + 8); + 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) + printf("%c", isgraph(data[i+j])?data[i+j]:'.'); + else + break; + puts(""); + } + + 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 0000000..307148e --- /dev/null +++ b/Utilities/cmcurl/base64.h @@ -0,0 +1,27 @@ +#ifndef __BASE64_H +#define __BASE64_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$ + ***************************************************************************/ +size_t Curl_base64_encode(const char *input, size_t size, char **str); +size_t Curl_base64_decode(const char *source, char *dest); +#endif diff --git a/Utilities/cmcurl/ca-bundle.h b/Utilities/cmcurl/ca-bundle.h new file mode 100644 index 0000000..12390bc --- /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 0000000..1086027 --- /dev/null +++ b/Utilities/cmcurl/config.h.in @@ -0,0 +1,512 @@ +/* lib/config.h.in. Generated from configure.in by autoheader. */ + +/* 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 GOPHER */ +#cmakedefine CURL_DISABLE_GOPHER ${CURL_DISABLE_GOPHER} + +/* 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} + +/* Set to explicitly specify we don't want to use thread-safe functions */ +#cmakedefine DISABLED_THREADSAFE ${DISABLED_THREADSAFE} + +/* 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 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 <assert.h> header file. */ +#cmakedefine HAVE_ASSERT_H ${HAVE_ASSERT_H} + +/* 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 <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 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 `getpass_r' function. */ +#cmakedefine HAVE_GETPASS_R ${HAVE_GETPASS_R} + +/* Define to 1 if you have the `getpwuid' function. */ +#cmakedefine HAVE_GETPWUID ${HAVE_GETPWUID} + +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY ${HAVE_GETTIMEOFDAY} + +/* we have a glibc-style strerror_r() */ +#cmakedefine HAVE_GLIBC_STRERROR_R ${HAVE_GLIBC_STRERROR_R} + +/* 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 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 `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 `crypto' library (-lcrypto). */ +#cmakedefine HAVE_LIBCRYPTO ${HAVE_LIBCRYPTO} + +/* Define to 1 if you have the `dl' library (-ldl). */ +#cmakedefine HAVE_LIBDL ${HAVE_LIBDL} + +/* 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 `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} + +/* 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 <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 if NI_WITHSCOPEID exists and works */ +#cmakedefine HAVE_NI_WITHSCOPEID ${HAVE_NI_WITHSCOPEID} + +/* 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/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 `poll' function. */ +#cmakedefine HAVE_POLL ${HAVE_POLL} + +/* If you have a fine poll */ +#cmakedefine HAVE_POLL_FINE ${HAVE_POLL_FINE} + +/* we have a POSIX-style strerror_r() */ +#cmakedefine HAVE_POSIX_STRERROR_R ${HAVE_POSIX_STRERROR_R} + +/* 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 <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 <setjmp.h> header file. */ +#cmakedefine HAVE_SETJMP_H ${HAVE_SETJMP_H} + +/* 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} + +/* If you have sigsetjmp */ +#cmakedefine HAVE_SIGSETJMP ${HAVE_SIGSETJMP} + +/* 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 <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 `strerror_r' function. */ +#cmakedefine HAVE_STRERROR_R ${HAVE_STRERROR_R} + +/* Define to 1 if you have the `strftime' function. */ +#cmakedefine HAVE_STRFTIME ${HAVE_STRFTIME} + +/* 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} + +/* 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/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 `tcgetattr' function. */ +#cmakedefine HAVE_TCGETATTR ${HAVE_TCGETATTR} + +/* Define to 1 if you have the `tcsetattr' function. */ +#cmakedefine HAVE_TCSETATTR ${HAVE_TCSETATTR} + +/* 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 `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 <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 <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} + +/* 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 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} + +/* The size of a `curl_off_t', as computed by sizeof. */ +#cmakedefine SIZEOF_CURL_OFF_T ${SIZEOF_CURL_OFF_T} + +/* The size of a `size_t', as computed by sizeof. */ +#cmakedefine SIZEOF_SIZE_T ${SIZEOF_SIZE_T} + +/* 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 you want to build curl with the built-in manual */ +#cmakedefine USE_MANUAL ${USE_MANUAL} + +/* Version number of package */ +#cmakedefine VERSION "${VERSION}" + +/* 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 to empty if `const' does not conform to ANSI C. */ +#cmakedefine const ${const} + +/* type to use in place of in_addr_t if not defined */ +#cmakedefine in_addr_t ${in_addr_t} + +/* Define to `unsigned' if <sys/types.h> does not define. */ +#cmakedefine size_t ${size_t} + +/* type to use in place of socklen_t if not defined */ +#cmakedefine socklen_t ${socklen_t} + +/* the signed version of size_t */ +#cmakedefine ssize_t ${ssize_t} + +/* define if the compiler supports number 0x3627676LL */ +#cmakedefine HAVE_LONG_LONG_CONSTANT ${HAVE_LONG_LONG_CONSTANT} + +/* 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 0000000..c107354 --- /dev/null +++ b/Utilities/cmcurl/connect.c @@ -0,0 +1,788 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 WIN32 +/* headers for non-win32 */ +#include <sys/time.h> +#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 WIN32 +#include <windows.h> +#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 "curl_memory.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, + Curl_addrinfo *ai, /* start connecting to this */ + long timeout_ms, + bool *connected); + +/* + * Curl_ourerrno() returns the errno (or equivalent) on this platform to + * hide platform specific for the function that calls this. + */ +int Curl_ourerrno(void) +{ +#ifdef WIN32 + return (int)GetLastError(); +#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 +#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)); + } +#define SETBLOCK 1 +#endif + +#ifdef HAVE_FIONBIO + { + /* older unix versions */ + int flags; + + flags = nonblock; + return ioctl(sockfd, FIONBIO, &flags); + } +#ifdef SETBLOCK +# undef SETBLOCK +#endif +#define SETBLOCK 2 +#endif + +#ifdef HAVE_IOCTLSOCKET + /* Windows? */ + unsigned long flags; + flags = nonblock; + return ioctlsocket(sockfd, FIONBIO, &flags); +#define SETBLOCK 3 +#endif + +#ifdef HAVE_IOCTLSOCKET_CASE + /* presumably for Amiga */ + return IoctlSocket(sockfd, FIONBIO, (long)nonblock); +#ifdef SETBLOCK +# undef SETBLOCK +#endif +#define SETBLOCK 4 +#endif + +#ifdef HAVE_SO_NONBLOCK + /* BeOS */ + long b = nonblock ? 1 : 0; + return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); +#ifdef SETBLOCK +# undef SETBLOCK +#endif +#define SETBLOCK 5 +#endif + +#ifdef HAVE_DISABLED_NONBLOCKING + (void)nonblock; + (void)sockfd; + return 0; /* returns success */ +#ifdef SETBLOCK +# undef SETBLOCK +#endif +#define SETBLOCK 6 +#endif + +#ifndef SETBLOCK +#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) +{ + fd_set fd; + fd_set errfd; + struct timeval interval; + 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. */ + verifyconnect(sockfd, NULL); +#endif + + /* now select() until we get connect or timeout */ + FD_ZERO(&fd); + FD_SET(sockfd, &fd); + + FD_ZERO(&errfd); + FD_SET(sockfd, &errfd); + + interval.tv_sec = (int)(timeout_msec/1000); + timeout_msec -= interval.tv_sec*1000; + + interval.tv_usec = timeout_msec*1000; + + rc = select(sockfd+1, NULL, &fd, &errfd, &interval); + if(-1 == rc) + /* error, no connect here, try next */ + return WAITCONN_SELECT_ERROR; + + else if(0 == rc) + /* timeout, no connect today */ + return WAITCONN_TIMEOUT; + + if(FD_ISSET(sockfd, &errfd)) + /* error condition caught */ + return WAITCONN_FDSET_ERROR; + + /* we have a connect! */ + return WAITCONN_CONNECTED; +} + +static CURLcode bindlocal(struct connectdata *conn, + curl_socket_t sockfd) +{ +#ifdef HAVE_INET_NTOA + bool bindworked = FALSE; + struct SessionHandle *data = conn->data; + + /************************************************************* + * Select device to bind socket to + *************************************************************/ + if (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; + } + + 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) + /* we know data->set.device is shorter than the myhost array */ + strcpy(myhost, data->set.device); + } + + if(! *myhost) { + /* need to fix this + h=Curl_gethost(data, + getmyhost(*myhost,sizeof(myhost)), + hostent_buf, + sizeof(hostent_buf)); + */ + failf(data, "Couldn't bind to '%s'", data->set.device); + return CURLE_HTTP_PORT_FAILED; + } + + infof(data, "We bind local end 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_ourerrno())); */ + 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) { + + if ( h ) { + Curl_addrinfo *addr = h->addr; + + Curl_resolv_unlock(data, h); + /* we don't need it anymore after this function has returned */ + + if( bind(sockfd, addr->ai_addr, (socklen_t)addr->ai_addrlen) >= 0) { + /* we succeeded to bind */ +#ifdef ENABLE_IPV6 + struct sockaddr_in6 add; +#else + struct sockaddr_in add; +#endif + +#ifdef __hpux + int gsize = sizeof(add); +#else + socklen_t gsize = sizeof(add); +#endif + bindworked = TRUE; + + if(getsockname(sockfd, (struct sockaddr *) &add, + &gsize)<0) { + failf(data, "getsockname() failed"); + return CURLE_HTTP_PORT_FAILED; + } + } + + if(!bindworked) { + failf(data, "%s", Curl_strerror(conn, Curl_ourerrno())); + return CURLE_HTTP_PORT_FAILED; + } + + } /* end of if h */ + else { + failf(data,"could't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; + } + } /* end of inet_addr */ + + else { + failf(data, "could't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; + } + + return CURLE_OK; + + } /* end of device selection support */ +#endif /* end of HAVE_INET_NTOA */ + + 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; +#ifdef SO_ERROR + int err = 0; +#ifdef __hpux + int errSize = sizeof(err); +#else + socklen_t errSize = sizeof(err); +#endif + + +#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." + */ + SleepEx(0, FALSE); +#endif + + if( -1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR, + (void *)&err, &errSize)) + err = Curl_ourerrno(); + + 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_ourerrno(); +#endif + return rc; +} + +/* 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; + + 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; + 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 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.start); + + /* subtract the most strict timeout of the ones */ + if(data->set.timeout && data->set.connecttimeout) { + if (data->set.timeout < data->set.connecttimeout) + allow = data->set.timeout*1000; + else + allow = data->set.connecttimeout*1000; + } + else if(data->set.timeout) { + 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! */ + *connected = TRUE; + return CURLE_OK; + } + + /* check for connect without timeout as we want to return immediately */ + rc = waitconnect(sockfd, 0); + + if(WAITCONN_CONNECTED == rc) { + if (verifyconnect(sockfd, NULL)) { + /* we are connected, awesome! */ + *connected = TRUE; + return CURLE_OK; + } + /* nope, not connected for real */ + infof(data, "Connection failed\n"); + if(trynextip(conn, sockindex, connected)) { + code = CURLE_COULDNT_CONNECT; + } + } + else if(WAITCONN_TIMEOUT != rc) { + /* nope, not connected */ + infof(data, "Connection failed\n"); + if(trynextip(conn, sockindex, connected)) { + int error = Curl_ourerrno(); + 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; + if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&onoff, + sizeof(onoff)) < 0) + infof(data, "Could not set TCP_NODELAY: %s\n", + Curl_strerror(conn, Curl_ourerrno())); + else + infof(data,"TCP_NODELAY set\n"); +#else + (void)conn; + (void)sockfd; +#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, + Curl_addrinfo *ai, + long timeout_ms, + bool *connected) +{ + char addr_buf[128]; + int rc; + int error; + bool conected; + struct SessionHandle *data = conn->data; + curl_socket_t sockfd = socket(ai->ai_family, ai->ai_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); + + if(conn->data->set.device) { + /* user selected to bind the outgoing socket to a specified "device" + before doing connect */ + CURLcode res = bindlocal(conn, sockfd); + if(res) + return res; + } + + /* set socket non-blocking */ + Curl_nonblock(sockfd, TRUE); + + rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen); + + if(-1 == rc) { + error = Curl_ourerrno(); + + 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)); + 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; + } + + conected = verifyconnect(sockfd, &error); + + if(!rc && conected) { + /* 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 + 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 */ + 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.start); + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + + /* get the most strict timeout of the ones converted to milliseconds */ + if(data->set.timeout && data->set.connecttimeout) { + if (data->set.timeout < data->set.connecttimeout) + timeout_ms = data->set.timeout*1000; + else + timeout_ms = data->set.connecttimeout*1000; + } + else if(data->set.timeout) + timeout_ms = data->set.timeout*1000; + else + timeout_ms = data->set.connecttimeout*1000; + + /* subtract the passed time */ + timeout_ms -= 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; + } + } + + /* 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 = timeout_ms = 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; + 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 */ + + return CURLE_OK; +} diff --git a/Utilities/cmcurl/connect.h b/Utilities/cmcurl/connect.h new file mode 100644 index 0000000..d39495c --- /dev/null +++ b/Utilities/cmcurl/connect.h @@ -0,0 +1,44 @@ +#ifndef __CONNECT_H +#define __CONNECT_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_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, + 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_ourerrno(void); + +#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 0000000..b8c68bd --- /dev/null +++ b/Utilities/cmcurl/content_encoding.c @@ -0,0 +1,363 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" + +#ifdef HAVE_LIBZ + +#include <stdlib.h> +#include <string.h> + +#include "urldata.h" +#include <curl/curl.h> +#include "sendf.h" +#include "content_encoding.h" +#include "curl_memory.h" + +#include "memdebug.h" + +#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 */ + +static CURLcode +process_zlib_error(struct SessionHandle *data, z_stream *z) +{ + if (z->msg) + failf (data, "Error while processing content unencoding.\n%s", + z->msg); + else + failf (data, "Error while processing content unencoding.\n" + "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 = 0; + return result; +} + +CURLcode +Curl_unencode_deflate_write(struct SessionHandle *data, + struct Curl_transfer_keeper *k, + ssize_t nread) +{ + int status; /* zlib status */ + CURLcode result = CURLE_OK; /* Curl_client_write status */ + char decomp[DSIZ]; /* Put the decompressed data here. */ + z_stream *z = &k->z; /* zlib state structure */ + + /* Initialize zlib? */ + if (!k->zlib_init) { + 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(data, z); + k->zlib_init = 1; + } + + /* Set the compressed input when this function is called */ + z->next_in = (Bytef *)k->str; + z->avail_in = (uInt)nread; + + /* 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[0]; + z->avail_out = DSIZ; + + status = inflate(z, Z_SYNC_FLUSH); + if (status == Z_OK || status == Z_STREAM_END) { + if (DSIZ - z->avail_out) { + result = Curl_client_write(data, CLIENTWRITE_BODY, decomp, + DSIZ - z->avail_out); + /* if !CURLE_OK, clean up, return */ + if (result) + return exit_zlib(z, &k->zlib_init, result); + } + + /* Done?; clean up, return */ + if (status == Z_STREAM_END) { + if (inflateEnd(z) == Z_OK) + return exit_zlib(z, &k->zlib_init, result); + else + return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z)); + } + + /* Done with these bytes, exit */ + if (status == Z_OK && z->avail_in == 0 && z->avail_out > 0) + return result; + } + else { /* Error; exit loop, handle below */ + return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z)); + } + } +} + +/* 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); + } + + 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; +} + +CURLcode +Curl_unencode_gzip_write(struct SessionHandle *data, + struct Curl_transfer_keeper *k, + ssize_t nread) +{ + int status; /* zlib status */ + CURLcode result = CURLE_OK; /* Curl_client_write status */ + char decomp[DSIZ]; /* Put the decompressed data here. */ + z_stream *z = &k->z; /* zlib state structure */ + + /* Initialize zlib? */ + if (!k->zlib_init) { + z->zalloc = (alloc_func)Z_NULL; + z->zfree = (free_func)Z_NULL; + z->opaque = 0; + z->next_in = NULL; + z->avail_in = 0; + if (inflateInit2(z, -MAX_WBITS) != Z_OK) + return process_zlib_error(data, z); + k->zlib_init = 1; /* Initial call state */ + } + + /* 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. + */ + + /* Skip over gzip header? */ + if (k->zlib_init == 1) { + /* 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 = 3; /* 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 = 2; /* 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(data, z)); + } + + } + else if (k->zlib_init == 2) { + /* 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 = 3; /* 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(data, z)); + } + + } + else { + /* Inflating stream state */ + z->next_in = (Bytef *)k->str; + z->avail_in = (uInt)nread; + } + + if (z->avail_in == 0) { + /* We don't have any data to inflate; wait until next time */ + return CURLE_OK; + } + + /* 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[0]; + z->avail_out = DSIZ; + + status = inflate(z, Z_SYNC_FLUSH); + if (status == Z_OK || status == Z_STREAM_END) { + if(DSIZ - z->avail_out) { + result = Curl_client_write(data, CLIENTWRITE_BODY, decomp, + DSIZ - z->avail_out); + /* if !CURLE_OK, clean up, return */ + if (result) + return exit_zlib(z, &k->zlib_init, result); + } + + /* Done?; clean up, return */ + /* We should really check the gzip CRC here */ + if (status == Z_STREAM_END) { + if (inflateEnd(z) == Z_OK) + return exit_zlib(z, &k->zlib_init, result); + else + return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z)); + } + + /* Done with these bytes, exit */ + if (status == Z_OK && z->avail_in == 0 && z->avail_out > 0) + return result; + } + else { /* Error; exit loop, handle below */ + return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z)); + } + } +} +#endif /* HAVE_LIBZ */ diff --git a/Utilities/cmcurl/content_encoding.h b/Utilities/cmcurl/content_encoding.h new file mode 100644 index 0000000..a65dbd2 --- /dev/null +++ b/Utilities/cmcurl/content_encoding.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" + +/* + * 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 SessionHandle *data, + struct Curl_transfer_keeper *k, + ssize_t nread); + +CURLcode +Curl_unencode_gzip_write(struct SessionHandle *data, + 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 0000000..3ba5627 --- /dev/null +++ b/Utilities/cmcurl/cookie.c @@ -0,0 +1,879 @@ +/*************************************************************************** + * _ _ ____ _ + * 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$ + ***************************************************************************/ + +/*** + + +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" + +#ifndef CURL_DISABLE_HTTP + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "urldata.h" +#include "cookie.h" +#include "getdate.h" +#include "strequal.h" +#include "strtok.h" +#include "sendf.h" +#include "curl_memory.h" + +/* The last #include file should be: */ +#ifdef CURLDEBUG +#include "memdebug.h" +#endif + +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); + + 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); +} + +/**************************************************************************** + * + * 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; + 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 && isspace((int)*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 && isspace((int)what[len-1])) { + what[len-1]=0; + len--; + } + + /* Skip leading whitespace from the 'what' */ + whatptr=what; + while(isspace((int)*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]) + 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 && isspace((int)*ptr)) + ptr++; + semiptr=strchr(ptr, ';'); /* now, find the next semicolon */ + + if(!semiptr && *ptr) + /* There are no more semicolons, but there's a final name=value pair + coming up */ + semiptr=strchr(ptr, '\0'); + } while(semiptr); + + if(!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 = atoi(ptr); + 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 + 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 && isspace((int)*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 || + checkprefix(co->path, 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_freelist() + * + * Free a list of cookies previously returned by Curl_cookie_getlist(); + * + ****************************************************************************/ + +void Curl_cookie_freelist(struct Cookie *co) +{ + struct Cookie *next; + if(co) { + while(co) { + next = co->next; + free(co); /* we only free the struct since the "members" are all + just copied! */ + co = next; + } + } +} + +/***************************************************************************** + * + * Curl_cookie_cleanup() + * + * Free a "cookie object" previous created with cookie_init(). + * + ****************************************************************************/ +void Curl_cookie_cleanup(struct CookieInfo *c) +{ + struct Cookie *co; + struct Cookie *next; + if(c) { + if(c->filename) + free(c->filename); + co = c->cookies; + + while(co) { + next = co->next; + freecookie(co); + co = next; + } + free(c); /* free the base struct as well */ + } +} + +/* + * Curl_cookie_output() + * + * Writes all internally known cookies to the specified file. Specify + * "-" as file name to write to stdout. + * + * The function returns non-zero on write failure. + */ +int Curl_cookie_output(struct CookieInfo *c, char *dumphere) +{ + struct Cookie *co; + FILE *out; + bool use_stdout=FALSE; + + if((NULL == c) || (0 == c->numcookies)) + /* If there are no known cookies, we don't write or even create any + destination file */ + return 0; + + if(strequal("-", dumphere)) { + /* use stdout */ + out = stdout; + use_stdout=TRUE; + } + else { + out = fopen(dumphere, "w"); + if(!out) + return 1; /* failure */ + } + + if(c) { + fputs("# Netscape HTTP Cookie File\n" + "# http://www.netscape.com/newsref/std/cookie_spec.html\n" + "# This file was generated by libcurl! Edit at your own risk.\n\n", + out); + co = c->cookies; + + while(co) { + fprintf(out, + "%s%s\t" /* domain */ + "%s\t" /* tailmatch */ + "%s\t" /* path */ + "%s\t" /* secure */ + "%u\t" /* expires */ + "%s\t" /* name */ + "%s\n", /* 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", + (unsigned int)co->expires, + co->name, + co->value?co->value:""); + + co=co->next; + } + } + + if(!use_stdout) + fclose(out); + + return 0; +} + +#endif /* CURL_DISABLE_HTTP */ diff --git a/Utilities/cmcurl/cookie.h b/Utilities/cmcurl/cookie.h new file mode 100644 index 0000000..6f8e8e5 --- /dev/null +++ b/Utilities/cmcurl/cookie.h @@ -0,0 +1,95 @@ +#ifndef __COOKIE_H +#define __COOKIE_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 <stdio.h> +#ifdef WIN32 +#include <time.h> +#else +#include <sys/time.h> +#endif + +#include <curl/curl.h> + +struct Cookie { + struct Cookie *next; /* next in the chain */ + char *name; /* <this> = value */ + char *value; /* name = <this> */ + char *path; /* path = <this> */ + char *domain; /* domain = <this> */ + long expires; /* expires = <this> */ + char *expirestr; /* the plain text version */ + 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_cleanup(struct CookieInfo *); +int Curl_cookie_output(struct CookieInfo *, char *); + +#endif diff --git a/Utilities/cmcurl/curl.copyright b/Utilities/cmcurl/curl.copyright new file mode 100644 index 0000000..16ea773 --- /dev/null +++ b/Utilities/cmcurl/curl.copyright @@ -0,0 +1,25 @@ +This package was cmakified by Andy Cedilnik <andy . cedilnik @ kitware.com> + +It was downloaded from http://curl.haxx.se + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1996 - 2004, 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/curl/curl.h b/Utilities/cmcurl/curl/curl.h new file mode 100644 index 0000000..96c8fcc --- /dev/null +++ b/Utilities/cmcurl/curl/curl.h @@ -0,0 +1,1340 @@ +#ifndef __CURL_CURL_H +#define __CURL_CURL_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$ + ***************************************************************************/ + +/* 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 + +/* + * 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. + */ +#if defined(_MSC_VER) || defined(__LCC__) +/* MSVC */ + typedef signed __int64 curl_off_t; +#define CURL_FORMAT_OFF_T "%I64d" +#else /* _MSC_VER || __LCC__ */ +#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 || __LCC__ */ + +#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 + +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 brand new return code for the read callback that will signal + the caller 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); + + /* 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); + +/* + * 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 */ + CURLE_FTP_USER_PASSWORD_INCORRECT, /* 10 */ + 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 */ + 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 */ + + CURL_LAST /* never use! */ +} CURLcode; + +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 + +/* backwards compatibility with older names */ +#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR +#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED + +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) + +/* 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 + +/* This is 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 + +#define CURL_ERROR_SIZE 256 + +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; + +/* 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__) + /* 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 become OBSOLETE 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), + + /* When doing 3rd party transfer, set the source host name with this */ + CINIT(SOURCE_HOST, OBJECTPOINT, 122), + + /* When doing 3rd party transfer, set the source user and password with + this */ + CINIT(SOURCE_USERPWD, OBJECTPOINT, 123), + + /* When doing 3rd party transfer, set the source file path with this */ + CINIT(SOURCE_PATH, OBJECTPOINT, 124), + + /* When doing 3rd party transfer, set the source server's port number + with this */ + CINIT(SOURCE_PORT, LONG, 125), + + /* When doing 3rd party transfer, decide which server that should get the + PASV command (and the other gets the PORT). + 0 (default) - The target host issues PASV. + 1 - The source host issues PASV */ + CINIT(PASV_HOST, LONG, 126), + + /* When doing 3rd party transfer, set the source pre-quote linked list + of commands with this */ + CINIT(SOURCE_PREQUOTE, OBJECTPOINT, 127), + + /* When doing 3rd party transfer, set the source post-quote linked list + of commands with this */ + CINIT(SOURCE_POSTQUOTE, OBJECTPOINT, 128), + + 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! */ +#define CURLOPT_HTTPREQUEST -1 +#define CURLOPT_FTPASCII CURLOPT_TRANSFERTEXT +#define CURLOPT_MUTE -2 +#define CURLOPT_PASSWDFUNCTION -3 +#define CURLOPT_PASSWDDATA -4 +#define CURLOPT_CLOSEFUNCTION -5 + +#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 __BEOS__ +#include <support/SupportDefs.h> +#endif + + +/* curl_strequal() and curl_strnequal() are subject for removal in a future + libcurl, see lib/README.curlx for details */ +extern int (curl_strequal)(const char *s1, const char *s2); +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. + */ +CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...); + +/* + * NAME curl_formfree() + * + * DESCRIPTION + * + * Free a multipart formpost previously built with curl_formadd(). + */ +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 + */ +char *curl_getenv(const char *variable); + +/* + * NAME curl_version() + * + * DESCRIPTION + * + * Returns a static ascii string of the libcurl version. + */ +char *curl_version(void); + +/* + * NAME curl_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. + */ +char *curl_escape(const char *string, int length); + +/* + * NAME curl_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. + */ +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 + */ +void curl_free(void *p); + +/* + * NAME curl_global_init() + * + * DESCRIPTION + * + * curl_global_init() should be invoked exactly once for each application that + * uses libcurl + */ +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. + */ +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 + */ +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. + */ +struct curl_slist *curl_slist_append(struct curl_slist *, const char *); + +/* + * NAME curl_slist_free_all() + * + * DESCRIPTION + * + * free a previously built curl_slist. + */ +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 for cases + * where the specified time is relative now, like 'two weeks' or 'tomorrow' + * etc. + */ +time_t curl_getdate(const char *p, const time_t *now); + +#define CURLINFO_STRING 0x100000 +#define CURLINFO_LONG 0x200000 +#define CURLINFO_DOUBLE 0x300000 +#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, + /* Fill in new entries below here! */ + + CURLINFO_LASTONE = 23 +} 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; + +CURLSH *curl_share_init(void); +CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); +CURLSHcode curl_share_cleanup(CURLSH *); + +/**************************************************************************** + * Structures for querying information about the curl library at runtime. + */ + +typedef enum { + CURLVERSION_FIRST, + CURLVERSION_SECOND, + CURLVERSION_THIRD, + 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 redfine the NOW to another enum + from above. */ +#define CURLVERSION_NOW CURLVERSION_THIRD + +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 */ + char *ssl_version; /* human readable string */ + long ssl_version_num; /* number */ + const char *libz_version; /* human readable string */ + /* protocols is terminated by an entry with a NULL protoname */ + const char **protocols; + + /* The fields below this were added in CURLVERSION_SECOND */ + const char *ares; + int ares_num; + + /* This field was aded in CURLVERSION_THIRD */ + const char *libidn; +} 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 */ + +/* + * NAME curl_version_info() + * + * DESCRIPTION + * + * This function returns a pointer to a static copy of the version info + * struct. See above. + */ +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. + */ +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. + */ +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 0000000..ef0a4c5 --- /dev/null +++ b/Utilities/cmcurl/curl/curlver.h @@ -0,0 +1,55 @@ +#ifndef __CURL_CURLVER_H +#define __CURL_CURLVER_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$ + ***************************************************************************/ + +/* 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.12.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. 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 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 0x70C01 + +/* The numeric version number is also available "in parts" by using these + defines: */ +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 12 +#define LIBCURL_VERSION_PATCH 1 + +#endif /* __CURL_CURLVER_H */ diff --git a/Utilities/cmcurl/curl/easy.h b/Utilities/cmcurl/curl/easy.h new file mode 100644 index 0000000..336e542 --- /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 *curl_easy_init(void); +CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); +CURLcode curl_easy_perform(CURL *curl); +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. + */ +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* 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. + */ +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 0000000..65dc114 --- /dev/null +++ b/Utilities/cmcurl/curl/mprintf.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 H_MPRINTF +#define H_MPRINTF + +#include <stdarg.h> +#include <stdio.h> /* needed for FILE */ + +int curl_mprintf(const char *format, ...); +int curl_mfprintf(FILE *fd, const char *format, ...); +int curl_msprintf(char *buffer, const char *format, ...); +int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...); +int curl_mvprintf(const char *format, va_list args); +int curl_mvfprintf(FILE *fd, const char *format, va_list args); +int curl_mvsprintf(char *buffer, const char *format, va_list args); +int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, va_list args); +char *curl_maprintf(const char *format, ...); +char *curl_mvaprintf(const char *format, va_list args); + +#ifdef _MPRINTF_REPLACE +# define printf curl_mprintf +# define fprintf curl_mfprintf +# define sprintf curl_msprintf +# define snprintf curl_msnprintf +# define vprintf curl_mvprintf +# define vfprintf curl_mvfprintf +# define vsprintf curl_mvsprintf +# define vsnprintf curl_mvsnprintf +# define aprintf curl_maprintf +# define vaprintf curl_mvaprintf +#endif + +#endif /* H_MPRINTF */ diff --git a/Utilities/cmcurl/curl/multi.h b/Utilities/cmcurl/curl/multi.h new file mode 100644 index 0000000..3a867ab --- /dev/null +++ b/Utilities/cmcurl/curl/multi.h @@ -0,0 +1,221 @@ +#ifndef __CURL_MULTI_H +#define __CURL_MULTI_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$ + ***************************************************************************/ +/* + This is meant to be the "external" header file. Don't give away any + internals here! + + This document presents a mixture of ideas from at least: + - Daniel Stenberg + - Steve Dekorte + - Sterling Hughes + - Ben Greear + + ------------------------------------------- + GOALS + + o Enable a "pull" interface. The application that uses libcurl decides where + and when to ask libcurl to get/send data. + + o Enable multiple simultaneous transfers in the same thread without making it + complicated for the application. + + o Enable the application to select() on its own file descriptors and curl's + file descriptors simultaneous easily. + +*/ +#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 + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock2.h> +#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) +#include <sys/select.h> +#endif + +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/types.h> +#endif + +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void CURLM; + +typedef enum { + CURLM_CALL_MULTI_PERFORM=-1, /* please call curl_multi_perform() soon */ + CURLM_OK, + CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ + CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ + CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */ + CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ + CURLM_LAST +} CURLMcode; + +typedef enum { + CURLMSG_NONE, /* first, not used */ + CURLMSG_DONE, /* This easy handle has completed. '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. + */ +CURLM *curl_multi_init(void); + +/* + * Name: curl_multi_add_handle() + * + * Desc: add a standard curl handle to the multi stack + * Returns: CURLMcode type, general multi error code. + */ +CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * Returns: CURLMcode type, general multi error code. + */ +CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * Returns: CURLMcode type, general multi error code. + */ +CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); + + /* + * Name: curl_multi_perform() + * + * Desc: When the app thinks there's data available for curl it calls this + * function to read/write whatever there is right now. This returns + * as soon as the reads and writes are done. This function does not + * require that there actually is data available for reading or that + * data can be written, it can be called just in case. It returns + * the number of handles that still transfer data in the second + * argument's integer-pointer. + * + * Returns: CURLMcode type, general multi error code. *NOTE* that this only + * returns errors etc regarding the whole multi stack. There might + * still have occurred problems on invidual transfers even when this + * returns OK. + */ +CURLMcode curl_multi_perform(CURLM *multi_handle, + int *running_handles); + + /* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles will be if this function is called + * in the middle of a transfer. + * Returns: CURLMcode type, general multi error code. + */ +CURLMcode curl_multi_cleanup(CURLM *multi_handle); + +/* + * Name: curl_multi_info_read() + * + * Desc: Ask the multi handle if there's any messages/informationals from + * the individual transfers. Messages include informationals such as + * error code from the transfer or just the fact that a transfer is + * completed. More details on these should be written down as well. + * + * Repeated calls to this function will return a new struct each + * time, until a special "end of msgs" struct is returned as a signal + * that there is no more to get at this point. + * + * The data the returned pointer points to will not survive calling + * curl_multi_cleanup(). + * + * The 'CURLMsg' struct is meant to be very simple and only contain + * very basic informations. If more involved information is wanted, + * we will provide the particular "transfer handle" in that struct + * and that should/could/would be used in subsequent + * curl_easy_getinfo() calls (or similar). The point being that we + * must never expose complex structs to applications, as then we'll + * undoubtably get backwards compatibility problems in the future. + * + * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out + * of structs. It also writes the number of messages left in the + * queue (after this read) in the integer the second argument points + * to. + */ +CURLMsg *curl_multi_info_read(CURLM *multi_handle, + int *msgs_in_queue); + +/* + * NAME curl_multi_strerror() + * + * DESCRIPTION + * + * 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. + */ +const char *curl_multi_strerror(CURLMcode); + +#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 0000000..11c1e2f --- /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 0000000..d37d6ae --- /dev/null +++ b/Utilities/cmcurl/curl/types.h @@ -0,0 +1 @@ +/* not used */ diff --git a/Utilities/cmcurl/curl_memory.h b/Utilities/cmcurl/curl_memory.h new file mode 100644 index 0000000..4e32a67 --- /dev/null +++ b/Utilities/cmcurl/curl_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/curlx.h b/Utilities/cmcurl/curlx.h new file mode 100644 index 0000000..0dd9a09 --- /dev/null +++ b/Utilities/cmcurl/curlx.h @@ -0,0 +1,95 @@ +#ifndef __CURLX_H +#define __CURLX_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$ + ***************************************************************************/ + +/* + * 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. */ +# 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 0000000..06cb6b6 --- /dev/null +++ b/Utilities/cmcurl/dict.c @@ -0,0 +1,221 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <sys/time.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "transfer.h" +#include "sendf.h" + +#include "progress.h" +#include "strequal.h" +#include "dict.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +CURLcode Curl_dict(struct connectdata *conn) +{ + char *word; + char *ppath; + char *database = NULL; + char *strategy = NULL; + char *nthdef = NULL; /* This is not part of the protocol, but required + by RFC 2229 */ + CURLcode result; + struct SessionHandle *data=conn->data; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + + char *path = conn->path; + curl_off_t *bytecount = &conn->bytecount; + + if(conn->bits.user_passwd) { + /* AUTH is missing */ + } + + if (strnequal(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || + strnequal(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || + strnequal(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { + + word = strchr(path, ':'); + if (word) { + word++; + database = strchr(word, ':'); + if (database) { + *database++ = (char)0; + strategy = strchr(database, ':'); + if (strategy) { + *strategy++ = (char)0; + nthdef = strchr(strategy, ':'); + if (nthdef) { + *nthdef++ = (char)0; + } + } + } + } + + if ((word == NULL) || (*word == (char)0)) { + failf(data, "lookup word is missing"); + } + if ((database == NULL) || (*database == (char)0)) { + database = (char *)"!"; + } + if ((strategy == NULL) || (*strategy == (char)0)) { + strategy = (char *)"."; + } + + result = Curl_sendf(sockfd, conn, + "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n" + "MATCH " + "%s " /* database */ + "%s " /* strategy */ + "%s\n" /* word */ + "QUIT\n", + + database, + strategy, + word + ); + if(result) + failf(data, "Failed sending DICT request"); + else + result = Curl_Transfer(conn, 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 *)"!"; + } + + result = Curl_sendf(sockfd, conn, + "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n" + "DEFINE " + "%s " /* database */ + "%s\n" /* word */ + "QUIT\n", + database, + word); + if(result) + failf(data, "Failed sending DICT request"); + else + result = Curl_Transfer(conn, 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 "\n" + "%s\n" + "QUIT\n", ppath); + if(result) + failf(data, "Failed sending DICT request"); + else + result = Curl_Transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, + -1, NULL); + if(result) + return result; + } + } + (void)nthdef; + + return CURLE_OK; +} diff --git a/Utilities/cmcurl/dict.h b/Utilities/cmcurl/dict.h new file mode 100644 index 0000000..4301f01 --- /dev/null +++ b/Utilities/cmcurl/dict.h @@ -0,0 +1,30 @@ +#ifndef __DICT_H +#define __DICT_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_DICT +CURLcode Curl_dict(struct connectdata *conn); +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 0000000..fb116fd --- /dev/null +++ b/Utilities/cmcurl/easy.c @@ -0,0 +1,583 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#include "strequal.h" + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <sys/time.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#endif /* WIN32 ... */ + +#include "urldata.h" +#include <curl/curl.h> +#include "transfer.h" +#include "ssluse.h" +#include "url.h" +#include "getinfo.h" +#include "hostip.h" +#include "share.h" +#include "curl_memory.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#include "memdebug.h" + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +/* 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; + +#ifdef ENABLE_IPV6 + wVersionRequested = MAKEWORD(2, 0); +#else + wVersionRequested = MAKEWORD(1, 1); +#endif + + 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 = 0; +static long init_flags = 0; + +/* + * 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)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)strdup; + Curl_ccalloc = (curl_calloc_callback)calloc; + + if (flags & CURL_GLOBAL_SSL) + Curl_SSL_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 + + initialized = 1; + 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; + + /* 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; + + 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 + + initialized = 0; + 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. + */ +typedef int (*func_T)(void); +CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) +{ + va_list arg; + func_T param_func; + long param_long; + void *param_obj; + curl_off_t param_offset; + struct SessionHandle *data = curl; + CURLcode ret; + + if(!curl) + return CURLE_BAD_FUNCTION_ARGUMENT; + + va_start(arg, tag); + + /* PORTING NOTE: + Object pointers can't necessarily be casted to function pointers and + therefore we need to know what type it is and read the correct type + at once. This should also correct problems with different sizes of + the types. + */ + + if(tag < CURLOPTTYPE_OBJECTPOINT) { + /* This is a LONG type */ + param_long = va_arg(arg, long); + ret = Curl_setopt(data, tag, param_long); + } + else if(tag < CURLOPTTYPE_FUNCTIONPOINT) { + /* This is a object pointer type */ + param_obj = va_arg(arg, void *); + ret = Curl_setopt(data, tag, param_obj); + } + else if(tag < CURLOPTTYPE_OFF_T) { + /* This is a function pointer type */ + param_func = va_arg(arg, func_T ); + ret = Curl_setopt(data, tag, param_func); + } + else { + /* This is a curl_off_t type */ + param_offset = va_arg(arg, curl_off_t); + ret = Curl_setopt(data, tag, param_offset); + } + + va_end(arg); + return ret; +} + +/* + * 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->hostcache != Curl_global_host_cache_get()) { + if (data->hostcache) + Curl_hash_destroy(data->hostcache); + data->hostcache = Curl_global_host_cache_get(); + } + + if (!data->hostcache) { + data->hostcache = Curl_mk_dnscache(); + + if(!data->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; + } + + } + + return Curl_perform(data); +} + +/* + * 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; + + if ( ! (data->share && data->share->hostcache) ) { + if ( !Curl_global_host_cache_use(data)) { + Curl_hash_destroy(data->hostcache); + } + } + Curl_close(data); +} + +/* + * 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; + outcurl->state.numconnects = data->state.numconnects; + outcurl->state.connects = (struct connectdata **) + malloc(sizeof(struct connectdata *) * outcurl->state.numconnects); + + if(!outcurl->state.connects) { + break; + } + + memset(outcurl->state.connects, 0, + sizeof(struct connectdata *)*outcurl->state.numconnects); + + outcurl->progress.flags = data->progress.flags; + outcurl->progress.callback = data->progress.callback; + +#ifndef CURL_DISABLE_HTTP + 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.proxy) { + outcurl->change.proxy = strdup(data->change.proxy); + if(!outcurl->change.proxy) + break; + outcurl->change.proxy_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 + + fail = FALSE; /* we reach this point and thus we are OK */ + + } while(0); + + if(fail) { + if(outcurl) { + if(outcurl->state.connects) + free(outcurl->state.connects); + if(outcurl->state.headerbuff) + free(outcurl->state.headerbuff); + if(outcurl->change.proxy) + free(outcurl->change.proxy); + 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; + + /* zero out UserDefined data: */ + memset(&data->set, 0, sizeof(struct UserDefined)); + + /* zero out Progress data: */ + memset(&data->progress, 0, sizeof(struct Progress)); + + /* 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->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 */ + + /* 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 prefered CA cert bundle since install time */ + data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE; +#endif +} diff --git a/Utilities/cmcurl/escape.c b/Utilities/cmcurl/escape.c new file mode 100644 index 0000000..2b9a883 --- /dev/null +++ b/Utilities/cmcurl/escape.c @@ -0,0 +1,135 @@ +/*************************************************************************** + * _ _ ____ _ + * 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$ + ***************************************************************************/ + +/* 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 "curl_memory.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#include "memdebug.h" + +char *curl_escape(const char *string, int inlength) +{ + size_t alloc = (inlength?(size_t)inlength:strlen(string))+1; + char *ns; + char *testing_ptr; + unsigned char in; + size_t newlen = alloc; + int strindex=0; + size_t length; + + 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; + } + } + snprintf(&ns[strindex], 4, "%%%02X", in); + + strindex+=3; + } + else { + /* just copy this */ + ns[strindex++]=in; + } + string++; + } + ns[strindex]=0; /* terminate it */ + return ns; +} + +#define ishex(in) ((in >= 'a' && in <= 'f') || \ + (in >= 'A' && in <= 'F') || \ + (in >= '0' && in <= '9')) + +char *curl_unescape(const char *string, int length) +{ + int alloc = (length?length:(int)strlen(string))+1; + char *ns = malloc(alloc); + unsigned char in; + int strindex=0; + long hex; + + if( !ns ) + return NULL; + + while(--alloc > 0) { + in = *string; + if(('%' == in) && ishex(string[1]) && ishex(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 */ + string+=2; + alloc-=2; + } + + ns[strindex++] = in; + string++; + } + ns[strindex]=0; /* terminate it */ + 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 0000000..4d29236 --- /dev/null +++ b/Utilities/cmcurl/escape.h @@ -0,0 +1,32 @@ +#ifndef __ESCAPE_H +#define __ESCAPE_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$ + ***************************************************************************/ +/* Escape and unescape URL encoding in strings. The functions return a new + * allocated string or NULL if an error occurred. */ + +char *curl_escape(const char *string, int length); +char *curl_unescape(const char *string, int length); + +#endif diff --git a/Utilities/cmcurl/file.c b/Utilities/cmcurl/file.c new file mode 100644 index 0000000..cd4366c --- /dev/null +++ b/Utilities/cmcurl/file.c @@ -0,0 +1,390 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 CURL_DISABLE_FILE +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <time.h> +#include <io.h> +#include <fcntl.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#include <sys/time.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "progress.h" +#include "sendf.h" +#include "escape.h" +#include "file.h" +#include "speedcheck.h" +#include "getinfo.h" +#include "transfer.h" +#include "url.h" +#include "curl_memory.h" + +#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_unescape(conn->path, 0); + struct FILEPROTO *file; + int fd; +#if defined(WIN32) || 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; + } + + conn->proto.file = file; + +#if defined(WIN32) || 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 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 */ + + if(!conn->data->set.upload && (fd == -1)) { + failf(conn->data, "Couldn't open file %s", conn->path); + Curl_file_done(conn, CURLE_FILE_COULDNT_READ_FILE); + return CURLE_FILE_COULDNT_READ_FILE; + } + file->fd = fd; + + return CURLE_OK; +} + +#if defined(WIN32) && (SIZEOF_CURL_OFF_T > 4) +#define lseek(x,y,z) _lseeki64(x, y, z) +#endif + +CURLcode Curl_file_done(struct connectdata *conn, + CURLcode status) +{ + struct FILEPROTO *file = conn->proto.file; + (void)status; /* not used */ + Curl_safefree(file->freepath); + + return CURLE_OK; +} + +#if defined(WIN32) || defined(__EMX__) +#define DIRSEP '\\' +#else +#define DIRSEP '/' +#endif + +static CURLcode file_upload(struct connectdata *conn) +{ + struct FILEPROTO *file = conn->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->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) + return res; + + nread = (size_t)readcount; + + if (nread <= 0) + break; + + /* 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) +{ + /* 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; + 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(); + + 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->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(data, CLIENTWRITE_BOTH, buf, 0); + if(result) + return result; + + result = Curl_client_write(data, CLIENTWRITE_BOTH, + (char *)"Accept-ranges: bytes\r\n", 0); + if(result) + return result; + +#ifdef HAVE_STRFTIME + if(fstated) { + struct tm *tm; + time_t cuClock = (time_t)statbuf.st_mtime; +#ifdef HAVE_GMTIME_R + struct tm buffer; + tm = (struct tm *)gmtime_r(&cuClock, &buffer); +#else + tm = gmtime(&cuClock); +#endif + /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ + strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n", + tm); + result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + } +#endif + return result; + } + + /* Added by Dolbneff A.V & Spiridonoff A.V */ + if (conn->resume_from <= expected_size) + expected_size -= conn->resume_from; + else + /* Is this error code suitable in such situation? */ + return CURLE_FTP_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(conn->resume_from) + lseek(fd, conn->resume_from, SEEK_SET); + + 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(data, 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; + + close(fd); + + return res; +} +#endif diff --git a/Utilities/cmcurl/file.h b/Utilities/cmcurl/file.h new file mode 100644 index 0000000..689b8ae --- /dev/null +++ b/Utilities/cmcurl/file.h @@ -0,0 +1,31 @@ +#ifndef __FILE_H +#define __FILE_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_FILE +CURLcode Curl_file(struct connectdata *); +CURLcode Curl_file_done(struct connectdata *, CURLcode); +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 0000000..5abff6e --- /dev/null +++ b/Utilities/cmcurl/formdata.c @@ -0,0 +1,1484 @@ +/*************************************************************************** + * _ _ ____ _ + * 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$ + ***************************************************************************/ + +/* + Debug the form generator stand-alone by compiling this source file with: + + gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -o formdata -I../include formdata.c strequal.c + + run the 'formdata' executable the output should end with: + All Tests seem to have worked ... + and the following parts should be there: + +Content-Disposition: form-data; name="simple_COPYCONTENTS" +value for simple COPYCONTENTS + +Content-Disposition: form-data; name="COPYCONTENTS_+_CONTENTTYPE" +Content-Type: image/gif +value for COPYCONTENTS + CONTENTTYPE + +Content-Disposition: form-data; name="PRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH" +vlue for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH +(or you might see P^@RNAME and v^@lue at the start) + +Content-Disposition: form-data; name="simple_PTRCONTENTS" +value for simple PTRCONTENTS + +Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH" +vlue for PTRCONTENTS + CONTENTSLENGTH +(or you might see v^@lue at the start) + +Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE" +Content-Type: text/plain +vlue for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE +(or you might see v^@lue at the start) + +Content-Disposition: form-data; name="FILE1_+_CONTENTTYPE"; filename="inet_ntoa_r.h" +Content-Type: text/html +... + +Content-Disposition: form-data; name="FILE1_+_FILE2" +Content-Type: multipart/mixed, boundary=curlz1s0dkticx49MV1KGcYP5cvfSsz +... +Content-Disposition: attachment; filename="inet_ntoa_r.h" +Content-Type: text/plain +... +Content-Disposition: attachment; filename="Makefile.b32.resp" +Content-Type: text/plain +... + +Content-Disposition: form-data; name="FILE1_+_FILE2_+_FILE3" +Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1 +... +Content-Disposition: attachment; filename="inet_ntoa_r.h" +Content-Type: text/plain +... +Content-Disposition: attachment; filename="Makefile.b32.resp" +Content-Type: text/plain +... +Content-Disposition: attachment; filename="inet_ntoa_r.h" +Content-Type: text/plain +... + + +Content-Disposition: form-data; name="ARRAY: FILE1_+_FILE2_+_FILE3" +Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1 +... +Content-Disposition: attachment; filename="inet_ntoa_r.h" +Content-Type: text/plain +... +Content-Disposition: attachment; filename="Makefile.b32.resp" +Content-Type: text/plain +... +Content-Disposition: attachment; filename="inet_ntoa_r.h" +Content-Type: text/plain +... + +Content-Disposition: form-data; name="FILECONTENT" +... + + */ + +#include "setup.h" +#include <curl/curl.h> + +/* Length of the random boundary string. */ +#define BOUNDARY_LENGTH 40 + +#ifndef CURL_DISABLE_HTTP + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <time.h> +#include <sys/stat.h> +#include "formdata.h" +#include "strequal.h" +#include "curl_memory.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#include "memdebug.h" + +/* 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; + unsigned int i; + /* + * No type was specified, we scan through a few well-known + * extensions and pick the first we match! + */ + struct ContentType { + const char *extension; + const char *type; + }; + static struct ContentType ctts[]={ + {".gif", "image/gif"}, + {".jpg", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".txt", "text/plain"}, + {".html", "text/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; + 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: + current_form->flags |= HTTPPOST_PTRNAME; /* fall through */ + 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: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))) + 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))) + 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))) + 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 = 0; + if ( array_state ) + { + memcpy(&list, &array_value, sizeof(struct curl_slist*)); + } + else + { + list = 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) + *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) +{ + struct FormData *next; + + 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)); /* continue */ +} + +/* + * 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)); /* continue */ +} + +/* + * 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. + */ + +CURLcode Curl_getFormData(struct FormData **finalform, + struct curl_httppost *post, + 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, + "Content-Type: multipart/form-data;" + " boundary=%s\r\n", + 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; + + 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 */ + result = AddFormDataf(&form, &size, + "\r\n--%s\r\nContent-Disposition: " + "attachment; filename=\"%s\"", + fileboundary, + (file->showfilename?file->showfilename: + file->contents)); + if (result) + break; + } + else if((post->flags & HTTPPOST_FILENAME) || + (post->flags & HTTPPOST_BUFFER)) { + + result = AddFormDataf(&form, &size, + "; filename=\"%s\"", + (post->showfilename?post->showfilename: + post->contents)); + 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 */ + size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0); + } +#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))) { + result = AddFormData(&form, FORM_DATA, buffer, nread, &size); + if (result) + break; + } + } + + if (result) { + Curl_formclean(firstform); + free(boundary); + return result; + } + + } + else { + 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_DATA, post->buffer, + post->bufferlength, &size); + if (result) + break; + } + + else { + /* include the contents we got */ + result = AddFormData(&form, FORM_DATA, post->contents, + post->contentslength, &size); + if (result) + break; + } + } while((file = file->more)); /* 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)); /* 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 -1; /* failure */ + } + nread = fread(buffer, 1, size, form->fp); + + if(nread != size) { + /* this is the last chunk form 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) + return readfromfile(form, buffer, wantedsize); + + 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_DATA)); + /* If we got an empty line and we have more data, we proceed to the next + line immediately to avoid returning zero before we've reached the end. + This is the bug reported November 22 1999 on curl 6.3. (Daniel) */ + + return gotsize; +} + +/* + * 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.resp"; + char type2[] = "image/gif"; + char type6[] = "text/plain"; + char type7[] = "text/html"; + int name3length = strlen(name3); + int value3length = strlen(value3); + int value5length = strlen(value4); + int value6length = strlen(value5); + int errors = 0; + int size; + 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; + + form=Curl_getFormData(httppost, &size); + + Curl_FormInit(&formread, form); + + do { + nread = Curl_FormReader(buffer, 1, sizeof(buffer), + (FILE *)&formread); + + if(-1 == nread) + break; + fwrite(buffer, nread, 1, stdout); + } while(1); + + fprintf(stdout, "size: %d\n", size); + if (errors) + fprintf(stdout, "\n==> %d Test(s) failed!\n", errors); + else + fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n"); + + return 0; +} + +#endif + +#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; +} + +void curl_formfree(struct curl_httppost *form) +{ + (void)form; + /* does nothing HTTP is disabled */ +} + +#endif /* CURL_DISABLE_HTTP */ + +/* + * 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=0; /* this is just so that two boundaries within + the same form won't be identical */ + size_t i; + + static char table16[]="abcdef0123456789"; + + retstring = (char *)malloc(BOUNDARY_LENGTH+1); + + if(!retstring) + return NULL; /* failed */ + + srand(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; +} diff --git a/Utilities/cmcurl/formdata.h b/Utilities/cmcurl/formdata.h new file mode 100644 index 0000000..c6a78cd --- /dev/null +++ b/Utilities/cmcurl/formdata.h @@ -0,0 +1,92 @@ +#ifndef __FORMDATA_H +#define __FORMDATA_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$ + ***************************************************************************/ + +enum formtype { + FORM_DATA, /* regular data */ + FORM_FILE /* 'line' points to a file name we should read from */ +}; + +/* 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, + 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 *); + +#endif + diff --git a/Utilities/cmcurl/ftp.c b/Utilities/cmcurl/ftp.c new file mode 100644 index 0000000..2501412 --- /dev/null +++ b/Utilities/cmcurl/ftp.c @@ -0,0 +1,2774 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 CURL_DISABLE_FTP +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <errno.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) + +#else /* some kind of unix */ +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <sys/types.h> +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#include <sys/utsname.h> +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#endif +#endif + +#if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__) +#include <errno.h> +#endif + +#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 "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 "security.h" +#include "krb4.h" +#endif + +#include "strtoofft.h" +#include "strequal.h" +#include "ssluse.h" +#include "connect.h" +#include "strerror.h" +#include "curl_memory.h" +#include "inet_ntop.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_cwd(struct connectdata *conn, char *path); +static CURLcode ftp_mkd(struct connectdata *conn, char *path); +static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path); +static CURLcode ftp_quit(struct connectdata *conn); +static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn); +static CURLcode ftp_3rdparty_transfer(struct connectdata *conn); +static CURLcode ftp_regular_transfer(struct connectdata *conn); +static CURLcode ftp_3rdparty(struct connectdata *conn); + +/* easy-to-use macro: */ +#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result + +static void freedirs(struct FTP *ftp) +{ + int i; + if(ftp->dirs) { + for (i=0; i < ftp->dirdepth; i++){ + if(ftp->dirs[i]) { + free(ftp->dirs[i]); + ftp->dirs[i]=NULL; + } + } + free(ftp->dirs); + ftp->dirs = NULL; + } + if(ftp->file) { + free(ftp->file); + ftp->file = NULL; + } +} + +/*********************************************************************** + * + * 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) +{ + fd_set rdset; + struct timeval dt; + 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); + + FD_ZERO(&rdset); + + FD_SET(sock, &rdset); + + if(timeout) { + timeout -= timespent; + if(timeout<=0) { + failf(data, "Timed out before server could connect to us"); + return CURLE_OPERATION_TIMEDOUT; + } + } + + /* we give the server 60 seconds to connect to us, or a custom timeout */ + dt.tv_sec = (int)(timeout?timeout:60); + dt.tv_usec = 0; + + switch (select(sock+1, &rdset, NULL, NULL, &dt)) { + case -1: /* error */ + /* let's die here */ + failf(data, "Error while waiting for server connect"); + return CURLE_FTP_PORT_FAILED; + case 0: /* timeout */ + /* let's die here */ + failf(data, "Timeout while waiting for server connect"); + return CURLE_FTP_PORT_FAILED; + default: + /* we have received data here */ + { + curl_socket_t s; +#ifdef __hpux + int size = sizeof(struct sockaddr_in); +#else + socklen_t size = sizeof(struct sockaddr_in); +#endif + struct sockaddr_in add; + + getsockname(sock, (struct sockaddr *) &add, &size); + 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; +} + + +/* --- 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 */ +{ + /* Brand new implementation. + * We cannot read just one byte per read() and then go back to select() + * as it seems that the OpenSSL read() stuff doesn't grok that properly. + * + * Alas, read as much as possible, split up into lines, use the ending + * line in a response or continue reading. */ + + 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 */ + struct timeval interval; + fd_set rkeepfd; + fd_set readfd; + 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 *ftp = conn->proto.ftp; + struct timeval now = Curl_tvnow(); + + if (ftpcode) + *ftpcode = 0; /* 0 for errors */ + + FD_ZERO (&readfd); /* clear it */ + FD_SET (sockfd, &readfd); /* read socket */ + + /* get this in a backup variable to be able to restore it on each lap in the + select() loop */ + rkeepfd = readfd; + + ptr=buf; + line_start = buf; + + *nreadp=0; + perline=0; + + 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 = ftp->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(!ftp->cache) { + readfd = rkeepfd; /* set every lap */ + interval.tv_sec = 1; /* use 1 second timeout intervals */ + interval.tv_usec = 0; + + switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) { + case -1: /* select() error, stop reading */ + result = CURLE_RECV_ERROR; + failf(data, "FTP response aborted due to select() error: %d", errno); + 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(ftp->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, ftp->cache, (int)ftp->cache_size); + gotbytes = (int)ftp->cache_size; + free(ftp->cache); /* free the cache */ + ftp->cache = NULL; /* clear the pointer */ + ftp->cache_size = 0; /* zero the size just in case */ + } + else { + int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes); + if(res < 0) + /* EWOULDBLOCK */ + continue; /* go looping again */ + + 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, perline, conn->host.dispname); + + /* + * We pass all response-lines to the callback function registered + * for "headers". The response lines can be seen as a kind of + * headers. + */ + result = Curl_client_write(data, CLIENTWRITE_HEADER, + line_start, perline); + if(result) + return result; + +#define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \ + isdigit((int)line[2]) && (' ' == line[3])) + + if(perline>3 && lastline(line_start)) { + /* This is the end of the last line, copy the last + * line to the start of the buffer and zero terminate, + * for old times sake (and krb4)! */ + char *meow; + int n; + for(meow=line_start, n=0; meow<ptr; meow++, n++) + buf[n] = *meow; + *meow=0; /* zero terminate */ + keepon=FALSE; + line_start = ptr+1; /* advance pointer */ + i++; /* skip this before getting out */ + break; + } + perline=0; /* line starts over here */ + line_start = ptr+1; + } + } + if(!keepon && (i != gotbytes)) { + /* We found the end of the response lines, but we didn't parse the + full chunk of data we have read from the server. We therefore + need to store the rest of the data to be checked on the next + invoke as it may actually contain another end of response + already! Cleverly figured out by Eric Lavigne in December + 2001. */ + ftp->cache_size = gotbytes - i; + ftp->cache = (char *)malloc((int)ftp->cache_size); + if(ftp->cache) + memcpy(ftp->cache, line_start, (int)ftp->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; +} + +static const char *ftpauth[]= { + "SSL", "TLS", NULL +}; + +/* + * Curl_ftp_connect() should do everything that is to be considered a part of + * the connection phase. + */ +CURLcode Curl_ftp_connect(struct connectdata *conn) +{ + /* this is FTP and no proxy */ + ssize_t nread; + struct SessionHandle *data=conn->data; + char *buf = data->state.buffer; /* this is our buffer */ + struct FTP *ftp; + CURLcode result; + int ftpcode, try; + + ftp = (struct FTP *)malloc(sizeof(struct FTP)); + if(!ftp) + return CURLE_OUT_OF_MEMORY; + + memset(ftp, 0, sizeof(struct FTP)); + conn->proto.ftp = ftp; + + /* We always support persistant connections on ftp */ + conn->bits.close = FALSE; + + /* get some initial data into the ftp struct */ + ftp->bytecountp = &conn->bytecount; + + /* no need to duplicate them, this connectdata struct won't change */ + ftp->user = conn->user; + ftp->passwd = conn->passwd; + ftp->response_time = 3600; /* set default response time-out */ + +#ifndef CURL_DISABLE_HTTP + if (conn->bits.tunnel_proxy) { + /* We want "seamless" FTP operations through HTTP proxy tunnel */ + result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET, + conn->host.name, conn->remote_port); + if(CURLE_OK != result) + return result; + } +#endif /* CURL_DISABLE_HTTP */ + + if(conn->protocol & PROT_FTPS) { + /* FTPS is simply ftp with SSL for the control channel */ + /* now, perform the SSL initialization for this socket */ + result = Curl_SSLConnect(conn, FIRSTSOCKET); + if(result) + return result; + } + + /* The first thing we do is wait for the "220*" line: */ + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if(ftpcode != 220) { + failf(data, "This doesn't seem like a nice ftp-server response"); + return CURLE_FTP_WEIRD_SERVER_REPLY; + } + +#ifdef HAVE_KRB4 + /* if not anonymous login, try a secure login */ + if(data->set.krb4) { + + /* request data protection level (default is 'clear') */ + Curl_sec_request_prot(conn, "private"); + + /* We set private first as default, in case the line below fails to + set a valid level */ + Curl_sec_request_prot(conn, data->set.krb4_level); + + if(Curl_sec_login(conn) != 0) + infof(data, "Logging in with password in cleartext!\n"); + else + infof(data, "Authentication successful\n"); + } +#endif + + if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) { + /* we don't have a SSL/TLS connection, try a FTPS connection now */ + + for (try = 0; ftpauth[try]; try++) { + + FTPSENDF(conn, "AUTH %s", ftpauth[try]); + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + + if(result) + return result; + + /* 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)) { + result = Curl_SSLConnect(conn, FIRSTSOCKET); + if(result) + return result; + conn->protocol |= PROT_FTPS; + conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */ + break; + } + } + } + + /* send USER */ + FTPSENDF(conn, "USER %s", ftp->user?ftp->user:""); + + /* wait for feedback */ + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if(ftpcode == 530) { + /* 530 User ... access denied + (the server denies to log the specified user) */ + failf(data, "Access denied: %s", &buf[4]); + return CURLE_FTP_ACCESS_DENIED; + } + else if(ftpcode == 331) { + /* 331 Password required for ... + (the server requires to send the user's password too) */ + FTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:""); + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if(ftpcode == 530) { + /* 530 Login incorrect. + (the username and/or the password are incorrect) */ + failf(data, "the username and/or the password are incorrect"); + return CURLE_FTP_USER_PASSWORD_INCORRECT; + } + else if(ftpcode == 230) { + /* 230 User ... logged in. + (user successfully logged in) */ + + infof(data, "We have successfully logged in\n"); + } + else { + failf(data, "Odd return code after PASS"); + return CURLE_FTP_WEIRD_PASS_REPLY; + } + } + else if(buf[0] == '2') { + /* 230 User ... logged in. + (the user logged in without password) */ + infof(data, "We have successfully logged in\n"); + if (conn->ssl[FIRSTSOCKET].use) { +#ifdef HAVE_KRB4 + /* We are logged in with Kerberos, now set the requested protection + * level + */ + if(conn->sec_complete) + Curl_sec_set_protection_level(conn); + + /* We may need to issue a KAUTH here to have access to the files + * do it if user supplied a password + */ + if(conn->passwd && *conn->passwd) { + result = Curl_krb_kauth(conn); + if(result) + return result; + } +#endif + } + } + else { + failf(data, "Odd return code after USER"); + return CURLE_FTP_WEIRD_USER_REPLY; + } + + 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. + */ + FTPSENDF(conn, "PBSZ %d", 0); + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + /* 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) { + FTPSENDF(conn, "PROT %c", 'P'); + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if(ftpcode == 200) + /* We have enabled SSL for the data connection! */ + conn->ssl[SECONDARYSOCKET].use = TRUE; + + /* FTP servers typically responds with 500 if they decide to reject + our 'P' request */ + } + } + + /* send PWD to discover our entry point */ + FTPSENDF(conn, "PWD", NULL); + + /* wait for feedback */ + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if(ftpcode == 257) { + char *dir = (char *)malloc(nread+1); + char *store=dir; + char *ptr=&buf[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++; + } + ftp->entrypath =dir; /* remember this */ + infof(data, "Entry path is '%s'\n", ftp->entrypath); + } + else { + /* couldn't get the path */ + free(dir); + infof(data, "Failed to figure out path\n"); + } + + } + else { + /* We couldn't read the PWD response! */ + } + + return CURLE_OK; +} + +/*********************************************************************** + * + * Curl_ftp_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status) +{ + struct SessionHandle *data = conn->data; + struct FTP *ftp = conn->proto.ftp; + ssize_t nread; + int ftpcode; + CURLcode result=CURLE_OK; + + bool was_ctl_valid = ftp->ctl_valid; + + /* free the dir tree and file parts */ + freedirs(ftp); + + ftp->ctl_valid = FALSE; + + if(data->set.upload) { + if((-1 != data->set.infilesize) && + (data->set.infilesize != *ftp->bytecountp) && + !data->set.crlf) { + failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T + " out of %" FORMAT_OFF_T " bytes)", + *ftp->bytecountp, data->set.infilesize); + conn->bits.close = TRUE; /* close this connection since we don't + know what state this error leaves us in */ + return CURLE_PARTIAL_FILE; + } + } + else { + if((-1 != conn->size) && (conn->size != *ftp->bytecountp) && + (conn->maxdownload != *ftp->bytecountp)) { + failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes", + *ftp->bytecountp); + conn->bits.close = TRUE; /* close this connection since we don't + know what state this error leaves us in */ + return CURLE_PARTIAL_FILE; + } + else if(!ftp->dont_check && + !*ftp->bytecountp && + (conn->size>0)) { + /* We consider this an error, but there's no true FTP error received + why we need to continue to "read out" the server response too. + We don't want to leave a "waiting" server reply if we'll get told + to make a second request on this same connection! */ + failf(data, "No data was received!"); + result = CURLE_FTP_COULDNT_RETR_FILE; + } + } + + 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_ACCESS_DENIED: + /* the connection stays alive fine even though this happened */ + /* fall-through */ + case CURLE_OK: /* doesn't affect the control connection's status */ + ftp->ctl_valid = was_ctl_valid; + break; + default: /* by default, an error means the control connection is + wedged and should not be used anymore */ + ftp->ctl_valid = FALSE; + break; + } + +#ifdef HAVE_KRB4 + Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]); +#endif + /* shut down the socket to inform the server we're done */ + sclose(conn->sock[SECONDARYSOCKET]); + conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; + + if(!ftp->no_transfer && !status) { + /* 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. + */ + ftp->response_time = 60; /* give it only a minute for now */ + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + + ftp->response_time = 3600; /* set this back to one hour waits */ + + if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) { + failf(data, "control connection looks dead"); + return result; + } + + if(result) + return result; + + if(!ftp->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); + return CURLE_FTP_WRITE_ERROR; + } + } + } + + /* clear these for next connection */ + ftp->no_transfer = FALSE; + ftp->dont_check = FALSE; + + if (!result && conn->sec_conn) { /* 3rd party transfer */ + /* "done" with the secondary connection */ + result = Curl_ftp_done(conn->sec_conn, status); + } + + /* Send any post-transfer QUOTE strings? */ + if(!status && !result && data->set.postquote) + result = ftp_sendquote(conn, data->set.postquote); + + return result; +} + +/*********************************************************************** + * + * ftp_sendquote() + * + * Where a 'quote' means a list of custom commands to send to the server. + * The quote list is passed as an argument. + */ + +static +CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote) +{ + struct curl_slist *item; + ssize_t nread; + int ftpcode; + CURLcode result; + + item = quote; + while (item) { + if (item->data) { + FTPSENDF(conn, "%s", item->data); + + 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_getfiletime() + * + * Get the timestamp of the given file. + */ +static +CURLcode ftp_getfiletime(struct connectdata *conn, char *file) +{ + CURLcode result; + int ftpcode; /* for ftp status */ + ssize_t nread; + char *buf = conn->data->state.buffer; + + /* we have requested to get the modified-time of the file, this is yet + again a grey area as the MDTM is not kosher RFC959 */ + FTPSENDF(conn, "MDTM %s", file); + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + 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; + 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); + 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: */ + conn->data->info.filetime = curl_getdate(buf, &secs); + } + } + break; + default: + infof(conn->data, "unsupported MDTM reply format\n"); + break; + case 550: /* "No such file or directory" */ + failf(conn->data, "Given file does not exist"); + result = CURLE_FTP_COULDNT_RETR_FILE; + break; + } + return result; +} + +/*********************************************************************** + * + * ftp_transfertype() + * + * Set transfer type. We only deal with ASCII or BINARY so this function + * sets one of them. + */ +static CURLcode ftp_transfertype(struct connectdata *conn, + bool ascii) +{ + struct SessionHandle *data = conn->data; + int ftpcode; + ssize_t nread; + CURLcode result; + + FTPSENDF(conn, "TYPE %s", ascii?"A":"I"); + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if(ftpcode != 200) { + failf(data, "Couldn't set %s mode", + ascii?"ASCII":"binary"); + return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY; + } + + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_getsize() + * + * Returns the file size (in bytes) of the given remote file. + */ + +static +CURLcode ftp_getsize(struct connectdata *conn, char *file, + curl_off_t *size) +{ + struct SessionHandle *data = conn->data; + int ftpcode; + ssize_t nread; + char *buf=data->state.buffer; + CURLcode result; + + FTPSENDF(conn, "SIZE %s", file); + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if(ftpcode == 213) { + /* get the size from the ascii string: */ + *size = curlx_strtoofft(buf+4, NULL, 0); + } + else + return CURLE_FTP_COULDNT_GET_SIZE; + + return CURLE_OK; +} + +/*************************************************************************** + * + * ftp_pasv_verbose() + * + * This function only outputs some informationals about this second connection + * when we've issued a PASV command before and thus we have connected to a + * possibly new IP address. + * + */ +static void +ftp_pasv_verbose(struct connectdata *conn, + Curl_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); +} + +/*********************************************************************** + * + * ftp_use_port() + * + * Send the proper PORT command. PORT is the ftp client's way of telling the + * server that *WE* open a port that we listen on an awaits the server to + * connect to. This is the opposite of PASV. + */ + +static +CURLcode ftp_use_port(struct connectdata *conn) +{ + struct SessionHandle *data=conn->data; + curl_socket_t portsock; + ssize_t nread; + int ftpcode; /* receive FTP response codes in this */ + CURLcode result; + +#ifdef ENABLE_IPV6 + /****************************************************************** + * + * Here's a piece of IPv6-specific code coming up + * + */ + + struct addrinfo hints, *res, *ai; + struct sockaddr_storage ss; + socklen_t sslen; + char hbuf[NI_MAXHOST]; + + struct sockaddr *sa=(struct sockaddr *)&ss; + unsigned char *ap; + unsigned char *pp; + char portmsgbuf[1024], tmp[1024]; + + const char *mode[] = { "EPRT", "LPRT", "PORT", NULL }; + char **modep; + int rc; + int error; + + /* + * we should use Curl_if2ip? given pickiness of recent ftpd, + * I believe we should use the same address as the control connection. + */ + sslen = sizeof(ss); + rc = getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen); + if(rc < 0) { + failf(data, "getsockname() returned %d\n", rc); + return CURLE_FTP_PORT_FAILED; + } + + 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; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = sa->sa_family; + /*hints.ai_family = ss.ss_family; + this way can be used if sockaddr_storage is properly defined, as glibc + 2.1.X doesn't do*/ + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + rc = getaddrinfo(hbuf, NULL, &hints, &res); + if(rc) { + failf(data, "getaddrinfo() returned %d\n", rc); + return CURLE_FTP_PORT_FAILED; + } + + 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 = hints.ai_socktype; + + portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (portsock == CURL_SOCKET_BAD) { + error = Curl_ourerrno(); + continue; + } + + if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) { + error = Curl_ourerrno(); + sclose(portsock); + portsock = CURL_SOCKET_BAD; + continue; + } + + if (listen(portsock, 1) < 0) { + error = Curl_ourerrno(); + sclose(portsock); + portsock = CURL_SOCKET_BAD; + continue; + } + + break; + } + freeaddrinfo(res); + if (portsock == CURL_SOCKET_BAD) { + failf(data, "%s", Curl_strerror(conn,error)); + return CURLE_FTP_PORT_FAILED; + } + + sslen = sizeof(ss); + if (getsockname(portsock, sa, &sslen) < 0) { + failf(data, "%s", Curl_strerror(conn,Curl_ourerrno())); + return CURLE_FTP_PORT_FAILED; + } + + for (modep = (char **)(data->set.ftp_use_eprt?&mode[0]:&mode[2]); + modep && *modep; modep++) { + int lprtaf, eprtaf; + int alen=0, plen=0; + + switch (sa->sa_family) { + case AF_INET: + ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr; + alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr); + pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port; + plen = sizeof(((struct sockaddr_in *)&ss)->sin_port); + lprtaf = 4; + eprtaf = 1; + break; + case AF_INET6: + ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr; + alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr); + pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port; + plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port); + lprtaf = 6; + eprtaf = 2; + break; + default: + ap = pp = NULL; + lprtaf = eprtaf = -1; + break; + } + + if (strcmp(*modep, "EPRT") == 0) { + if (eprtaf < 0) + continue; + if (getnameinfo((struct sockaddr *)&ss, sslen, + portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), + NIFLAGS)) + continue; + + /* do not transmit IPv6 scope identifier to the wire */ + if (sa->sa_family == AF_INET6) { + char *q = strchr(portmsgbuf, '%'); + if (q) + *q = '\0'; + } + + result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf, + portmsgbuf, tmp); + if(result) + return result; + } + else if (strcmp(*modep, "LPRT") == 0 || + strcmp(*modep, "PORT") == 0) { + int i; + + if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0) + continue; + if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET) + continue; + + portmsgbuf[0] = '\0'; + if (strcmp(*modep, "LPRT") == 0) { + snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen); + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= + sizeof(portmsgbuf)) { + continue; + } + } + + for (i = 0; i < alen; i++) { + if (portmsgbuf[0]) + snprintf(tmp, sizeof(tmp), ",%u", ap[i]); + else + snprintf(tmp, sizeof(tmp), "%u", ap[i]); + + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= + sizeof(portmsgbuf)) { + continue; + } + } + + if (strcmp(*modep, "LPRT") == 0) { + snprintf(tmp, sizeof(tmp), ",%d", plen); + + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) + continue; + } + + for (i = 0; i < plen; i++) { + snprintf(tmp, sizeof(tmp), ",%u", pp[i]); + + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= + sizeof(portmsgbuf)) { + continue; + } + } + + result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf); + if(result) + return result; + } + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if (ftpcode != 200) { + continue; + } + else + break; + } + + if (!*modep) { + sclose(portsock); + failf(data, "PORT command attempts failed"); + return CURLE_FTP_PORT_FAILED; + } + /* we set the secondary socket variable to this for now, it + is only so that the cleanup function will close it in case + we fail before the true secondary stuff is made */ + conn->sock[SECONDARYSOCKET] = portsock; + +#else + /****************************************************************** + * + * Here's a piece of IPv4-specific code coming up + * + */ + struct sockaddr_in sa; + unsigned short porttouse; + char myhost[256] = ""; + bool sa_filled_in = FALSE; + Curl_addrinfo *addr = NULL; + unsigned short ip[4]; + + 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, myhost, 0, &h); + if(rc == CURLRESOLV_PENDING) + rc = Curl_wait_for_resolv(conn, &h); + (void)rc; + if(h) { + addr = 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) */ + } /* strlen */ + } /* CURL_INADDR_NONE */ + } /* data->set.ftpport */ + + if(!addr) { + /* pick a suitable default here */ + +#ifdef __hpux + int sslen; +#else + socklen_t sslen; +#endif + + sslen = sizeof(sa); + if (getsockname(conn->sock[FIRSTSOCKET], + (struct sockaddr *)&sa, &sslen) < 0) { + failf(data, "getsockname() failed"); + return CURLE_FTP_PORT_FAILED; + } + + 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) { + socklen_t size; + + /* we set the secondary socket variable to this for now, it + is only so that the cleanup function will close it in case + we fail before the true secondary stuff is made */ + conn->sock[SECONDARYSOCKET] = portsock; + + if(!sa_filled_in) { + memcpy(&sa, addr->ai_addr, sizeof(sa)); + sa.sin_addr.s_addr = INADDR_ANY; + } + + sa.sin_port = 0; + size = sizeof(sa); + + if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) { + /* we succeeded to bind */ + struct sockaddr_in add; +#ifdef __hpux + int socksize = sizeof(add); +#else + socklen_t socksize = sizeof(add); +#endif + + if(getsockname(portsock, (struct sockaddr *) &add, + &socksize)<0) { + failf(data, "getsockname() failed"); + return CURLE_FTP_PORT_FAILED; + } + porttouse = ntohs(add.sin_port); + + if ( listen(portsock, 1) < 0 ) { + failf(data, "listen(2) failed on socket"); + 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, "could'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_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d", + ip[0], ip[1], ip[2], ip[3], + porttouse >> 8, + porttouse & 255); + if(result) + return result; + + } + else + return CURLE_FTP_PORT_FAILED; + + Curl_freeaddrinfo(addr); + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if(ftpcode != 200) { + failf(data, "Server does not grok PORT, try without it!"); + return CURLE_FTP_PORT_FAILED; + } +#endif /* end of ipv4-specific code */ + + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_use_pasv() + * + * Send the PASV command. PASV is the ftp client's way of asking the server to + * open a second port that we can connect to (for the data transfer). This is + * the opposite of PORT. + */ + +static +CURLcode ftp_use_pasv(struct connectdata *conn, + bool *connected) +{ + struct SessionHandle *data = conn->data; + ssize_t nread; + char *buf = data->state.buffer; /* this is our buffer */ + int ftpcode; /* receive FTP response codes in this */ + CURLcode result; + struct Curl_dns_entry *addr=NULL; + Curl_addrinfo *conninfo; + int rc; + + /* + 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 results[] = { 229, 227, 0 }; + int modeoff; + unsigned short connectport; /* the local port connect() should use! */ + unsigned short newport=0; /* remote port, not necessary the local one */ + + /* newhost must be able to hold a full IP-style address in ASCII, which + in the IPv6 case means 5*8-1 = 39 letters */ + char newhost[48]; + char *newhostp=NULL; + + for (modeoff = (data->set.ftp_use_epsv?0:1); + mode[modeoff]; modeoff++) { + result = Curl_ftpsendf(conn, "%s", mode[modeoff]); + if(result) + return result; + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + if (ftpcode == results[modeoff]) + break; + } + + if (!mode[modeoff]) { + failf(data, "Odd return code after PASV"); + return CURLE_FTP_WEIRD_PASV_REPLY; + } + else if (227 == results[modeoff]) { + int ip[4]; + int port[2]; + char *str=buf; + + /* + * New 227-parser June 3rd 1999. + * It now scans for a sequence of six comma-separated numbers and + * will take them as IP+port indicators. + * + * Found reply-strings include: + * "227 Entering Passive Mode (127,0,0,1,4,51)" + * "227 Data transfer will passively listen to 127,0,0,1,4,51" + * "227 Entering passive mode. 127,0,0,1,4,51" + */ + + while(*str) { + if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d", + &ip[0], &ip[1], &ip[2], &ip[3], + &port[0], &port[1])) + break; + str++; + } + + if(!*str) { + failf(data, "Couldn't interpret this 227-reply: %s", buf); + return CURLE_FTP_WEIRD_227_FORMAT; + } + + snprintf(newhost, sizeof(newhost), + "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + newhostp = newhost; + newport = (port[0]<<8) + port[1]; + } + else if (229 == results[modeoff]) { + char *ptr = strchr(buf, '('); + if(ptr) { + unsigned int num; + char separator[4]; + ptr++; + if(5 == sscanf(ptr, "%c%c%c%u%c", + &separator[0], + &separator[1], + &separator[2], + &num, + &separator[3])) { + 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; + + /* we should use the same host we already are connected to */ + newhostp = conn->host.name; + } + } + else + ptr=NULL; + } + if(!ptr) { + failf(data, "Weirdly formatted EPSV reply"); + return CURLE_FTP_WEIRD_PASV_REPLY; + } + } + else + return CURLE_FTP_CANT_RECONNECT; + + if(data->change.proxy && *data->change.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) + 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, newhostp, newport, &addr); + if(rc == CURLRESOLV_PENDING) + rc = Curl_wait_for_resolv(conn, &addr); + + if(!addr) { + failf(data, "Can't resolve new host %s:%d", newhostp, 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) + return result; + + /* + * 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, newhostp, connectport); + +#ifndef CURL_DISABLE_HTTP + if(conn->bits.tunnel_proxy) { + /* We want "seamless" FTP operations through HTTP proxy tunnel */ + result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET, + newhostp, newport); + if(CURLE_OK != result) + return result; + } +#endif /* CURL_DISABLE_HTTP */ + + (void)rc; + return CURLE_OK; +} + +/* + * Curl_ftp_nextconnect() + * + * This function shall be called when the second FTP connection has been + * established and is confirmed connected. + */ + +CURLcode Curl_ftp_nextconnect(struct connectdata *conn) +{ + struct SessionHandle *data=conn->data; + char *buf = data->state.buffer; /* this is our buffer */ + CURLcode result; + ssize_t nread; + int ftpcode; /* for ftp status */ + + /* the ftp struct is already inited in Curl_ftp_connect() */ + struct FTP *ftp = conn->proto.ftp; + curl_off_t *bytecountp = ftp->bytecountp; + + if(data->set.upload) { + + /* Set type to binary (unless specified ASCII) */ + result = ftp_transfertype(conn, data->set.ftp_ascii); + if(result) + return result; + + /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/ + if(data->set.prequote) { + if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK) + return result; + } + + if(conn->resume_from) { + /* we're about to continue the uploading of a file */ + /* 1. get already existing file's size. We use the SIZE + command for this which may not exist in the server! + The SIZE command is not in RFC959. */ + + /* 2. This used to set REST. But since we can do append, we + don't another ftp command. We just skip the source file + offset and then we APPEND the rest on the file instead */ + + /* 3. pass file-size number of bytes in the source file */ + /* 4. lower the infilesize counter */ + /* => transfer as usual */ + + if(conn->resume_from < 0 ) { + /* we could've got a specified offset from the command line, + but now we know we didn't */ + curl_off_t gottensize; + + if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) { + failf(data, "Couldn't get remote file size"); + return CURLE_FTP_COULDNT_GET_SIZE; + } + conn->resume_from = gottensize; + } + + if(conn->resume_from) { + /* do we still game? */ + curl_off_t passed=0; + /* enable append instead */ + data->set.ftp_append = 1; + + /* Now, let's read off the proper amount of bytes from the + input. If we knew it was a proper file we could've just + fseek()ed but we only have a stream here */ + do { + curl_off_t readthisamountnow = (conn->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 != conn->resume_from); + + /* now, decrease the size of the read */ + if(data->set.infilesize>0) { + data->set.infilesize -= conn->resume_from; + + if(data->set.infilesize <= 0) { + infof(data, "File already completely uploaded\n"); + + /* no data to transfer */ + result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + (void)result; + + /* 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; + + return CURLE_OK; + } + } + /* we've passed, proceed as normal */ + } + } + + /* Send everything on data->state.in to the socket */ + if(data->set.ftp_append) { + /* we append onto the file instead of rewriting it */ + FTPSENDF(conn, "APPE %s", ftp->file); + } + else { + FTPSENDF(conn, "STOR %s", ftp->file); + } + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if(ftpcode>=400) { + failf(data, "Failed FTP upload:%s", buf+3); + /* oops, we never close the sockets! */ + return CURLE_FTP_COULDNT_STOR_FILE; + } + + if(data->set.ftp_use_port) { + /* PORT means we are now awaiting the server to connect to us. */ + result = AllowServerConnect(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_SSLConnect(conn, SECONDARYSOCKET); + if(result) + return result; + } + + *bytecountp=0; + + /* When we know we're uploading a specified file, we can get the file + size prior to the actual upload. */ + + Curl_pgrsSetUploadSize(data, data->set.infilesize); + + result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */ + SECONDARYSOCKET, bytecountp); + if(result) + return result; + + } + else if(!conn->bits.no_body) { + /* Retrieve file or directory */ + bool dirlist=FALSE; + curl_off_t downloadsize=-1; + + if(conn->bits.use_range && conn->range) { + curl_off_t from, to; + curl_off_t totalsize; + char *ptr; + char *ptr2; + + from=curlx_strtoofft(conn->range, &ptr, 0); + while(ptr && *ptr && (isspace((int)*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 - */ + conn->resume_from = from; + infof(data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n", from); + } + else if(from < 0) { + /* -Y */ + totalsize = -from; + conn->maxdownload = -from; + conn->resume_from = from; + infof(data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n", totalsize); + } + else { + /* X-Y */ + totalsize = to-from; + conn->maxdownload = totalsize+1; /* include the last mentioned byte */ + conn->resume_from = from; + infof(data, "FTP RANGE from %" FORMAT_OFF_T + " getting %" FORMAT_OFF_T " bytes\n", from, conn->maxdownload); + } + infof(data, "range-download from %" FORMAT_OFF_T + " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n", + from, to, conn->maxdownload); + ftp->dont_check = TRUE; /* dont check for successful transfer */ + } + + if((data->set.ftp_list_only) || !ftp->file) { + /* The specified path ends with a slash, and therefore we think this + is a directory that is requested, use LIST. But before that we + need to set ASCII transfer mode. */ + dirlist = TRUE; + + /* Set type to ASCII */ + result = ftp_transfertype(conn, TRUE /* ASCII enforced */); + if(result) + return result; + + /* if this output is to be machine-parsed, the NLST command will be + better used since the LIST command output is not specified or + standard in any way */ + + FTPSENDF(conn, "%s", + data->set.customrequest?data->set.customrequest: + (data->set.ftp_list_only?"NLST":"LIST")); + } + else { + curl_off_t foundsize; + + /* Set type to binary (unless specified ASCII) */ + result = ftp_transfertype(conn, data->set.ftp_ascii); + if(result) + return result; + + /* Send any PREQUOTE strings after transfer type is set? */ + if(data->set.prequote) { + if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK) + return result; + } + + /* Attempt to get the size, it'll be useful in some cases: for resumed + downloads and when talking to servers that don't give away the size + in the RETR response line. */ + result = ftp_getsize(conn, ftp->file, &foundsize); + if(CURLE_OK == result) { + if (data->set.max_filesize && foundsize > data->set.max_filesize) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + downloadsize = foundsize; + } + + if(conn->resume_from) { + + /* Daniel: (August 4, 1999) + * + * We start with trying to use the SIZE command to figure out the size + * of the file we're gonna get. If we can get the size, this is by far + * the best way to know if we're trying to resume beyond the EOF. + * + * Daniel, November 28, 2001. We *always* get the size on downloads + * now, so it is done before this even when not doing resumes. I saved + * the comment above for nostalgical reasons! ;-) + */ + if(CURLE_OK != result) { + infof(data, "ftp server doesn't support SIZE\n"); + /* We couldn't get the size and therefore we can't know if there + really is a part of the file left to get, although the server + will just close the connection when we start the connection so it + won't cause us any harm, just not make us exit as nicely. */ + } + else { + /* We got a file size report, so we check that there actually is a + part of the file left to get, or else we go home. */ + if(conn->resume_from< 0) { + /* We're supposed to download the last abs(from) bytes */ + if(foundsize < -conn->resume_from) { + failf(data, "Offset (%" FORMAT_OFF_T + ") was beyond file size (%" FORMAT_OFF_T ")", + conn->resume_from, foundsize); + return CURLE_FTP_BAD_DOWNLOAD_RESUME; + } + /* convert to size to download */ + downloadsize = -conn->resume_from; + /* download from where? */ + conn->resume_from = foundsize - downloadsize; + } + else { + if(foundsize < conn->resume_from) { + failf(data, "Offset (%" FORMAT_OFF_T + ") was beyond file size (%" FORMAT_OFF_T ")", + conn->resume_from, foundsize); + return CURLE_FTP_BAD_DOWNLOAD_RESUME; + } + /* Now store the number of bytes we are expected to download */ + downloadsize = foundsize-conn->resume_from; + } + } + + if (downloadsize == 0) { + /* no data to transfer */ + result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + (void)result; + 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; + return CURLE_OK; + } + + /* Set resume file transfer offset */ + infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T + "\n", + conn->resume_from); + + FTPSENDF(conn, "REST %" FORMAT_OFF_T, conn->resume_from); + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + if(ftpcode != 350) { + failf(data, "Couldn't use REST: %s", buf+4); + return CURLE_FTP_COULDNT_USE_REST; + } + } + + FTPSENDF(conn, "RETR %s", ftp->file); + } + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + 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(!dirlist && + !data->set.ftp_ascii && + (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=bytes-buf; + /* this is a hint there is size information in there! ;-) */ + while(--in) { + /* scan for the parenthesis and break there */ + if('(' == *bytes) + break; + /* if only skip digits, or else we're in deep trouble */ + if(!isdigit((int)*bytes)) { + bytes=NULL; + break; + } + /* one more estep backwards */ + bytes--; + } + /* only if we have nothing but digits: */ + if(bytes++) { + /* get the number! */ + size = curlx_strtoofft(bytes, NULL, 0); + } + + } + } + else if(downloadsize > -1) + size = downloadsize; + + if(data->set.ftp_use_port) { + 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_SSLConnect(conn, SECONDARYSOCKET); + if(result) + return result; + } + + if(size > conn->maxdownload && conn->maxdownload > 0) + size = conn->size = conn->maxdownload; + + infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size); + + /* FTP download: */ + result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE, + bytecountp, + -1, NULL); /* no upload here */ + if(result) + return result; + } + else { + if(dirlist && (ftpcode == 450)) { + /* simply no matching files */ + ftp->no_transfer = TRUE; /* don't think we should download anything */ + } + else { + failf(data, "%s", buf+4); + return CURLE_FTP_COULDNT_RETR_FILE; + } + } + + } + /* end of transfer */ + + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_perform() + * + * This is the actual DO function for FTP. Get a file/directory according to + * the options previously setup. + */ + +static +CURLcode ftp_perform(struct connectdata *conn, + bool *connected) /* for the TCP connect status after + PASV / PORT */ +{ + /* this is FTP and no proxy */ + CURLcode result=CURLE_OK; + struct SessionHandle *data=conn->data; + char *buf = data->state.buffer; /* this is our buffer */ + + /* the ftp struct is already inited in Curl_ftp_connect() */ + struct FTP *ftp = conn->proto.ftp; + + /* Send any QUOTE strings? */ + if(data->set.quote) { + if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) + return result; + } + + /* This is a re-used connection. Since we change directory to where the + transfer is taking place, we must now get back to the original dir + where we ended up after login: */ + if (conn->bits.reuse && ftp->entrypath) { + if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK) + return result; + } + + { + int i; /* counter for loop */ + for (i=0; i < ftp->dirdepth; i++) { + /* RFC 1738 says empty components should be respected too, but + that is plain stupid since CWD can't be used with an empty argument */ + if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK) + return result; + } + } + + /* Requested time of file or time-depended transfer? */ + if((data->set.get_filetime || data->set.timecondition) && + ftp->file) { + result = ftp_getfiletime(conn, ftp->file); + switch( result ) + { + case CURLE_FTP_COULDNT_RETR_FILE: + case CURLE_OK: + 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 */ + 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 */ + return CURLE_OK; + } + break; + } /* switch */ + } + else { + infof(data, "Skipping time comparison\n"); + } + } + break; + default: + return result; + } /* switch */ + } + + /* 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) { + /* 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! */ + curl_off_t filesize; + ssize_t nread; + int ftpcode; + + ftp->no_transfer = TRUE; /* this means no actual transfer is made */ + + /* Some servers return different sizes for different modes, and thus we + must set the proper type before we check the size */ + result = ftp_transfertype(conn, data->set.ftp_ascii); + if(result) + return result; + + /* failing to get size is not a serious error */ + result = ftp_getsize(conn, ftp->file, &filesize); + + if(CURLE_OK == result) { + snprintf(buf, sizeof(data->state.buffer), + "Content-Length: %" FORMAT_OFF_T "\r\n", filesize); + result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + if(result) + return result; + } + + /* Determine if server can respond to REST command and therefore + whether it can do a range */ + FTPSENDF(conn, "REST 0", NULL); + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + + if ((CURLE_OK == result) && (ftpcode == 350)) { + result = Curl_client_write(data, CLIENTWRITE_BOTH, + (char *)"Accept-ranges: bytes\r\n", 0); + if(result) + return result; + } + + /* If we asked for a time of the file and we actually got one as + well, we "emulate" a HTTP-style header in our output. */ + +#ifdef HAVE_STRFTIME + if(data->set.get_filetime && (data->info.filetime>=0) ) { + struct tm *tm; + time_t cuClock = (time_t)data->info.filetime; +#ifdef HAVE_GMTIME_R + struct tm buffer; + tm = (struct tm *)gmtime_r(&cuClock, &buffer); +#else + tm = gmtime(&cuClock); +#endif + /* format: "Tue, 15 Nov 1994 12:45:26" */ + strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n", + tm); + result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + if(result) + return result; + } +#endif + + return CURLE_OK; + } + + if(conn->bits.no_body) + /* doesn't really transfer any data */ + ftp->no_transfer = TRUE; + /* Get us a second connection up and connected */ + else if(data->set.ftp_use_port) { + /* We have chosen to use the PORT command */ + result = ftp_use_port(conn); + if(CURLE_OK == result) { + /* we have the data connection ready */ + infof(data, "Ordered connect of the data stream with PORT!\n"); + *connected = TRUE; /* mark us "still connected" */ + } + } + else { + /* We have chosen (this is default) to use the PASV command */ + result = ftp_use_pasv(conn, connected); + if(CURLE_OK == result && *connected) + infof(data, "Connected the data stream with PASV!\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) +{ + CURLcode retcode; + + if (conn->sec_conn) /* 3rd party transfer */ + retcode = ftp_3rdparty(conn); + else + retcode = ftp_regular_transfer(conn); + + return retcode; +} + +/*********************************************************************** + * + * Curl_ftpsendf() + * + * Sends the formated string as a ftp command to a ftp server + * + * NOTE: we build the command in a fixed-length buffer, which sets length + * restrictions on the command! + */ +CURLcode Curl_ftpsendf(struct connectdata *conn, + const char *fmt, ...) +{ + ssize_t bytes_written; + char s[256]; + size_t write_len; + char *sptr=s; + CURLcode res; + + 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); + + 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, bytes_written, conn->host.dispname); + + 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) +{ + ssize_t nread; + int ftpcode; + CURLcode ret = CURLE_OK; + + if(conn->proto.ftp->ctl_valid) { + ret = Curl_ftpsendf(conn, "%s", "QUIT"); + if(CURLE_OK == ret) + ret = Curl_GetFTPResponse(&nread, conn, &ftpcode); + } + + return ret; +} + +/*********************************************************************** + * + * Curl_ftp_disconnect() + * + * Disconnect from an FTP server. Cleanup protocol-specific per-connection + * resources + */ +CURLcode Curl_ftp_disconnect(struct connectdata *conn) +{ + struct FTP *ftp= conn->proto.ftp; + + /* 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(ftp) { + (void)ftp_quit(conn); /* ignore errors on the QUIT */ + + if(ftp->entrypath) + free(ftp->entrypath); + if(ftp->cache) { + free(ftp->cache); + ftp->cache = NULL; + } + freedirs(ftp); + } + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_mkd() + * + * Makes a directory on the FTP server. + * + * Calls failf() + */ +static CURLcode ftp_mkd(struct connectdata *conn, char *path) +{ + CURLcode result; + int ftpcode; /* for ftp status */ + ssize_t nread; + + /* Create a directory on the remote server */ + FTPSENDF(conn, "MKD %s", path); + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(result) + return result; + + switch(ftpcode) { + case 257: + /* success! */ + infof( conn->data , "Created remote directory %s\n" , path ); + break; + case 550: + failf(conn->data, "Permission denied to make directory %s", path); + result = CURLE_FTP_ACCESS_DENIED; + break; + default: + failf(conn->data, "unrecognized MKD response: %d", ftpcode ); + result = CURLE_FTP_ACCESS_DENIED; + break; + } + return result; +} + +/*********************************************************************** + * + * ftp_cwd() + * + * Send 'CWD' to the remote server to Change Working Directory. It is the ftp + * version of the unix 'cd' command. This function is only called from the + * ftp_cwd_and_mkd() function these days. + * + * This function does NOT call failf(). + */ +static +CURLcode ftp_cwd(struct connectdata *conn, char *path) +{ + ssize_t nread; + int ftpcode; + CURLcode result; + + FTPSENDF(conn, "CWD %s", path); + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if (!result) { + /* According to RFC959, CWD is supposed to return 250 on success, but + there seem to be non-compliant FTP servers out there that return 200, + so we accept any '2xy' code here. */ + if (ftpcode/100 != 2) + result = CURLE_FTP_ACCESS_DENIED; + } + + return result; +} + +/*********************************************************************** + * + * ftp_cwd_and_mkd() + * + * Change to the given directory. If the directory is not present, and we + * have been told to allow it, then create the directory and cd to it. + * + */ +static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path) +{ + CURLcode result; + + result = ftp_cwd(conn, path); + if (result) { + if(conn->data->set.ftp_create_missing_dirs) { + result = ftp_mkd(conn, path); + if (result) + /* ftp_mkd() calls failf() itself */ + return result; + result = ftp_cwd(conn, path); + } + if(result) + failf(conn->data, "Couldn't cd to %s", path); + } + return result; +} + + + +/*********************************************************************** + * + * ftp_3rdparty_pretransfer() + * + * Preparation for 3rd party transfer. + * + */ +static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn) +{ + CURLcode result; + struct SessionHandle *data = conn->data; + struct connectdata *sec_conn = conn->sec_conn; + + /* sets transfer type */ + result = ftp_transfertype(conn, data->set.ftp_ascii); + if (result) + return result; + + result = ftp_transfertype(sec_conn, data->set.ftp_ascii); + if (result) + return result; + + /* Send any PREQUOTE strings after transfer type is set? */ + if (data->set.source_prequote) { + /* sends command(s) to source server before file transfer */ + result = ftp_sendquote(sec_conn, data->set.source_prequote); + } + if (!result && data->set.prequote) + result = ftp_sendquote(conn, data->set.prequote); + + return result; +} + + + +/*********************************************************************** + * + * ftp_3rdparty_transfer() + * + * Performs 3rd party transfer. + * + */ +static CURLcode ftp_3rdparty_transfer(struct connectdata *conn) +{ + CURLcode result; + ssize_t nread; + int ftpcode, ip[4], port[2]; + struct SessionHandle *data = conn->data; + struct connectdata *sec_conn = conn->sec_conn; + char *buf = data->state.buffer; /* this is our buffer */ + char *str = buf; + char pasv_port[50]; + const char *stor_cmd; + struct connectdata *pasv_conn; + struct connectdata *port_conn; + + if (data->set.pasvHost == CURL_TARGET_PASV) { + pasv_conn = conn; + port_conn = sec_conn; + } + else { + pasv_conn = sec_conn; + port_conn = conn; + } + + /* sets the passive mode */ + FTPSENDF(pasv_conn, "%s", "PASV"); + result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode); + if (result) return result; + if (ftpcode != 227) { + failf(data, "Odd return code after PASV:%s", buf + 3); + return CURLE_FTP_WEIRD_PASV_REPLY; + } + + 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(pasv_conn->data, "Couldn't interpret this 227-reply: %s", buf); + return CURLE_FTP_WEIRD_227_FORMAT; + } + + snprintf(pasv_port, sizeof(pasv_port), "%d,%d,%d,%d,%d,%d", ip[0], ip[1], + ip[2], ip[3], port[0], port[1]); + + /* sets data connection between remote hosts */ + FTPSENDF(port_conn, "PORT %s", pasv_port); + result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode); + if (result) + return result; + + if (ftpcode != 200) { + failf(data, "PORT command attempts failed:%s", buf + 3); + return CURLE_FTP_PORT_FAILED; + } + + /* we might append onto the file instead of overwriting it */ + stor_cmd = data->set.ftp_append?"APPE":"STOR"; + + /* transfers file between remote hosts */ + FTPSENDF(sec_conn, "RETR %s", data->set.source_path); + + if(data->set.pasvHost == CURL_TARGET_PASV) { + + result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode); + if (result) + return result; + + if (ftpcode != 150) { + failf(data, "Failed RETR: %s", buf + 4); + return CURLE_FTP_COULDNT_RETR_FILE; + } + + result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path); + if(CURLE_OK == result) + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if (result) + return result; + + if (ftpcode != 150) { + failf(data, "Failed FTP upload: %s", buf + 4); + return CURLE_FTP_COULDNT_STOR_FILE; + } + + } + else { + + result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path); + if(CURLE_OK == result) + result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode); + if (result) + return result; + + if (ftpcode != 150) { + failf(data, "Failed FTP upload: %s", buf + 4); + return CURLE_FTP_COULDNT_STOR_FILE; + } + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if (result) + return result; + + if (ftpcode != 150) { + failf(data, "Failed FTP upload: %s", buf + 4); + return CURLE_FTP_COULDNT_STOR_FILE; + } + } + + return CURLE_OK; +} + + + +/*********************************************************************** + * + * ftp_regular_transfer() + * + * The input argument is already checked for validity. + * Performs a regular transfer between local and remote hosts. + * + * 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) +{ + CURLcode retcode=CURLE_OK; + bool connected=0; + struct SessionHandle *data = conn->data; + struct FTP *ftp; + + char *slash_pos; /* position of the first '/' char in curpos */ + char *cur_pos=conn->path; /* current position in ppath. point at the begin + of next path component */ + + /* the ftp struct is already inited in ftp_connect() */ + ftp = conn->proto.ftp; + ftp->ctl_valid = FALSE; + conn->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); + + ftp->dirdepth = 0; + ftp->diralloc = 5; /* default dir depth to allocate */ + ftp->dirs = (char **)malloc(ftp->diralloc * sizeof(ftp->dirs[0])); + if(!ftp->dirs) + return CURLE_OUT_OF_MEMORY; + ftp->dirs[0] = NULL; /* to start with */ + + /* parse the URL path into separate path components */ + while((slash_pos=strchr(cur_pos, '/'))) { + /* 1 or 0 to indicate absolute directory */ + bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->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); + ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len); + + if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */ + failf(data, "no memory"); + freedirs(ftp); + return CURLE_OUT_OF_MEMORY; + } + } + 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(++ftp->dirdepth >= ftp->diralloc) { + /* enlarge array */ + char **bigger; + ftp->diralloc *= 2; /* double the size each time */ + bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0])); + if(!bigger) { + freedirs(ftp); + return CURLE_OUT_OF_MEMORY; + } + ftp->dirs = (char **)bigger; + } + } + } + + ftp->file = cur_pos; /* the rest is the file name */ + + if(*ftp->file) { + ftp->file = curl_unescape(ftp->file, 0); + if(NULL == ftp->file) { + freedirs(ftp); + failf(data, "no memory"); + return CURLE_OUT_OF_MEMORY; + } + } + else + ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL + pointer */ + + retcode = ftp_perform(conn, &connected); + + if(CURLE_OK == retcode) { + if(connected) + retcode = Curl_ftp_nextconnect(conn); + + if(retcode && (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; + } + + if(ftp->no_transfer) + /* no data to transfer */ + retcode=Curl_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; + } + else + freedirs(ftp); + + ftp->ctl_valid = TRUE; /* seems good */ + + return retcode; +} + + + +/*********************************************************************** + * + * ftp_3rdparty() + * + * The input argument is already checked for validity. + * Performs a 3rd party transfer between two remote hosts. + */ +static CURLcode ftp_3rdparty(struct connectdata *conn) +{ + CURLcode retcode; + + conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE; + conn->size = conn->sec_conn->size = -1; + + retcode = ftp_3rdparty_pretransfer(conn); + if (!retcode) + retcode = ftp_3rdparty_transfer(conn); + + return retcode; +} + +#endif /* CURL_DISABLE_FTP */ diff --git a/Utilities/cmcurl/ftp.h b/Utilities/cmcurl/ftp.h new file mode 100644 index 0000000..dc7cf79 --- /dev/null +++ b/Utilities/cmcurl/ftp.h @@ -0,0 +1,37 @@ +#ifndef __FTP_H +#define __FTP_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_FTP +CURLcode Curl_ftp(struct connectdata *conn); +CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode); +CURLcode Curl_ftp_connect(struct connectdata *conn); +CURLcode Curl_ftp_disconnect(struct connectdata *conn); +CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...); +CURLcode Curl_GetFTPResponse(ssize_t *nread, struct connectdata *conn, + int *ftpcode); +CURLcode Curl_ftp_nextconnect(struct connectdata *conn); +#endif + +#endif diff --git a/Utilities/cmcurl/getdate.c b/Utilities/cmcurl/getdate.c new file mode 100644 index 0000000..6aca48e --- /dev/null +++ b/Utilities/cmcurl/getdate.c @@ -0,0 +1,2471 @@ +/* A Bison parser, made by GNU Bison 1.875a. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + tAGO = 258, + tDAY = 259, + tDAY_UNIT = 260, + tDAYZONE = 261, + tDST = 262, + tHOUR_UNIT = 263, + tID = 264, + tMERIDIAN = 265, + tMINUTE_UNIT = 266, + tMONTH = 267, + tMONTH_UNIT = 268, + tSEC_UNIT = 269, + tSNUMBER = 270, + tUNUMBER = 271, + tYEAR_UNIT = 272, + tZONE = 273 + }; +#endif +#define tAGO 258 +#define tDAY 259 +#define tDAY_UNIT 260 +#define tDAYZONE 261 +#define tDST 262 +#define tHOUR_UNIT 263 +#define tID 264 +#define tMERIDIAN 265 +#define tMINUTE_UNIT 266 +#define tMONTH 267 +#define tMONTH_UNIT 268 +#define tSEC_UNIT 269 +#define tSNUMBER 270 +#define tUNUMBER 271 +#define tYEAR_UNIT 272 +#define tZONE 273 + + + + +/* Copy the first part of user declarations. */ +#line 1 "getdate.y" + +/* +** Originally written by Steven M. Bellovin <smb@research.att.com> while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990. +** +** This code has been modified since it was included in curl, to make it +** thread-safe and to make compilers complain less about it. +** +** This code is in the public domain and has no copyright. +*/ + +#include "setup.h" + +# ifdef HAVE_ALLOCA_H +# include <alloca.h> +# endif + +# ifdef HAVE_TIME_H +# include <time.h> +# endif + +#ifndef YYDEBUG + /* to satisfy gcc -Wundef, we set this to 0 */ +#define YYDEBUG 0 +#endif + +#ifndef YYSTACK_USE_ALLOCA + /* to satisfy gcc -Wundef, we set this to 0 */ +#define YYSTACK_USE_ALLOCA 0 +#endif + +/* Since the code of getdate.y is not included in the Emacs executable + itself, there is no need to #define static in this file. Even if + the code were included in the Emacs executable, it probably + wouldn't do any harm to #undef it here; this will only cause + problems if we try to write to a static variable, which I don't + think this code needs to do. */ +#ifdef emacs +# undef static +#endif + +#ifdef __APPLE__ +#include <sys/types.h> +#include <sys/malloc.h> +#else + +#endif +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#if HAVE_STDLIB_H +# include <stdlib.h> /* for `free'; used by Bison 1.27 */ +#else + +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif + +#endif + +#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) +# define IN_CTYPE_DOMAIN(c) 1 +#else +# define IN_CTYPE_DOMAIN(c) isascii(c) +#endif + +#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) +#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) +#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) +#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) + +/* ISDIGIT differs from ISDIGIT_LOCALE, as follows: + - Its arg may be any int or unsigned int; it need not be an unsigned char. + - It's guaranteed to evaluate its argument exactly once. + - It's typically faster. + Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that + only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless + it's important to use the locale's definition of `digit' even when the + host does not conform to Posix. */ +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) + +#if defined (STDC_HEADERS) || defined (USG) +# include <string.h> +#endif + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 0 +#endif + +#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __attribute__(x) +#endif + +#ifndef ATTRIBUTE_UNUSED +# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +# define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), + as well as gratuitiously global symbol names, so we can have multiple + yacc generated parsers in the same program. Note that these are only + the variables produced by yacc. If other parser generators (bison, + byacc, etc) produce additional global names that conflict at link time, + then those parser generators need to be fixed instead of adding those + names to this list. */ + +#define yymaxdepth Curl_gd_maxdepth +#define yyparse Curl_gd_parse +#define yylex Curl_gd_lex +#define yyerror Curl_gd_error +#define yylval Curl_gd_lval +#define yychar Curl_gd_char +#define yydebug Curl_gd_debug +#define yypact Curl_gd_pact +#define yyr1 Curl_gd_r1 +#define yyr2 Curl_gd_r2 +#define yydef Curl_gd_def +#define yychk Curl_gd_chk +#define yypgo Curl_gd_pgo +#define yyact Curl_gd_act +#define yyexca Curl_gd_exca +#define yyerrflag Curl_gd_errflag +#define yynerrs Curl_gd_nerrs +#define yyps Curl_gd_ps +#define yypv Curl_gd_pv +#define yys Curl_gd_s +#define yy_yys Curl_gd_yys +#define yystate Curl_gd_state +#define yytmp Curl_gd_tmp +#define yyv Curl_gd_v +#define yy_yyv Curl_gd_yyv +#define yyval Curl_gd_val +#define yylloc Curl_gd_lloc +#define yyreds Curl_gd_reds /* With YYDEBUG defined */ +#define yytoks Curl_gd_toks /* With YYDEBUG defined */ +#define yylhs Curl_gd_yylhs +#define yylen Curl_gd_yylen +#define yydefred Curl_gd_yydefred +#define yydgoto Curl_gd_yydgoto +#define yysindex Curl_gd_yysindex +#define yyrindex Curl_gd_yyrindex +#define yygindex Curl_gd_yygindex +#define yytable Curl_gd_yytable +#define yycheck Curl_gd_yycheck + +#define EPOCH 1970 +#define HOUR(x) ((x) * 60) + +#define MAX_BUFF_LEN 128 /* size of buffer to read the date into */ + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + const char *name; + int type; + int value; +} TABLE; + + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + +/* parse results and input string */ +typedef struct _CURL_CONTEXT { + const char *yyInput; + int yyDayOrdinal; + int yyDayNumber; + int yyHaveDate; + int yyHaveDay; + int yyHaveRel; + int yyHaveTime; + int yyHaveZone; + int yyTimezone; + int yyDay; + int yyHour; + int yyMinutes; + int yyMonth; + int yySeconds; + int yyYear; + MERIDIAN yyMeridian; + int yyRelDay; + int yyRelHour; + int yyRelMinutes; + int yyRelMonth; + int yyRelSeconds; + int yyRelYear; +} CURL_CONTEXT; + +/* enable use of extra argument to yyparse and yylex which can be used to pass +** in a user defined value (CURL_CONTEXT struct in our case) +*/ +#define YYPARSE_PARAM cookie +#define YYLEX_PARAM cookie +#define context ((CURL_CONTEXT *) cookie) + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +#line 223 "getdate.y" +typedef union YYSTYPE { + int Number; + enum _MERIDIAN Meridian; +} YYSTYPE; +/* Line 191 of yacc.c. */ +#line 331 "y.tab.c" +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ +#line 228 "getdate.y" + +static int yylex (YYSTYPE *yylval, void *cookie); +static int yyerror (const char *s); + + +/* Line 214 of yacc.c. */ +#line 347 "y.tab.c" + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# else +# ifndef YYSTACK_USE_ALLOCA +# if defined (alloca) || defined (_ALLOCA_H) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC malloc +# define YYSTACK_FREE free +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + (void)yyptr; \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 50 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 22 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 11 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 51 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 61 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 273 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 20, 2, 2, 21, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 19, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned char yyprhs[] = +{ + 0, 0, 3, 4, 7, 9, 11, 13, 15, 17, + 19, 22, 27, 32, 39, 46, 48, 50, 53, 55, + 58, 61, 65, 71, 75, 79, 82, 87, 90, 94, + 97, 99, 102, 105, 107, 110, 113, 115, 118, 121, + 123, 126, 129, 131, 134, 137, 139, 142, 145, 147, + 149, 150 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yysigned_char yyrhs[] = +{ + 23, 0, -1, -1, 23, 24, -1, 25, -1, 26, + -1, 28, -1, 27, -1, 29, -1, 31, -1, 16, + 10, -1, 16, 19, 16, 32, -1, 16, 19, 16, + 15, -1, 16, 19, 16, 19, 16, 32, -1, 16, + 19, 16, 19, 16, 15, -1, 18, -1, 6, -1, + 18, 7, -1, 4, -1, 4, 20, -1, 16, 4, + -1, 16, 21, 16, -1, 16, 21, 16, 21, 16, + -1, 16, 15, 15, -1, 16, 12, 15, -1, 12, + 16, -1, 12, 16, 20, 16, -1, 16, 12, -1, + 16, 12, 16, -1, 30, 3, -1, 30, -1, 16, + 17, -1, 15, 17, -1, 17, -1, 16, 13, -1, + 15, 13, -1, 13, -1, 16, 5, -1, 15, 5, + -1, 5, -1, 16, 8, -1, 15, 8, -1, 8, + -1, 16, 11, -1, 15, 11, -1, 11, -1, 16, + 14, -1, 15, 14, -1, 14, -1, 16, -1, -1, + 10, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned short yyrline[] = +{ + 0, 244, 244, 245, 248, 251, 254, 257, 260, 263, + 266, 272, 278, 287, 293, 305, 308, 312, 317, 321, + 325, 331, 335, 353, 359, 365, 369, 374, 378, 385, + 393, 396, 399, 402, 405, 408, 411, 414, 417, 420, + 423, 426, 429, 432, 435, 438, 441, 444, 447, 452, + 487, 490 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE +/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "tAGO", "tDAY", "tDAY_UNIT", "tDAYZONE", + "tDST", "tHOUR_UNIT", "tID", "tMERIDIAN", "tMINUTE_UNIT", "tMONTH", + "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER", "tUNUMBER", "tYEAR_UNIT", + "tZONE", "':'", "','", "'/'", "$accept", "spec", "item", "time", "zone", + "day", "date", "rel", "relunit", "number", "o_merid", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 58, + 44, 47 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 22, 23, 23, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 26, 26, 26, 27, 27, + 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, + 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, + 32, 32 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 0, 2, 1, 1, 1, 1, 1, 1, + 2, 4, 4, 6, 6, 1, 1, 2, 1, 2, + 2, 3, 5, 3, 3, 2, 4, 2, 3, 2, + 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, + 2, 2, 1, 2, 2, 1, 2, 2, 1, 1, + 0, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 2, 0, 1, 18, 39, 16, 42, 45, 0, 36, + 48, 0, 49, 33, 15, 3, 4, 5, 7, 6, + 8, 30, 9, 19, 25, 38, 41, 44, 35, 47, + 32, 20, 37, 40, 10, 43, 27, 34, 46, 0, + 31, 0, 0, 17, 29, 0, 24, 28, 23, 50, + 21, 26, 51, 12, 0, 11, 0, 50, 22, 14, + 13 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yysigned_char yydefgoto[] = +{ + -1, 1, 15, 16, 17, 18, 19, 20, 21, 22, + 55 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -20 +static const yysigned_char yypact[] = +{ + -20, 0, -20, -19, -20, -20, -20, -20, -13, -20, + -20, 30, 15, -20, 14, -20, -20, -20, -20, -20, + -20, 19, -20, -20, 4, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -6, -20, -20, 16, + -20, 17, 23, -20, -20, 24, -20, -20, -20, 27, + 28, -20, -20, -20, 29, -20, 32, -8, -20, -20, + -20 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yysigned_char yypgoto[] = +{ + -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -7 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const unsigned char yytable[] = +{ + 2, 23, 52, 24, 3, 4, 5, 59, 6, 46, + 47, 7, 8, 9, 10, 11, 12, 13, 14, 31, + 32, 43, 44, 33, 45, 34, 35, 36, 37, 38, + 39, 48, 40, 49, 41, 25, 42, 52, 26, 50, + 51, 27, 53, 28, 29, 57, 54, 30, 58, 56, + 60 +}; + +static const unsigned char yycheck[] = +{ + 0, 20, 10, 16, 4, 5, 6, 15, 8, 15, + 16, 11, 12, 13, 14, 15, 16, 17, 18, 4, + 5, 7, 3, 8, 20, 10, 11, 12, 13, 14, + 15, 15, 17, 16, 19, 5, 21, 10, 8, 16, + 16, 11, 15, 13, 14, 16, 19, 17, 16, 21, + 57 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 23, 0, 4, 5, 6, 8, 11, 12, 13, + 14, 15, 16, 17, 18, 24, 25, 26, 27, 28, + 29, 30, 31, 20, 16, 5, 8, 11, 13, 14, + 17, 4, 5, 8, 10, 11, 12, 13, 14, 15, + 17, 19, 21, 7, 3, 20, 15, 16, 15, 16, + 16, 16, 10, 15, 19, 32, 21, 16, 16, 15, + 32 +}; + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrlab1 + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up");\ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + Current.first_line = Rhs[1].first_line; \ + Current.first_column = Rhs[1].first_column; \ + Current.last_line = Rhs[N].last_line; \ + Current.last_column = Rhs[N].last_column; +#endif + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YYDSYMPRINT(Args) \ +do { \ + if (yydebug) \ + yysymprint Args; \ +} while (0) + +# define YYDSYMPRINTF(Title, Token, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Token, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (cinluded). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short *bottom, short *top) +#else +static void +yy_stack_print (bottom, top) + short *bottom; + short *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned int yylineno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + yyrule - 1, yylineno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YYDSYMPRINT(Args) +# define YYDSYMPRINTF(Title, Token, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +#endif /* !YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + { + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); +# ifdef YYPRINT + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + } + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yytype, yyvaluep) + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + /* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short yyssa[YYINITDEPTH]; + short *yyss = yyssa; + register short *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyoverflowlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 4: +#line 248 "getdate.y" + { + context->yyHaveTime++; + } + break; + + case 5: +#line 251 "getdate.y" + { + context->yyHaveZone++; + } + break; + + case 6: +#line 254 "getdate.y" + { + context->yyHaveDate++; + } + break; + + case 7: +#line 257 "getdate.y" + { + context->yyHaveDay++; + } + break; + + case 8: +#line 260 "getdate.y" + { + context->yyHaveRel++; + } + break; + + case 10: +#line 266 "getdate.y" + { + context->yyHour = yyvsp[-1].Number; + context->yyMinutes = 0; + context->yySeconds = 0; + context->yyMeridian = yyvsp[0].Meridian; + } + break; + + case 11: +#line 272 "getdate.y" + { + context->yyHour = yyvsp[-3].Number; + context->yyMinutes = yyvsp[-1].Number; + context->yySeconds = 0; + context->yyMeridian = yyvsp[0].Meridian; + } + break; + + case 12: +#line 278 "getdate.y" + { + context->yyHour = yyvsp[-3].Number; + context->yyMinutes = yyvsp[-1].Number; + context->yyMeridian = MER24; + context->yyHaveZone++; + context->yyTimezone = (yyvsp[0].Number < 0 + ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60 + : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60)); + } + break; + + case 13: +#line 287 "getdate.y" + { + context->yyHour = yyvsp[-5].Number; + context->yyMinutes = yyvsp[-3].Number; + context->yySeconds = yyvsp[-1].Number; + context->yyMeridian = yyvsp[0].Meridian; + } + break; + + case 14: +#line 293 "getdate.y" + { + context->yyHour = yyvsp[-5].Number; + context->yyMinutes = yyvsp[-3].Number; + context->yySeconds = yyvsp[-1].Number; + context->yyMeridian = MER24; + context->yyHaveZone++; + context->yyTimezone = (yyvsp[0].Number < 0 + ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60 + : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60)); + } + break; + + case 15: +#line 305 "getdate.y" + { + context->yyTimezone = yyvsp[0].Number; + } + break; + + case 16: +#line 308 "getdate.y" + { + context->yyTimezone = yyvsp[0].Number - 60; + } + break; + + case 17: +#line 312 "getdate.y" + { + context->yyTimezone = yyvsp[-1].Number - 60; + } + break; + + case 18: +#line 317 "getdate.y" + { + context->yyDayOrdinal = 1; + context->yyDayNumber = yyvsp[0].Number; + } + break; + + case 19: +#line 321 "getdate.y" + { + context->yyDayOrdinal = 1; + context->yyDayNumber = yyvsp[-1].Number; + } + break; + + case 20: +#line 325 "getdate.y" + { + context->yyDayOrdinal = yyvsp[-1].Number; + context->yyDayNumber = yyvsp[0].Number; + } + break; + + case 21: +#line 331 "getdate.y" + { + context->yyMonth = yyvsp[-2].Number; + context->yyDay = yyvsp[0].Number; + } + break; + + case 22: +#line 335 "getdate.y" + { + /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. + The goal in recognizing YYYY/MM/DD is solely to support legacy + machine-generated dates like those in an RCS log listing. If + you want portability, use the ISO 8601 format. */ + if (yyvsp[-4].Number >= 1000) + { + context->yyYear = yyvsp[-4].Number; + context->yyMonth = yyvsp[-2].Number; + context->yyDay = yyvsp[0].Number; + } + else + { + context->yyMonth = yyvsp[-4].Number; + context->yyDay = yyvsp[-2].Number; + context->yyYear = yyvsp[0].Number; + } + } + break; + + case 23: +#line 353 "getdate.y" + { + /* ISO 8601 format. yyyy-mm-dd. */ + context->yyYear = yyvsp[-2].Number; + context->yyMonth = -yyvsp[-1].Number; + context->yyDay = -yyvsp[0].Number; + } + break; + + case 24: +#line 359 "getdate.y" + { + /* e.g. 17-JUN-1992. */ + context->yyDay = yyvsp[-2].Number; + context->yyMonth = yyvsp[-1].Number; + context->yyYear = -yyvsp[0].Number; + } + break; + + case 25: +#line 365 "getdate.y" + { + context->yyMonth = yyvsp[-1].Number; + context->yyDay = yyvsp[0].Number; + } + break; + + case 26: +#line 369 "getdate.y" + { + context->yyMonth = yyvsp[-3].Number; + context->yyDay = yyvsp[-2].Number; + context->yyYear = yyvsp[0].Number; + } + break; + + case 27: +#line 374 "getdate.y" + { + context->yyMonth = yyvsp[0].Number; + context->yyDay = yyvsp[-1].Number; + } + break; + + case 28: +#line 378 "getdate.y" + { + context->yyMonth = yyvsp[-1].Number; + context->yyDay = yyvsp[-2].Number; + context->yyYear = yyvsp[0].Number; + } + break; + + case 29: +#line 385 "getdate.y" + { + context->yyRelSeconds = -context->yyRelSeconds; + context->yyRelMinutes = -context->yyRelMinutes; + context->yyRelHour = -context->yyRelHour; + context->yyRelDay = -context->yyRelDay; + context->yyRelMonth = -context->yyRelMonth; + context->yyRelYear = -context->yyRelYear; + } + break; + + case 31: +#line 396 "getdate.y" + { + context->yyRelYear += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 32: +#line 399 "getdate.y" + { + context->yyRelYear += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 33: +#line 402 "getdate.y" + { + context->yyRelYear += yyvsp[0].Number; + } + break; + + case 34: +#line 405 "getdate.y" + { + context->yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 35: +#line 408 "getdate.y" + { + context->yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 36: +#line 411 "getdate.y" + { + context->yyRelMonth += yyvsp[0].Number; + } + break; + + case 37: +#line 414 "getdate.y" + { + context->yyRelDay += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 38: +#line 417 "getdate.y" + { + context->yyRelDay += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 39: +#line 420 "getdate.y" + { + context->yyRelDay += yyvsp[0].Number; + } + break; + + case 40: +#line 423 "getdate.y" + { + context->yyRelHour += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 41: +#line 426 "getdate.y" + { + context->yyRelHour += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 42: +#line 429 "getdate.y" + { + context->yyRelHour += yyvsp[0].Number; + } + break; + + case 43: +#line 432 "getdate.y" + { + context->yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 44: +#line 435 "getdate.y" + { + context->yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 45: +#line 438 "getdate.y" + { + context->yyRelMinutes += yyvsp[0].Number; + } + break; + + case 46: +#line 441 "getdate.y" + { + context->yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 47: +#line 444 "getdate.y" + { + context->yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number; + } + break; + + case 48: +#line 447 "getdate.y" + { + context->yyRelSeconds += yyvsp[0].Number; + } + break; + + case 49: +#line 453 "getdate.y" + { + if (context->yyHaveTime && context->yyHaveDate && + !context->yyHaveRel) + context->yyYear = yyvsp[0].Number; + else + { + if (yyvsp[0].Number>10000) + { + context->yyHaveDate++; + context->yyDay= (yyvsp[0].Number)%100; + context->yyMonth= (yyvsp[0].Number/100)%100; + context->yyYear = yyvsp[0].Number/10000; + } + else + { + context->yyHaveTime++; + if (yyvsp[0].Number < 100) + { + context->yyHour = yyvsp[0].Number; + context->yyMinutes = 0; + } + else + { + context->yyHour = yyvsp[0].Number / 100; + context->yyMinutes = yyvsp[0].Number % 100; + } + context->yySeconds = 0; + context->yyMeridian = MER24; + } + } + } + break; + + case 50: +#line 487 "getdate.y" + { + yyval.Meridian = MER24; + } + break; + + case 51: +#line 491 "getdate.y" + { + yyval.Meridian = yyvsp[0].Meridian; + } + break; + + + } + +/* Line 999 of yacc.c. */ +#line 1688 "y.tab.c" + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + int yytype = YYTRANSLATE (yychar); + char *yymsg; + int yyx, yycount; + + yycount = 0; + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + yysize += yystrlen (yytname[yyx]) + 15, yycount++; + yysize += yystrlen ("syntax error, unexpected ") + 1; + yysize += yystrlen (yytname[yytype]); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); + yyp = yystpcpy (yyp, yytname[yytype]); + + if (yycount < 5) + { + yycount = 0; + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); + yyx++) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + const char *yyq = ! yycount ? ", expecting " : " or "; + yyp = yystpcpy (yyp, yyq); + yyp = yystpcpy (yyp, yytname[yyx]); + yycount++; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("syntax error; also virtual memory exhausted"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror ("syntax error"); + } + (void)yynerrs; + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + /* Return failure if at end of input. */ + if (yychar == YYEOF) + { + /* Pop the error token. */ + YYPOPSTACK; + /* Pop the rest of the stack. */ + while (yyss < yyssp) + { + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[*yyssp], yyvsp); + YYPOPSTACK; + } + YYABORT; + } + + YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); + yydestruct (yytoken, &yylval); + yychar = YYEMPTY; + + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*----------------------------------------------------. +| yyerrlab1 -- error raised explicitly by an action. | +`----------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[yystate], yyvsp); + yyvsp--; + yystate = *--yyssp; + + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; + + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*----------------------------------------------. +| yyoverflowlab -- parser overflow comes here. | +`----------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + +#line 496 "getdate.y" + + +/* Include this file down here because bison inserts code above which + may define-away `const'. We want the prototype for get_date to have + the same signature as the function definition does. */ +#include "getdate.h" + +#ifndef WIN32 /* the windows dudes don't need these, does anyone really? */ +extern struct tm *gmtime (const time_t *); +extern struct tm *localtime (const time_t *); +extern time_t mktime (struct tm *); +#endif + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL, 0, 0 } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tYEAR_UNIT, 1 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tDAY_UNIT, 14 }, + { "week", tDAY_UNIT, 7 }, + { "day", tDAY_UNIT, 1 }, + { "hour", tHOUR_UNIT, 1 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL, 0, 0 } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 1 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL, 0, 0 } +}; + +/* The timezone table. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR ( 0) }, + { "wet", tZONE, HOUR ( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */ + { "wat", tZONE, HOUR ( 1) }, /* West Africa */ + { "at", tZONE, HOUR ( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR ( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR (10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR (11) }, /* Nome */ + { "idlw", tZONE, HOUR (12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR (1) }, /* Central European */ + { "met", tZONE, -HOUR (1) }, /* Middle European */ + { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ + { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR (1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */ + { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR (3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Standard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */ +#if 0 + { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR (12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR (12) }, /* International Date Line East */ + { NULL, 0, 0 } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR ( 1) }, + { "b", tZONE, HOUR ( 2) }, + { "c", tZONE, HOUR ( 3) }, + { "d", tZONE, HOUR ( 4) }, + { "e", tZONE, HOUR ( 5) }, + { "f", tZONE, HOUR ( 6) }, + { "g", tZONE, HOUR ( 7) }, + { "h", tZONE, HOUR ( 8) }, + { "i", tZONE, HOUR ( 9) }, + { "k", tZONE, HOUR ( 10) }, + { "l", tZONE, HOUR ( 11) }, + { "m", tZONE, HOUR ( 12) }, + { "n", tZONE, HOUR (- 1) }, + { "o", tZONE, HOUR (- 2) }, + { "p", tZONE, HOUR (- 3) }, + { "q", tZONE, HOUR (- 4) }, + { "r", tZONE, HOUR (- 5) }, + { "s", tZONE, HOUR (- 6) }, + { "t", tZONE, HOUR (- 7) }, + { "u", tZONE, HOUR (- 8) }, + { "v", tZONE, HOUR (- 9) }, + { "w", tZONE, HOUR (-10) }, + { "x", tZONE, HOUR (-11) }, + { "y", tZONE, HOUR (-12) }, + { "z", tZONE, HOUR ( 0) }, + { NULL, 0, 0 } +}; + + + + +/* ARGSUSED */ +static int +yyerror (const char *s ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +ToHour (int Hours, MERIDIAN Meridian) +{ + switch (Meridian) + { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return Hours; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return Hours; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return Hours + 12; + default: + break; /* used to do abort() here */ + } + /* NOTREACHED - but make gcc happy! */ + return -1; +} + +static int +ToYear (int Year) +{ + if (Year < 0) + Year = -Year; + + /* XPG4 suggests that years 00-68 map to 2000-2068, and + years 69-99 map to 1969-1999. */ + if (Year < 69) + Year += 2000; + else if (Year < 100) + Year += 1900; + + return Year; +} + +static int +LookupWord (YYSTYPE *yylval, char *buff) +{ + char *p; + char *q; + const TABLE *tp; + size_t i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (ISUPPER ((unsigned char) *p)) + *p = tolower ((int)*p); + + if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) + { + yylval->Meridian = MERam; + return tMERIDIAN; + } + if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) + { + yylval->Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen (buff) == 3) + abbrev = 1; + else if (strlen (buff) == 4 && buff[3] == '.') + { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) + { + if (abbrev) + { + if (strncmp (buff, tp->name, 3) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + } + else if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + + if (strcmp (buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen (buff) - 1; + if (buff[i] == 's') + { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff)) + { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + + return tID; +} + +static int +yylex (YYSTYPE *yylval, void *cookie) +{ + register unsigned char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for (;;) + { + while (ISSPACE ((unsigned char) *context->yyInput)) + context->yyInput++; + + if (ISDIGIT (c = *context->yyInput) || c == '-' || c == '+') + { + if (c == '-' || c == '+') + { + sign = c == '-' ? -1 : 1; + if (!ISDIGIT (*++context->yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval->Number = 0; ISDIGIT (c = *context->yyInput++);) + yylval->Number = 10 * yylval->Number + c - '0'; + context->yyInput--; + if (sign < 0) + yylval->Number = -yylval->Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (ISALPHA (c)) + { + for (p = buff; (c = *context->yyInput++, ISALPHA (c)) || c == '.';) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + context->yyInput--; + return LookupWord (yylval, buff); + } + if (c != '(') + return *context->yyInput++; + Count = 0; + do + { + c = *context->yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } + while (Count > 0); + } +} + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static long +difftm (struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + long days = ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay / 100 - by / 100) + + ((ay / 100 >> 2) - (by / 100 >> 2)) + /* + difference in years * 365 */ + + (long) (ay - by) * 365 + ); + return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} + +time_t +curl_getdate (const char *p, const time_t *now) +{ + struct tm tm, tm0, *tmp; + time_t Start; + CURL_CONTEXT cookie; +#ifdef HAVE_LOCALTIME_R + struct tm keeptime; +#endif + cookie.yyInput = p; + Start = now ? *now : time ((time_t *) NULL); +#ifdef HAVE_LOCALTIME_R + tmp = (struct tm *)localtime_r(&Start, &keeptime); +#else + tmp = localtime (&Start); +#endif + if (!tmp) + return -1; + cookie.yyYear = tmp->tm_year + TM_YEAR_ORIGIN; + cookie.yyMonth = tmp->tm_mon + 1; + cookie.yyDay = tmp->tm_mday; + cookie.yyHour = tmp->tm_hour; + cookie.yyMinutes = tmp->tm_min; + cookie.yySeconds = tmp->tm_sec; + tm.tm_isdst = tmp->tm_isdst; + cookie.yyMeridian = MER24; + cookie.yyRelSeconds = 0; + cookie.yyRelMinutes = 0; + cookie.yyRelHour = 0; + cookie.yyRelDay = 0; + cookie.yyRelMonth = 0; + cookie.yyRelYear = 0; + cookie.yyHaveDate = 0; + cookie.yyHaveDay = 0; + cookie.yyHaveRel = 0; + cookie.yyHaveTime = 0; + cookie.yyHaveZone = 0; + + if (yyparse ((void*)&cookie) + || cookie.yyHaveTime > 1 || cookie.yyHaveZone > 1 || + cookie.yyHaveDate > 1 || cookie.yyHaveDay > 1) + return -1; + + tm.tm_year = ToYear (cookie.yyYear) - TM_YEAR_ORIGIN + cookie.yyRelYear; + tm.tm_mon = cookie.yyMonth - 1 + cookie.yyRelMonth; + tm.tm_mday = cookie.yyDay + cookie.yyRelDay; + if (cookie.yyHaveTime || + (cookie.yyHaveRel && !cookie.yyHaveDate && !cookie.yyHaveDay)) + { + tm.tm_hour = ToHour (cookie.yyHour, cookie.yyMeridian); + if (tm.tm_hour < 0) + return -1; + tm.tm_min = cookie.yyMinutes; + tm.tm_sec = cookie.yySeconds; + } + else + { + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + } + tm.tm_hour += cookie.yyRelHour; + tm.tm_min += cookie.yyRelMinutes; + tm.tm_sec += cookie.yyRelSeconds; + + /* Let mktime deduce tm_isdst if we have an absolute timestamp, + or if the relative timestamp mentions days, months, or years. */ + if (cookie.yyHaveDate | cookie.yyHaveDay | cookie.yyHaveTime | + cookie.yyRelDay | cookie.yyRelMonth | cookie.yyRelYear) + tm.tm_isdst = -1; + + tm0 = tm; + + Start = mktime (&tm); + + if (Start == (time_t) -1) + { + + /* Guard against falsely reporting errors near the time_t boundaries + when parsing times in other time zones. For example, if the min + time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead + of UTC, then the min localtime value is 1970-01-01 08:00:00; if + we apply mktime to 1970-01-01 00:00:00 we will get an error, so + we apply mktime to 1970-01-02 08:00:00 instead and adjust the time + zone by 24 hours to compensate. This algorithm assumes that + there is no DST transition within a day of the time_t boundaries. */ + if (cookie.yyHaveZone) + { + tm = tm0; + if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) + { + tm.tm_mday++; + cookie.yyTimezone -= 24 * 60; + } + else + { + tm.tm_mday--; + cookie.yyTimezone += 24 * 60; + } + Start = mktime (&tm); + } + + if (Start == (time_t) -1) + return Start; + } + + if (cookie.yyHaveDay && !cookie.yyHaveDate) + { + tm.tm_mday += ((cookie.yyDayNumber - tm.tm_wday + 7) % 7 + + 7 * (cookie.yyDayOrdinal - (0 < cookie.yyDayOrdinal))); + Start = mktime (&tm); + if (Start == (time_t) -1) + return Start; + } + + if (cookie.yyHaveZone) + { + long delta; + struct tm *gmt; +#ifdef HAVE_GMTIME_R + /* thread-safe version */ + struct tm keeptime2; + gmt = (struct tm *)gmtime_r(&Start, &keeptime2); +#else + gmt = gmtime(&Start); +#endif + if (!gmt) + return -1; + delta = cookie.yyTimezone * 60L + difftm (&tm, gmt); + if ((Start + delta < Start) != (delta < 0)) + return -1; /* time_t overflow */ + Start += delta; + } + + return Start; +} + + diff --git a/Utilities/cmcurl/getdate.h b/Utilities/cmcurl/getdate.h new file mode 100644 index 0000000..85650e3 --- /dev/null +++ b/Utilities/cmcurl/getdate.h @@ -0,0 +1,37 @@ +/* +** Originally written by Steven M. Bellovin <smb@research.att.com> while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990. +** +** This code is in the public domain and has no copyright. +*/ + +# include "setup.h" + +#ifndef PARAMS +# if defined PROTOTYPES || (defined __STDC__ && __STDC__) +# define PARAMS(Args) Args +# else +# define PARAMS(Args) () +# endif +#endif + +#ifdef vms +# include <types.h> +# include <time.h> +#else +# include <sys/types.h> +# if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +# else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +# endif +#endif /* defined (vms) */ + +time_t curl_getdate PARAMS ((const char *p, const time_t *now)); diff --git a/Utilities/cmcurl/getenv.c b/Utilities/cmcurl/getenv.c new file mode 100644 index 0000000..302db2e --- /dev/null +++ b/Utilities/cmcurl/getenv.c @@ -0,0 +1,70 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef WIN32 +#include <windows.h> +#endif + +#ifdef VMS +#include <unixlib.h> +#endif + +#include <curl/curl.h> +#include "curl_memory.h" + +#include "memdebug.h" + +static +char *GetEnv(const char *variable) +{ +#ifdef WIN32 + /* This shit requires windows.h (HUGE) to be included */ + char env[MAX_PATH]; /* MAX_PATH is from windef.h */ + char *temp = getenv(variable); + env[0] = '\0'; + if (temp != NULL) + ExpandEnvironmentStrings(temp, env, sizeof(env)); +#else +#ifdef VMS + char *env = getenv(variable); + if (env && strcmp("HOME",variable) == 0) { + env = decc$translate_vms(env); + } +#else + /* no length control */ + char *env = getenv(variable); +#endif +#endif + return (env && env[0])?strdup(env):NULL; +} + +char *curl_getenv(const char *v) +{ + return GetEnv(v); +} diff --git a/Utilities/cmcurl/getinfo.c b/Utilities/cmcurl/getinfo.c new file mode 100644 index 0000000..7316d3a --- /dev/null +++ b/Utilities/cmcurl/getinfo.c @@ -0,0 +1,174 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <curl/curl.h> + +#include "urldata.h" +#include "getinfo.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include "curl_memory.h" + +/* Make this the last #include */ +#include "memdebug.h" + +/* + * This is supposed to be called in the beginning of a permform() session + * and should reset all session-info variables + */ +CURLcode Curl_initinfo(struct SessionHandle *data) +{ + struct Progress *pro = &data->progress; + struct PureInfo *info =&data->info; + + pro->t_nslookup = 0; + pro->t_connect = 0; + pro->t_pretransfer = 0; + pro->t_starttransfer = 0; + pro->timespent = 0; + 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; + 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; + va_start(arg, info); + + switch(info&CURLINFO_TYPEMASK) { + default: + return CURLE_BAD_FUNCTION_ARGUMENT; + case CURLINFO_STRING: + param_charp = va_arg(arg, char **); + if(NULL == param_charp) + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + case CURLINFO_LONG: + param_longp = va_arg(arg, long *); + if(NULL == param_longp) + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + case CURLINFO_DOUBLE: + param_doublep = va_arg(arg, double *); + if(NULL == param_doublep) + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + } + + switch(info) { + case CURLINFO_EFFECTIVE_URL: + *param_charp = data->change.url?data->change.url:(char *)""; + break; + case CURLINFO_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; + break; + case CURLINFO_HTTPAUTH_AVAIL: + *param_longp = data->info.httpauthavail; + break; + case CURLINFO_PROXYAUTH_AVAIL: + *param_longp = data->info.proxyauthavail; + 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 0000000..2fe1b5c --- /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/hash.c b/Utilities/cmcurl/hash.c new file mode 100644 index 0000000..614d692 --- /dev/null +++ b/Utilities/cmcurl/hash.c @@ -0,0 +1,267 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <stdlib.h> + +#include "hash.h" +#include "llist.h" +#include "curl_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) +{ + curl_hash *h = (curl_hash *) user; + curl_hash_element *e = (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(curl_hash *h, int slots, curl_hash_dtor dtor) +{ + int i; + + h->dtor = dtor; + h->size = 0; + h->slots = slots; + + h->table = (curl_llist **) malloc(slots * sizeof(curl_llist *)); + 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 */ +} + +curl_hash * +Curl_hash_alloc(int slots, curl_hash_dtor dtor) +{ + curl_hash *h; + + h = (curl_hash *) malloc(sizeof(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 curl_hash_element * +mk_hash_element(char *key, size_t key_len, const void *p) +{ + curl_hash_element *he = + (curl_hash_element *) malloc(sizeof(curl_hash_element)); + + if(he) { + char *dup = strdup(key); + if(dup) { + 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(curl_hash *h, char *key, size_t key_len, void *p) +{ + curl_hash_element *he; + curl_llist_element *le; + curl_llist *l = FETCH_LIST(h, key, key_len); + + for (le = l->head; le; le = le->next) { + he = (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 */ +} + +void * +Curl_hash_pick(curl_hash *h, char *key, size_t key_len) +{ + curl_llist_element *le; + curl_hash_element *he; + 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)) +{ + 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(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(curl_hash *h, void *user, + int (*comp)(void *, void *)) +{ + curl_llist_element *le; + curl_llist_element *lnext; + 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) { + 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(curl_hash *h) +{ + if (!h) + return; + + Curl_hash_clean(h); + free(h); +} + diff --git a/Utilities/cmcurl/hash.h b/Utilities/cmcurl/hash.h new file mode 100644 index 0000000..7814674 --- /dev/null +++ b/Utilities/cmcurl/hash.h @@ -0,0 +1,60 @@ +#ifndef __HASH_H +#define __HASH_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 <stddef.h> + +#include "llist.h" + +typedef void (*curl_hash_dtor)(void *); + +typedef struct _curl_hash { + curl_llist **table; + curl_hash_dtor dtor; + int slots; + size_t size; +} curl_hash; + +typedef struct _curl_hash_element { + void *ptr; + char *key; + size_t key_len; +} curl_hash_element; + + +int Curl_hash_init(curl_hash *, int, curl_hash_dtor); +curl_hash *Curl_hash_alloc(int, curl_hash_dtor); +void *Curl_hash_add(curl_hash *, char *, size_t, void *); +int Curl_hash_delete(curl_hash *h, char *key, size_t key_len); +void *Curl_hash_pick(curl_hash *, char *, size_t); +void Curl_hash_apply(curl_hash *h, void *user, + void (*cb)(void *user, void *ptr)); +int Curl_hash_count(curl_hash *h); +void Curl_hash_clean(curl_hash *h); +void Curl_hash_clean_with_criterium(curl_hash *h, void *user, int (*comp)(void *, void *)); +void Curl_hash_destroy(curl_hash *h); + +#endif diff --git a/Utilities/cmcurl/hostares.c b/Utilities/cmcurl/hostares.c new file mode 100644 index 0000000..197f540 --- /dev/null +++ b/Utilities/cmcurl/hostares.c @@ -0,0 +1,301 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <errno.h> + +#define _REENTRANT + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <malloc.h> +#else +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* required for free() prototypes */ +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for the close() proto */ +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#include <stdlib.h> +#endif +#endif + +#ifdef HAVE_SETJMP_H +#include <setjmp.h> +#endif + +#ifdef WIN32 +#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" + +#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 "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +/*********************************************************************** + * Only for ares-enabled builds + **********************************************************************/ + +#ifdef CURLRES_ARES + +/* + * Curl_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! + */ + +CURLcode Curl_fdset(struct connectdata *conn, + fd_set *read_fd_set, + fd_set *write_fd_set, + int *max_fdp) + +{ + int max = ares_fds(conn->data->state.areschannel, + read_fd_set, write_fd_set); + *max_fdp = max; + + return CURLE_OK; +} + +/* + * 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 && errno != 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 */ + Curl_disconnect(conn); + } + + 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, + 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, + 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 0000000..b3c9dfa --- /dev/null +++ b/Utilities/cmcurl/hostasyn.c @@ -0,0 +1,170 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <errno.h> + +#define _REENTRANT + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <malloc.h> +#else +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* required for free() prototypes */ +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for the close() proto */ +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#include <stdlib.h> +#endif +#endif + +#ifdef HAVE_SETJMP_H +#include <setjmp.h> +#endif + +#ifdef WIN32 +#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" + +#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 "curl_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 void addrinfo_callback(void *arg, /* "struct connectdata *" */ + int status, + void *addr) +{ + struct connectdata *conn = (struct connectdata *)arg; + struct Curl_dns_entry *dns = NULL; + + conn->async.done = TRUE; + conn->async.status = status; + + if(CURL_ASYNC_SUCCESS == status) { + + /* + * IPv4: 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); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + } + } + + conn->async.dns = dns; + + /* ipv4: The input hostent struct will be freed by ares when we return from + this function */ +} + +void Curl_addrinfo4_callback(void *arg, /* "struct connectdata *" */ + int status, + struct hostent *hostent) +{ + addrinfo_callback(arg, status, hostent); +} + +#ifdef CURLRES_IPV6 +void Curl_addrinfo6_callback(void *arg, /* "struct connectdata *" */ + int status, + struct addrinfo *ai) +{ + 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 0000000..43ade26 --- /dev/null +++ b/Utilities/cmcurl/hostip.c @@ -0,0 +1,540 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <errno.h> + +#define _REENTRANT + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <malloc.h> +#else +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* required for free() prototypes */ +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for the close() proto */ +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#include <stdlib.h> +#endif +#endif + +#ifdef HAVE_SETJMP_H +#include <setjmp.h> +#endif + +#ifdef WIN32 +#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 "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 "curl_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. It cannot have ENABLE_IPV6 defined at the same + * time, as c-ares has no ipv6 support. 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 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 + */ +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++); + return i; +} + +#define GET_SIN_ADDR_FROM_CURL_ADDRINFO(ai_addr, si, sin, sinaddr, ip) \ + { \ + union { \ + struct si* vsi; \ + struct sin* vsin;\ + } vi; \ + vi.vsi = ai_addr; \ + ip = &(vi.vsin->sinaddr); \ + } + +/* + * 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) +{ + int af = ip->ai_family; + const void *ip4; +#ifdef CURLRES_IPV6 + const void *ip6; + GET_SIN_ADDR_FROM_CURL_ADDRINFO(ip->ai_addr, sockaddr, sockaddr_in6, + sin6_addr, ip6); +#else + const void *ip6 = NULL; +#endif + GET_SIN_ADDR_FROM_CURL_ADDRINFO(ip->ai_addr, sockaddr, sockaddr_in, + sin_addr, ip4); + + 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(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(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) + /* cache forever means never prune! */ + 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->hostcache, + data->set.dns_cache_timeout, + now); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); +} + +#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, + 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 *) malloc(sizeof(struct Curl_dns_entry)); + if (!dns) { + free(entry_id); + return NULL; + } + + dns->inuse = 0; /* init to not used */ + dns->addr = addr; /* this is the address(es) */ + + /* 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->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, + char *hostname, + int port, + struct Curl_dns_entry **entry) +{ + char *entry_id; + struct Curl_dns_entry *dns = NULL; + size_t entry_len; + int wait; + struct SessionHandle *data = conn->data; + CURLcode result; + + /* default to failure */ + 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 && sigsetjmp(curl_jmpenv, 1)) { + /* this is coming from a siglongjmp() */ + failf(data, "name lookup timed out"); + return CURLRESOLV_ERROR; + } +#endif + rc = CURLRESOLV_ERROR; + + /* 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->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); + + 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 { + dns->inuse++; /* we use it! */ + 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. + */ +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(void *org, int port) +{ + struct hostent *orig = org; + + return Curl_he2ai(orig, port); +} +#endif /* CURLRES_ADDRINFO_COPY */ diff --git a/Utilities/cmcurl/hostip.h b/Utilities/cmcurl/hostip.h new file mode 100644 index 0000000..545fec0 --- /dev/null +++ b/Utilities/cmcurl/hostip.h @@ -0,0 +1,255 @@ +#ifndef __HOSTIP_H +#define __HOSTIP_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 "hash.h" + +#ifdef HAVE_NETDB_H +#include <netdb.h> +#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 + +#ifdef CURLRES_IPV4 +#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_hostent_relocate() function)) */ +#define CURLRES_ADDRINFO_COPY +#endif +#endif /* IPv4-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 +#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; + size_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct Curl_addrinfo *ai_next; +}; +typedef struct Curl_addrinfo Curl_addrinfo; +#endif + +struct addrinfo; +struct SessionHandle; +struct connectdata; + +void Curl_global_host_cache_init(void); +void Curl_global_host_cache_dtor(void); +curl_hash *Curl_global_host_cache_get(void); + +#define Curl_global_host_cache_use(__p) ((__p)->set.global_dns_cache) + +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, 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, + 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_fdset() 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 curl_multi_fdset() function */ +CURLcode Curl_fdset(struct connectdata *conn, + fd_set *read_fd_set, + fd_set *write_fd_set, + int *max_fdp); +/* 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 */ +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(char *hostname, char *service, + struct addrinfo *hints, + struct addrinfo **result, + int line, const char *source); +int curl_dogetnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, int flags, + int line, const char *source); +#endif + +/* This is the callback function that is used when we build with asynch + resolve, ipv4 */ +void 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 */ +void Curl_addrinfo6_callback(void *arg, + int status, + struct addrinfo *ai); + + +/* [ipv4 only] Creates a Curl_addrinfo struct from a numerical-only IP + address */ +Curl_addrinfo *Curl_ip2addr(in_addr_t num, char *hostname, int port); + +/* [ipv4 only] Curl_he2ai() converts a struct hostent to a Curl_addrinfo chain + and returns it */ +Curl_addrinfo *Curl_he2ai(struct hostent *, int port); + +/* relocate a hostent struct */ +void Curl_hostent_relocate(struct hostent *h, long offset); + +/* Clone a Curl_addrinfo struct, works protocol independently */ +Curl_addrinfo *Curl_addrinfo_copy(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, + char *hostname, int port); + +#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 0000000..1b4c3c1 --- /dev/null +++ b/Utilities/cmcurl/hostip4.c @@ -0,0 +1,456 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <errno.h> + +#define _REENTRANT + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <malloc.h> +#else +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* required for free() prototypes */ +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for the close() proto */ +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#include <stdlib.h> +#endif +#endif + +#ifdef HAVE_SETJMP_H +#include <setjmp.h> +#endif + +#ifdef WIN32 +#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" + +#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 "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/*********************************************************************** + * Only for plain-ipv4 builds + **********************************************************************/ +#ifdef CURLRES_IPV4 /* plain ipv4 code coming up */ + +/* + * 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; + } +} + +/* + * 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 */ +} + +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, 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; + addrentry->s_addr = num; + 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; +} + +#ifdef CURLRES_SYNCH /* the functions below are for synchronous resolves */ + +/* + * Curl_getaddrinfo() - the ipv4 synchronous version. + * + * The original code to this function was once stolen from the Dancer source + * code, written by Bjorn Reese, it has since been patched and modified + * considerably. + * + * 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, + 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 */ + + 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); + } + +#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 */ + +/* + * 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. + +struct Curl_addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; +}; + +struct hostent { + char *h_name; * official name of host * + char **h_aliases; * alias list * + int h_addrtype; * host address type * + int h_length; * length of address * + char **h_addr_list; * list of addresses * +} +#define h_addr h_addr_list[0] * for backward compatibility * + +*/ + +Curl_addrinfo *Curl_he2ai(struct hostent *he, int port) +{ + Curl_addrinfo *ai; + Curl_addrinfo *prevai = NULL; + Curl_addrinfo *firstai = NULL; + int i; + + union { + struct in_addr *addr; + char* list; + } curr; + union { + struct sockaddr_in* addr_in; + struct sockaddr* addr; + } address; + + if(!he) + /* no input == no output! */ + return NULL; + + for(i=0; (curr.list = he->h_addr_list[i]); 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 */ + ai->ai_socktype = SOCK_STREAM; /* we only support this */ + 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 *) (ai + 1); + + /* leave the rest of the struct filled with zero */ + + address.addr = ai->ai_addr; /* storage area for this info */ + + memcpy((char *)&(address.addr_in->sin_addr), curr.addr, sizeof(struct in_addr)); + address.addr_in->sin_family = he->h_addrtype; + address.addr_in->sin_port = htons((unsigned short)port); + + prevai = ai; + } + return firstai; +} + +#endif /* CURLRES_IPV4 */ diff --git a/Utilities/cmcurl/hostip6.c b/Utilities/cmcurl/hostip6.c new file mode 100644 index 0000000..6717c00 --- /dev/null +++ b/Utilities/cmcurl/hostip6.c @@ -0,0 +1,263 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <errno.h> + +#define _REENTRANT + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <malloc.h> +#else +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* required for free() prototypes */ +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for the close() proto */ +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#include <stdlib.h> +#endif +#endif + +#ifdef HAVE_SETJMP_H +#include <setjmp.h> +#endif + +#ifdef WIN32 +#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" + +#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 "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/*********************************************************************** + * Only for ipv6-enabled builds + **********************************************************************/ +#ifdef CURLRES_IPV6 +/* + * 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(void *source, int port) +{ + (void) port; + return source; +} +#endif + +#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(char *hostname, char *service, + struct addrinfo *hints, + struct addrinfo **result, + int line, const char *source) +{ + int res=(getaddrinfo)(hostname, service, hints, result); + if(0 == res) { + /* success */ + if(logfile) + fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n", + source, line, (void *)*result); + } + else { + if(logfile) + fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n", + source, line); + } + return res; +} + +int curl_dogetnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, int 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; +} + +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 + +/* + * 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; +} + +#ifndef USE_THREADING_GETADDRINFO +/* + * Curl_getaddrinfo() when built ipv6-enabled (non-threading 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, + 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=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 < 0) { + /* 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 = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + snprintf(sbuf, sizeof(sbuf), "%d", port); + error = getaddrinfo(hostname, sbuf, &hints, &res); + if (error) { + infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port); + return NULL; + } + + return res; +} +#endif /* USE_THREADING_GETADDRINFO */ +#endif /* ipv6 */ + diff --git a/Utilities/cmcurl/hostsyn.c b/Utilities/cmcurl/hostsyn.c new file mode 100644 index 0000000..786f9d9 --- /dev/null +++ b/Utilities/cmcurl/hostsyn.c @@ -0,0 +1,149 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <errno.h> + +#define _REENTRANT + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <malloc.h> +#else +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* required for free() prototypes */ +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for the close() proto */ +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#include <stdlib.h> +#endif +#endif + +#ifdef HAVE_SETJMP_H +#include <setjmp.h> +#endif + +#ifdef WIN32 +#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" + +#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 "curl_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 + */ + +CURLcode Curl_fdset(struct connectdata *conn, + fd_set *read_fd_set, + fd_set *write_fd_set, + int *max_fdp) +{ + (void)conn; + (void)read_fd_set; + (void)write_fd_set; + (void)max_fdp; + + return CURLE_OK; +} + +#endif /* truly sync */ diff --git a/Utilities/cmcurl/hostthre.c b/Utilities/cmcurl/hostthre.c new file mode 100644 index 0000000..4f56ccb --- /dev/null +++ b/Utilities/cmcurl/hostthre.c @@ -0,0 +1,551 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <errno.h> + +#define _REENTRANT + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <malloc.h> +#else +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* required for free() prototypes */ +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for the close() proto */ +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#include <stdlib.h> +#endif +#endif + +#ifdef HAVE_SETJMP_H +#include <setjmp.h> +#endif + +#ifdef WIN32 +#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" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#include "inet_ntop.h" + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/*********************************************************************** + * 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_fdset() */ + FILE *stderr_file; +#ifdef CURLRES_IPV6 + struct addrinfo hints; +#endif +}; + +#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; + + /* Sharing the same _iob[] element with our parent thread should + * hopefully make printouts synchronised. I'm not sure it works + * with a static runtime lib (MSVC's libc.lib). + */ + *stderr = *td->stderr_file; + + WSASetLastError (conn->async.status = NO_DATA); /* pending status */ + he = gethostbyname (conn->async.hostname); + if (he) { + Curl_addrinfo4_callback(conn, CURL_ASYNC_SUCCESS, he); + rc = 1; + } + else { + Curl_addrinfo4_callback(conn, (int)WSAGetLastError(), NULL); + rc = 0; + } + TRACE(("Winsock-error %d, addr %s\n", conn->async.status, + he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown")); + 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; + + *stderr = *td->stderr_file; + + itoa(conn->async.port, service, 10); + + WSASetLastError(conn->async.status = NO_DATA); /* pending status */ + + rc = getaddrinfo(conn->async.hostname, service, &td->hints, &res); + + if (rc == 0) { +#ifdef DEBUG_THREADING_GETADDRINFO + dump_addrinfo (conn, res); +#endif + Curl_addrinfo6_callback(conn, CURL_ASYNC_SUCCESS, res); + } + else { + Curl_addrinfo6_callback(conn, (int)WSAGetLastError(), NULL); + TRACE(("Winsock-error %d, no address\n", conn->async.status)); + } + return (rc); + /* An implicit _endthreadex() here */ +} +#endif + +/* + * destroy_thread_data() cleans up async resolver data. + * Complementary of ares_destroy. + */ +static void destroy_thread_data (struct Curl_async *async) +{ + if (async->hostname) + free(async->hostname); + + if (async->os_specific) { + curl_socket_t sock = ((const struct thread_data*)async->os_specific)->dummy_sock; + + if (sock != CURL_SOCKET_BAD) + sclose(sock); + 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); + + 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; + td->stderr_file = stderr; + td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC, + conn, 0, &td->thread_id); +#ifdef CURLRES_IPV6 + curlassert(hints); + td->hints = *hints; +#else + (void) hints; +#endif + + if (!td->thread_hnd) { + SetLastError(errno); + TRACE(("_beginthreadex() failed; %s\n", Curl_strerror(conn,errno))); + destroy_thread_data(&conn->async); + return FALSE; + } + /* This socket is only to keep Curl_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(); + (void)ticks; + + status = WaitForSingleObject(td->thread_hnd, 1000UL*timeout); + if (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) { + /* 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)); + + CloseHandle(td->thread_hnd); + + if(entry) + *entry = conn->async.dns; + + rc = CURLE_OK; + + if (!conn->async.dns) { + /* a name was not resolved */ + 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 if(conn->async.done) { + failf(data, "Could not resolve host: %s; %s", + conn->host.name, Curl_strerror(conn,conn->async.status)); + rc = CURLE_COULDNT_RESOLVE_HOST; + } + else + rc = CURLE_OPERATION_TIMEDOUT; + } + + destroy_thread_data(&conn->async); + + if(CURLE_OK != rc) + /* close the connection, since we must not return failure from here + without cleaning up this connection properly */ + Curl_disconnect(conn); + + 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 */ + 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)); + } + else + TRACE(("not yet\n")); + return CURLE_OK; +} + +CURLcode Curl_fdset(struct connectdata *conn, + fd_set *read_fd_set, + fd_set *write_fd_set, + int *max_fdp) +{ + const struct thread_data *td = + (const struct thread_data *) conn->async.os_specific; + + if (td && td->dummy_sock != CURL_SOCKET_BAD) { + FD_SET(td->dummy_sock,write_fd_set); + *max_fdp = td->dummy_sock; + } + (void) read_fd_set; + return CURLE_OK; +} + +#ifdef CURLRES_IPV4 +/* + * Curl_getaddrinfo() - for Windows threading without ENABLE_IPV6. + */ +Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, + char *hostname, + int port, + int *waitp) +{ + struct hostent *h; + 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; code %lu\n", + hostname, 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, + 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 = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + 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; code %lu\n", + hostname, 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 0000000..7cf543b --- /dev/null +++ b/Utilities/cmcurl/http.c @@ -0,0 +1,1996 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 CURL_DISABLE_HTTP +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#include <sys/time.h> + +#ifdef HAVE_TIME_H +#ifdef TIME_WITH_SYS_TIME +#include <time.h> +#endif +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "transfer.h" +#include "sendf.h" +#include "formdata.h" +#include "progress.h" +#include "base64.h" +#include "cookie.h" +#include "strequal.h" +#include "ssluse.h" +#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 "curl_memory.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->state.buffer, + strlen(data->state.buffer), + &authorization) > 0) { + if(*userp) + free(*userp); + *userp = aprintf( "%sAuthorization: Basic %s\015\012", + 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; +} + +/* + * 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(data->state.authproblem) + return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK; + + if(conn->bits.user_passwd && + ((conn->keep.httpcode == 401) || + (conn->bits.authprobe && conn->keep.httpcode < 300))) { + pickhost = pickoneauth(&data->state.authhost); + if(!pickhost) + data->state.authproblem = TRUE; + } + if(conn->bits.proxy_user_passwd && + ((conn->keep.httpcode == 407) || + (conn->bits.authprobe && conn->keep.httpcode < 300))) { + pickproxy = pickoneauth(&data->state.authproxy); + if(!pickproxy) + data->state.authproblem = TRUE; + } + + if(pickhost || pickproxy) + conn->newurl = strdup(data->change.url); /* clone URL */ + + else if((conn->keep.httpcode < 300) && + (!data->state.authhost.done) && + conn->bits.authprobe) { + /* 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)) { + conn->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", + conn->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; + + curlassert(data); + + if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) || + conn->bits.user_passwd) + /* continue please */ ; + else { + data->state.authhost.done = TRUE; + data->state.authproxy.done = TRUE; + return CURLE_OK; /* no authentication with no user or password */ + } + + if(data->state.authhost.want && !data->state.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. */ + data->state.authhost.picked = data->state.authhost.want; + + if(data->state.authproxy.want && !data->state.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. */ + data->state.authproxy.picked = data->state.authproxy.want; + + /* To prevent the user+password to get sent to other than the original + host due to a location-follow, we do some weirdo checks here */ + if(!data->state.this_is_a_follow || + !data->state.auth_host || + curl_strequal(data->state.auth_host, conn->host.name) || + data->set.http_disable_hostname_check_before_authentication) { + + /* Send proxy authentication header if needed */ + if (conn->bits.httpproxy && + (conn->bits.tunnel_proxy == proxytunnel)) { +#ifdef USE_SSLEAY + if(data->state.authproxy.want == CURLAUTH_NTLM) { + auth=(char *)"NTLM"; + result = Curl_output_ntlm(conn, TRUE); + if(result) + return result; + } + else +#endif + if(data->state.authproxy.want == 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; + } + data->state.authproxy.done = TRUE; + } + else if(data->state.authproxy.want == CURLAUTH_DIGEST) { + auth=(char *)"Digest"; + result = Curl_output_digest(conn, + TRUE, /* proxy */ + (unsigned char *)request, + (unsigned char *)path); + if(result) + return result; + } + + infof(data, "Proxy auth using %s with user '%s'\n", + auth, conn->proxyuser?conn->proxyuser:""); + } + else + /* we have no proxy so let's pretend we're done authenticating + with it */ + data->state.authproxy.done = TRUE; + + /* Send web authentication header if needed */ + { + auth = NULL; +#ifdef HAVE_GSSAPI + if((data->state.authhost.want == 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; + data->state.authhost.done = TRUE; + } + else +#endif +#ifdef USE_SSLEAY + if(data->state.authhost.picked == CURLAUTH_NTLM) { + auth=(char *)"NTLM"; + result = Curl_output_ntlm(conn, FALSE); + if(result) + return result; + } + else +#endif + { + if(data->state.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 if(data->state.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 */ + data->state.authhost.done = TRUE; + } + } + if(auth) + infof(data, "Server auth using %s with user '%s'\n", + auth, conn->user); + } + } + else + data->state.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((int)*start)) + start++; + + /* + * Here we check if we want the specific single authentiction (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 authenticaion 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) { + conn->newurl = strdup(data->change.url); + data->state.authproblem = (conn->newurl == NULL); + } + else { + infof(data, "Authentication problem. Ignoring this.\n"); + data->state.authproblem = TRUE; + } + } + } + else +#endif +#ifdef USE_SSLEAY + /* 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 + if(checkprefix("Digest", start)) { + 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 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 = &conn->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; + + /* + ** 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__,conn->newurl ? conn->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->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= (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE; + + if(http->postsize <= (curl_off_t)fullsize) { + memcpy(buffer, http->postdata, (size_t)http->postsize); + fullsize = (size_t)http->postsize; + + if(http->backup.postsize) { + /* move backup data into focus and continue on that */ + http->postdata = http->backup.postdata; + http->postsize = http->backup.postsize; + conn->fread = 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_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 buffer and frees all associated memory. + * + * 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 */ +{ + ssize_t amount; + CURLcode res; + char *ptr; + size_t size; + struct HTTP *http = conn->proto.http; + size_t sendsize; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + + /* 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; + + 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, amount, + conn->host.dispname); + + *bytes_written += amount; + + 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 */ + } + 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((int)*start)) + start++; + + /* find the end of the header line */ + end = strchr(start, '\r'); /* lines end with CRLF */ + if(!end) { + /* in case there's a non-standard compliant line here */ + end = strchr(start, '\n'); + + if(!end) + /* hm, there's no line ending here, 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 */ +} + +/* + * ConnectHTTPProxyTunnel() requires that we're connected to a HTTP proxy. This + * function will issue the necessary commands to get a seamless tunnel through + * this proxy. After that, the socket can be used just as a normal socket. + */ + +CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, + int sockindex, + char *hostname, + int remote_port) +{ + int subversion=0; + struct SessionHandle *data=conn->data; + struct Curl_transfer_keeper *k = &conn->keep; + CURLcode result; + int res; + + size_t nread; /* total size read */ + int perline; /* count bytes per line */ + bool keepon=TRUE; + ssize_t gotbytes; + char *ptr; + long timeout; /* default timeout in seconds */ + struct timeval interval; + fd_set rkeepfd; + fd_set readfd; + char *line_start; + char *host_port; + curl_socket_t tunnelsocket = conn->sock[sockindex]; + +#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); + + do { + if(conn->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(conn->newurl); + conn->newurl = NULL; + } + + 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) { + + /* OK, now send the connect request to the proxy */ + result = + Curl_sendf(tunnelsocket, conn, + "CONNECT %s:%d HTTP/1.0\015\012" + "%s" + "%s" + "\r\n", + hostname, remote_port, + conn->bits.proxy_user_passwd? + conn->allocptr.proxyuserpwd:"", + data->set.useragent?conn->allocptr.uagent:"" + ); + if(result) + failf(data, "Failed sending CONNECT to proxy"); + } + free(host_port); + if(result) + return result; + + FD_ZERO (&readfd); /* clear it */ + FD_SET (tunnelsocket, &readfd); /* read socket */ + + /* get this in a backup variable to be able to restore it on each lap in + the select() loop */ + rkeepfd = readfd; + + ptr=data->state.buffer; + line_start = ptr; + + nread=0; + perline=0; + + while((nread<BUFSIZE) && (keepon && !error)) { + readfd = rkeepfd; /* set every lap */ + interval.tv_sec = 1; /* timeout each second and check the timeout */ + interval.tv_usec = 0; + + if(data->set.timeout) { + /* if timeout is requested, find out how much remaining time we have */ + timeout = data->set.timeout - /* timeout time */ + Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */ + if(timeout <=0 ) { + failf(data, "Proxy connection aborted due to timeout"); + error = SELECT_TIMEOUT; /* already too little time */ + break; + } + } + + switch (select (tunnelsocket+1, &readfd, NULL, NULL, &interval)) { + case -1: /* select() error, stop reading */ + error = SELECT_ERROR; + failf(data, "Proxy CONNECT aborted due to select() error"); + break; + case 0: /* timeout */ + break; + default: + /* + * This code previously didn't use the kerberos sec_read() code + * to read, but when we use Curl_read() it may do so. Do confirm + * that this is still ok and then remove this comment! + */ + res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, &gotbytes); + if(res< 0) + /* EWOULDBLOCK */ + continue; /* go loop yourself */ + else if(res) + keepon = FALSE; + else if(gotbytes <= 0) { + keepon = FALSE; + error = SELECT_ERROR; + failf(data, "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. + * + * TODO: To make this code work less error-prone, we need to make + * sure that we read and create full lines before we compare them, + * as there is really nothing that stops the proxy from delivering + * the response lines in multiple parts, each part consisting of + * only a little piece of the line(s). */ + int i; + + nread += gotbytes; + 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 output if that is requested */ + if(data->set.verbose) + Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, + conn->host.dispname); + + /* send the header to the callback */ + writetype = CLIENTWRITE_HEADER; + if(data->set.include_header) + writetype |= CLIENTWRITE_BODY; + + result = Curl_client_write(data, 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 */ + 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(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); + + } while(conn->newurl); + + if(200 != k->httpcode) { + failf(data, "Received HTTP code %d from proxy after CONNECT", + k->httpcode); + 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) +{ + 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 occured, can we start talking SSL + */ + + if(conn->bits.tunnel_proxy) { + + /* either SSL over proxy, or explicitly asked for */ + result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET, + conn->host.name, + conn->remote_port); + if(CURLE_OK != result) + return result; + } + + if(conn->protocol & PROT_HTTPS) { + /* now, perform the SSL initialization for this socket */ + result = Curl_SSLConnect(conn, FIRSTSOCKET); + if(result) + return result; + } + + if(conn->bits.user_passwd && !data->state.this_is_a_follow) { + /* Authorization: is requested, this is not a followed location, get the + original host name */ + if (data->state.auth_host) + /* Free to avoid leaking memory on multiple requests*/ + free(data->state.auth_host); + + data->state.auth_host = strdup(conn->host.name); + } + + return CURLE_OK; +} + +/* + * 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) +{ + struct SessionHandle *data; + struct HTTP *http; + (void)status; /* no use for us */ + + data=conn->data; + http=conn->proto.http; + + /* 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; /* cleaer the pointer */ + } + + if(HTTPREQ_POST_FORM == data->set.httpreq) { + conn->bytecount = http->readbytecount + http->writebytecount; + + Curl_formclean(http->sendit); /* Now free that whole lot */ + } + else if(HTTPREQ_PUT == data->set.httpreq) + conn->bytecount = http->readbytecount + http->writebytecount; + + 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; +} + +/* + * 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) +{ + struct SessionHandle *data=conn->data; + char *buf = data->state.buffer; /* this is a short cut to the buffer */ + CURLcode result; + struct HTTP *http; + char *ppath = conn->path; + char *host = conn->host.name; + const char *te = ""; /* tranfer-encoding */ + char *ptr; + char *request; + Curl_HttpReq httpreq = data->set.httpreq; + char *addcookies = NULL; + + if(!conn->proto.http) { + /* Only allocate this struct if we don't already have it! */ + + http = (struct HTTP *)malloc(sizeof(struct HTTP)); + if(!http) + return CURLE_OUT_OF_MEMORY; + memset(http, 0, sizeof(struct HTTP)); + conn->proto.http = http; + } + else + http = conn->proto.http; + + /* We default to persistant connections */ + conn->bits.close = FALSE; + + if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) && + data->set.upload) { + 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.done || !data->state.authproxy.done ) && + (httpreq != HTTPREQ_GET)) { + /* Until we are authenticated, we switch over to HEAD. Unless its a GET + we want to do. The explanation for this is rather long and boring, but + the point is that it can't be done otherwise without risking having to + send the POST or PUT data multiple times. */ + httpreq = HTTPREQ_HEAD; + request = (char *)"HEAD"; + conn->bits.no_body = TRUE; + conn->bits.authprobe = TRUE; /* this is a request done to probe for + authentication methods */ + } + else + conn->bits.authprobe = FALSE; + + Curl_safefree(conn->allocptr.ref); + if(data->change.referer && !checkheaders(data, "Referer:")) + conn->allocptr.ref = aprintf("Referer: %s\015\012", data->change.referer); + else + conn->allocptr.ref = NULL; + + if(data->set.cookie && !checkheaders(data, "Cookie:")) + addcookies = data->set.cookie; + + if(!conn->bits.upload_chunky && (httpreq != HTTPREQ_GET)) { + /* not a chunky transfer yet, but data is to be sent */ + 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"); + te = ""; + } + } + else if(conn->bits.upload_chunky) { + /* RFC2616 section 4.4: + Messages MUST NOT include both a Content-Length header field and a + non-identity transfer-coding. If the message does include a non- + identity transfer-coding, the Content-Length MUST be ignored. */ + + if(!checkheaders(data, "Transfer-Encoding:")) { + te = "Transfer-Encoding: chunked\r\n"; + } + else { + te = ""; + conn->bits.upload_chunky = FALSE; /* transfer-encoding was disabled, + so don't chunkify this! */ + } + } + + Curl_safefree(conn->allocptr.host); + + ptr = checkheaders(data, "Host:"); + if(ptr && !data->state.this_is_a_follow) { + /* 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. */ + char *start = ptr+strlen("Host:"); + while(*start && isspace((int)*start )) + start++; + ptr = start; /* start host-scanning here */ + + /* scan through the string to find the end (space or colon) */ + while(*ptr && !isspace((int)*ptr) && !(':'==*ptr)) + ptr++; + + if(ptr != start) { + size_t len=ptr-start; + 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; + } + + 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; + char *iPtr = strstr(url, conn->host.dispname); + if(iPtr) { + /* 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, iPtr - url); + /* append the new host name instead of the old */ + memcpy(newurl + (iPtr - url), conn->host.name, newlen); + /* append the piece after the host name */ + memcpy(newurl + newlen + (iPtr - url), + iPtr + currlen, /* copy the trailing zero byte too */ + urllen - (iPtr-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, + &http->postsize); + if(CURLE_OK != result) { + /* Curl_getFormData() doesn't use failf() */ + failf(data, "failed creating formpost data"); + return result; + } + } + + + if(!checkheaders(data, "Pragma:")) + http->p_pragma = "Pragma: no-cache\r\n"; + + if(!checkheaders(data, "Accept:")) + http->p_accept = "Accept: */*\r\n"; + + if(( (HTTPREQ_POST == httpreq) || + (HTTPREQ_POST_FORM == httpreq) || + (HTTPREQ_PUT == httpreq) ) && + conn->resume_from) { + /********************************************************************** + * Resuming upload in HTTP means that we PUT or POST and that we have + * got a resume_from value set. The resume value has already created + * a Range: header that will be passed along. We need to "fast forward" + * the file the given number of bytes and decrease the assume upload + * file size before we continue this venture in the dark lands of HTTP. + *********************************************************************/ + + if(conn->resume_from < 0 ) { + /* + * This is meant to get the size of the present remote-file by itself. + * We don't support this now. Bail out! + */ + conn->resume_from = 0; + } + + if(conn->resume_from) { + /* do we still game? */ + 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)(conn->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 != conn->resume_from); /* loop until done */ + + /* now, decrease the size of the read */ + if(data->set.infilesize>0) { + data->set.infilesize -= conn->resume_from; + + if(data->set.infilesize <= 0) { + failf(data, "File already completely uploaded"); + return CURLE_PARTIAL_FILE; + } + } + /* we've passed, proceed as normal */ + } + } + if(conn->bits.use_range) { + /* + * A range is selected. We use different headers whether we're downloading + * or uploading and we always let customized headers override our internal + * ones if any such are specified. + */ + if((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", conn->range); + } + else if((httpreq != HTTPREQ_GET) && + !checkheaders(data, "Content-Range:")) { + + if(conn->resume_from) { + /* This is because "resume" was selected */ + curl_off_t total_expected_size= + conn->resume_from + data->set.infilesize; + conn->allocptr.rangeline = + aprintf("Content-Range: bytes %s%" FORMAT_OFF_T + "/%" FORMAT_OFF_T "\r\n", + conn->range, total_expected_size-1, + total_expected_size); + } + else { + /* Range was selected and then we just pass the incoming range and + append total size */ + conn->allocptr.rangeline = + aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n", + conn->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; + struct curl_slist *headers=data->set.headers; + 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",/* transfer-encoding */ + + request, + ppath, + httpstring, + conn->allocptr.proxyuserpwd? + conn->allocptr.proxyuserpwd:"", + conn->allocptr.userpwd?conn->allocptr.userpwd:"", + (conn->bits.use_range && conn->allocptr.rangeline)? + conn->allocptr.rangeline:"", + (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)? + conn->allocptr.uagent:"", + (conn->allocptr.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> */, + te + ); + + if(result) + return result; + + 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, ppath, + (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; + } + + if(data->set.timecondition) { + struct tm *thistime; + + /* Phil Karn (Fri, 13 Apr 2001) pointed out that the If-Modified-Since + * header family should have their times set in GMT as RFC2616 defines: + * "All HTTP date/time stamps MUST be represented in Greenwich Mean Time + * (GMT), without exception. For the purposes of HTTP, GMT is exactly + * equal to UTC (Coordinated Universal Time)." (see page 20 of RFC2616). + */ + +#ifdef HAVE_GMTIME_R + /* thread-safe version */ + struct tm keeptime; + thistime = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime); +#else + thistime = gmtime(&data->set.timevalue); +#endif + +#ifdef HAVE_STRFTIME + /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ + strftime(buf, BUFSIZE-1, "%a, %d %b %Y %H:%M:%S GMT", thistime); +#else + /* TODO: Right, we *could* write a replacement here */ + strcpy(buf, "no strftime() support"); +#endif + switch(data->set.timecondition) { + case 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; + } + + 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((int)*ptr)) + ptr++; + + if(*ptr) { + /* only send this if the contents was non-blank */ + + result = add_bufferf(req_buffer, "%s\r\n", headers->data); + if(result) + return result; + } + } + headers = headers->next; + } + + 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(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; + } + + if(!checkheaders(data, "Expect:")) { + /* if not disabled explicitly we add a Expect: 100-continue + to the headers which actually speeds up post operations (as + there is one packet coming back from the web server) */ + result = add_bufferf(req_buffer, + "Expect: 100-continue\r\n"); + if(result) + return result; + data->set.expect100header = TRUE; + } + + if(!checkheaders(data, "Content-Type:")) { + /* Get Content-Type: line from Curl_formpostheader. + + The Content-Type header line also contains the MIME boundary + string etc why disabling this header is likely to not make things + work, but we support disabling it anyway. + */ + 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); + if(result) + failf(data, "Failed sending POST request"); + else + /* setup variables for the upcoming transfer */ + result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE, + &http->readbytecount, + FIRSTSOCKET, + &http->writebytecount); + if(result) { + Curl_formclean(http->sendit); /* free that whole lot */ + return result; + } + break; + + case HTTPREQ_PUT: /* Let's PUT the data to the server! */ + + if((data->set.infilesize>0) && !conn->bits.upload_chunky) { + /* only add Content-Length if not uploading chunked */ + result = add_bufferf(req_buffer, + "Content-Length: %" FORMAT_OFF_T "\r\n", /* size */ + data->set.infilesize ); + if(result) + return result; + } + + if(!checkheaders(data, "Expect:")) { + /* if not disabled explicitly we add a Expect: 100-continue + to the headers which actually speeds up post operations (as + there is one packet coming back from the web server) */ + result = add_bufferf(req_buffer, + "Expect: 100-continue\r\n"); + if(result) + return result; + data->set.expect100header = TRUE; + } + + 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, data->set.infilesize); + + /* this sends the buffer and frees all the buffer resources */ + result = add_buffer_send(req_buffer, conn, + &data->info.request_size); + if(result) + failf(data, "Failed sending POST request"); + else + /* prepare for transfer */ + result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE, + &http->readbytecount, + FIRSTSOCKET, + &http->writebytecount); + if(result) + return result; + break; + + case HTTPREQ_POST: + /* this is the simple POST, using x-www-form-urlencoded style */ + + /* store the size of the postfields */ + postsize = data->set.postfieldsize? + 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) { + + if((data->state.authhost.done || data->state.authproxy.done ) + && (postsize < (100*1024))) { + /* If we're not done with the authentication phase, we don't expect + to actually send off any data yet. Hence, we delay the sending of + the body until we receive that friendly 100-continue response */ + + /* The post data is less than 100K, then append it to the 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); + } + 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, + "\r\n0\r\n\r\n", 7); /* end of a chunked + transfer stream */ + } + 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); + + if(!checkheaders(data, "Expect:")) { + /* if not disabled explicitly we add a Expect: 100-continue to the + headers which actually speeds up post operations (as there is + one packet coming back from the web server) */ + add_bufferf(req_buffer, + "Expect: 100-continue\r\n"); + data->set.expect100header = TRUE; + } + + add_buffer(req_buffer, "\r\n", 2); /* end of headers! */ + } + } + else { + add_buffer(req_buffer, "\r\n", 2); /* end of headers! */ + + /* set the upload size to the progress meter */ + Curl_pgrsSetUploadSize(data, data->set.infilesize); + + /* 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); + + if(result) + failf(data, "Failed sending HTTP POST request"); + else + result = + Curl_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); + + if(result) + failf(data, "Failed sending HTTP request"); + else + /* HTTP GET/HEAD download: */ + result = Curl_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 0000000..80c1807 --- /dev/null +++ b/Utilities/cmcurl/http.h @@ -0,0 +1,61 @@ +#ifndef __HTTP_H +#define __HTTP_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_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_ConnectHTTPProxyTunnel(struct connectdata *conn, + int tunnelsocket, + char *hostname, int remote_port); + +/* protocol-specific functions set up to be called by the main engine */ +CURLcode Curl_http(struct connectdata *conn); +CURLcode Curl_http_done(struct connectdata *, CURLcode); +CURLcode Curl_http_connect(struct connectdata *conn); + +/* The following functions are defined in http_chunks.c */ +void Curl_httpchunk_init(struct connectdata *conn); +CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap, + ssize_t length, ssize_t *wrote); + +/* 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 */ + +#endif +#endif diff --git a/Utilities/cmcurl/http_chunks.c b/Utilities/cmcurl/http_chunks.c new file mode 100644 index 0000000..02fdfc5 --- /dev/null +++ b/Utilities/cmcurl/http_chunks.c @@ -0,0 +1,264 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 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 "curl_memory.h" + +#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->proto.http->chunk; + chunk->hexindex=0; /* start at 0 */ + chunk->dataleft=0; /* no data left yet! */ + chunk->state = CHUNK_HEX; /* we get hex first! */ +} + +/* + * chunk_read() returns a OK for normal operations, or a positive return code + * for errors. STOP means this sequence of chunks is complete. The 'wrote' + * argument is set to tell the caller how many bytes we actually passed to the + * client (for byte-counting and whatever). + * + * The states and the state-machine is further explained in the header file. + */ +CHUNKcode Curl_httpchunk_read(struct connectdata *conn, + char *datap, + ssize_t datalen, + ssize_t *wrotep) +{ + CURLcode result=CURLE_OK; + struct Curl_chunker *ch = &conn->proto.http->chunk; + struct Curl_transfer_keeper *k = &conn->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: + if(isxdigit((int)*datap)) { + if(ch->hexindex < MAXNUM_SIZE) { + ch->hexbuffer[ch->hexindex] = *datap; + datap++; + length--; + ch->hexindex++; + } + else { + return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */ + } + } + else { + if(0 == ch->hexindex) { + /* This is illegal data, we received junk where we expected + a hexadecimal digit. */ + return CHUNKE_ILLEGAL_HEX; + } + /* length and datap are unmodified */ + ch->hexbuffer[ch->hexindex]=0; + ch->datasize=strtoul(ch->hexbuffer, NULL, 16); + ch->state = CHUNK_POSTHEX; + } + break; + + case CHUNK_POSTHEX: + /* In this state, we're waiting for CRLF to arrive. We support + this to allow so called chunk-extensions to show up here + before the CRLF comes. */ + if(*datap == '\r') + ch->state = CHUNK_CR; + length--; + datap++; + break; + + case CHUNK_CR: + /* waiting for the LF */ + if(*datap == '\n') { + /* we're now expecting data to come, unless size was zero! */ + if(0 == ch->datasize) { + ch->state = CHUNK_STOP; /* stop reading! */ + if(1 == length) { + /* This was the final byte, return right now */ + return CHUNKE_STOP; + } + } + else + ch->state = CHUNK_DATA; + } + else + /* previously we got a fake CR, go back to CR waiting! */ + ch->state = CHUNK_CR; + datap++; + length--; + break; + + case CHUNK_DATA: + /* we get pure and fine data + + We expect another 'datasize' of data. We have 'length' right now, + it can be more or less than 'datasize'. Get the smallest piece. + */ + piece = (ch->datasize >= length)?length:ch->datasize; + + /* Write the data portion available */ +#ifdef HAVE_LIBZ + switch (conn->keep.content_encoding) { + case IDENTITY: +#endif + if(!k->ignorebody) + result = Curl_client_write(conn->data, CLIENTWRITE_BODY, datap, + piece); +#ifdef HAVE_LIBZ + break; + + case DEFLATE: + /* update conn->keep.str to point to the chunk data. */ + conn->keep.str = datap; + result = Curl_unencode_deflate_write(conn->data, &conn->keep, + (ssize_t)piece); + break; + + case GZIP: + /* update conn->keep.str to point to the chunk data. */ + conn->keep.str = datap; + result = Curl_unencode_gzip_write(conn->data, &conn->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 == '\r') { + ch->state = CHUNK_POSTLF; + datap++; + length--; + } + else + return CHUNKE_BAD_CHUNK; + break; + + case CHUNK_POSTLF: + if(*datap == '\n') { + /* + * The last one before we go back to hex state and start all + * over. + */ + Curl_httpchunk_init(conn); + datap++; + length--; + } + else + return CHUNKE_BAD_CHUNK; + break; + + case CHUNK_STOP: + /* If we arrive here, there is data left in the end of the buffer + even if there's no more chunks to read */ + ch->dataleft = length; + return CHUNKE_STOP; /* return stop */ + default: + return CHUNKE_STATE_ERROR; + } + } + return CHUNKE_OK; +} +#endif /* CURL_DISABLE_HTTP */ diff --git a/Utilities/cmcurl/http_chunks.h b/Utilities/cmcurl/http_chunks.h new file mode 100644 index 0000000..26b79de --- /dev/null +++ b/Utilities/cmcurl/http_chunks.h @@ -0,0 +1,88 @@ +#ifndef __HTTP_CHUNKS_H +#define __HTTP_CHUNKS_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$ + ***************************************************************************/ +/* + * The longest possible hexadecimal number we support in a chunked transfer. + * Weird enough, RFC2616 doesn't set a maximum size! Since we use strtoul() + * to convert it, we "only" support 2^32 bytes chunk data. + */ +#define MAXNUM_SIZE 16 + +typedef enum { + CHUNK_FIRST, /* never use */ + + /* In this we await and buffer all hexadecimal digits until we get one + that isn't a hexadecimal digit. When done, we go POSTHEX */ + CHUNK_HEX, + + /* We have received the hexadecimal digit and we eat all characters until + we get a CRLF pair. When we see a CR we go to the CR state. */ + CHUNK_POSTHEX, + + /* A single CR has been found and we should get a LF right away in this + state or we go back to POSTHEX. When LF is received, we go to DATA. + If the size given was zero, we set state to STOP and return. */ + CHUNK_CR, + + /* We eat the amount of data specified. When done, we move on to the + POST_CR state. */ + CHUNK_DATA, + + /* POSTCR should get a CR and nothing else, then move to POSTLF */ + CHUNK_POSTCR, + + /* POSTLF should get a LF and nothing else, then move back to HEX as + the CRLF combination marks the end of a chunk */ + CHUNK_POSTLF, + + /* This is mainly used to really mark that we're out of the game. + NOTE: that there's a 'dataleft' field in the struct that will tell how + many bytes that were not passed to the client in the end of the last + buffer! */ + CHUNK_STOP, + + CHUNK_LAST /* never use */ +} ChunkyState; + +typedef enum { + CHUNKE_STOP = -1, + CHUNKE_OK = 0, + CHUNKE_TOO_LONG_HEX = 1, + CHUNKE_ILLEGAL_HEX, + CHUNKE_BAD_CHUNK, + CHUNKE_WRITE_ERROR, + CHUNKE_STATE_ERROR, + CHUNKE_BAD_ENCODING, + 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 0000000..b997ccb --- /dev/null +++ b/Utilities/cmcurl/http_digest.c @@ -0,0 +1,482 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 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 "md5.h" +#include "http_digest.h" +#include "strtok.h" +#include "url.h" /* for Curl_safefree() */ +#include "curl_memory.h" + +#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; + char *tmp; + 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((int)*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; + + while(*header && isspace((int)*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; + struct timeval now; + + char **allocuserpwd; + char *userp; + char *passwdp; + struct auth *authp; + + struct SessionHandle *data = conn->data; + struct digestdata *d; + + 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(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_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); + free(ha1); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + ha1 = (unsigned char *)tmp; + } + + /* + 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_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_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 0000000..b4fca06 --- /dev/null +++ b/Utilities/cmcurl/http_digest.h @@ -0,0 +1,53 @@ +#ifndef __HTTP_DIGEST_H +#define __HTTP_DIGEST_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$ + ***************************************************************************/ + +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(struct SessionHandle *data); +void Curl_digest_cleanup_one(struct digestdata *dig); + +#endif diff --git a/Utilities/cmcurl/http_negotiate.c b/Utilities/cmcurl/http_negotiate.c new file mode 100644 index 0000000..62a23f1 --- /dev/null +++ b/Utilities/cmcurl/http_negotiate.c @@ -0,0 +1,332 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" + +#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 <errno.h> + +#include "urldata.h" +#include "sendf.h" +#include "strequal.h" +#include "base64.h" +#include "http_negotiate.h" +#include "curl_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, 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((int)*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((int)*header)) + header++; + + len = strlen(header); + if (len > 0) { + int rawlen; + input_token.length = (len+3)/4 * 3; + input_token.value = malloc(input_token.length); + if (input_token.value == NULL) + return ENOMEM; + rawlen = Curl_base64_decode(header, 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 succeded\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 succeded\n"); + } + } +#endif + len = Curl_base64_encode(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 0000000..ce0d083 --- /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 0000000..fe0b653 --- /dev/null +++ b/Utilities/cmcurl/http_ntlm.c @@ -0,0 +1,585 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" + +/* NTLM details: + + http://davenport.sourceforge.net/ntlm.html + http://www.innovation.ch/java/ntlm.html + +*/ + +#ifndef CURL_DISABLE_HTTP +#ifdef USE_SSLEAY +/* We need OpenSSL for the crypto lib to provide us with MD4 and DES */ + +/* -- 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_ntlm.h" +#include "url.h" +#include "curl_memory.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#include <openssl/des.h> +#include <openssl/md4.h> +#include <openssl/ssl.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 + +/* 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 + +/* + (*) = 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; + + ntlm = proxy?&conn->proxyntlm:&conn->ntlm; + + /* skip initial whitespaces */ + while(*header && isspace((int)*header)) + header++; + + if(checkprefix("NTLM", header)) { + unsigned char buffer[256]; + header += strlen("NTLM"); + + while(*header && isspace((int)*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 = Curl_base64_decode(header, (char *)buffer); + + ntlm->state = NTLMSTATE_TYPE2; /* we got a type-2 */ + + if(size >= 48) + /* the nonce of interest is index [24 .. 31], 8 bytes */ + memcpy(ntlm->nonce, &buffer[24], 8); + + /* at index decimal 20, there's a 32bit NTLM flag field */ + + } + else { + if(ntlm->state >= NTLMSTATE_TYPE1) + return CURLNTLM_BAD; + + ntlm->state = NTLMSTATE_TYPE1; /* we should sent away a type-1 */ + } + } + return CURLNTLM_FINE; +} + +/* + * 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] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); + key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); + key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); + key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); + key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); + key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); + key[7] = (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 calc_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 and nt hashed passwords + */ +static void mkhash(char *password, + unsigned char *nonce, /* 8 bytes */ + unsigned char *lmresp /* must fit 0x18 bytes */ +#ifdef USE_NTRESPONSES + , unsigned char *ntresp /* must fit 0x18 bytes */ +#endif + ) +{ + unsigned char lmbuffer[21]; +#ifdef USE_NTRESPONSES + unsigned char ntbuffer[21]; +#endif + unsigned char *pw; + static const unsigned char magic[] = { + 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 + }; + unsigned int i; + size_t len = strlen(password); + + /* make it fit at least 14 bytes */ + pw = malloc(len<7?14:len*2); + if(!pw) + return; /* this will lead to a badly generated package */ + + if (len > 14) + len = 14; + + for (i=0; i<len; i++) + pw[i] = toupper(password[i]); + + for (; i<14; i++) + pw[i] = 0; + + { + /* 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, 5); + } + /* create LM responses */ + calc_resp(lmbuffer, nonce, lmresp); + +#ifdef USE_NTRESPONSES + { + /* create NT hashed password */ + MD4_CTX MD4; + + len = strlen(password); + + for (i=0; i<len; i++) { + pw[2*i] = password[i]; + pw[2*i+1] = 0; + } + + MD4_Init(&MD4); + MD4_Update(&MD4, pw, 2*len); + MD4_Final(ntbuffer, &MD4); + + memset(ntbuffer+16, 0, 8); + } + + calc_resp(ntbuffer, nonce, ntresp); +#endif + + free(pw); +} + +#define SHORTPAIR(x) ((x) & 0xff), ((x) >> 8) +#define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \ + (((x) >>16)&0xff), ((x)>>24) + +/* this is for creating ntlm header output */ +CURLcode Curl_output_ntlm(struct connectdata *conn, + bool proxy) +{ + const char *domain=""; /* empty */ + const char *host=""; /* empty */ + int domlen=(int)strlen(domain); + int hostlen = (int)strlen(host); + int hostoff; /* host name offset */ + int domoff; /* domain name offset */ + size_t size; + char *base64=NULL; + unsigned char ntlmbuf[256]; /* enough, unless the 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 *)""; + + switch(ntlm->state) { + case NTLMSTATE_TYPE1: + default: /* for the weird cases we (re)start here */ + hostoff = 32; + domoff = hostoff + hostlen; + + /* 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 + + */ + + snprintf((char *)ntlmbuf, sizeof(ntlmbuf), "NTLMSSP%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| /* 2 */ + NTLMFLAG_NEGOTIATE_NTLM_KEY /* 200 */ + /* equals 0x0202 */ + ), + SHORTPAIR(domlen), + SHORTPAIR(domlen), + SHORTPAIR(domoff), + 0,0, + SHORTPAIR(hostlen), + SHORTPAIR(hostlen), + SHORTPAIR(hostoff), + 0,0, + host, domain); + + /* initial packet length */ + size = 32 + hostlen + domlen; + + /* now keeper of the base64 encoded package size */ + size = Curl_base64_encode((char *)ntlmbuf, size, &base64); + + if(size >0 ) { + Curl_safefree(*allocuserpwd); + *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", + proxy?"Proxy-":"", + base64); + free(base64); + } + else + return CURLE_OUT_OF_MEMORY; /* FIX TODO */ + + break; + + case NTLMSTATE_TYPE2: + /* We received the type-2 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 + + */ + + { + int lmrespoff; + int ntrespoff; + int useroff; + unsigned char lmresp[0x18]; /* fixed-size */ +#ifdef USE_NTRESPONSES + unsigned char ntresp[0x18]; /* fixed-size */ +#endif + const char *user; + int userlen; + + user = strchr(userp, '\\'); + if(!user) + user = strchr(userp, '/'); + + if (user) { + domain = userp; + domlen = (int)(user - domain); + user++; + } + else + user = userp; + userlen = (int)strlen(user); + + mkhash(passwdp, &ntlm->nonce[0], lmresp +#ifdef USE_NTRESPONSES + , ntresp +#endif + ); + + domoff = 64; /* always */ + useroff = domoff + domlen; + hostoff = useroff + userlen; + lmrespoff = hostoff + hostlen; + ntrespoff = lmrespoff + 0x18; + + /* Create the big type-3 message binary blob */ + size = snprintf((char *)ntlmbuf, sizeof(ntlmbuf), + "NTLMSSP%c" + "\x03%c%c%c" /* type-3, 32 bits */ + + "%c%c%c%c" /* LanManager length + 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%c%c%c%c" /* 6 zeroes */ + + "\xff\xff" /* message length */ + "%c%c" /* 2 zeroes */ + + "\x01\x82" /* flags */ + "%c%c" /* 2 zeroes */ + + /* 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, + +#ifdef USE_NTRESPONSES + SHORTPAIR(0x18), /* NT-response length, twice */ + SHORTPAIR(0x18), +#else + 0x0, 0x0, + 0x0, 0x0, +#endif + SHORTPAIR(ntrespoff), + 0x0, 0x0, + + 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); + + /* size is now 64 */ + size=64; + ntlmbuf[62]=ntlmbuf[63]=0; + + memcpy(&ntlmbuf[size], domain, domlen); + size += domlen; + + memcpy(&ntlmbuf[size], user, userlen); + size += userlen; + + /* we append the binary hashes to the end of the blob */ + if(size < ((int)sizeof(ntlmbuf) - 0x18)) { + memcpy(&ntlmbuf[size], lmresp, 0x18); + size += 0x18; + } + +#ifdef USE_NTRESPONSES + if(size < ((int)sizeof(ntlmbuf) - 0x18)) { + memcpy(&ntlmbuf[size], ntresp, 0x18); + size += 0x18; + } +#endif + + ntlmbuf[56] = (unsigned char)(size & 0xff); + ntlmbuf[57] = (unsigned char)(size >> 8); + + /* convert the binary blob into base64 */ + size = Curl_base64_encode((char *)ntlmbuf, size, &base64); + + if(size >0 ) { + Curl_safefree(*allocuserpwd); + *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", + proxy?"Proxy-":"", + base64); + 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; +} +#endif /* USE_SSLEAY */ +#endif /* !CURL_DISABLE_HTTP */ diff --git a/Utilities/cmcurl/http_ntlm.h b/Utilities/cmcurl/http_ntlm.h new file mode 100644 index 0000000..4386a1c --- /dev/null +++ b/Utilities/cmcurl/http_ntlm.h @@ -0,0 +1,143 @@ +#ifndef __HTTP_NTLM_H +#define __HTTP_NTLM_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$ + ***************************************************************************/ + +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 SessionHandle *data); + + +/* 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 0000000..8bccc94 --- /dev/null +++ b/Utilities/cmcurl/if2ip.c @@ -0,0 +1,142 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#if !defined(WIN32) && !defined(__BEOS__) && !defined(__CYGWIN32__) && \ + !defined(__riscos__) && !defined(__INTERIX) && !defined(NETWARE) + +#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 + +/* -- if2ip() -- */ +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif + +#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) +#include "inet_ntoa_r.h" +#endif + +#ifdef VMS +#include <inet.h> +#endif + +#include "if2ip.h" +#include "curl_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; + + union { + struct sockaddr_in *sin; + struct sockaddr *s; + } soadd; + + soadd.s = &req.ifr_dstaddr; + memcpy(&in, &(soadd.sin->sin_addr.s_addr), sizeof(in)); +#if defined(HAVE_INET_NTOA_R) + ip = inet_ntoa_r(in,buf,buf_size); +#else + ip = strncpy(buf,inet_ntoa(in),buf_size); + ip[buf_size - 1] = 0; +#endif + } + sclose(dummy); + } + return ip; +} + +/* -- end of if2ip() -- */ +#else +char *Curl_if2ip(const char *interface, char *buf, int buf_size) +{ + (void) interface; + (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 0000000..45a1805 --- /dev/null +++ b/Utilities/cmcurl/if2ip.h @@ -0,0 +1,69 @@ +#ifndef __IF2IP_H +#define __IF2IP_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" + +#if !defined(WIN32) && !defined(__BEOS__) && !defined(__CYGWIN32__) && \ + !defined(__riscos__) && !defined(__INTERIX) +extern char *Curl_if2ip(const char *interface, char *buf, int buf_size); +#else +#define Curl_if2ip(a,b,c) NULL +#endif +#ifdef __INTERIX +/* 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 0000000..7959c49 --- /dev/null +++ b/Utilities/cmcurl/inet_ntoa_r.h @@ -0,0 +1,9 @@ +#ifndef __INET_NTOA_R_H +#define __INET_NTOA_R_H +/* + * My solaris 5.6 system running gcc 2.8.1 does *not* have this prototype + * in any system include file! Isn't that weird? + */ +char *inet_ntoa_r(const struct in_addr in, char *buffer, int buflen); + +#endif diff --git a/Utilities/cmcurl/inet_ntop.c b/Utilities/cmcurl/inet_ntop.c new file mode 100644 index 0000000..6809a92 --- /dev/null +++ b/Utilities/cmcurl/inet_ntop.c @@ -0,0 +1,204 @@ +/* + * 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 WIN32 +#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 u_char* not an in_addr as input + */ +static const char *inet_ntop4 (const u_char *src, char *dst, size_t size) +{ +#ifdef HAVE_INET_NTOA_R + return inet_ntoa_r(*(struct in_addr*)src, dst, size); +#else + union { + const u_char* uch; + const struct in_addr* iad; + } srcaddr; + const char *addr; + srcaddr.uch = src; + addr = inet_ntoa(*srcaddr.iad); + + 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 const char *inet_ntop6 (const u_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; + u_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; + 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 (`dst'), + * Returns NULL on error (see errno). + */ +const char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size) +{ + switch (af) { + case AF_INET: + return inet_ntop4((const u_char*)src, buf, size); +#ifdef ENABLE_IPV6 + case AF_INET6: + return inet_ntop6((const u_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 0000000..5948a12 --- /dev/null +++ b/Utilities/cmcurl/inet_ntop.h @@ -0,0 +1,37 @@ +#ifndef __INET_NTOP_H +#define __INET_NTOP_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" + +#ifdef HAVE_INET_NTOP +#define Curl_inet_ntop(af,addr,buf,size) inet_ntop(af,addr,buf,size) +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#else +const char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t 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 0000000..5e8e9b3 --- /dev/null +++ b/Utilities/cmcurl/inet_pton.c @@ -0,0 +1,240 @@ +/* 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 WIN32 +#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, 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, 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) = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr(digits, ch)) != NULL) { + u_int new = *tp * 10 + (pch - digits); + + if (new > 255) + return (0); + *tp = new; + 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; + u_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 0000000..b0a70d4 --- /dev/null +++ b/Utilities/cmcurl/inet_pton.h @@ -0,0 +1,37 @@ +#ifndef __INET_PTON_H +#define __INET_PTON_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" + +#ifdef HAVE_INET_PTON +#define Curl_inet_pton(x,y,z) inet_pton(x,y,z) +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#else +int Curl_inet_pton(int, const char *, void *); +#endif + +#endif /* __INET_PTON_H */ diff --git a/Utilities/cmcurl/krb4.c b/Utilities/cmcurl/krb4.c new file mode 100644 index 0000000..50467e3 --- /dev/null +++ b/Utilities/cmcurl/krb4.c @@ -0,0 +1,408 @@ +/* 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 away like a madman 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) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + +#include "setup.h" + +#ifndef CURL_DISABLE_FTP +#ifdef HAVE_KRB4 + +#include "security.h" +#include "base64.h" +#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 "ftp.h" +#include "sendf.h" +#include "krb4.h" +#include "curl_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; + int 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) { + Curl_infof(data, "%s\n", krb_get_err_text(ret)); + return AUTH_CONTINUE; + } + +#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM + if (krb_get_config_bool("nat_in_use")) { + struct sockaddr_in *localaddr = (struct sockaddr_in *)LOCAL_ADDR; + struct in_addr natAddr; + + if (krb_get_our_ip_for_realm(krb_realmofhost(host), + &natAddr) != KSUCCESS + && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS) + Curl_infof(data, "Can't get address for realm %s\n", + krb_realmofhost(host)); + else { + if (natAddr.s_addr != localaddr->sin_addr.s_addr) { +#ifdef HAVE_INET_NTOA_R + char ntoa_buf[64]; + char *ip = (char *)inet_ntoa_r(natAddr, ntoa_buf, sizeof(ntoa_buf)); +#else + char *ip = (char *)inet_ntoa(natAddr); +#endif + Curl_infof(data, "Using NAT IP address (%s) for kerberos 4\n", ip); + localaddr->sin_addr = natAddr; + } + } + } +#endif + + if(Curl_base64_encode((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, (char *)adat.dat); + if(len < 0) { + Curl_failf(data, "Failed to decode base64 from server"); + return AUTH_ERROR; + } + adat.length = len; + ret = krb_rd_safe(adat.dat, adat.length, &d->key, + (struct sockaddr_in *)hisctladdr, + (struct sockaddr_in *)myctladdr, &msg_data); + if(ret) { + Curl_failf(data, "Error reading reply from server: %s", + krb_get_err_text(ret)); + return AUTH_ERROR; + } + krb_get_int(msg_data.app_data, &cs, 4, 0); + if(cs - checksum != 1) { + Curl_failf(data, "Bad checksum returned from server"); + return AUTH_ERROR; + } + return AUTH_OK; +} + +struct Curl_sec_client_mech Curl_krb4_client_mech = { + "KERBEROS_V4", + sizeof(struct krb4_data), + NULL, /* init */ + krb4_auth, + NULL, /* end */ + krb4_check_prot, + krb4_overhead, + krb4_encode, + krb4_decode +}; + +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]; + int tmp; + ssize_t nread; + int save; + CURLcode result; + + 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, (char *)tkt.dat); + if(tmp < 0) { + Curl_failf(conn->data, "Failed to decode base64 in reply.\n"); + Curl_set_command_prot(conn, save); + return CURLE_FTP_WEIRD_SERVER_REPLY; + } + 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((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 0000000..cded35b --- /dev/null +++ b/Utilities/cmcurl/krb4.h @@ -0,0 +1,27 @@ +#ifndef __KRB4_H +#define __KRB4_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_krb_kauth(struct connectdata *conn); + +#endif diff --git a/Utilities/cmcurl/ldap.c b/Utilities/cmcurl/ldap.c new file mode 100644 index 0000000..62e5b26 --- /dev/null +++ b/Utilities/cmcurl/ldap.c @@ -0,0 +1,625 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 CURL_DISABLE_LDAP +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +#if defined(WIN32) +# include <windows.h> +# include <malloc.h> +# 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 "curl_memory.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 (-Gr). + */ +#if !defined(WIN32) && !defined(__cdecl) +#define __cdecl +#endif + +#ifndef LDAP_SIZELIMIT_EXCEEDED +#define LDAP_SIZELIMIT_EXCEEDED 4 +#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 + +typedef void * (*dynafunc)(void *input); + +/*********************************************************************** + */ +static void *libldap = NULL; +#ifndef WIN32 +static void *liblber = NULL; +#endif + +static int DynaOpen(const char **mod_name) +{ +#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) + if (libldap == NULL) { + /* + * libldap.so should be able to resolve its dependency on + * liblber.so automatically, but since it does not we will + * handle it here by opening liblber.so as global. + */ + *mod_name = "liblber.so"; + liblber = dlopen(*mod_name, DLOPEN_MODE); + + /* Assume loading libldap.so will fail if loading of liblber.so failed + */ + if (liblber) { + *mod_name = "libldap.so"; + libldap = dlopen(*mod_name, RTLD_LAZY); + } + } + return (libldap != NULL && liblber != NULL); + +#elif defined(WIN32) + *mod_name = "wldap32.dll"; + if (!libldap) + libldap = (void*)LoadLibrary(*mod_name); + return (libldap != NULL); + +#else + return (0); +#endif +} + +static void DynaClose(void) +{ +#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) + if (libldap) { + dlclose(libldap); + libldap=NULL; + } + if (liblber) { + dlclose(liblber); + liblber=NULL; + } +#elif defined(WIN32) + if (libldap) { + FreeLibrary ((HMODULE)libldap); + libldap = NULL; + } +#endif +} + +static dynafunc DynaGetFunction(const char *name) +{ + dynafunc func = (dynafunc)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); + } +#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) +{ + 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 *); + char **(__cdecl *ldap_get_values)(void *, void *, const char *); + void (__cdecl *ldap_value_free)(char **); + void (__cdecl *ldap_memfree)(void *); + void (__cdecl *ber_free)(void *, int); + + 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; + + 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 *(*)(char *, int), ldap_init); + DYNA_GET_FUNCTION(int (*)(void *, char *, char *), ldap_simple_bind_s); + DYNA_GET_FUNCTION(int (*)(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 (*)(void *, char *, int, char *, char **, int, + void **), ldap_search_s); + DYNA_GET_FUNCTION(void *(*)(void *, void *), ldap_first_entry); + DYNA_GET_FUNCTION(void *(*)(void *, void *), ldap_next_entry); + DYNA_GET_FUNCTION(char *(*)(int), ldap_err2string); + DYNA_GET_FUNCTION(char *(*)(void *, void *), ldap_get_dn); + DYNA_GET_FUNCTION(char *(*)(void *, void *, void **), ldap_first_attribute); + DYNA_GET_FUNCTION(char *(*)(void *, void *, void *), ldap_next_attribute); + DYNA_GET_FUNCTION(char **(*)(void *, void *, const char *), ldap_get_values); + DYNA_GET_FUNCTION(void (*)(char **), ldap_value_free); + DYNA_GET_FUNCTION(void (*)(void *), ldap_memfree); + DYNA_GET_FUNCTION(void (*)(void *, int), ber_free); + + 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; + } + + 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(data, CLIENTWRITE_BODY, (char *)"DN: ", 4); + Curl_client_write(data, CLIENTWRITE_BODY, (char *)dn, 0); + Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); + + for (attribute = (*ldap_first_attribute)(server, entryIterator, &ber); + attribute; + attribute = (*ldap_next_attribute)(server, entryIterator, ber)) + { + char **vals = (*ldap_get_values)(server, entryIterator, attribute); + + if (vals != NULL) + { + for (i = 0; (vals[i] != NULL); i++) + { + Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1); + Curl_client_write(data, CLIENTWRITE_BODY, (char*) attribute, 0); + Curl_client_write(data, CLIENTWRITE_BODY, (char *)": ", 2); + Curl_client_write(data, CLIENTWRITE_BODY, vals[i], 0); + Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 0); + } + + /* Free memory used to store values */ + (*ldap_value_free)(vals); + } + Curl_client_write(data, 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_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + + 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 (LDAPURLDesc *ludp) +{ + int i; + + if (ludp->lud_filter) { + ludp->lud_filter = curl_unescape(ludp->lud_filter, 0); + if (!ludp->lud_filter) + return (FALSE); + } + + for (i = 0; ludp->lud_attrs && ludp->lud_attrs[i]; i++) { + ludp->lud_attrs[i] = curl_unescape(ludp->lud_attrs[i], 0); + if (!ludp->lud_attrs[i]) + return (FALSE); + } + + for (i = 0; ludp->lud_exts && ludp->lud_exts[i]; i++) { + ludp->lud_exts[i] = curl_unescape(ludp->lud_exts[i], 0); + if (!ludp->lud_exts[i]) + return (FALSE); + } + + if (ludp->lud_dn) { + char *dn = ludp->lud_dn; + char *new_dn = curl_unescape(dn, 0); + + free(dn); + if (!new_dn) + return (FALSE); + ludp->lud_dn = new_dn; + } + 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->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->path || conn->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->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(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 0000000..b95cf74 --- /dev/null +++ b/Utilities/cmcurl/ldap.h @@ -0,0 +1,29 @@ +#ifndef __LDAP_H +#define __LDAP_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_LDAP +CURLcode Curl_ldap(struct connectdata *conn); +#endif +#endif /* __LDAP_H */ diff --git a/Utilities/cmcurl/llist.c b/Utilities/cmcurl/llist.c new file mode 100644 index 0000000..90ac1c8 --- /dev/null +++ b/Utilities/cmcurl/llist.c @@ -0,0 +1,130 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <stdlib.h> + +#include "llist.h" +#include "curl_memory.h" + +/* this must be the last include file */ +#include "memdebug.h" + +void +Curl_llist_init(curl_llist *l, curl_llist_dtor dtor) +{ + l->size = 0; + l->dtor = dtor; + l->head = NULL; + l->tail = NULL; +} + +curl_llist * +Curl_llist_alloc(curl_llist_dtor dtor) +{ + curl_llist *list; + + list = (curl_llist *)malloc(sizeof(curl_llist)); + if(NULL == list) + return NULL; + + Curl_llist_init(list, dtor); + + return list; +} + +/* + * Curl_llist_insert_next() returns 1 on success and 0 on failure. + */ +int +Curl_llist_insert_next(curl_llist *list, curl_llist_element *e, const void *p) +{ + curl_llist_element *ne = + (curl_llist_element *) malloc(sizeof(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(curl_llist *list, curl_llist_element *e, void *user) +{ + if (e == NULL || list->size == 0) + return 1; + + if (e == list->head) { + list->head = e->next; + + if (list->head == NULL) + list->tail = NULL; + else + e->next->prev = NULL; + } else { + e->prev->next = e->next; + if (!e->next) + list->tail = e->prev; + else + e->next->prev = e->prev; + } + + list->dtor(user, e->ptr); + free(e); + --list->size; + + return 1; +} + +void +Curl_llist_destroy(curl_llist *list, void *user) +{ + if(list) { + while (list->size > 0) + Curl_llist_remove(list, list->tail, user); + + free(list); + } +} diff --git a/Utilities/cmcurl/llist.h b/Utilities/cmcurl/llist.h new file mode 100644 index 0000000..4f76513 --- /dev/null +++ b/Utilities/cmcurl/llist.h @@ -0,0 +1,56 @@ +#ifndef __LLIST_H +#define __LLIST_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 <stddef.h> + +typedef void (*curl_llist_dtor)(void *, void *); + +typedef struct _curl_llist_element { + void *ptr; + + struct _curl_llist_element *prev; + struct _curl_llist_element *next; +} curl_llist_element; + +typedef struct _curl_llist { + curl_llist_element *head; + curl_llist_element *tail; + + curl_llist_dtor dtor; + + size_t size; +} curl_llist; + +void Curl_llist_init(curl_llist *, curl_llist_dtor); +curl_llist *Curl_llist_alloc(curl_llist_dtor); +int Curl_llist_insert_next(curl_llist *, curl_llist_element *, const void *); +int Curl_llist_insert_prev(curl_llist *, curl_llist_element *, const void *); +int Curl_llist_remove(curl_llist *, curl_llist_element *, void *); +int Curl_llist_remove_next(curl_llist *, curl_llist_element *, void *); +size_t Curl_llist_count(curl_llist *); +void Curl_llist_destroy(curl_llist *, void *); + +#endif diff --git a/Utilities/cmcurl/md5.c b/Utilities/cmcurl/md5.c new file mode 100644 index 0000000..269726b --- /dev/null +++ b/Utilities/cmcurl/md5.c @@ -0,0 +1,348 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 USE_SSLEAY +/* 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 *, 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], unsigned char [64]); +static void Encode(unsigned char *, UINT4 *, unsigned int); +static void Decode(UINT4 *, unsigned char *, unsigned int); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, 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 */ + 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], + 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, + 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 */ + unsigned char *input) +{ + MD5_CTX ctx; + MD5_Init(&ctx); + MD5_Update(&ctx, input, strlen((char *)input)); + MD5_Final(outbuffer, &ctx); +} diff --git a/Utilities/cmcurl/md5.h b/Utilities/cmcurl/md5.h new file mode 100644 index 0000000..12b3a5e --- /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, + unsigned char *input); + +#endif diff --git a/Utilities/cmcurl/memdebug.c b/Utilities/cmcurl/memdebug.c new file mode 100644 index 0000000..799fe7c --- /dev/null +++ b/Utilities/cmcurl/memdebug.c @@ -0,0 +1,293 @@ +#ifdef CURLDEBUG +/*************************************************************************** + * _ _ ____ _ + * 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 <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 "curl_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; +static bool memlimit; /* enable memory limit */ +static long memsize; /* set number of mallocs allowed */ + +/* this sets the log file name */ +void curl_memdebug(const char *logname) +{ + 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) +{ + 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); + 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(0x%x, %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 0000000..42574cf --- /dev/null +++ b/Utilities/cmcurl/memdebug.h @@ -0,0 +1,108 @@ +#ifdef CURLDEBUG +#ifndef _CURL_MEDEBUG_H +#define _CURL_MEDEBUG_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" + +#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 */ +void *curl_domalloc(size_t size, int line, const char *source); +void *curl_docalloc(size_t elements, size_t size, int line, const char *source); +void *curl_dorealloc(void *ptr, size_t size, int line, const char *source); +void curl_dofree(void *ptr, int line, const char *source); +char *curl_dostrdup(const char *str, int line, const char *source); +void curl_memdebug(const char *logname); +void curl_memlimit(long limit); + +/* file descriptor manipulators */ +int curl_socket(int domain, int type, int protocol, int line , const char *); +int curl_sclose(int sockfd, int, const char *source); +int curl_accept(int s, void *addr, void *addrlen, + int line, const char *source); + +/* FILE functions */ +FILE *curl_fopen(const char *file, const char *mode, int line, + const char *source); +int curl_fclose(FILE *file, int line, const char *source); + +#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__) + +#define getaddrinfo(host,serv,hint,res) \ + curl_dogetaddrinfo(host,serv,hint,res,__LINE__,__FILE__) +#define getnameinfo(sa,salen,host,hostlen,serv,servlen,flags) \ + curl_dogetnameinfo(sa,salen,host,hostlen,serv,servlen,flags, __LINE__, \ + __FILE__) +#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/mprintf.c b/Utilities/cmcurl/mprintf.c new file mode 100644 index 0000000..d620fcd --- /dev/null +++ b/Utilities/cmcurl/mprintf.c @@ -0,0 +1,1219 @@ +/**************************************************************************** + * + * $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> + +#include <curl/mprintf.h> + +#ifndef SIZEOF_LONG_DOUBLE +#define SIZEOF_LONG_DOUBLE 0 +#endif + +#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 "curl_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 + + +/* 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((int)*input)) { + number *= 10; + number += *input-'0'; + input++; + } + if(number && ('$'==*input++)) { + *end = input; + return number; + } + return 0; +} + +static BOOL dprintf_IsQualifierNoDollar(char c) +{ + switch (c) { + case '-': case '+': case ' ': case '#': case '.': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'h': case 'l': case 'L': case 'z': case 'q': + 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 -= workend - w; + prec -= workend - w; + + if (alt && base == 8 && prec <= 0) { + *w-- = '0'; + --width; + } + + if (prec > 0) { + width -= prec; + while (prec-- > 0) + *w-- = '0'; + } + + if (alt && base == 16) + width -= 2; + + if (is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE)) + --width; + + if (!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL)) + while (width-- > 0) + OUTCHAR(' '); + + if (is_neg) + OUTCHAR('-'); + else if (p->flags & FLAGS_SHOWSIGN) + OUTCHAR('+'); + else if (p->flags & FLAGS_SPACE) + OUTCHAR(' '); + + if (alt && base == 16) { + OUTCHAR('0'); + if(p->flags & FLAGS_UPPER) + OUTCHAR('X'); + else + OUTCHAR('x'); + } + + if (!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL)) + while (width-- > 0) + OUTCHAR('0'); + + /* Write the number. */ + while (++w <= workend) { + OUTCHAR(*w); + } + + if (p->flags & FLAGS_LEFT) + while (width-- > 0) + OUTCHAR(' '); + } + break; + + case FORMAT_STRING: + /* String. */ + { + static char null[] = "(nil)"; + char *str; + size_t len; + + str = (char *) p->data.str; + if ( str == NULL) { + /* Write null[] if there's space. */ + if (prec == -1 || prec >= (long) sizeof(null) - 1) { + str = null; + len = sizeof(null) - 1; + /* Disable quotes around (nil) */ + p->flags &= (~FLAGS_ALT); + } + else { + str = (char *)""; + len = 0; + } + } + else + len = strlen(str); + + if (prec != -1 && (size_t) prec < len) + len = prec; + width -= len; + + if (p->flags & FLAGS_ALT) + OUTCHAR('"'); + + if (!(p->flags&FLAGS_LEFT)) + while (width-- > 0) + OUTCHAR(' '); + + while (len-- > 0) + OUTCHAR(*str++); + if (p->flags&FLAGS_LEFT) + while (width-- > 0) + OUTCHAR(' '); + + if (p->flags & FLAGS_ALT) + OUTCHAR('"'); + } + break; + + case FORMAT_PTR: + /* Generic pointer. */ + { + void *ptr; + ptr = (void *) p->data.ptr; + if (ptr != NULL) { + /* If the pointer is not NULL, write it as a %#x spec. */ + base = 16; + digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits; + alt = 1; + num = (unsigned long) ptr; + is_neg = 0; + goto number; + } + else { + /* Write "(nil)" for a nil pointer. */ + static char strnil[] = "(nil)"; + 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; + } + (void)left; + 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; +} + +#ifndef WIN32 /* not needed on win32 */ +extern int fputc(int, FILE *); +#endif + +int curl_mprintf(const char *format, ...) +{ + int retcode; + va_list ap_save; /* argument pointer */ + va_start(ap_save, format); + retcode = dprintf_formatf(stdout, fputc, format, ap_save); + va_end(ap_save); + return retcode; +} + +int curl_mfprintf(FILE *whereto, const char *format, ...) +{ + int retcode; + va_list ap_save; /* argument pointer */ + va_start(ap_save, format); + retcode = dprintf_formatf(whereto, fputc, format, ap_save); + va_end(ap_save); + return retcode; +} + +int curl_mvsprintf(char *buffer, const char *format, va_list ap_save) +{ + int retcode; + retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save); + *buffer=0; /* we terminate this with a zero byte */ + return retcode; +} + +int curl_mvprintf(const char *format, va_list ap_save) +{ + return dprintf_formatf(stdout, fputc, format, ap_save); +} + +int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save) +{ + return dprintf_formatf(whereto, fputc, format, ap_save); +} + +#ifdef DPRINTF_DEBUG +int main() +{ + char buffer[129]; + char *ptr; +#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 0000000..a604af8 --- /dev/null +++ b/Utilities/cmcurl/multi.c @@ -0,0 +1,648 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <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 "curl_memory.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, + CURLM_STATE_CONNECT, /* resolve/connect has been sent off */ + CURLM_STATE_WAITRESOLVE, /* we're awaiting the resolve to finalize */ + CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */ + CURLM_STATE_DO, /* send off the request (part 1) */ + CURLM_STATE_DO_MORE, /* send off the request (part 2) */ + CURLM_STATE_PERFORM, /* transfer data */ + CURLM_STATE_DONE, /* post data transfer operation */ + CURLM_STATE_COMPLETED, /* operation complete */ + + CURLM_STATE_LAST /* not a true state, never use this */ +} CURLMstate; + +struct Curl_one_easy { + /* first, two fields for the linked list of these */ + struct Curl_one_easy *next; + struct Curl_one_easy *prev; + + struct SessionHandle *easy_handle; /* the easy handle for this unit */ + struct connectdata *easy_conn; /* the "unit's" connection */ + + CURLMstate state; /* the handle's state */ + CURLcode result; /* previous result */ + + 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 */ +}; + + +#define CURL_MULTI_HANDLE 0x000bab1e + +#define GOOD_MULTI_HANDLE(x) ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE)) +#define GOOD_EASY_HANDLE(x) (x) + +/* This is the struct known as CURLM on the outside */ +struct Curl_multi { + /* First a simple identifier to easier detect if a user mix up + this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */ + long type; + + /* We have a linked list with easy handles */ + struct Curl_one_easy easy; + /* This is the amount of entries in the linked list above. */ + int num_easy; + + int num_msgs; /* total amount of messages in the easy handles */ + + /* Hostname cache */ + curl_hash *hostcache; +}; + + +CURLM *curl_multi_init(void) +{ + struct Curl_multi *multi; + + multi = (void *)malloc(sizeof(struct Curl_multi)); + + if(multi) { + memset(multi, 0, sizeof(struct Curl_multi)); + multi->type = CURL_MULTI_HANDLE; + } + else + return NULL; + + multi->hostcache = Curl_mk_dnscache(); + if(!multi->hostcache) { + /* failure, free mem and bail out */ + free(multi); + multi = 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; + + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Verify that we got a somewhat good easy handle too */ + if(!GOOD_EASY_HANDLE(easy_handle)) + return CURLM_BAD_EASY_HANDLE; + + /* Now, time to add an easy handle to the multi stack */ + easy = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy)); + if(!easy) + return CURLM_OUT_OF_MEMORY; + + /* clean it all first (just to be sure) */ + memset(easy, 0, sizeof(struct Curl_one_easy)); + + /* set the easy handle */ + easy->easy_handle = easy_handle; + easy->state = CURLM_STATE_INIT; + + /* for multi interface connections, we share DNS cache automaticly */ + easy->easy_handle->hostcache = multi->hostcache; + + /* We add this new entry first in the list. We make our 'next' point to the + previous next and our 'prev' point back to the 'first' struct */ + easy->next = multi->easy.next; + easy->prev = &multi->easy; + + /* make 'easy' the first node in the chain */ + multi->easy.next = easy; + + /* if there was a next node, make sure its 'prev' pointer links back to + the new node */ + if(easy->next) + easy->next->prev = easy; + + /* increase the node-counter */ + multi->num_easy++; + + return CURLM_CALL_MULTI_PERFORM; +} + +CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Verify that we got a somewhat good easy handle too */ + if(!GOOD_EASY_HANDLE(curl_handle)) + return CURLM_BAD_EASY_HANDLE; + + /* scan through the list and remove the 'curl_handle' */ + easy = multi->easy.next; + while(easy) { + if(easy->easy_handle == (struct SessionHandle *)curl_handle) + break; + easy=easy->next; + } + if(easy) { + /* If the 'state' is not INIT or COMPLETED, we might need to do something + nice to put the easy_handle in a good known state when this returns. */ + + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->hostcache = NULL; + + /* make the previous node point to our next */ + if(easy->prev) + easy->prev->next = easy->next; + /* make our next point to our previous node */ + if(easy->next) + easy->next->prev = easy->prev; + + /* NOTE NOTE NOTE + We do not touch the easy handle here! */ + if (easy->msg) + free(easy->msg); + free(easy); + + multi->num_easy--; /* one less to care about now */ + + return CURLM_OK; + } + else + return CURLM_BAD_EASY_HANDLE; /* twasn't found */ +} + +CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, fd_set *write_fd_set, + fd_set *exc_fd_set, int *max_fd) +{ + /* Scan through all the easy handles to get the file descriptors set. + Some easy handles may not have connected to the remote host yet, + and then we must make sure that is done. */ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + int this_max_fd=-1; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + *max_fd = -1; /* so far none! */ + + easy=multi->easy.next; + while(easy) { + switch(easy->state) { + default: + break; + case CURLM_STATE_WAITRESOLVE: + /* waiting for a resolve to complete */ + Curl_fdset(easy->easy_conn, read_fd_set, write_fd_set, &this_max_fd); + if(this_max_fd > *max_fd) + *max_fd = this_max_fd; + break; + + case CURLM_STATE_WAITCONNECT: + case CURLM_STATE_DO_MORE: + { + /* when we're waiting for a connect, we wait for the socket to + become writable */ + struct connectdata *conn = easy->easy_conn; + curl_socket_t sockfd; + + if(CURLM_STATE_WAITCONNECT == easy->state) { + sockfd = conn->sock[FIRSTSOCKET]; + FD_SET(sockfd, write_fd_set); + } + else { + /* 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 */ + sockfd = conn->sock[SECONDARYSOCKET]; + FD_SET(sockfd, write_fd_set); + } + + if((int)sockfd > *max_fd) + *max_fd = (int)sockfd; + } + break; + case CURLM_STATE_PERFORM: + /* This should have a set of file descriptors for us to set. */ + /* after the transfer is done, go DONE */ + + Curl_single_fdset(easy->easy_conn, + read_fd_set, write_fd_set, + exc_fd_set, &this_max_fd); + + /* remember the maximum file descriptor */ + if(this_max_fd > *max_fd) + *max_fd = this_max_fd; + + break; + } + easy = easy->next; /* check next handle */ + } + + return CURLM_OK; +} + +CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + bool done; + CURLMcode result=CURLM_OK; + struct Curl_message *msg; + bool connected; + bool async; + + *running_handles = 0; /* bump this once for every living handle */ + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + easy=multi->easy.next; + while(easy) { +#if 0 + fprintf(stderr, "HANDLE %p: State: %x\n", + (char *)easy, easy->state); +#endif + do { + 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); + 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); + if(CURLE_OK == easy->result) + easy->state = CURLM_STATE_CONNECT; + else + free(gotourl); + } + else { + easy->result = CURLE_OUT_OF_MEMORY; + easy->state = 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 */ + easy->state = 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); + + if(CURLE_OK == easy->result) { + if(async) + /* We're now waiting for an asynchronous name lookup */ + easy->state = CURLM_STATE_WAITRESOLVE; + else { + /* after the connect has been sent off, go WAITCONNECT */ + easy->state = CURLM_STATE_WAITCONNECT; + result = CURLM_CALL_MULTI_PERFORM; + } + } + 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); + + 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 */ + + easy->state = 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); + + 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) { + /* after the connect has completed, go DO */ + easy->state = CURLM_STATE_DO; + result = CURLM_CALL_MULTI_PERFORM; + } + break; + + case CURLM_STATE_DO: + /* Do the fetch or put request */ + easy->result = Curl_do(&easy->easy_conn); + if(CURLE_OK == easy->result) { + + /* 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 */ + easy->state = CURLM_STATE_DO_MORE; + result = CURLM_OK; + } + else { + /* we're done with the DO, now PERFORM */ + easy->result = Curl_readwrite_init(easy->easy_conn); + if(CURLE_OK == easy->result) { + easy->state = CURLM_STATE_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; + } + } + } + break; + + case CURLM_STATE_DO_MORE: + /* + * First, check if we really are 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 PERFORM + */ + easy->result = Curl_do_more(easy->easy_conn); + + if(CURLE_OK == easy->result) + easy->result = Curl_readwrite_init(easy->easy_conn); + + if(CURLE_OK == easy->result) { + easy->state = CURLM_STATE_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; + } + } + break; + + case CURLM_STATE_PERFORM: + /* read/write data if it is ready to do so */ + easy->result = Curl_readwrite(easy->easy_conn, &done); + + 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]=-1; + } + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result); + } + + /* after the transfer is done, go DONE */ + else if(TRUE == done) { + + /* 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_conn->newurl) { + char *newurl = easy->easy_conn->newurl; + easy->easy_conn->newurl = NULL; + easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + if(easy->result == CURLE_OK) + easy->result = Curl_follow(easy->easy_handle, newurl); + if(CURLE_OK == easy->result) { + easy->state = CURLM_STATE_CONNECT; + result = CURLM_CALL_MULTI_PERFORM; + } + } + else { + easy->state = CURLM_STATE_DONE; + result = CURLM_CALL_MULTI_PERFORM; + } + } + break; + case CURLM_STATE_DONE: + /* post-transfer command */ + easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + + /* after we have DONE what we're supposed to do, go COMPLETED, and + it doesn't matter what the Curl_done() returned! */ + easy->state = CURLM_STATE_COMPLETED; + break; + + case CURLM_STATE_COMPLETED: + /* this is a completed transfer, it is likely to still be connected */ + + /* This node should be delinked from the list now and we should post + an information message that we are complete. */ + break; + default: + return CURLM_INTERNAL_ERROR; + } + + if(CURLM_STATE_COMPLETED != easy->state) { + 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->state = CURLM_STATE_COMPLETED; + } + else + /* this one still lives! */ + (*running_handles)++; + } + + } while (easy->easy_handle->change.url_changed); + + if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->hostcache = NULL; + + /* 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 */ + } + + easy = easy->next; /* operate on next handle */ + } + + return result; +} + +CURLMcode curl_multi_cleanup(CURLM *multi_handle) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + struct Curl_one_easy *nexteasy; + + if(GOOD_MULTI_HANDLE(multi)) { + multi->type = 0; /* not good anymore */ + Curl_hash_destroy(multi->hostcache); + + /* remove all easy handles */ + easy = multi->easy.next; + while(easy) { + nexteasy=easy->next; + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->hostcache = NULL; + + 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; +} diff --git a/Utilities/cmcurl/netrc.c b/Utilities/cmcurl/netrc.c new file mode 100644 index 0000000..770bfb5 --- /dev/null +++ b/Utilities/cmcurl/netrc.c @@ -0,0 +1,247 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <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 "curl_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) { + printf("NETRC: overridden " NETRC " file: %s\n", home); + 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 + printf("HOST: %s\n", tok); +#endif + retcode=0; /* we did find our host */ + } + else + /* not our host */ + state=NOTHING; + break; + case HOSTVALID: + /* we are now parsing sub-keywords concerning "our" host */ + if(state_login) { + if (specific_login) { + state_our_login = strequal(login, tok); + } + else { + strncpy(login, tok, LOGINSIZE-1); +#ifdef _NETRC_DEBUG + printf("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 + printf("PASSWORD: %s\n", password); +#endif + } + state_password=0; + } + else if(strequal("login", tok)) + state_login=1; + else if(strequal("password", tok)) + state_password=1; + else if(strequal("machine", tok)) { + /* ok, there's machine here go => */ + state = HOSTFOUND; + 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 0000000..939c552 --- /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 0000000..a999dfd --- /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 "curl_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/progress.c b/Utilities/cmcurl/progress.c new file mode 100644 index 0000000..36be56e --- /dev/null +++ b/Utilities/cmcurl/progress.c @@ -0,0 +1,426 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <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 */ + 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"); +} + +/* 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(conn->resume_from) + fprintf(data->set.err, + "** Resuming transfer from byte position %" FORMAT_OFF_T + "\n", + conn->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 > 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 0000000..dcfcaf7 --- /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 0000000..861f953 --- /dev/null +++ b/Utilities/cmcurl/security.c @@ -0,0 +1,483 @@ +/* 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 "security.h" +#include <stdlib.h> +#include <string.h> +#include <netdb.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "base64.h" +#include "sendf.h" +#include "ftp.h" +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +static struct { + enum protection_level level; + const char *name; +} level_names[] = { + { prot_clear, "clear" }, + { prot_safe, "safe" }, + { prot_confidential, "confidential" }, + { prot_private, "private" } +}; + +static enum protection_level +name_to_level(const char *name) +{ + int i; + for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) + if(!strncasecmp(level_names[i].name, name, strlen(name))) + return level_names[i].level; + return (enum protection_level)-1; +} + +static struct Curl_sec_client_mech *mechs[] = { +#ifdef KRB5 + /* not supported */ +#endif +#ifdef 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; +} + +int +Curl_sec_putc(struct connectdata *conn, int c, FILE *F) +{ + char ch = c; + if(conn->data_prot == prot_clear) + return putc(c, F); + + buffer_write(&conn->out_buffer, &ch, 1); + if(c == '\n' || conn->out_buffer.index >= 1024 /* XXX */) { + Curl_sec_write(conn, fileno(F), conn->out_buffer.data, + conn->out_buffer.index); + conn->out_buffer.index = 0; + } + return c; +} + +int +Curl_sec_read_msg(struct connectdata *conn, char *s, int level) +{ + int len; + char *buf; + int code; + + buf = malloc(strlen(s)); + len = Curl_base64_decode(s + 4, buf); /* XXX */ + + len = (conn->mech->decode)(conn->app_data, buf, len, level, conn); + if(len < 0) { + free(buf); + return -1; + } + + buf[len] = '\0'; + + if(buf[3] == '-') + code = 0; + else + sscanf(buf, "%d", &code); + if(buf[len-1] == '\n') + buf[len-1] = '\0'; + strcpy(s, buf); + free(buf); + return code; +} + +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; + struct Curl_sec_client_mech **m; + ssize_t nread; + struct SessionHandle *data=conn->data; + int ftpcode; + + for(m = mechs; *m && (*m)->name; m++) { + void *tmp; + + tmp = realloc(conn->app_data, (*m)->size); + if (tmp == NULL) { + failf (data, "realloc %u failed", (*m)->size); + return -1; + } + conn->app_data = tmp; + + if((*m)->init && (*(*m)->init)(conn->app_data) != 0) { + infof(data, "Skipping %s...\n", (*m)->name); + continue; + } + infof(data, "Trying %s...\n", (*m)->name); + + if(Curl_ftpsendf(conn, "AUTH %s", (*m)->name)) + return -1; + + 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/security.h b/Utilities/cmcurl/security.h new file mode 100644 index 0000000..5213ba5 --- /dev/null +++ b/Utilities/cmcurl/security.h @@ -0,0 +1,72 @@ +#ifndef __SECURITY_H +#define __SECURITY_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$ + ***************************************************************************/ + +/* this is a re-write */ + +#include <stdarg.h> +#include "urldata.h" /* for struct connectdata * */ + +struct Curl_sec_client_mech { + const char *name; + size_t size; + int (*init)(void *); + int (*auth)(void *, struct connectdata *); + void (*end)(void *); + int (*check_prot)(void *, int); + int (*overhead)(void *, int, int); + int (*encode)(void *, void*, int, int, void**, struct connectdata *); + int (*decode)(void *, void*, int, int, struct connectdata *); +}; + + +#define AUTH_OK 0 +#define AUTH_CONTINUE 1 +#define AUTH_ERROR 2 + +extern struct Curl_sec_client_mech Curl_krb4_client_mech; + +int Curl_sec_fflush_fd(struct connectdata *conn, int fd); +int Curl_sec_fprintf (struct connectdata *, FILE *, const char *, ...); +int Curl_sec_getc (struct connectdata *conn, FILE *); +int Curl_sec_putc (struct connectdata *conn, int, FILE *); +int Curl_sec_read (struct connectdata *conn, int, void *, int); +int Curl_sec_read_msg (struct connectdata *conn, char *, int); + +int Curl_sec_vfprintf(struct connectdata *, FILE *, const char *, va_list); +int Curl_sec_fprintf2(struct connectdata *conn, FILE *f, const char *fmt, ...); +int Curl_sec_vfprintf2(struct connectdata *conn, FILE *, const char *, va_list); +int Curl_sec_write (struct connectdata *conn, int, char *, int); + +void Curl_sec_end (struct connectdata *); +int Curl_sec_login (struct connectdata *); +void Curl_sec_prot (int, char **); +int Curl_sec_request_prot (struct connectdata *conn, const char *level); +void Curl_sec_set_protection_level(struct connectdata *conn); +void Curl_sec_status (void); + +enum protection_level Curl_set_command_prot(struct connectdata *, + enum protection_level); + +#endif diff --git a/Utilities/cmcurl/sendf.c b/Utilities/cmcurl/sendf.c new file mode 100644 index 0000000..fae2ff3 --- /dev/null +++ b/Utilities/cmcurl/sendf.c @@ -0,0 +1,496 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <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_ourerrno() proto */ + +#define _MPRINTF_REPLACE /* use the internal *printf() functions */ +#include <curl/mprintf.h> + +#ifdef HAVE_KRB4 +#include "security.h" +#endif +#include <string.h> +#include "curl_memory.h" +/* 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 *cuDup = strdup(data); + if(cuDup) { + new_item->next = NULL; + new_item->data = cuDup; + } + 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); +} + +/* 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; + char print_buffer[1024 + 1]; + va_start(ap, fmt); + vsnprintf(print_buffer, 1024, fmt, ap); + va_end(ap); + Curl_debug(data, CURLINFO_TEXT, print_buffer, strlen(print_buffer), 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; + va_start(ap, fmt); + if(data->set.errorbuffer && !data->state.errorbuf) { + vsnprintf(data->set.errorbuffer, CURL_ERROR_SIZE, fmt, ap); + data->state.errorbuf = TRUE; /* wrote error string */ + + if(data->set.verbose) { + size_t len = strlen(data->set.errorbuffer); + bool doneit=FALSE; + if(len < CURL_ERROR_SIZE - 1) { + doneit = TRUE; + data->set.errorbuffer[len] = '\n'; + data->set.errorbuffer[++len] = '\0'; + } + Curl_debug(data, CURLINFO_TEXT, data->set.errorbuffer, len, NULL); + if(doneit) + /* cut off the newline again */ + data->set.errorbuffer[--len]=0; + } + } + 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; + 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, bytes_written, + conn->host.dispname); + + if((size_t)bytes_written != write_len) { + /* if not all was written at once, we must advance the pointer, decrease + the size left and try again! */ + write_len -= bytes_written; + sptr += bytes_written; + } + else + break; + } + + free(s); /* free the output string */ + + return res; +} + +/* + * Curl_write() is an internal write function that sends plain (binary) data + * to the server. Works with plain sockets, 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; + +#ifdef USE_SSLEAY + /* 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]); + /* SSL_write() is said to return 'int' while write() and send() returns + 'size_t' */ + if (conn->ssl[num].use) { + 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[num].handle, mem, (int)len); + + if(rc < 0) { + err = SSL_get_error(conn->ssl[num].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. */ + *written = 0; + return CURLE_OK; + case SSL_ERROR_SYSCALL: + failf(conn->data, "SSL_write() returned SYSCALL, errno = %d\n", + Curl_ourerrno()); + return CURLE_SEND_ERROR; + 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 CURLE_SEND_ERROR; + } + /* a true error */ + failf(conn->data, "SSL_write() return error %d\n", err); + return CURLE_SEND_ERROR; + } + bytes_written = rc; + } + else { +#else + (void)conn; +#endif +#ifdef HAVE_KRB4 + if(conn->sec_complete) { + bytes_written = Curl_sec_write(conn, sockfd, mem, len); + } + else +#endif /* HAVE_KRB4 */ + { + bytes_written = (ssize_t)swrite(sockfd, mem, len); + } + if(-1 == bytes_written) { + int err = Curl_ourerrno(); + + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == err) +#else + /* As pointed out by Christophe Demory on March 11 2003, 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; + } +#ifdef USE_SSLEAY + } +#endif + + *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 SessionHandle *data, + int type, + char *ptr, + size_t len) +{ + size_t wrote; + + if(0 == len) + len = strlen(ptr); + + if(type & CLIENTWRITE_BODY) { + wrote = data->set.fwrite(ptr, 1, len, data->set.out); + if(wrote != len) { + failf (data, "Failed writing body"); + return CURLE_WRITE_ERROR; + } + } + if((type & CLIENTWRITE_HEADER) && + (data->set.fwrite_header || data->set.writeheader) ) { + /* + * Write headers to the same callback or to the especially setup + * header callback function (added after version 7.7.1). + */ + curl_write_callback writeit= + data->set.fwrite_header?data->set.fwrite_header:data->set.fwrite; + + wrote = writeit(ptr, 1, len, data->set.writeheader); + if(wrote != len) { + failf (data, "Failed writing header"); + return CURLE_WRITE_ERROR; + } + } + + return CURLE_OK; +} + +/* + * Internal read-from-socket function. This is meant to deal with plain + * sockets, SSL sockets and kerberos sockets. + * + * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return + * a regular CURLcode value. + */ +int Curl_read(struct connectdata *conn, /* connection data */ + curl_socket_t sockfd, /* read from this socket */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + ssize_t *n) /* amount bytes read */ +{ + ssize_t nread; +#ifdef USE_SSLEAY + /* 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 (conn->ssl[num].use) { + nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, (int)buffersize); + + 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() */ + return -1; /* basicly EWOULDBLOCK */ + default: + /* openssl/ssl.h says "look at error stack/return value/errno" */ + { + char error_buffer[120]; /* OpenSSL documents that this must be at + least 120 bytes long. */ + unsigned long sslerror = ERR_get_error(); + failf(conn->data, "SSL read: %s, errno %d", + ERR_error_string(sslerror, error_buffer), + Curl_ourerrno() ); + } + return CURLE_RECV_ERROR; + } + } + } + else { +#else + (void)conn; +#endif + *n=0; /* reset amount to zero */ +#ifdef HAVE_KRB4 + if(conn->sec_complete) + nread = Curl_sec_read(conn, sockfd, buf, buffersize); + else +#endif + nread = sread(sockfd, buf, buffersize); + + if(-1 == nread) { + int err = Curl_ourerrno(); +#ifdef WIN32 + if(WSAEWOULDBLOCK == err) +#else + if((EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)) +#endif + return -1; + } + +#ifdef USE_SSLEAY + } +#endif /* USE_SSLEAY */ + *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] = { + "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; + + 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); + break; + default: /* nada */ + break; + } + return 0; +} + +int Curl_debug(struct SessionHandle *data, curl_infotype type, + char *ptr, size_t size, char *host) +{ + int rc; + if(data->set.printhost && host) { + char buffer[160]; + const char *t=NULL; + switch (type) { + case CURLINFO_HEADER_IN: + case CURLINFO_DATA_IN: + t = "from"; + break; + case CURLINFO_HEADER_OUT: + case CURLINFO_DATA_OUT: + t = "to"; + break; + default: + break; + } + + if(t) { + snprintf(buffer, sizeof(buffer), "[Data %s %s]", t, host); + 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 0000000..bdd3a79 --- /dev/null +++ b/Utilities/cmcurl/sendf.h @@ -0,0 +1,56 @@ +#ifndef __SENDF_H +#define __SENDF_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_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, ...); + +#define infof Curl_infof +#define failf Curl_failf + +#define CLIENTWRITE_BODY 1 +#define CLIENTWRITE_HEADER 2 +#define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER) + +CURLcode Curl_client_write(struct SessionHandle *data, int type, char *ptr, + size_t len); + +/* internal read-function, does plain socket, SSL and krb4 */ +int Curl_read(struct connectdata *conn, 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, char *host); + + +#endif diff --git a/Utilities/cmcurl/setup.h b/Utilities/cmcurl/setup.h new file mode 100644 index 0000000..fff6974 --- /dev/null +++ b/Utilities/cmcurl/setup.h @@ -0,0 +1,294 @@ +#ifndef __SETUP_H +#define __SETUP_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 HTTP_ONLY +#define CURL_DISABLE_FTP +#define CURL_DISABLE_LDAP +#define CURL_DISABLE_TELNET +#define CURL_DISABLE_DICT +#define CURL_DISABLE_FILE +#define CURL_DISABLE_GOPHER +#endif + +#if !defined(WIN32) && defined(__WIN32__) +/* This should be a good Borland fix. Alexander J. Oss told us! */ +#define WIN32 +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" /* the configure script results */ +#else +#ifdef WIN32 +/* hand-modified win32 config.h! */ +#include "config-win32.h" +#endif +#endif + +#ifdef macintosh +/* hand-modified MacOS config.h! */ +#include "config-mac.h" +#endif +#ifdef AMIGA +/* hand-modified AmigaOS config.h! */ +#include "amigaos.h" +#endif + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#if !defined(__cplusplus) && !defined(__BEOS__) +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 +#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 prefered (CURL_)FORMAT_OFF_T here */ +#if SIZEOF_CURL_OFF_T > 4 +#define FORMAT_OFF_T "lld" +#else +#define FORMAT_OFF_T "ld" +#endif + +#ifdef NEED_REENTRANT +/* Solaris machines needs _REENTRANT set for a few function prototypes and + things to appear in the #include files. We need to #define it before all + #include files */ +#define _REENTRANT +#endif + +#include <stdio.h> +#ifdef HAVE_ASSERT_H +#include <assert.h> +#endif +#include <errno.h> + +#ifdef __TANDEM /* for nsr-tandem-nsk systems */ +#include <floss.h> +#endif + +#if defined(HAVE_X509_H) && defined(HAVE_SSL_H) && defined(HAVE_RSA_H) && \ +defined(HAVE_PEM_H) && defined(HAVE_ERR_H) && defined(HAVE_CRYPTO_H) && \ +defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) + /* the six important includes files all exist and so do both libs, + defined SSLeay usage */ +#define USE_SSLEAY 1 +#endif +#if defined(HAVE_OPENSSL_X509_H) && defined(HAVE_OPENSSL_SSL_H) && \ +defined(HAVE_OPENSSL_RSA_H) && defined(HAVE_OPENSSL_PEM_H) && \ +defined(HAVE_OPENSSL_ERR_H) && defined(HAVE_OPENSSL_CRYPTO_H) && \ +defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) + /* the six important includes files all exist and so do both libs, + defined SSLeay usage */ +#define USE_SSLEAY 1 +#define USE_OPENSSL 1 +#endif + +#ifndef STDC_HEADERS /* no standard C headers! */ +#include <curl/stdcheaders.h> +#endif + +#if defined(CURLDEBUG) && defined(HAVE_ASSERT_H) +#define curlassert(x) assert(x) +#else +/* does nothing without CURLDEBUG defined */ +#define curlassert(x) +#endif + +#ifdef MSG_NOSIGNAL +/* If we have the MSG_NOSIGNAL define, we make sure to use that in the forth + argument to send() and recv() */ +#define SEND_4TH_ARG MSG_NOSIGNAL +#define HAVE_MSG_NOSIGNAL 1 /* we have MSG_NOSIGNAL */ +#else +#define SEND_4TH_ARG 0 +#endif + + +/* Below we define four functions. They should + 1. close a socket + 2. read from a socket + 3. write to a socket + + 4. set the SIGALRM signal timeout + 5. set dir/file naming defines + */ + +#ifdef WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN /* Prevent including <winsock*.h> in <windows.h> */ +#endif + +#if (defined(ENABLE_IPV6) || defined(CURLDEBUG)) && defined(_MSC_VER) && \ + (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0500) +/* + * Needed to pull in the real getaddrinfo() and not the inline version + * in <wspiAPI.H> which doesn't support IPv6 (IPv4 only). <wspiAPI.H> is + * included from <ws2tcpip.h> for <= 0x0500 SDKs. + */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif + +#include <winsock2.h> /* required by telnet.c */ + +#if defined(ENABLE_IPV6) || defined(USE_SSLEAY) +#include <ws2tcpip.h> +#endif + +#if !defined(__GNUC__) || defined(__MINGW32__) +#define sclose(x) closesocket(x) +#define sread(x,y,z) recv(x,y,z, SEND_4TH_ARG) +#define swrite(x,y,z) (size_t)send(x,y,z, SEND_4TH_ARG) +#undef HAVE_ALARM +#else + /* gcc-for-win is still good :) */ +#define sclose(x) close(x) +#define sread(x,y,z) recv(x,y,z, SEND_4TH_ARG) +#define swrite(x,y,z) send(x,y,z, SEND_4TH_ARG) +#define HAVE_ALARM +#endif + +#define DIR_CHAR "\\" +#define DOT_CHAR "_" + +#else + +#ifdef DJGPP +#define sclose(x) close_s(x) +#define sread(x,y,z) read_s(x,y,z) +#define swrite(x,y,z) write_s(x,y,z) +#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 + +#ifdef __BEOS__ +#define sclose(x) closesocket(x) +#define sread(x,y,z) (ssize_t)recv(x,y,z, SEND_4TH_ARG) +#define swrite(x,y,z) (ssize_t)send(x,y,z, SEND_4TH_ARG) +#else +#define sclose(x) close(x) +#define sread(x,y,z) recv(x,y,z, SEND_4TH_ARG) +#define swrite(x,y,z) send(x,y,z, SEND_4TH_ARG) +#endif + +#define HAVE_ALARM + +#endif + +#ifdef _AMIGASF +#undef HAVE_ALARM +#undef sclose +#define sclose(x) CloseSocket(x) +#endif + +#define DIR_CHAR "/" +#define DOT_CHAR "." + +#ifdef DJGPP +#undef DOT_CHAR +#define DOT_CHAR "_" +#endif + +#ifndef fileno /* sunos 4 have this as a macro! */ +int fileno( FILE *stream); +#endif + +#endif + +/* now typedef our socket type */ +#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 + +#if defined(ENABLE_IPV6) && defined(USE_ARES) +#error "ares does not yet support IPv6. Disable IPv6 or ares and rebuild" +#endif + +#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 + +#ifdef mpeix +#define IOCTL_3_ARGS +#endif + +#ifndef ECONNRESET +#ifdef WSAECONNRESET +#define ECONNRESET WSAECONNRESET +#else +/* This will effectively prevent the code from working in this particular + aspect, but it still compile fine! */ +#define ECONNRESET 10000 +#endif +#endif + +#ifdef NETWARE +#undef HAVE_ALARM +#endif + +#ifdef HAVE_LIBIDN +/* This could benefit from additional checks that some of the used/important + header files are present as well before we define the USE_* define. */ +#define USE_LIBIDN +#define LIBIDN_REQUIRED_VERSION "0.4.1" +#endif + +#endif /* __CONFIG_H */ diff --git a/Utilities/cmcurl/share.c b/Utilities/cmcurl/share.c new file mode 100644 index 0000000..1022d97 --- /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 "curl_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; + +#ifndef CURL_DISABLE_HTTP + 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; + +#ifndef CURL_DISABLE_HTTP + 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); + +#ifndef CURL_DISABLE_HTTP + 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 0000000..5c85c80 --- /dev/null +++ b/Utilities/cmcurl/share.h @@ -0,0 +1,55 @@ +#ifndef __CURL_SHARE_H +#define __CURL_SHARE_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 <curl/curl.h> +#include "cookie.h" + +/* this struct is libcurl-private, don't export details */ +struct Curl_share { + unsigned int specifier; + volatile unsigned int dirty; + + curl_lock_function lockfunc; + curl_unlock_function unlockfunc; + void *clientdata; + + 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/speedcheck.c b/Utilities/cmcurl/speedcheck.c new file mode 100644 index 0000000..33a8e5d --- /dev/null +++ b/Utilities/cmcurl/speedcheck.c @@ -0,0 +1,67 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <stdio.h> +#include <string.h> + +#include <curl/curl.h> +#include "urldata.h" +#include "sendf.h" +#include "speedcheck.h" + +void Curl_speedinit(struct SessionHandle *data) +{ + memset(&data->state.keeps_speed, 0, sizeof(struct timeval)); +} + +CURLcode Curl_speedcheck(struct SessionHandle *data, + struct timeval now) +{ + if((data->progress.current_speed >= 0) && + data->set.low_speed_time && + (Curl_tvlong(data->state.keeps_speed) != 0) && + (data->progress.current_speed < data->set.low_speed_limit)) { + + /* We are now below the "low speed limit". If we are below it + for "low speed time" seconds we consider that enough reason + to abort the download. */ + + if( (Curl_tvdiff(now, data->state.keeps_speed)/1000) > + data->set.low_speed_time) { + /* we have been this slow for long enough, now die */ + failf(data, + "Operation too slow. " + "Less than %d bytes/sec transfered the last %d seconds", + data->set.low_speed_limit, + data->set.low_speed_time); + return CURLE_OPERATION_TIMEOUTED; + } + } + else { + /* we keep up the required speed all right */ + data->state.keeps_speed = now; + } + return CURLE_OK; +} diff --git a/Utilities/cmcurl/speedcheck.h b/Utilities/cmcurl/speedcheck.h new file mode 100644 index 0000000..62475f6 --- /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/ssluse.c b/Utilities/cmcurl/ssluse.c new file mode 100644 index 0000000..04d1c94 --- /dev/null +++ b/Utilities/cmcurl/ssluse.c @@ -0,0 +1,1487 @@ +/*************************************************************************** + * _ _ ____ _ + * 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$ + ***************************************************************************/ + +/* + * The original SSL code for curl was written by + * Linas Vepstas <linas@linas.org> and Sampo Kellomaki <sampo@iki.fi> + */ + +#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_ourerrno() proto */ +#include "strequal.h" + +#define _MPRINTF_REPLACE /* use the internal *printf() functions */ +#include <curl/mprintf.h> + +#ifdef USE_SSLEAY +#include <openssl/rand.h> +#include <openssl/x509v3.h> + +#include "curl_memory.h" + +/* 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 >= 0x00906001L +#define HAVE_ERR_ERROR_STRING_N 1 +#endif + + +#ifndef HAVE_USERDATA_IN_PWD_CALLBACK +static char global_passwd[64]; +#endif + +static int passwd_callback(char *buf, int num, int verify +#if HAVE_USERDATA_IN_PWD_CALLBACK + /* This was introduced in 0.9.4, we can set this + using SSL_CTX_set_default_passwd_cb_userdata() + */ + , void *global_passwd +#endif + ) +{ + if(verify) + fprintf(stderr, "%s\n", buf); + else { + if(num > (int)strlen((char *)global_passwd)) { + strcpy(buf, global_passwd); + return (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 RAND_status()?TRUE:FALSE; +} +#else +#define seed_enough(x) rand_enough(x) +static bool rand_enough(int nread) +{ + /* this is a very silly decision to make */ + return (nread > 500)?TRUE:FALSE; +} +#endif + +static +int random_the_seed(struct SessionHandle *data) +{ + char *buf = data->state.buffer; /* point to the big buffer */ + int nread=0; + + /* Q: should we add support for a random file name as a libcurl option? + A: Yes, it is here */ + +#ifndef RANDOM_FILE + /* if RANDOM_FILE isn't defined, we only perform this if an option tells + us to! */ + if(data->set.ssl.random_file) +#define RANDOM_FILE "" /* doesn't matter won't be used */ +#endif + { + /* let the option override the define */ + nread += RAND_load_file((data->set.ssl.random_file? + data->set.ssl.random_file:RANDOM_FILE), + 16384); + if(seed_enough(nread)) + return nread; + } + +#if defined(HAVE_RAND_EGD) + /* only available in OpenSSL 0.9.5 and later */ + /* EGD_SOCKET is set at configure time or not at all */ +#ifndef EGD_SOCKET + /* If we don't have the define set, we only do this if the egd-option + is set */ + if(data->set.ssl.egdsocket) +#define EGD_SOCKET "" /* doesn't matter won't be used */ +#endif + { + /* If there's an option and a define, the option overrides the + define */ + int ret = RAND_egd(data->set.ssl.egdsocket? + data->set.ssl.egdsocket:EGD_SOCKET); + if(-1 != ret) { + nread += ret; + if(seed_enough(nread)) + return nread; + } + } +#endif + + /* If we get here, it means we need to seed the PRNG using a "silly" + approach! */ +#ifdef HAVE_RAND_SCREEN + /* This one gets a random value by reading the currently shown screen */ + RAND_screen(); + nread = 100; /* just a value */ +#else + { + int len; + char *area; + + /* 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, 16384); + if(seed_enough(nread)) + return nread; + } + + infof(data, "libcurl is now using a weak random seed!\n"); + return nread; +} + +#ifndef SSL_FILETYPE_ENGINE +#define SSL_FILETYPE_ENGINE 42 +#endif +static int do_file_type(const char *type) +{ + if(!type || !type[0]) + return SSL_FILETYPE_PEM; + if(curl_strequal(type, "PEM")) + return SSL_FILETYPE_PEM; + if(curl_strequal(type, "DER")) + return SSL_FILETYPE_ASN1; + if(curl_strequal(type, "ENG")) + return SSL_FILETYPE_ENGINE; + return -1; +} + +static +int cert_stuff(struct connectdata *conn, + 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; + + 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); + + 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, "unable to set certificate file (wrong password?)"); + 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, "unable to set certificate file (wrong password?)"); + return 0; + } + break; + case SSL_FILETYPE_ENGINE: + failf(data, "file type ENG for certificate not implemented"); + return 0; + + default: + failf(data, "not supported file type '%s' for certificate", cert_type); + return 0; + } + + file_type = do_file_type(key_type); + + switch(file_type) { + case SSL_FILETYPE_PEM: + if(key_file == NULL) + /* cert & key can only be in PEM case in the same file */ + key_file=cert_file; + case SSL_FILETYPE_ASN1: + if(SSL_CTX_use_PrivateKey_file(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->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->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 + default: + failf(data, "not supported file type for private key\n"); + return 0; + } + + ssl=SSL_new(ctx); + 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; +} + +/* "global" init done? */ +static int init_ssl=0; + +/* we have the "SSL is seeded" boolean global for the application to + prevent multiple time-consuming seedings in vain */ +static bool ssl_seeded = FALSE; +#endif /* USE_SSLEAY */ + +/* Global init */ +void Curl_SSL_init(void) +{ +#ifdef USE_SSLEAY + /* make sure this is only done once */ + if(0 != init_ssl) + return; + + init_ssl++; /* never again */ + +#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES + ENGINE_load_builtin_engines(); +#endif + + /* Lets get nice error messages */ + SSL_load_error_strings(); + + /* Setup all the global SSL stuff */ + SSLeay_add_ssl_algorithms(); +#else + /* SSL disabled, do nothing */ +#endif +} + +/* Global cleanup */ +void Curl_SSL_cleanup(void) +{ +#ifdef USE_SSLEAY + if(init_ssl) { + /* only cleanup if we did a previous init */ + + /* Free the SSL error strings */ + ERR_free_strings(); + + /* EVP_cleanup() removes all ciphers and digests from the + table. */ + EVP_cleanup(); + +#ifdef HAVE_ENGINE_cleanup + ENGINE_cleanup(); +#endif + +#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 + + init_ssl=0; /* not inited any more */ + } +#else + /* SSL disabled, do nothing */ +#endif +} + +#ifndef USE_SSLEAY +void Curl_SSL_Close(struct connectdata *conn) +{ + (void)conn; +} +#endif + +#ifdef USE_SSLEAY + +/* + * This function is called when an SSL connection is closed. + */ +void Curl_SSL_Close(struct connectdata *conn) +{ + if(conn->ssl[FIRSTSOCKET].use) { + 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 sets up a session cache to the specified size. + */ +CURLcode Curl_SSL_InitSessions(struct SessionHandle *data, long amount) +{ + struct curl_ssl_session *session; + + if(data->state.session) + /* this is just a precaution to prevent multiple inits */ + return CURLE_OK; + + session = (struct curl_ssl_session *) + malloc(amount * sizeof(struct curl_ssl_session)); + if(!session) + return CURLE_OUT_OF_MEMORY; + + /* "blank out" the newly allocated memory */ + memset(session, 0, amount * sizeof(struct curl_ssl_session)); + + /* store the info in the SSL section */ + data->set.ssl.numsessions = amount; + data->state.session = session; + data->state.sessionage = 1; /* this is brand new */ + + return CURLE_OK; +} + +/* + * Check if there's a session ID for the given connection in the cache, + * and if there's one suitable, it is returned. + */ +static int Get_SSL_Session(struct connectdata *conn, + SSL_SESSION **ssl_sessionid) +{ + struct curl_ssl_session *check; + struct SessionHandle *data = conn->data; + long i; + + for(i=0; i< data->set.ssl.numsessions; i++) { + check = &data->state.session[i]; + if(!check->sessionid) + /* not session ID means blank entry */ + continue; + if(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; + return FALSE; + } + } + *ssl_sessionid = (SSL_SESSION *)NULL; + return TRUE; +} + +/* + * Kill a single session ID entry in the cache. + */ +static int Kill_Single_Session(struct curl_ssl_session *session) +{ + if(session->sessionid) { + /* defensive check */ + + /* free the ID */ + SSL_SESSION_free(session->sessionid); + session->sessionid=NULL; + session->age = 0; /* fresh */ + + Curl_free_ssl_config(&session->ssl_config); + + Curl_safefree(session->name); + session->name = NULL; /* no name */ + + return 0; /* ok */ + } + else + return 1; +} + +/* + * This function is called when the 'data' struct is going away. Close + * down everything and free all resources! + */ +int Curl_SSL_Close_All(struct SessionHandle *data) +{ + int i; + + if(data->state.session) { + for(i=0; i< data->set.ssl.numsessions; i++) + /* the single-killer function handles empty table slots */ + Kill_Single_Session(&data->state.session[i]); + + /* free the cache data */ + free(data->state.session); + } +#ifdef HAVE_OPENSSL_ENGINE_H + if(data->engine) + { + ENGINE_free(data->engine); + data->engine = NULL; + } +#endif + return 0; +} + +/* + * Extract the session id and store it in the session cache. + */ +static int Store_SSL_Session(struct connectdata *conn, + struct ssl_connect_data *ssl) +{ + SSL_SESSION *ssl_sessionid; + int i; + struct SessionHandle *data=conn->data; /* the mother of all structs */ + struct curl_ssl_session *store = &data->state.session[0]; + long oldest_age=data->state.session[0].age; /* zero if unused */ + char *clone_host; + + clone_host = strdup(conn->host.name); + if(!clone_host) + return -1; /* bail out */ + + /* ask OpenSSL, say please */ + +#ifdef HAVE_SSL_GET1_SESSION + ssl_sessionid = SSL_get1_session(ssl->handle); + + /* SSL_get1_session() will increment the reference + count and the session will stay in memory until explicitly freed with + SSL_SESSION_free(3), regardless of its state. + This function was introduced in openssl 0.9.5a. */ +#else + ssl_sessionid = SSL_get_session(ssl->handle); + + /* if SSL_get1_session() is unavailable, use SSL_get_session(). + This is an inferior option because the session can be flushed + at any time by openssl. It is included only so curl compiles + under versions of openssl < 0.9.5a. + + WARNING: How curl behaves if it's session is flushed is + untested. + */ +#endif + + /* Now we should add the session ID and the host name to the cache, (remove + the oldest if necessary) */ + + /* find an empty slot for us, or find the oldest */ + for(i=1; (i<data->set.ssl.numsessions) && + data->state.session[i].sessionid; i++) { + if(data->state.session[i].age < oldest_age) { + oldest_age = data->state.session[i].age; + store = &data->state.session[i]; + } + } + if(i == data->set.ssl.numsessions) + /* cache is full, we must "kill" the oldest entry! */ + Kill_Single_Session(store); + else + store = &data->state.session[i]; /* use this slot */ + + /* now init the session struct wisely */ + store->sessionid = ssl_sessionid; + store->age = data->state.sessionage; /* set current age */ + store->name = clone_host; /* clone host name */ + store->remote_port = conn->remote_port; /* port number */ + + Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config); + + 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; + } + return HOST_NOMATCH; + } + + if (toupper(c) != toupper(*hostname++)) + 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 + +#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; + + 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); + OPENSSL_free(peer_CN); + return 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); + OPENSSL_free(peer_CN); + } + } + return CURLE_OK; +} +#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 = conn->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 = 1 + 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, txt_len, NULL); + + Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT : + CURLINFO_SSL_DATA_IN, (char *)buf, len, NULL); + (void) ssl; +} +#endif + +/* ====================================================== */ +CURLcode +Curl_SSLConnect(struct connectdata *conn, + int sockindex) +{ + CURLcode retcode = CURLE_OK; + +#ifdef USE_SSLEAY + struct SessionHandle *data = conn->data; + int err; + long lerr; + int what; + char * str; + SSL_METHOD *req_method; + SSL_SESSION *ssl_sessionid=NULL; + ASN1_TIME *certdate; + curl_socket_t sockfd = conn->sock[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + + /* mark this is being ssl enabled from here on out. */ + connssl->use = TRUE; + + if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { + /* Make funny stuff to get random input */ + random_the_seed(data); + + ssl_seeded = TRUE; + } + + /* check to see if we've been told to use an explicit SSL/TLS version */ + switch(data->set.ssl.version) { + default: + case CURL_SSLVERSION_DEFAULT: + /* we try to figure out version */ + req_method = SSLv23_client_method(); + break; + case CURL_SSLVERSION_TLSv1: + req_method = TLSv1_client_method(); + break; + case CURL_SSLVERSION_SSLv2: + req_method = SSLv2_client_method(); + break; + case CURL_SSLVERSION_SSLv3: + req_method = SSLv3_client_method(); + break; + } + + 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) { + SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK, + ssl_tls_trace); + SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, conn); + } +#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 + ll 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; + } + 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 */ + connssl->handle = SSL_new(connssl->ctx); + SSL_set_connect_state(connssl->handle); + + connssl->server_cert = 0x0; + + if(!conn->bits.reuse) { + /* We're not re-using a connection, check if there's a cached ID we + can/should use here! */ + if(!Get_SSL_Session(conn, &ssl_sessionid)) { + /* we got a session id, use it! */ + SSL_set_session(connssl->handle, ssl_sessionid); + /* Informational message */ + infof (data, "SSL re-using session ID\n"); + } + } + + /* pass the raw socket into the SSL layers */ + SSL_set_fd(connssl->handle, sockfd); + + while(1) { + fd_set writefd; + fd_set readfd; + struct timeval interval; + long timeout_ms; + + /* 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) { + long has_passed; + + /* Evaluate in milliseconds how much time that has passed */ + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); + + /* get the most strict timeout of the ones converted to milliseconds */ + if(data->set.timeout && + (data->set.timeout>data->set.connecttimeout)) + timeout_ms = data->set.timeout*1000; + else + timeout_ms = data->set.connecttimeout*1000; + + /* subtract the passed time */ + timeout_ms -= has_passed; + + if(timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEOUTED; + } + } + else + /* no particular time-out has been set */ + timeout_ms= DEFAULT_CONNECT_TIMEOUT; + + + FD_ZERO(&writefd); + FD_ZERO(&readfd); + + 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) + FD_SET(sockfd, &readfd); + else if(SSL_ERROR_WANT_WRITE == detail) + FD_SET(sockfd, &writefd); + else { + /* untreated error */ + unsigned long errdetail; + char error_buffer[120]; /* OpenSSL documents that this must be at least + 120 bytes long. */ + CURLcode rc; + const char *cert_problem = NULL; + + 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 */ + +#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(errdetail, error_buffer, sizeof(error_buffer)); +#else + ERR_error_string(errdetail, error_buffer); +#endif + failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer); + return rc; + } + } + else + /* we have been connected fine, get out of the connect loop */ + break; + + interval.tv_sec = (int)(timeout_ms/1000); + timeout_ms -= interval.tv_sec*1000; + + interval.tv_usec = timeout_ms*1000; + + while(1) { + what = select(sockfd+1, &readfd, &writefd, NULL, &interval); + if(what > 0) + /* reabable or writable, go loop in the outer loop */ + break; + else if(0 == what) { + /* timeout */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + else { +#if !defined(WIN32) && defined(EINTR) + /* For platforms without EINTR all errnos are bad */ + if (errno == EINTR) + continue; /* retry the select() */ +#endif + /* anything other than the unimportant EINTR is fatally bad */ + failf(data, "select on SSL socket, errno: %d", Curl_ourerrno()); + return CURLE_SSL_CONNECT_ERROR; + } + } /* while()-loop for the select() */ + } /* while()-loop for the SSL_connect() */ + + /* Informational message */ + infof (data, "SSL connection using %s\n", + SSL_get_cipher(connssl->handle)); + + if(!ssl_sessionid) { + /* Since this is not a cached session ID, then we want to stach this one + in the cache! */ + Store_SSL_Session(conn, connssl); + } + + + /* 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); + 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); + 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(err), lerr); + } + else + infof(data, "SSL certificate verify ok.\n"); + } + + X509_free(connssl->server_cert); +#else /* USE_SSLEAY */ + (void)conn; + (void)sockindex; +#endif + return retcode; +} diff --git a/Utilities/cmcurl/ssluse.h b/Utilities/cmcurl/ssluse.h new file mode 100644 index 0000000..886d2ca --- /dev/null +++ b/Utilities/cmcurl/ssluse.h @@ -0,0 +1,38 @@ +#ifndef __SSLUSE_H +#define __SSLUSE_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" +CURLcode Curl_SSLConnect(struct connectdata *conn, int sockindex); + +void Curl_SSL_init(void); /* Global SSL init */ +void Curl_SSL_cleanup(void); /* Global SSL cleanup */ + +/* init the SSL session ID cache */ +CURLcode Curl_SSL_InitSessions(struct SessionHandle *, long); +void Curl_SSL_Close(struct connectdata *conn); /* close a SSL connection */ + +/* tell the SSL stuff to close down all open information regarding + connections (and thus session ID caching etc) */ +int Curl_SSL_Close_All(struct SessionHandle *data); +#endif diff --git a/Utilities/cmcurl/strequal.c b/Utilities/cmcurl/strequal.c new file mode 100644 index 0000000..66e2e4f --- /dev/null +++ b/Utilities/cmcurl/strequal.c @@ -0,0 +1,140 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <ctype.h> + +#include "strequal.h" + +#ifdef HAVE_STRCASECMP +/* this is for "-ansi -Wall -pedantic" to stop complaining! */ +extern int (strcasecmp)(const char *s1, const char *s2); +extern int (strncasecmp)(const char *s1, const char *s2, size_t n); +#endif + +int curl_strequal(const char *first, const char *second) +{ +#if defined(HAVE_STRCASECMP) + return !(strcasecmp)(first, second); +#elif defined(HAVE_STRICMP) + return !(stricmp)(first, second); +#elif defined(HAVE_STRCMPI) + return !(strcmpi)(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_STRICMP) + return !strnicmp(first, second, max); +#elif defined(HAVE_STRCMPI) + return !strncmpi(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 0000000..5865211 --- /dev/null +++ b/Utilities/cmcurl/strequal.h @@ -0,0 +1,47 @@ +#ifndef __STREQUAL_H +#define __STREQUAL_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$ + ***************************************************************************/ + +/* + * These two actually are public functions. + */ +int curl_strequal(const char *first, const char *second); +int curl_strnequal(const char *first, const char *second, size_t max); + +#define strequal(a,b) curl_strequal(a,b) +#define strnequal(a,b,c) curl_strnequal(a,b,c) + +/* 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) +size_t Curl_strlcat(char *dst, const char *src, size_t siz); +#endif + +#endif diff --git a/Utilities/cmcurl/strerror.c b/Utilities/cmcurl/strerror.c new file mode 100644 index 0000000..ea5bcd8 --- /dev/null +++ b/Utilities/cmcurl/strerror.c @@ -0,0 +1,569 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 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. + * + ***************************************************************************/ + +#include "setup.h" + +#include <curl/curl.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "strerror.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#ifdef 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) +{ + 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 "couldnt resolve proxy"; + + case CURLE_COULDNT_RESOLVE_HOST: + return "couldnt resolve host"; + + case CURLE_COULDNT_CONNECT: + return "couldn't connect"; + + case CURLE_FTP_WEIRD_SERVER_REPLY: + return "FTP: weird server reply"; + + case CURLE_FTP_ACCESS_DENIED: + return "FTP: access denied"; + + case CURLE_FTP_USER_PASSWORD_INCORRECT: + return "FTP: user and/or password incorrect"; + + 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: + return "out of memory"; + + 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_FTP_BAD_DOWNLOAD_RESUME: + return "couldn't resume FTP 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_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 "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_URL_MALFORMAT_USER: /* not used by current libcurl */ + case CURLE_MALFORMAT_USER: /* not used by current libcurl */ + case CURLE_BAD_CALLING_ORDER: /* not used by current libcurl */ + case CURLE_BAD_PASSWORD_ENTERED:/* not used by current libcurl */ + case CURLE_OBSOLETE: /* not used by current libcurl */ + 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"; +} + +const char * +curl_multi_strerror(CURLMcode error) +{ + 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_LAST: + break; + } + + return "unknown error"; +} + +const char * +curl_share_strerror(CURLSHcode error) +{ + 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"; +} + +#if defined(WIN32) && !defined(__CYGWIN__) + +/* 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) +{ + char *p; + + 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; + case WSAEDISCON: + p = "Disconnected"; + break; + + /* Extended Winsock errors */ + case WSASYSNOTREADY: + p = "Winsock library is not ready"; + break; + case WSANOTINITIALISED: + p = "Winsock library not initalised"; + 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; + } + strncpy (buf, p, len); + buf [len-1] = '\0'; + return buf; +} +#endif /* WIN32 && !__CYGWIN__ */ + +/* + * 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'; + +#if defined(WIN32) && !defined(__CYGWIN__) + /* '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, max, NULL)) + snprintf(buf, max, "Unknown error %d (%#x)", err, err); + } +#else /* not native Windows 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 ( !msg ) + { + msg = "Unknown System Error"; + } + strncpy(buf, msg, max); + } +#endif /* end of HAVE_GLIBC_STRERROR_R */ +#else /* HAVE_STRERROR_R */ + { + char *msg = strerror(err); + if ( !msg ) + { + msg = "Unknown System Error"; + } + strncpy(buf, msg, max); + } +#endif /* end of HAVE_STRERROR_R */ +#endif /* end of ! Windows */ + + 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; +} diff --git a/Utilities/cmcurl/strerror.h b/Utilities/cmcurl/strerror.h new file mode 100644 index 0000000..7d68723 --- /dev/null +++ b/Utilities/cmcurl/strerror.h @@ -0,0 +1,30 @@ +#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); + +#endif diff --git a/Utilities/cmcurl/strtok.c b/Utilities/cmcurl/strtok.c new file mode 100644 index 0000000..630e4e0 --- /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 0000000..cf9fac8 --- /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 0000000..e2b02c4 --- /dev/null +++ b/Utilities/cmcurl/strtoofft.c @@ -0,0 +1,164 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "strtoofft.h" + +#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((int)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 { +#ifdef HAVE_LONG_LONG_CONSTANT + if (is_negative) + value = 0x8000000000000000LL; + else + value = 0x7FFFFFFFFFFFFFFFLL; +#else + if (is_negative) + value = 0x8000000000000000L; + else + value = 0x7FFFFFFFFFFFFFFFL; +#endif + + 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 0000000..4c5d265 --- /dev/null +++ b/Utilities/cmcurl/strtoofft.h @@ -0,0 +1,62 @@ +#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 +#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 */ +/* simply use strtol() to get 32bit numbers */ +#define curlx_strtoofft strtol +#endif + +#endif + diff --git a/Utilities/cmcurl/telnet.c b/Utilities/cmcurl/telnet.c new file mode 100644 index 0000000..98a1adf --- /dev/null +++ b/Utilities/cmcurl/telnet.c @@ -0,0 +1,1389 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 CURL_DISABLE_TELNET +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <sys/time.h> +#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 "telnet.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#define TELOPTS +#define TELCMDS + +#include "arpa_telnet.h" +#include "curl_memory.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 WIN32 +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 */ + char subbuffer[SUBBUFSIZE]; + char *subpointer, *subend; /* buffer for sub-options */ + + TelnetReceive telrcv_state; +}; + +#ifdef WIN32 +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->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->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)) + Curl_infof(data, "%s IAC %s\n", direction, CURL_TELCMD(option)); + else + Curl_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) + Curl_infof(data, "%s %s %s\n", direction, fmt, opt); + else + Curl_infof(data, "%s %s %d\n", direction, fmt, option); + } + else + Curl_infof(data, "%s %d %d\n", direction, cmd, option); + } + } +} + +static void send_negotiation(struct connectdata *conn, int cmd, int option) +{ + unsigned char buf[3]; + + buf[0] = CURL_IAC; + buf[1] = cmd; + buf[2] = option; + + (void)swrite(conn->sock[FIRSTSOCKET], buf, 3); + + printoption(conn->data, "SENT", cmd, option); +} + +static +void set_remote_option(struct connectdata *conn, int option, int newstate) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + if(newstate == 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->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->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->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->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->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) + { + Curl_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) + { + Curl_infof(data, "(terminated by "); + if (CURL_TELOPT_OK(i)) + Curl_infof(data, "%s ", CURL_TELOPT(i)); + else if (CURL_TELCMD_OK(i)) + Curl_infof(data, "%s ", CURL_TELCMD(i)); + else + Curl_infof(data, "%d ", i); + if (CURL_TELOPT_OK(j)) + Curl_infof(data, "%s", CURL_TELOPT(j)); + else if (CURL_TELCMD_OK(j)) + Curl_infof(data, "%s", CURL_TELCMD(j)); + else + Curl_infof(data, "%d", j); + Curl_infof(data, ", not IAC SE!) "); + } + } + length -= 2; + } + if (length < 1) + { + Curl_infof(data, "(Empty suboption?)"); + return; + } + + if (CURL_TELOPT_OK(pointer[0])) { + switch(pointer[0]) { + case CURL_TELOPT_TTYPE: + case CURL_TELOPT_XDISPLOC: + case CURL_TELOPT_NEW_ENVIRON: + Curl_infof(data, "%s", CURL_TELOPT(pointer[0])); + break; + default: + Curl_infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0])); + break; + } + } + else + Curl_infof(data, "%d (unknown)", pointer[i]); + + switch(pointer[1]) { + case CURL_TELQUAL_IS: + Curl_infof(data, " IS"); + break; + case CURL_TELQUAL_SEND: + Curl_infof(data, " SEND"); + break; + case CURL_TELQUAL_INFO: + Curl_infof(data, " INFO/REPLY"); + break; + case CURL_TELQUAL_NAME: + Curl_infof(data, " NAME"); + break; + } + + switch(pointer[0]) { + case CURL_TELOPT_TTYPE: + case CURL_TELOPT_XDISPLOC: + pointer[length] = 0; + Curl_infof(data, " \"%s\"", &pointer[2]); + break; + case CURL_TELOPT_NEW_ENVIRON: + if(pointer[1] == CURL_TELQUAL_IS) { + Curl_infof(data, " "); + for(i = 3;i < length;i++) { + switch(pointer[i]) { + case CURL_NEW_ENV_VAR: + Curl_infof(data, ", "); + break; + case CURL_NEW_ENV_VALUE: + Curl_infof(data, " = "); + break; + default: + Curl_infof(data, "%c", pointer[i]); + break; + } + } + } + break; + default: + for (i = 2; i < length; i++) + Curl_infof(data, " %.2x", pointer[i]); + break; + } + + if (direction) + { + Curl_infof(data, "\n"); + } + } +} + +static 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->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]; + size_t len; + size_t tmplen; + char varname[128]; + char varval[128]; + struct SessionHandle *data = conn->data; + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + + printsub(data, '<', (unsigned char *)tn->subbuffer, 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); + (void)swrite(conn->sock[FIRSTSOCKET], temp, len); + 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); + (void)swrite(conn->sock[FIRSTSOCKET], temp, len); + 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; + (void)swrite(conn->sock[FIRSTSOCKET], temp, len); + 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 *)conn->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(data, 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(data, 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(data, 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, (unsigned char)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, (unsigned char)CURL_IAC); + CURL_SB_ACCUM(tn, (unsigned char)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) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + (void)status; /* unused */ + + curl_slist_free_all(tn->telnet_vars); + + free(conn->proto.telnet); + conn->proto.telnet = NULL; + + return CURLE_OK; +} + +CURLcode Curl_telnet(struct connectdata *conn) +{ + CURLcode code; + struct SessionHandle *data = conn->data; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; +#ifdef WIN32 + 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 + fd_set readfd; + fd_set keepfd; +#endif + ssize_t nread; + bool keepon = TRUE; + char *buf = data->state.buffer; + struct TELNET *tn; + + code = init_telnet(conn); + if(code) + return code; + + tn = (struct TELNET *)conn->proto.telnet; + + code = check_telnet_options(conn); + if(code) + return code; + +#ifdef WIN32 + /* + ** 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 (create_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; + ssize_t bytes_written; + char *buffer = buf; + + for(;;) { + 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; + 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; + (void)create_event_func; + (void)close_event_func; + (void)event_select_func; + (void)enum_netevents_func; + + /* We called LoadLibrary, so call FreeLibrary */ + if (!FreeLibrary(wsock2)) + infof(data,"FreeLibrary(wsock2) failed (%d)",GetLastError()); +#else + FD_ZERO (&readfd); /* clear it */ + FD_SET (sockfd, &readfd); + FD_SET (0, &readfd); + + keepfd = readfd; + + while (keepon) { + struct timeval interval; + + readfd = keepfd; /* set this every lap in the loop */ + interval.tv_sec = 1; + interval.tv_usec = 0; + + switch (select (sockfd + 1, &readfd, NULL, NULL, &interval)) { + case -1: /* error, stop reading */ + keepon = FALSE; + continue; + case 0: /* timeout */ + break; + default: /* read! */ + if(FD_ISSET(0, &readfd)) { /* read from stdin */ + unsigned char outbuf[2]; + int out_count = 0; + ssize_t bytes_written; + char *buffer = buf; + + nread = read(0, buf, 255); + + while(nread--) { + outbuf[0] = *buffer++; + out_count = 1; + if(outbuf[0] == CURL_IAC) + outbuf[out_count++] = CURL_IAC; + + Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf, + out_count, &bytes_written); + } + } + + if(FD_ISSET(sockfd, &readfd)) { + /* 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_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 0000000..86dd99b --- /dev/null +++ b/Utilities/cmcurl/telnet.h @@ -0,0 +1,30 @@ +#ifndef __TELNET_H +#define __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 +CURLcode Curl_telnet(struct connectdata *conn); +CURLcode Curl_telnet_done(struct connectdata *conn, CURLcode); +#endif +#endif diff --git a/Utilities/cmcurl/timeval.c b/Utilities/cmcurl/timeval.c new file mode 100644 index 0000000..499fba5 --- /dev/null +++ b/Utilities/cmcurl/timeval.c @@ -0,0 +1,116 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" + +#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; + unsigned long Sec; + unsigned long Usec; + 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 0000000..67b885a --- /dev/null +++ b/Utilities/cmcurl/timeval.h @@ -0,0 +1,74 @@ +#ifndef __TIMEVAL_H +#define __TIMEVAL_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" + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <time.h> +#else +#include <sys/time.h> +#endif + +#ifndef HAVE_GETTIMEOFDAY +#if !defined(_WINSOCKAPI_) && !defined(__MINGW32__) && !defined(_AMIGASF) && \ + !defined(__LCC__) +struct timeval { + long tv_sec; + long tv_usec; +}; +#endif +#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 0000000..4a108cc --- /dev/null +++ b/Utilities/cmcurl/transfer.c @@ -0,0 +1,2191 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" + +/* -- 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 +#include <sys/stat.h> + +#include <errno.h> + +#include "strtoofft.h" +#include "strequal.h" + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#include <sys/time.h> +#ifdef HAVE_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_SELECT +#error "We can't compile without select() support!" +#endif +#ifndef HAVE_SOCKET +#error "We can't compile without socket() support!" +#endif + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "netrc.h" + +#include "content_encoding.h" +#include "hostip.h" +#include "transfer.h" +#include "sendf.h" +#include "speedcheck.h" +#include "progress.h" +#include "getdate.h" +#include "http.h" +#include "url.h" +#include "getinfo.h" +#include "ssluse.h" +#include "http_digest.h" +#include "http_ntlm.h" +#include "http_negotiate.h" +#include "share.h" +#include "curl_memory.h" + +#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 */ + +enum { + KEEP_NONE, + KEEP_READ, + KEEP_WRITE +}; + +/* We keep this static and global since this is read-only and NEVER + changed. It should just remain a blanked-out timeout value. */ +static struct timeval notimeout={0,0}; + +/* + * 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 */ + conn->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(conn->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 */ + conn->upload_fromhere -= hexlen; + nread += hexlen; + + /* copy the prefix to the buffer */ + memcpy(conn->upload_fromhere, hexbuffer, hexlen); + + /* always append CRLF to the data */ + memcpy(conn->upload_fromhere + nread, "\r\n", 2); + + if((nread - hexlen) == 0) { + /* mark this as done once this chunk is transfered */ + conn->keep.upload_done = TRUE; + } + + nread+=2; /* for the added CRLF */ + } + + *nreadp = nread; + + 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; + + while (head) { + if (checkprefix(head->data, s)) + return TRUE; + head = head->next; + } + + if(checkprefix("HTTP/", s)) + return TRUE; + + return FALSE; +} + + +/* + * 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 Curl_transfer_keeper *k = &conn->keep; + struct SessionHandle *data = conn->data; + CURLcode result; + ssize_t nread; /* number of bytes read */ + int didwhat=0; + + /* These two are used only if no other select() or _fdset() have been + invoked before this. This typicly happens if you use the multi interface + and call curl_multi_perform() without calling curl_multi_fdset() + first. */ + fd_set extrareadfd; + fd_set extrawritefd; + + fd_set *readfdp = k->readfdp; + fd_set *writefdp = k->writefdp; + curl_off_t contentlength; + + if((k->keepon & KEEP_READ) && !readfdp) { + /* reading is requested, but no socket descriptor pointer was set */ + FD_ZERO(&extrareadfd); + FD_SET(conn->sockfd, &extrareadfd); + readfdp = &extrareadfd; + + /* no write, no exceptions, no timeout */ + select(conn->sockfd+1, readfdp, NULL, NULL, ¬imeout); + } + if((k->keepon & KEEP_WRITE) && !writefdp) { + /* writing is requested, but no socket descriptor pointer was set */ + FD_ZERO(&extrawritefd); + FD_SET(conn->writesockfd, &extrawritefd); + writefdp = &extrawritefd; + + /* no read, no exceptions, no timeout */ + select(conn->writesockfd+1, NULL, writefdp, NULL, ¬imeout); + } + + do { + /* If we still have reading to do, we check if we have a readable + socket. Sometimes the reafdp is NULL, if no fd_set was done using + the multi interface and then we can do nothing but to attempt a + read to be sure. */ + if((k->keepon & KEEP_READ) && + (!readfdp || FD_ISSET(conn->sockfd, readfdp))) { + + bool readdone = TRUE; + + /* 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 -1; + + /* receive data from the network! */ + int readrc = Curl_read(conn, conn->sockfd, k->buf, buffersize, &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; + + /* NULL terminate, allowing string ops to be used */ + if (0 < nread) + k->buf[nread] = 0; + + /* if we receive 0 or less here, the server closed the connection and + we bail out from this! */ + else if (0 >= nread) { + k->keepon &= ~KEEP_READ; + FD_ZERO(&k->rkeepfd); + readdone = TRUE; + 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; + + k->end_ptr = strchr (k->str_start, '\n'); + + 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 -= 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 */ + strncpy (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; + } + } + + if (('\n' == *k->p) || ('\r' == *k->p)) { + size_t headerlen; + /* Zero-length header line means end of headers! */ + + if ('\r' == *k->p) + k->p++; /* pass the \r byte */ + if ('\n' == *k->p) + k->p++; /* pass the \n byte */ + + if(100 == k->httpcode) { + /* + * We have made a HTTP PUT or POST and this is 1.1-lingo + * that tells us that the server is OK with this and ready + * to receive 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; + FD_SET (conn->writesockfd, &k->writefd); /* write */ + k->keepon |= KEEP_WRITE; + k->wkeepfd = k->writefd; + } + } + else + k->header = FALSE; /* no more header to parse! */ + + if (417 == k->httpcode) { + /* + * we got: "417 Expectation Failed" this means: + * we have made a HTTP call and our Expect Header + * seems to cause a problem => abort the write operations + * (or prevent them from starting). + */ + k->write_after_100_header = FALSE; + k->keepon &= ~KEEP_WRITE; + FD_ZERO(&k->wkeepfd); + } + +#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(data, writetype, + data->state.headerbuff, + headerlen); + if(result) + return result; + + data->info.header_size += headerlen; + conn->headerbytecount += headerlen; + + conn->deductheadercount = + (100 == k->httpcode)?conn->headerbytecount:0; + + if (conn->resume_from && + !k->content_range && + (data->set.httpreq==HTTPREQ_GET)) { + if(k->httpcode == 416) { + /* "Requested Range Not Satisfiable" */ + stop_reading = TRUE; + } + else { + /* 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; + } + } +#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; + } +#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) + conn->size=-1; + + } + if(-1 != conn->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, conn->size); + conn->maxdownload = conn->size; + } + /* If max download size is *zero* (nothing) we already + have nothing and can safely return ok now! */ + if(0 == conn->maxdownload) + stop_reading = TRUE; + + if(stop_reading) { + /* we make sure that this socket isn't read more now */ + k->keepon &= ~KEEP_READ; + FD_ZERO(&k->rkeepfd); + } + + break; /* exit header line loop */ + } + + /* We continue reading headers, so reset the line-based + header parsing variables hbufp && hbuflen */ + k->hbufp = data->state.headerbuff; + k->hbuflen = 0; + continue; + } + + /* + * Checks for special headers coming up. + */ + + if (!k->headerline++) { + /* This is the first header, it MUST be the error code line + or else we consiser this to be the body right away! */ + int httpversion_major; + int nc=sscanf(k->p, " HTTP/%d.%d %3d", + &httpversion_major, + &k->httpversion, + &k->httpcode); + if (nc==3) { + k->httpversion += 10 * httpversion_major; + } + else { + /* this is the real world, not a Nirvana + NCSA 1.5.x returns this crap when asked for HTTP/1.1 + */ + nc=sscanf(k->p, " HTTP %3d", &k->httpcode); + k->httpversion = 10; + + /* If 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) && + (k->httpcode != 407)) { + /* 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. */ + conn->size=0; + conn->maxdownload=0; + break; + default: + /* nothing */ + break; + } + } + else { + k->header = FALSE; /* this is not a header line */ + break; + } + } + + /* Check for Content-Length: header lines to get size. 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->httpcode != 416) && + 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) + conn->size = contentlength; + 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((int)*start); + start++); + + end = strchr(start, '\r'); + if(!end) + end = strchr(start, '\n'); + + if(end) { + /* skip all trailing space letters */ + for(; isspace((int)*end) && (end > start); end--); + + /* 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 == 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("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((int)*start); + start++); + + /* 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 (Curl_compareheader(k->p, "Content-Range:", "bytes")) { + /* Content-Range: bytes [num]- + Content-Range: bytes: [num]- + + The second format was added August 1st 2000 by Igor + Khristophorov since Sun's webserver JavaWebServer/1.1.1 + obviously sends the header this way! :-( */ + + char *ptr = strstr(k->p, "bytes"); + ptr+=5; + + if(*ptr == ':') + /* stupid colon skip */ + ptr++; + + k->offset = curlx_strtoofft(ptr, NULL, 10); + + if (conn->resume_from == k->offset) + /* we asked for a resume and we got it */ + k->content_range = TRUE; + } + 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, + conn->path); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + 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 = 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((int)*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((int)*ptr)) + ptr--; + ptr++; + + backup = *ptr; /* store the ending letter */ + if(ptr != start) { + *ptr = '\0'; /* zero terminate */ + conn->newurl = strdup(start); /* clone string */ + *ptr = backup; /* restore ending letter */ + if(!conn->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, k->hbuflen, conn->host.dispname); + + result = Curl_client_write(data, writetype, k->p, k->hbuflen); + if(result) + return result; + + data->info.header_size += k->hbuflen; + conn->headerbytecount += k->hbuflen; + + /* reset hbufp pointer && hbuflen */ + k->hbufp = data->state.headerbuff; + k->hbuflen = 0; + } + while (!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)) { + + if(0 == k->bodywrites) { + /* These checks are only made the first time we are about to + write a piece of the body */ + if(conn->protocol&PROT_HTTP) { + /* HTTP-only checks */ + + if (conn->newurl) { + if(conn->bits.close) { + /* Abort after the headers if "follow Location" is set + and we're set to close anyway. */ + k->keepon &= ~KEEP_READ; + FD_ZERO(&k->rkeepfd); + *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->set.timecondition && !conn->range) { + /* A time condition has been set AND no ranges have been + requested. This seems to be what chapter 13.3.4 of + RFC 2616 defines to be the correct action for a + HTTP/1.1 client */ + if((k->timeofdoc > 0) && (data->set.timevalue > 0)) { + switch(data->set.timecondition) { + case 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, + k->hbuflen, conn->host.dispname); + if(k->badheader == HEADER_PARTHEADER) + Curl_debug(data, CURLINFO_DATA_IN, k->str, nread, + conn->host.dispname); + } + else + Curl_debug(data, CURLINFO_DATA_IN, k->str, nread, + conn->host.dispname); + } + +#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 */ + FD_ZERO(&k->rkeepfd); + + /* There are now possibly N number of bytes at the end of the + str buffer that weren't written to the client, but we don't + care about them right now. */ + } + /* If it returned OK, we just keep going */ + } +#endif /* CURL_DISABLE_HTTP */ + + if((-1 != conn->maxdownload) && + (k->bytecount + nread >= conn->maxdownload)) { + nread = (ssize_t) (conn->maxdownload - k->bytecount); + if(nread < 0 ) /* this should be unusual */ + nread = 0; + + k->keepon &= ~KEEP_READ; /* we're done reading */ + FD_ZERO(&k->rkeepfd); + } + + k->bytecount += nread; + + Curl_pgrsSetDownloadCounter(data, k->bytecount); + + if(!conn->bits.chunk && (nread || k->badheader)) { + /* 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(data, CLIENTWRITE_BODY, + data->state.headerbuff, + k->hbuflen); + } + 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(data, CLIENTWRITE_BODY, k->str, + nread); +#ifdef HAVE_LIBZ + break; + + case DEFLATE: + /* Assume CLIENTWRITE_BODY; headers are not encoded. */ + result = Curl_unencode_deflate_write(data, k, nread); + break; + + case GZIP: + /* Assume CLIENTWRITE_BODY; headers are not encoded. */ + result = Curl_unencode_gzip_write(data, 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 ) */ + + } while(!readdone); + + } /* if( read from socket ) */ + + /* If we still have writing to do, we check if we have a writable + socket. Sometimes the writefdp is NULL, if no fd_set was done using + the multi interface and then we can do nothing but to attempt a + write to be sure. */ + if((k->keepon & KEEP_WRITE) && + (!writefdp || FD_ISSET(conn->writesockfd, writefdp)) ) { + /* 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 == conn->upload_present) { + /* init the "upload from here" pointer */ + conn->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 && + (conn->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 */ + FD_ZERO (&k->writefd); /* clear it */ + k->wkeepfd = k->writefd; /* set the keeper variable */ + 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 */ + FD_ZERO(&k->wkeepfd); + writedone = TRUE; + (void)writedone; + break; + } + + /* store number of bytes available for upload */ + conn->upload_present = nread; + + /* convert LF to CRLF if so asked */ + if (data->set.crlf) { + if(data->state.scratch == NULL) + data->state.scratch = malloc(2*BUFSIZE); + if(data->state.scratch == NULL) { + failf (data, "Failed to alloc scratch buffer!"); + return CURLE_OUT_OF_MEMORY; + } + for(i = 0, si = 0; i < nread; i++, si++) { + if (conn->upload_fromhere[i] == 0x0a) { + data->state.scratch[si++] = 0x0d; + data->state.scratch[si] = 0x0a; + } + else + data->state.scratch[si] = conn->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 */ + conn->upload_fromhere = data->state.scratch; + + /* set the new amount too */ + conn->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 */ + conn->upload_fromhere, /* buffer pointer */ + conn->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, conn->upload_fromhere, + bytes_written, conn->host.dispname); + + if(conn->upload_present != bytes_written) { + /* we only wrote a part of the buffer (if anything), deal with it! */ + + /* store the amount of bytes left in the buffer to write */ + conn->upload_present -= bytes_written; + + /* advance the pointer where to find the buffer when the next send + is to happen */ + conn->upload_fromhere += bytes_written; + + writedone = TRUE; /* we are done, stop the loop */ + } + else { + /* we've uploaded that buffer now */ + conn->upload_fromhere = k->uploadbuf; + conn->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 */ + FD_ZERO(&k->wkeepfd); + 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(conn->bytecountp) + *conn->bytecountp = k->bytecount; /* read count */ + if(conn->writebytecountp) + *conn->writebytecountp = k->writebytecount; /* write count */ + } + else { + /* no read no write, this is a timeout? */ + if (k->write_after_100_header) { + /* This should allow some time for the header to arrive, but only a + very short time as otherwise it'll be too much wasted times too + often. */ + + /* 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; + FD_SET (conn->writesockfd, &k->writefd); /* write socket */ + k->keepon |= KEEP_WRITE; + k->wkeepfd = k->writefd; + } + } + } + + if(Curl_pgrsUpdate(conn)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, k->now); + if (result) + return result; + + if (data->set.timeout && + ((Curl_tvdiff(k->now, k->start)/1000) >= data->set.timeout)) { + failf(data, "Operation timed out with %" FORMAT_OFF_T + " out of %" FORMAT_OFF_T " bytes received", + k->bytecount, conn->size); + return CURLE_OPERATION_TIMEOUTED; + } + + if(!k->keepon) { + /* + * The transfer has been performed. Just make some general checks before + * returning. + */ + + if(!(conn->bits.no_body) && (conn->size != -1) && + (k->bytecount != conn->size) && + !conn->newurl) { + failf(data, "transfer closed with %" FORMAT_OFF_T + " bytes remaining to read", + conn->size - k->bytecount); + return CURLE_PARTIAL_FILE; + } + else if(conn->bits.chunk && conn->proto.http->chunk.datasize) { + failf(data, "transfer closed with at least %d bytes remaining", + conn->proto.http->chunk.datasize); + return CURLE_PARTIAL_FILE; + } + if(Curl_pgrsUpdate(conn)) + return CURLE_ABORTED_BY_CALLBACK; + } + + /* Now update the "done" boolean we return */ + *done = !k->keepon; + + return CURLE_OK; +} + + +/* + * Curl_readwrite_init() inits the readwrite session. + */ + +CURLcode Curl_readwrite_init(struct connectdata *conn) +{ + struct SessionHandle *data; + struct Curl_transfer_keeper *k = &conn->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 */ + + data = conn->data; /* there's the root struct */ + k->buf = data->state.buffer; + k->uploadbuf = data->state.uploadbuffer; + k->maxfd = (conn->sockfd>conn->writesockfd? + conn->sockfd:conn->writesockfd)+1; + k->hbufp = data->state.headerbuff; + 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(conn->size > 0) + Curl_pgrsSetDownloadSize(data, conn->size); + } + /* we want header and/or body, if neither then don't do this! */ + if(conn->bits.getheader || !conn->bits.no_body) { + + FD_ZERO (&k->readfd); /* clear it */ + if(conn->sockfd != CURL_SOCKET_BAD) { + FD_SET (conn->sockfd, &k->readfd); /* read socket */ + k->keepon |= KEEP_READ; + } + + FD_ZERO (&k->writefd); /* clear it */ + 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->set.expect100header && + (conn->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->set.expect100header) + /* when we've sent off the rest of the headers, we must await a + 100-continue */ + k->wait100_after_headers = TRUE; + FD_SET (conn->writesockfd, &k->writefd); /* write socket */ + k->keepon |= KEEP_WRITE; + } + } + + /* get these in backup variables to be able to restore them on each lap in + the select() loop */ + k->rkeepfd = k->readfd; + k->wkeepfd = k->writefd; + + } + + return CURLE_OK; +} + +/* + * Curl_single_fdset() gets called by the multi interface code when the app + * has requested to get the fd_sets 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. + */ +void Curl_single_fdset(struct connectdata *conn, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd) +{ + *max_fd = -1; /* init */ + if(conn->keep.keepon & KEEP_READ) { + FD_SET(conn->sockfd, read_fd_set); + *max_fd = conn->sockfd; + conn->keep.readfdp = read_fd_set; /* store the address of the set */ + } + if(conn->keep.keepon & KEEP_WRITE) { + FD_SET(conn->writesockfd, write_fd_set); + + /* since sockets are curl_socket_t nowadays, we typecast it to int here + to compare it nicely */ + if((int)conn->writesockfd > *max_fd) + *max_fd = conn->writesockfd; + conn->keep.writefdp = write_fd_set; /* store the address of the set */ + } + /* we don't use exceptions, only touch that one to prevent compiler + warnings! */ + *exc_fd_set = *exc_fd_set; +} + + +/* + * Transfer() + * + * This function is what performs the actual transfer. It is capable of + * doing both ways simultaneously. + * The transfer must already have been setup by a call to Curl_Transfer(). + * + * Note that headers are created in a preallocated buffer of a default size. + * That buffer can be enlarged on demand, but it is never 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 Curl_transfer_keeper *k = &conn->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; + + k->writefdp = &k->writefd; /* store the address of the set */ + k->readfdp = &k->readfd; /* store the address of the set */ + + while (!done) { + struct timeval interval; + k->readfd = k->rkeepfd; /* set these every lap in the loop */ + k->writefd = k->wkeepfd; + interval.tv_sec = 1; + interval.tv_usec = 0; + + switch (select (k->maxfd, k->readfdp, k->writefdp, NULL, &interval)) { + case -1: /* select() error, stop reading */ +#ifdef EINTR + /* The EINTR is not serious, and it seems you might get this more + ofen when using the lib in a multi-threaded environment! */ + if(errno == EINTR) + ; + else +#endif + done = TRUE; /* no more read or write */ + continue; + case 0: /* timeout */ + 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) +{ + if(!data->change.url) + /* we can't do anything wihout URL */ + return CURLE_URL_MALFORMAT; + +#ifdef USE_SSLEAY + { + /* Init the SSL session ID cache here. We do it here since we want to do + it after the *_setopt() calls (that could change the size of the cache) + but before any transfer takes place. */ + CURLcode res = Curl_SSL_InitSessions(data, data->set.ssl.numsessions); + if(res) + return res; + } +#endif + + data->set.followlocation=0; /* reset the location-follow counter */ + data->state.this_is_a_follow = FALSE; /* reset this */ + data->state.errorbuf = FALSE; /* no error has occurred */ + + data->state.authproblem = FALSE; + data->state.authhost.want = data->set.httpauth; + data->state.authproxy.want = data->set.proxyauth; + +#ifndef CURL_DISABLE_HTTP + /* If there was a list of cookie files to read and we haven't done it before, + do it now! */ + if(data->change.cookielist) { + struct curl_slist *list = data->change.cookielist; + 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! */ + } +#endif /* CURL_DISABLE_HTTP */ + + + /* 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 + + 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 */ +{ + /* Location: redirect */ + char prot[16]; /* URL protocol string storage */ + char letter; /* used for a silly sscanf */ + size_t newlen; + char *newest; + + if (data->set.maxredirs && + (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 = strrchr(protsep, '?'); + if(pathsep) + *pathsep=0; + + /* we have a relative path to append to the last slash if + there's one available */ + pathsep = strrchr(protsep, '/'); + if(pathsep) + *pathsep=0; + + /* 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) + *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! */ + (void)newurl; + + 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; + int urlchanged; + + do { + bool async; + Curl_pgrsTime(data, TIMER_STARTSINGLE); + data->change.url_changed = FALSE; + res = Curl_connect(data, conn, &async); + + 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); + } + 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); + if(CURLE_OK == res) { + char *gotourl = strdup(data->change.url); + res = Curl_follow(data, gotourl); + if(res) + free(gotourl); + } + } + } while (urlchanged && res == CURLE_OK); + + return res; +} + + + +/* + * Curl_perform() is the internal high-level function that gets called by the + * external curl_easy_perform() function. It inits, performs and cleans up a + * single file transfer. + */ +CURLcode Curl_perform(struct SessionHandle *data) +{ + CURLcode res; + CURLcode res2; + struct connectdata *conn=NULL; + char *newurl = NULL; /* possibly a new URL to follow to! */ + + 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) { + if (data->set.source_host) /* 3rd party transfer */ + res = Curl_pretransfersec(conn); + else + conn->sec_conn = NULL; + } + + if(res == CURLE_OK) { + + res = Curl_do(&conn); + + /* for non 3rd party transfer only */ + if(res == CURLE_OK && !data->set.source_host) { + res = Transfer(conn); /* now fetch that URL please */ + if(res == CURLE_OK) { + + if((conn->keep.bytecount+conn->headerbytecount == 0) && + conn->bits.reuse) { + /* We got no data and we attempted to re-use a connection. 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(data, "Connection died, retrying a fresh connect\n"); + newurl = 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! */ + } + else + /* + * We must duplicate the new URL here as the connection data + * may be free()ed in the Curl_done() function. + */ + newurl = conn->newurl?strdup(conn->newurl):NULL; + } + else { + /* The transfer phase returned error, we mark the connection to get + * closed to prevent being re-used. This is becasue we can't + * possibly know if the connection is in a good shape or not now. */ + conn->bits.close = TRUE; + + 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); + + if(CURLE_OK == res) + res = res2; + } + else + /* Curl_do() failed, clean up left-overs in the done-call */ + res2 = Curl_done(&conn, res); + + (void)res2; + + /* + * 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); + 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); + + /* 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_Transfer() is called to setup some basic properties for the upcoming + * transfer. + */ +CURLcode +Curl_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; + 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->size = size; + conn->bits.getheader = getheader; + conn->bytecountp = bytecountp; + conn->writesockfd = writesockindex==-1? + CURL_SOCKET_BAD:conn->sock[writesockindex]; + conn->writebytecountp = writecountp; + + return CURLE_OK; + +} + +/* + * Curl_pretransfersec() prepares the secondary connection (used for 3rd party + * FTP transfers). + */ +CURLcode Curl_pretransfersec(struct connectdata *conn) +{ + CURLcode status; + struct SessionHandle *data = conn->data; + struct connectdata *sec_conn = NULL; /* secondary connection */ + + /* update data with source host options */ + char *url = aprintf( "%s://%s/", conn->protostr, data->set.source_host); + + if(!url) + return CURLE_OUT_OF_MEMORY; + + if(data->change.url_alloc) + free(data->change.url); + + data->change.url_alloc = TRUE; + data->change.url = url; + data->set.ftpport = data->set.source_port; + data->set.userpwd = data->set.source_userpwd; + + /* secondary connection */ + status = Curl_connect_host(data, &sec_conn); + if(CURLE_OK == status) { + sec_conn->data = data; + conn->sec_conn = sec_conn; + } + + return status; +} diff --git a/Utilities/cmcurl/transfer.h b/Utilities/cmcurl/transfer.h new file mode 100644 index 0000000..1b7eb82 --- /dev/null +++ b/Utilities/cmcurl/transfer.h @@ -0,0 +1,52 @@ +#ifndef __TRANSFER_H +#define __TRANSFER_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_perform(struct SessionHandle *data); +CURLcode Curl_pretransfer(struct SessionHandle *data); +CURLcode Curl_pretransfersec(struct connectdata *conn); +CURLcode Curl_posttransfer(struct SessionHandle *data); +CURLcode Curl_follow(struct SessionHandle *data, char *newurl); +CURLcode Curl_readwrite(struct connectdata *conn, bool *done); +void Curl_single_fdset(struct connectdata *conn, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); +CURLcode Curl_readwrite_init(struct connectdata *conn); + +CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp); + +/* This sets up a forthcoming transfer */ +CURLcode +Curl_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 0000000..b532e20 --- /dev/null +++ b/Utilities/cmcurl/url.c @@ -0,0 +1,3686 @@ +/*************************************************************************** + * _ _ ____ _ + * 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$ + ***************************************************************************/ + +/* -- WIN32 approved -- */ + +#include "setup.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <sys/time.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#ifdef VMS +#include <in.h> +#include <inet.h> +#endif + +#ifdef HAVE_SETJMP_H +#include <setjmp.h> +#endif + +#ifndef HAVE_SELECT +#error "We can't compile without select() support!" +#endif +#ifndef HAVE_SOCKET +#error "We can't compile without socket() support!" +#endif + + +#endif + +#ifdef USE_LIBIDN +#include <idna.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 + +#ifdef HAVE_OPENSSL_ENGINE_H +#include <openssl/engine.h> +#endif +#include "urldata.h" +#include "netrc.h" + +#include "formdata.h" +#include "base64.h" +#include "ssluse.h" +#include "hostip.h" +#include "if2ip.h" +#include "transfer.h" +#include "sendf.h" +#include "progress.h" +#include "cookie.h" +#include "strequal.h" +#include "escape.h" +#include "strtok.h" +#include "share.h" +#include "content_encoding.h" +#include "http_digest.h" +#include "http_negotiate.h" + +/* And now for the protocols */ +#include "ftp.h" +#include "dict.h" +#include "telnet.h" +#include "http.h" +#include "file.h" +#include "ldap.h" +#include "url.h" +#include "connect.h" +#include "inet_ntop.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 "security.h" +#endif +#include "curl_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 safe_strequal(char* str1, char* str2); + +#ifndef USE_ARES +/* not for Win32, unless it is cygwin + not for ares builds */ +#if !defined(WIN32) || defined(__CYGWIN32__) + +#ifndef RETSIGTYPE +#define RETSIGTYPE void +#endif +#ifdef HAVE_SIGSETJMP +extern sigjmp_buf curl_jmpenv; +#endif +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; +} +#endif +#endif /* USE_ARES */ + +void Curl_safefree(void *ptr) +{ + if(ptr) + free(ptr); +} + +/* + * This is the internal function curl_easy_cleanup() calls. This should + * cleanup and free all resources associated with this sessionhandle. + * + * NOTE: if we ever add something that attempts to write to a socket or + * similar here, we must ignore SIGPIPE first. It is currently only done + * when curl_easy_perform() is invoked. + */ + +CURLcode Curl_close(struct SessionHandle *data) +{ + /* Loop through all open connections and kill them one by one */ + while(-1 != ConnectionKillOne(data)); + +#ifdef USE_SSLEAY + /* Close down all open SSL info and sessions */ + Curl_SSL_Close_All(data); +#endif + + if(data->change.cookielist) /* clean up list if any */ + curl_slist_free_all(data->change.cookielist); + + Curl_safefree(data->state.auth_host); + Curl_safefree(data->state.scratch); + + if(data->change.proxy_alloc) + free(data->change.proxy); + + if(data->change.referer_alloc) + free(data->change.referer); + + if(data->change.url_alloc) + free(data->change.url); + + Curl_safefree(data->state.headerbuff); + +#ifndef CURL_DISABLE_HTTP + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + if(data->set.cookiejar) { + /* 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); + } + + if( !data->share || (data->cookies != data->share->cookies) ) { + Curl_cookie_cleanup(data->cookies); + } + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + + Curl_digest_cleanup(data); +#endif + + /* free the connection cache */ + free(data->state.connects); + + Curl_safefree(data->info.contenttype); + +#ifdef USE_ARES + /* this destroys the channel and we cannot use it anymore after this */ + ares_destroy(data->state.areschannel); +#endif + + /* No longer a dirty share, if it exists */ + if (data->share) + data->share->dirty--; + + free(data); + return CURLE_OK; +} + +/** + * 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; + +#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; + + data->set.infilesize = -1; /* we don't know any size */ + + data->state.current_speed = -1; /* init to negative == impossible */ + + data->set.httpreq = HTTPREQ_GET; /* Default HTTP request */ + data->set.ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ + data->set.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 */ + + /* create an array with connection data struct pointers */ + data->state.numconnects = 5; /* hard-coded right now */ + data->state.connects = (struct connectdata **) + malloc(sizeof(struct connectdata *) * data->state.numconnects); + + if(!data->state.connects) + res = CURLE_OUT_OF_MEMORY; + else + memset(data->state.connects, 0, + sizeof(struct connectdata *)*data->state.numconnects); + + /* + * 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 prefered CA cert bundle since install time */ + data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE; +#endif + } + + if(res) { +#ifdef USE_ARES + ares_destroy(data->state.areschannel); +#endif + if(data->state.headerbuff) + free(data->state.headerbuff); + free(data); + data = NULL; + } + + *curl = data; + return CURLE_OK; +} + +CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) +{ + va_list param; + char *cookiefile; + + va_start(param, option); + + switch(option) { + case CURLOPT_DNS_CACHE_TIMEOUT: + data->set.dns_cache_timeout = va_arg(param, int); + break; + case CURLOPT_DNS_USE_GLOBAL_CACHE: + { + int use_cache = va_arg(param, int); + if (use_cache) { + Curl_global_host_cache_init(); + } + + data->set.global_dns_cache = use_cache; + } + break; + case CURLOPT_SSL_CIPHER_LIST: + /* set a list of cipher we want to use in the SSL connection */ + data->set.ssl.cipher_list = va_arg(param, char *); + break; + + case CURLOPT_RANDOM_FILE: + /* + * This is the path name to a file that contains random data to seed + * the random SSL stuff with. The file is only used for reading. + */ + data->set.ssl.random_file = va_arg(param, char *); + break; + case CURLOPT_EGDSOCKET: + /* + * The Entropy Gathering Daemon socket pathname + */ + data->set.ssl.egdsocket = va_arg(param, char *); + break; + case CURLOPT_MAXCONNECTS: + /* + * Set the absolute number of maximum simultaneous alive connection that + * libcurl is allowed to have. + */ + { + long newconnects= va_arg(param, long); + struct connectdata **newptr; + long i; + + if(newconnects < data->state.numconnects) { + /* Since this number is *decreased* from the existing number, we must + close the possibly open connections that live on the indexes that + are being removed! */ + for(i=newconnects; i< data->state.numconnects; i++) + Curl_disconnect(data->state.connects[i]); + } + if(newconnects) { + newptr= (struct connectdata **) + realloc(data->state.connects, + sizeof(struct connectdata *) * newconnects); + if(!newptr) + /* we closed a few connections in vain, but so what? */ + return CURLE_OUT_OF_MEMORY; + + /* nullify the newly added pointers */ + for(i=data->state.numconnects; i<newconnects; i++) { + newptr[i] = NULL; + } + + data->state.connects = newptr; + data->state.numconnects = newconnects; + } + else { + /* zero makes NO cache at all */ + if(data->state.connects) + free(data->state.connects); + data->state.connects=NULL; + data->state.numconnects=0; + } + } + break; + case CURLOPT_FORBID_REUSE: + /* + * When this transfer is done, it must not be left to be reused by a + * subsequent transfer but shall be closed immediately. + */ + data->set.reuse_forbid = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_FRESH_CONNECT: + /* + * This transfer shall not use a previously cached connection but + * should be made with a fresh new connect! + */ + data->set.reuse_fresh = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_VERBOSE: + /* + * Verbose means infof() calls that give a lot of information about + * the connection and transfer procedures as well as internal choices. + */ + data->set.verbose = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_HEADER: + /* + * Set to include the header in the general data output stream. + */ + data->set.include_header = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_NOPROGRESS: + /* + * Shut off the internal supported progress meter + */ + data->set.hide_progress = va_arg(param, long)?TRUE:FALSE; + if(data->set.hide_progress) + data->progress.flags |= PGRS_HIDE; + else + data->progress.flags &= ~PGRS_HIDE; + break; + case CURLOPT_NOBODY: + /* + * Do not include the body part in the output data stream. + */ + data->set.opt_no_body = va_arg(param, long)?TRUE:FALSE; + 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 = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_UPLOAD: + case CURLOPT_PUT: + /* + * We want to sent data to the remote host. If this is HTTP, that equals + * using the PUT request. + */ + data->set.upload = va_arg(param, long)?TRUE:FALSE; + if(data->set.upload) + /* If this is HTTP, PUT is what's needed to "upload" */ + data->set.httpreq = HTTPREQ_PUT; + break; + case CURLOPT_FILETIME: + /* + * Try to get the file time of the remote document. The time will + * later (possibly) become available using curl_easy_getinfo(). + */ + data->set.get_filetime = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_FTP_CREATE_MISSING_DIRS: + /* + * An FTP option that modifies an upload to create missing directories on + * the server. + */ + data->set.ftp_create_missing_dirs = va_arg( param , long )?TRUE:FALSE; + 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 = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_FTPAPPEND: + /* + * We want to upload and append to an existing (FTP) file. + */ + data->set.ftp_append = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_NETRC: + /* + * Parse the $HOME/.netrc file + */ + data->set.use_netrc = (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.ftp_ascii = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_TIMECONDITION: + /* + * Set HTTP time condition. This must be one of the defines in the + * curl/curl.h header file. + */ + data->set.timecondition = (curl_TimeCond)va_arg(param, long); + break; + case CURLOPT_TIMEVALUE: + /* + * This is the value to compare with the remote document with the + * method set with CURLOPT_TIMECONDITION + */ + data->set.timevalue = (time_t)va_arg(param, long); + break; + case CURLOPT_SSLVERSION: + /* + * Set explicit SSL version to try to connect with, as some SSL + * implementations are lame. + */ + data->set.ssl.version = va_arg(param, long); + break; + +#ifndef CURL_DISABLE_HTTP + case CURLOPT_AUTOREFERER: + /* + * Switch on automatic referer that gets set if curl follows locations. + */ + data->set.http_auto_referer = va_arg(param, long)?1:0; + 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 = va_arg(param, long)?TRUE:FALSE; + break; + + case CURLOPT_UNRESTRICTED_AUTH: + /* + * Send authentication (user+password) when following locations, even when + * hostname changed. + */ + data->set.http_disable_hostname_check_before_authentication = + va_arg(param, long)?TRUE:FALSE; + break; + + case CURLOPT_MAXREDIRS: + /* + * The maximum amount of hops you allow curl to follow Location: + * headers. This should mostly be used to detect never-ending loops. + */ + data->set.maxredirs = va_arg(param, long); + break; + + case CURLOPT_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; + break; + + case CURLOPT_POSTFIELDS: + /* + * A string with POST data. Makes curl HTTP POST. + */ + data->set.postfields = va_arg(param, char *); + if(data->set.postfields) + data->set.httpreq = HTTPREQ_POST; + break; + + case CURLOPT_POSTFIELDSIZE: + /* + * The size of the POSTFIELD data 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 *); + if(data->set.httppost) + data->set.httpreq = HTTPREQ_POST_FORM; + 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; + + 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. + */ + cookiefile = (char *)va_arg(param, void *); + if(cookiefile) { + 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, cookiefile); + + 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)va_arg(param, long); + break; + + case CURLOPT_HTTPGET: + /* + * Set to force us do HTTP GET + */ + if(va_arg(param, long)) { + data->set.httpreq = HTTPREQ_GET; + data->set.upload = FALSE; /* switch off upload */ + } + break; + + case CURLOPT_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 = va_arg(param, long)?TRUE:FALSE; + 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_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. + */ + if(data->change.proxy_alloc) { + /* + * The already set string is allocated, free that first + */ + data->change.proxy_alloc=FALSE;; + free(data->change.proxy); + } + data->set.set_proxy = va_arg(param, char *); + data->change.proxy = data->set.set_proxy; + break; + + case CURLOPT_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_SSLEAY + 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_SSLEAY + 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_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 = data->set.ftpport?1:0; + break; + + case CURLOPT_FTP_USE_EPRT: + data->set.ftp_use_eprt = va_arg(param, long)?TRUE:FALSE; + break; + + case CURLOPT_FTP_USE_EPSV: + data->set.ftp_use_epsv = va_arg(param, long)?TRUE:FALSE; + break; + + case CURLOPT_INFILE: + /* + * FILE pointer to read the file to be uploaded from. Or possibly + * used as argument to the read callback. + */ + data->set.in = va_arg(param, FILE *); + break; + case CURLOPT_INFILESIZE: + /* + * If known, this should inform curl about the file size of the + * to-be-uploaded file. + */ + data->set.infilesize = va_arg(param, long); + break; + case CURLOPT_INFILESIZE_LARGE: + /* + * If known, this should inform curl about the file size of the + * to-be-uploaded file. + */ + data->set.infilesize = va_arg(param, curl_off_t); + break; + case CURLOPT_LOW_SPEED_LIMIT: + /* + * The low speed limit that if transfers are below this for + * CURLOPT_LOW_SPEED_TIME, the transfer is aborted. + */ + data->set.low_speed_limit=va_arg(param, long); + break; + case CURLOPT_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_SSLCERT: + /* + * String that holds file name of the SSL certificate to use + */ + data->set.cert = va_arg(param, char *); + break; + case CURLOPT_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use + */ + data->set.cert_type = va_arg(param, char *); + break; + case CURLOPT_SSLKEY: + /* + * String that holds file name of the SSL certificate to use + */ + data->set.key = va_arg(param, char *); + break; + case CURLOPT_SSLKEYTYPE: + /* + * String that holds file type of the SSL certificate to use + */ + data->set.key_type = va_arg(param, char *); + break; + case CURLOPT_SSLKEYPASSWD: + /* + * String that holds the SSL private key password. + */ + data->set.key_passwd = va_arg(param, char *); + break; + case CURLOPT_SSLENGINE: + /* + * String that holds the SSL crypto engine. + */ +#ifdef HAVE_OPENSSL_ENGINE_H + { + const char *cpTemp = va_arg(param, char *); + ENGINE *e; + if (cpTemp && cpTemp[0]) { + e = ENGINE_by_id(cpTemp); + if (e) { + if (data->engine) { + ENGINE_free(data->engine); + } + data->engine = e; + } + else { + failf(data, "SSL Engine '%s' not found", cpTemp); + return CURLE_SSL_ENGINE_NOTFOUND; + } + } + } + break; +#else + return CURLE_SSL_ENGINE_NOTFOUND; +#endif + case CURLOPT_SSLENGINE_DEFAULT: + /* + * flag to set engine as default. + */ +#ifdef HAVE_OPENSSL_ENGINE_H + if (data->engine) { + if (ENGINE_set_default(data->engine, ENGINE_METHOD_ALL) > 0) { +#ifdef DEBUG + fprintf(stderr,"set default crypto engine\n"); +#endif + } + else { +#ifdef DEBUG + failf(data, "set default crypto engine failed"); +#endif + return CURLE_SSL_ENGINE_SETFAILED; + } + } +#endif + break; + case CURLOPT_CRLF: + /* + * Kludgy option to enable CRLF convertions. Subject for removal. + */ + data->set.crlf = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_INTERFACE: + /* + * Set what interface to bind to when performing an operation and thus + * what from-IP your connection will use. + */ + data->set.device = va_arg(param, char *); + break; + case CURLOPT_KRB4LEVEL: + /* + * A string that defines the krb4 security level. + */ + data->set.krb4_level = va_arg(param, char *); + data->set.krb4=data->set.krb4_level?TRUE:FALSE; + break; + case CURLOPT_SSL_VERIFYPEER: + /* + * Enable peer SSL verifying. + */ + data->set.ssl.verifypeer = va_arg(param, long); + break; + case CURLOPT_SSL_VERIFYHOST: + /* + * Enable verification of the CN contained in the peer certificate + */ + data->set.ssl.verifyhost = va_arg(param, long); + break; + case CURLOPT_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 = va_arg(param, long) ? TRUE : FALSE; + break; + + case CURLOPT_SHARE: + { + struct Curl_share *set; + set = va_arg(param, struct Curl_share *); + + /* disconnect from old share, if any */ + if(data->share) { + Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + + if(data->share->hostcache == data->hostcache) + data->hostcache = NULL; + + 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 own one if any */ + if(data->hostcache) + Curl_hash_destroy(data->hostcache); + + data->hostcache = data->share->hostcache; + } +#ifndef CURL_DISABLE_HTTP + 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); + + } +#ifndef CURL_DISABLE_HTTP + /* 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 = 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_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)va_arg(param, long); + break; + + /*********** 3rd party transfer options ***********/ + case CURLOPT_SOURCE_HOST: + /* + * Use SOURCE HOST + */ + data->set.source_host = va_arg(param, char *); + data->set.printhost = (data->set.source_host != NULL); + break; + + case CURLOPT_SOURCE_PORT: + /* + * Use SOURCE PORT + */ + data->set.source_port = va_arg(param, char *); + break; + + case CURLOPT_SOURCE_USERPWD: + /* + * Use SOURCE USER[:PASSWORD] + */ + data->set.source_userpwd = va_arg(param, char *); + break; + + case CURLOPT_SOURCE_PATH: + /* + * Use SOURCE PATH + */ + data->set.source_path = va_arg(param, char *); + break; + + case CURLOPT_PASV_HOST: + /* + * Indicates whether source or target host is passive + */ + data->set.pasvHost = va_arg(param, long)?CURL_SOURCE_PASV:CURL_TARGET_PASV; + break; + + case CURLOPT_SOURCE_PREQUOTE: + /* + * List of RAW FTP commands to use before a transfer on the source host + */ + data->set.source_prequote = va_arg(param, struct curl_slist *); + break; + + case CURLOPT_SOURCE_POSTQUOTE: + /* + * List of RAW FTP commands to use after a transfer on the source host + */ + data->set.source_postquote = va_arg(param, struct curl_slist *); + break; + + default: + /* unknown tag and its companion, just ignore: */ + return CURLE_FAILED_INIT; /* correct this */ + } + return CURLE_OK; +} + +CURLcode Curl_disconnect(struct connectdata *conn) +{ + struct SessionHandle *data; + if(!conn) + return CURLE_OK; /* this is closed and fine already */ + + data = conn->data; + + /* + * The range string is usually freed in curl_done(), but we might + * get here *instead* if we fail prematurely. Thus we need to be able + * to free this resource here as well. + */ + if(conn->bits.rangestringalloc) { + free(conn->range); + conn->bits.rangestringalloc = FALSE; + } + + if((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.authhost.want; + + data->state.authproblem = FALSE; + } + + 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); + data->state.connects[conn->connectindex] = NULL; + } + + Curl_safefree(conn->proto.generic); + Curl_safefree(conn->newurl); + Curl_safefree(conn->pathbuffer); /* the URL path buffer */ + + Curl_safefree(conn->host.rawalloc); /* host name buffer */ + Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */ +#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); + + /* 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); + +#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \ + defined(USE_THREADING_GETADDRINFO) + /* possible left-overs from the async name resolve */ + Curl_safefree(conn->async.hostname); + Curl_safefree(conn->async.os_specific); +#endif + + Curl_free_ssl_config(&conn->ssl_config); + + free(conn); /* free all the connection oriented data */ + + return CURLE_OK; +} + +/* + * This function should return TRUE if the socket is to be assumed to + * be dead. Most commonly this happens when the server has closed the + * connection due to inactivity. + */ +static bool SocketIsDead(curl_socket_t sock) +{ + int sval; + bool ret_val = TRUE; + fd_set check_set; + struct timeval to; + + FD_ZERO(&check_set); + FD_SET(sock, &check_set); + + to.tv_sec = 0; + to.tv_usec = 0; + + sval = select(sock + 1, &check_set, 0, 0, &to); + if(sval == 0) + /* timeout */ + ret_val = FALSE; + + return ret_val; +} + +/* + * Given one filled in connection struct (named needle), this function should + * detect if there already is one that have all the significant details + * exactly the same and thus should be used instead. + */ +static bool +ConnectionExists(struct SessionHandle *data, + struct connectdata *needle, + struct connectdata **usethis) +{ + long i; + struct connectdata *check; + + for(i=0; i< data->state.numconnects; i++) { + 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.connects[i]; + if(!check) + /* NULL pointer means not filled-in entry */ + 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)) { + continue; + } + } + if((needle->protocol & PROT_FTP) || + ((needle->protocol & PROT_HTTP) && + (needle->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) { + bool dead = SocketIsDead(check->sock[FIRSTSOCKET]); + if(dead) { + /* + */ + infof(data, "Connection %d seems to be dead!\n", i); + Curl_disconnect(check); /* disconnect resources */ + data->state.connects[i]=NULL; /* nothing here */ + + /* There's no need to continue searching, because we only store + one connection for each unique set of identifiers */ + return FALSE; + } + + *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; i< data->state.numconnects; i++) { + conn = data->state.connects[i]; + + if(!conn) + continue; + + /* + * By using the set policy, we score each connection. + */ + switch(data->set.closepolicy) { + case CURLCLOSEPOLICY_LEAST_RECENTLY_USED: + default: + /* + * Set higher score for the age passed since the connection + * was used. + */ + score = Curl_tvdiff(now, conn->now); + break; + case CURLCLOSEPOLICY_OLDEST: + /* + * Set higher score for the age passed since the connection + * was created. + */ + score = Curl_tvdiff(now, conn->created); + break; + } + + if(score > highscore) { + highscore = score; + connindex = i; + } + } + if(connindex >= 0) { + + /* the winner gets the honour of being disconnected */ + (void) Curl_disconnect(data->state.connects[connindex]); + + /* clean the array entry */ + data->state.connects[connindex] = NULL; + } + + return connindex; /* return the available index or -1 */ +} + +/* + * The given input connection struct pointer is to be stored. If the "cache" + * is already full, we must clean out the most suitable using the previously + * set policy. + * + * The given connection should be unique. That must've been checked prior to + * this call. + */ +static long +ConnectionStore(struct SessionHandle *data, + struct connectdata *conn) +{ + long i; + for(i=0; i< data->state.numconnects; i++) { + if(!data->state.connects[i]) + break; + } + if(i == data->state.numconnects) { + /* there was no room available, kill one */ + i = ConnectionKillOne(data); + infof(data, "Connection (#%d) was killed to make room\n", i); + } + + if(-1 != i) { + /* only do this if a true index was returned, if -1 was returned there + is no room in the cache for an unknown reason and we cannot store + this there. */ + data->state.connects[i] = conn; /* fill in this */ + conn->connectindex = i; /* make the child know where the pointer to this + particular data is stored */ + } + return i; +} + +/* + * This function logs in to a SOCKS5 proxy and sends the specifies the final + * desitination server. + */ +static int handleSock5Proxy(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; + int sock = conn->sock[FIRSTSOCKET]; + + Curl_nonblock(sock, FALSE); + + 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 */ + + code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]), + &written); + if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) { + failf(conn->data, "Unable to send initial SOCKS5 request."); + return 1; + } + + result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread); + if ((result != CURLE_OK) || (actualread != 2)) { + failf(conn->data, "Unable to receive initial SOCKS5 response."); + return 1; + } + + if (socksreq[0] != 5) { + failf(conn->data, "Received invalid version in initial SOCKS5 response."); + return 1; + } + if (socksreq[1] == 0) { + /* Nothing to do, no authentication needed */ + ; + } + else if (socksreq[1] == 2) { + /* Needs user name and password */ + int userlen, pwlen, len; + + userlen = (int)strlen(proxy_name); + pwlen = proxy_password?(int)strlen(proxy_password):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(conn->data, "Failed to send SOCKS5 sub-negotiation request."); + return 1; + } + + result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread); + if ((result != CURLE_OK) || (actualread != 2)) { + failf(conn->data, "Unable to receive SOCKS5 sub-negotiation response."); + return 1; + } + + if ((socksreq[0] != 5) || /* version */ + (socksreq[1] != 0)) { /* status */ + failf(conn->data, "User was rejected by the SOCKS5 server (%d %d).", + socksreq[0], socksreq[1]); + return 1; + } + + /* Everything is good so far, user was authenticated! */ + } + else { + /* error */ + if (socksreq[1] == 1) { + failf(conn->data, + "SOCKS5 GSSAPI per-message authentication is not supported."); + return 1; + } + else if (socksreq[1] == 255) { + if (proxy_name[0] == 0) { + failf(conn->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(conn->data, "No authentication method was acceptable."); + } + return 1; + } + else { + failf(conn->data, + "Undocumented SOCKS5 mode attempted to be used by server."); + return 1; + } + } + + /* 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 1; + + if(rc == CURLRESOLV_PENDING) + /* this requires that we're in "wait for resolve" state */ + rc = Curl_wait_for_resolv(conn, &dns); + (void)rc; + + /* + * 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(conn->data, dns); /* not used anymore from now on */ + } + if(!hp) { + failf(conn->data, "Failed to resolve \"%s\" for SOCKS5 connect.", + conn->host.name); + return 1; + } + } + + { + unsigned short s = htons(conn->remote_port); + memcpy(socksreq+8, &s, sizeof(unsigned short)); + } + + { + const int packetsize = 10; + + code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); + if ((code != CURLE_OK) || (written != packetsize)) { + failf(conn->data, "Failed to send SOCKS5 connect request."); + return 1; + } + + result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread); + if ((result != CURLE_OK) || (actualread != packetsize)) { + failf(conn->data, "Failed to receive SOCKS5 connect request ack."); + return 1; + } + + if (socksreq[0] != 5) { /* version */ + failf(conn->data, + "SOCKS5 reply has wrong version, version should be 5."); + return 1; + } + if (socksreq[1] != 0) { /* Anything besides 0 is an error */ + unsigned short sh; + memcpy(&sh, socksreq+8, sizeof(unsigned short)); + + failf(conn->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(sh), + socksreq[1]); + return 1; + } + } + + Curl_nonblock(sock, TRUE); + return 0; /* Proxy was successful! */ +} + +static CURLcode ConnectPlease(struct connectdata *conn, + struct Curl_dns_entry *hostaddr, + bool *connected) +{ + CURLcode result; + Curl_addrinfo *addr; + struct SessionHandle *data = conn->data; + char *hostname = data->change.proxy?conn->proxy.name:conn->host.name; + + infof(data, "About to connect() to %s port %d\n", + hostname, conn->port); + + /************************************************************* + * 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; + + if (conn->data->set.proxytype == CURLPROXY_SOCKS5) { + return handleSock5Proxy(conn->proxyuser, + conn->proxypasswd, + conn) ? + CURLE_COULDNT_CONNECT : CURLE_OK; + } + else if (conn->data->set.proxytype == CURLPROXY_HTTP) { + /* do nothing here. handled later. */ + } + else { + failf(conn->data, "unknown proxytype option given"); + return CURLE_COULDNT_CONNECT; + } + } + + return result; +} + +/* + * verboseconnect() displays verbose information after a connect + */ +static void verboseconnect(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + char addrbuf[256]; + + /* Get a printable version of the network address. */ + Curl_printable_address(conn->ip_addr, addrbuf, sizeof(addrbuf)); + infof(data, "Connected to %s (%s) port %d\n", + conn->bits.httpproxy ? conn->proxy.dispname : conn->host.dispname, + addrbuf[0] ? addrbuf : "??", conn->port); +} + +/* + * We have discovered that the TCP connection has been successful, we can now + * proceed with some action. + * + * If we're using the multi interface, this host address pointer is most + * likely NULL at this point as we can't keep the resolved info around. This + * may call for some reworking, like a reference counter in the struct or + * something. + */ +CURLcode Curl_protocol_connect(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + CURLcode result=CURLE_OK; + + if(conn->bits.tcpconnect) + /* 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. */ + return CURLE_OK; + + Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ + + if(data->set.verbose) + verboseconnect(conn); + + 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); + } + + 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; +} +#endif + +static void fix_hostname(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; + struct SessionHandle *data = conn->data; + 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; IDNA error %d\n", + host->name, rc); + else { + host->encalloc = ace_hostname; + /* change the name pointer to point to the encoded hostname */ + host->name = host->encalloc; + } + } +#else + (void)conn; /* never used */ +#endif +} + + +/** + * 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 + * @param async is set TRUE/FALSE depending on the nature of this lookup + * @return CURLcode + * @see SetupConnection() + */ + +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; + 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; + +#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; /* 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 *)malloc(sizeof(struct connectdata)); + if(!conn) { + *in_connect = NULL; /* clear the pointer */ + return CURLE_OUT_OF_MEMORY; + } + /* We must set the return variable as soon as possible, so that our + parent can cleanup any possible allocs we may have done before + any failure */ + *in_connect = conn; + + /* we have to init the struct */ + memset(conn, 0, sizeof(struct connectdata)); + + /* and we setup a few fields in case we end up actually using this struct */ + conn->data = data; /* remember our daddy */ + conn->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 = (data->change.proxy && *data->change.proxy && + (data->set.proxytype == CURLPROXY_HTTP))? + TRUE:FALSE; /* http proxy or not */ + + /* Default protocol-independent behavior doesn't support persistant + connections, so we set this to force-close. Protocols that support + this need to set this to FALSE in their "curl_do" functions. */ + conn->bits.close = TRUE; + + /* maxdownload must be -1 on init, as 0 is a valid value! */ + conn->maxdownload = -1; /* might have been used previously! */ + + /* Store creation time to help future close decision making */ + conn->created = Curl_tvnow(); + + conn->bits.use_range = data->set.set_range?TRUE:FALSE; /* range status */ + conn->range = data->set.set_range; /* clone the range setting */ + conn->resume_from = data->set.set_resume_from; /* inherite resume_from */ + + /* Set the start time temporary to this creation time to allow easier + timeout checks before the transfer has started for real. The start time + is later set "for real" using Curl_pgrsStartNow(). */ + conn->data->progress.start = conn->created; + + conn->bits.user_passwd = data->set.userpwd?1:0; + conn->bits.proxy_user_passwd = data->set.proxyuserpwd?1:0; + conn->bits.no_body = data->set.opt_no_body; + conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; + + /* 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; + + conn->pathbuffer=(char *)malloc(urllen); + if(NULL == conn->pathbuffer) + return CURLE_OUT_OF_MEMORY; /* really bad error */ + conn->path = conn->pathbuffer; + + conn->host.rawalloc=(char *)malloc(urllen); + if(NULL == conn->host.rawalloc) + return CURLE_OUT_OF_MEMORY; + conn->host.name = conn->host.rawalloc; + + /************************************************************* + * 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, + conn->path)) && strequal(conn->protostr, "file")) { + if(conn->path[0] == '/' && conn->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(conn->path, conn->path + 2, strlen(conn->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(conn->path[0] != '/') { + /* the URL included a host name, we ignore host names in file:// URLs + as the standards don't define what to do with them */ + char *ptr=strchr(conn->path, '/'); + if(ptr) { + /* there was a slash present + + RFC1738 (section 3.1, page 5) says: + + The rest of the locator consists of data specific to the scheme, + and is known as the "url-path". It supplies the details of how the + specified resource can be accessed. Note that the "/" between the + host (or port) and the url-path is NOT part of the url-path. + + As most agents use file://localhost/foo to get '/foo' although the + slash preceeding foo is a separator and not a slash for the path, + a URL as file://localhost//foo must be valid as well, to refer to + the same file with an absolute path. + */ + + if(ptr[1] && ('/' == ptr[1])) + /* if there was two slashes, we skip the first one as that is then + used truly as a separator */ + ptr++; + + /* This cannot be made with strcpy, as the memory chunks overlap! */ + memmove(conn->path, ptr, strlen(ptr)+1); + } + } + + strcpy(conn->protostr, "file"); /* store protocol string lowercase */ + } + else { + /* Set default path */ + strcpy(conn->path, "/"); + + /* We need to search for '/' OR '?' - whichever comes first after host + * name but before the path. We need to change that to handle things like + * http://example.com?param= (notice the missing '/'). Later we'll insert + * that missing slash at the beginning of the path. + */ + if (2 > sscanf(data->change.url, + "%15[^\n:]://%[^\n/?]%[^\n]", + conn->protostr, + conn->host.name, conn->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, conn->path)) ) { + /* + * We couldn't even get this format. + */ + failf(data, "<url> malformed"); + return CURLE_URL_MALFORMAT; + } + + /* + * Since there was no protocol part specified, we guess what protocol it + * is based on the first letters of the server name. + */ + + /* Note: if you add a new protocol, please update the list in + * lib/version.c too! */ + + if(checkprefix("GOPHER", conn->host.name)) + strcpy(conn->protostr, "gopher"); +#ifdef USE_SSLEAY + else if(checkprefix("HTTPS", conn->host.name)) + strcpy(conn->protostr, "https"); + else if(checkprefix("FTPS", conn->host.name)) + strcpy(conn->protostr, "ftps"); +#endif /* USE_SSLEAY */ + else if(checkprefix("FTP", conn->host.name)) + strcpy(conn->protostr, "ftp"); + else if(checkprefix("TELNET", conn->host.name)) + strcpy(conn->protostr, "telnet"); + 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 */ + } + } + + /* 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(conn->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(&conn->path[1], conn->path, strlen(conn->path)+1); + conn->path[0] = '/'; + } + + /* + * So if the URL was A://B/C, + * conn->protostr is A + * conn->host.name is B + * conn->path is /C + */ + + /************************************************************* + * 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 = strdup(proxyuser); + if(!conn->proxyuser) + return CURLE_OUT_OF_MEMORY; + + conn->proxypasswd = strdup(proxypasswd); + if(!conn->proxypasswd) + return CURLE_OUT_OF_MEMORY; + } + + /************************************************************* + * Detect what (if any) proxy to use + *************************************************************/ + if(!data->change.proxy) { + /* If proxy was not specified, we check for default proxy environment + * variables, to enable i.e Lynx compliance: + * + * http_proxy=http://some.server.dom:port/ + * https_proxy=http://some.server.dom:port/ + * ftp_proxy=http://some.server.dom:port/ + * gopher_proxy=http://some.server.dom:port/ + * no_proxy=domain1.dom,host.domain2.dom + * (a comma-separated list of hosts which should + * not be proxied, or an asterisk to override + * all proxy variables) + * all_proxy=http://some.server.dom:port/ + * (seems to exist for the CERN www lib. Probably + * the first to check for.) + * + * For compatibility, the all-uppercase versions of these variables are + * checked if the lowercase versions don't exist. + */ + char *no_proxy; + char *no_proxy_tok_buf; + char *proxy=NULL; + char proxy_env[128]; + (void)proxy; + + 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++ = 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 = 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) { + /* we have a proxy here to set */ + char *ptr; + char proxyuser[MAX_CURL_USER_LENGTH]; + char proxypasswd[MAX_CURL_PASSWORD_LENGTH]; + + char *fineptr; + + /* skip the possible protocol piece */ + ptr=strstr(proxy, "://"); + if(ptr) + ptr += 3; + else + ptr = proxy; + + fineptr = ptr; + + /* check for an @-letter */ + ptr = strchr(ptr, '@'); + if(ptr && (2 == sscanf(fineptr, + "%" MAX_CURL_USER_LENGTH_TXT"[^:]:" + "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", + proxyuser, proxypasswd))) { + CURLcode res = CURLE_OK; + + /* found user and password, rip them out */ + Curl_safefree(conn->proxyuser); + conn->proxyuser = strdup(proxyuser); + + if(!conn->proxyuser) + res = CURLE_OUT_OF_MEMORY; + else { + Curl_safefree(conn->proxypasswd); + conn->proxypasswd = strdup(proxypasswd); + + if(!conn->proxypasswd) + res = CURLE_OUT_OF_MEMORY; + } + + if(CURLE_OK == res) { + conn->bits.proxy_user_passwd = TRUE; /* enable it */ + ptr = strdup(ptr+1); /* the right side of the @-letter */ + + if(ptr) { + free(proxy); /* free the former proxy string */ + proxy = ptr; /* now use this instead */ + } + else + res = CURLE_OUT_OF_MEMORY; + } + + if(res) { + free(proxy); /* free the allocated proxy string */ + return res; + } + } + + data->change.proxy = proxy; + data->change.proxy_alloc=TRUE; /* this needs to be freed later */ + conn->bits.httpproxy = TRUE; + } + } /* if (!nope) - it wasn't specified non-proxy */ + } /* NO_PROXY wasn't specified or '*' */ + if(no_proxy) + free(no_proxy); + } /* if not using proxy */ + + /************************************************************* + * No protocol part in URL was used, add it! + *************************************************************/ + if(conn->protocol&PROT_MISSING) { + /* We're guessing prefixes here and if we're told to use a proxy or if + we're gonna follow a Location: later or... then we need the protocol + part added so that we have a valid URL. */ + char *reurl; + + reurl = aprintf("%s://%s", conn->protostr, data->change.url); + + if(!reurl) + return CURLE_OUT_OF_MEMORY; + + data->change.url = reurl; + data->change.url_alloc = TRUE; /* free this later */ + conn->protocol &= ~PROT_MISSING; /* switch that one off again */ + } + +#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(conn->resume_from) { + if(!conn->bits.use_range) { + /* if it already was in use, we just skip this */ + conn->range = aprintf("%" FORMAT_OFF_T "-", conn->resume_from); + if(!conn->range) + return CURLE_OUT_OF_MEMORY; + conn->bits.rangestringalloc = TRUE; /* mark as allocated */ + conn->bits.use_range = 1; /* switch on range usage */ + } + } +#endif + /************************************************************* + * Setup internals depending on protocol + *************************************************************/ + + if (strequal(conn->protostr, "HTTP")) { +#ifndef CURL_DISABLE_HTTP + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_HTTP; + conn->remote_port = PORT_HTTP; + conn->protocol |= PROT_HTTP; + conn->curl_do = Curl_http; + conn->curl_do_more = 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_SSLEAY) && !defined(CURL_DISABLE_HTTP) + + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_HTTPS; + conn->remote_port = PORT_HTTPS; + conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL; + + conn->curl_do = Curl_http; + conn->curl_do_more = NULL; + conn->curl_done = Curl_http_done; + conn->curl_connect = Curl_http_connect; + +#else /* USE_SSLEAY */ + failf(data, LIBCURL_NAME + " was built with SSL disabled, https: not supported!"); + return CURLE_UNSUPPORTED_PROTOCOL; +#endif /* !USE_SSLEAY */ + } + else if (strequal(conn->protostr, "GOPHER")) { +#ifndef CURL_DISABLE_GOPHER + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_GOPHER; + conn->remote_port = PORT_GOPHER; + /* Skip /<item-type>/ in path if present */ + if (isdigit((int)conn->path[1])) { + conn->path = strchr(&conn->path[1], '/'); + if (conn->path == NULL) + conn->path = conn->pathbuffer; + } + conn->protocol |= PROT_GOPHER; + conn->curl_do = Curl_http; + conn->curl_do_more = NULL; + conn->curl_done = Curl_http_done; +#else + failf(data, LIBCURL_NAME + " was built with GOPHER disabled, gopher: not supported!"); +#endif + } + 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_SSLEAY + 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_SSLEAY */ + } + + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port:port; + conn->remote_port = port; + conn->protocol |= PROT_FTP; + + if(data->change.proxy && + *data->change.proxy && + !data->set.tunnel_thru_httpproxy) { + /* Unless we have asked to tunnel ftp operations through the proxy, we + switch and use HTTP operations only */ + if(conn->protocol & PROT_FTPS) { + /* FTPS is a hacked protocol and does not work through your + ordinary http proxy! */ + failf(data, "ftps does not work through http proxy!"); + return CURLE_UNSUPPORTED_PROTOCOL; + } +#ifndef CURL_DISABLE_HTTP + conn->curl_do = Curl_http; + conn->curl_done = Curl_http_done; +#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_disconnect = Curl_ftp_disconnect; + } + + conn->path++; /* don't include the initial slash */ + + /* FTP URLs support an extension like ";type=<typecode>" that + * we'll try to get now! */ + type=strstr(conn->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 = toupper((int)type[6]); + switch(command) { + case 'A': /* ASCII mode */ + data->set.ftp_ascii = 1; + break; + case 'D': /* directory mode */ + data->set.ftp_list_only = 1; + break; + case 'I': /* binary mode */ + default: + /* switch off ASCII */ + data->set.ftp_ascii = 0; + break; + } + } +#else /* 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 = (data->set.use_port && data->state.allow_port)? + data->set.use_port: PORT_TELNET; + conn->remote_port = PORT_TELNET; + conn->curl_do = Curl_telnet; + conn->curl_done = Curl_telnet_done; +#else + 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 = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_DICT; + conn->remote_port = PORT_DICT; + conn->curl_do = Curl_dict; + conn->curl_done = NULL; /* no DICT-specific done */ +#else + 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 = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_LDAP; + conn->remote_port = PORT_LDAP; + conn->curl_do = Curl_ldap; + conn->curl_done = NULL; /* no LDAP-specific done */ +#else + 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->bits.tcpconnect = TRUE; /* we are "connected */ + result = Curl_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 { + /* 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; + } + + /************************************************************* + * 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 (tmp) { + 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; + } + } + + if(data->change.proxy && *data->change.proxy) { + /* If this is supposed to use a proxy, we need to figure out the proxy + host name name, so that we can re-use an existing connection + that may exist registered to the same proxy host. */ + + char *prox_portno; + char *endofprot; + + /* We need to make a duplicate of the proxy so that we can modify the + string safely. */ + char *proxydup=strdup(data->change.proxy); + + /* We use 'proxyptr' to point to the proxy name from now on... */ + char *proxyptr=proxydup; + + if(NULL == proxydup) { + failf(data, "memory shortage"); + return CURLE_OUT_OF_MEMORY; + } + + /* Daniel Dec 10, 1998: + We do the proxy host string parsing here. We want the host name and the + port name. Accept a protocol:// prefix, even though it should just be + ignored. */ + + /* 1. skip the protocol part if present */ + endofprot=strstr(proxyptr, "://"); + if(endofprot) { + proxyptr = endofprot+3; + } + + /* allow user to specify proxy.server.com:1080 if desired */ + prox_portno = strchr (proxyptr, ':'); + if (prox_portno) { + *prox_portno = 0x0; /* cut off number from host name */ + prox_portno ++; + /* now set the local port number */ + conn->port = atoi(prox_portno); + } + else if(data->set.proxyport) { + /* None given in the proxy string, then get the default one if it is + given */ + conn->port = data->set.proxyport; + } + + /* now, clone the cleaned proxy host name */ + conn->proxy.rawalloc = strdup(proxyptr); + conn->proxy.name = conn->proxy.rawalloc; + + free(proxydup); /* free the duplicate pointer and not the modified */ + if(!conn->proxy.rawalloc) + return CURLE_OUT_OF_MEMORY; + } + + /************************************************************* + * If the protcol 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)) { + /* This is a FTP or HTTP 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, "%127[^:@]:%127[^@]", + user, passwd); + } + else + /* no name given, get the password only */ + sscanf(userpass, ":%127[^@]", passwd); + + if(user[0]) { + char *newname=curl_unescape(user, 0); + 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_unescape(passwd, 0); + if(!newpasswd) + return CURLE_OUT_OF_MEMORY; + if(strlen(newpasswd) < sizeof(passwd)) + strcpy(passwd, newpasswd); + + free(newpasswd); + } + } + } + } + + /* 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 "inherite 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); + } + + 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 .netrc file, using defaults\n", + conn->host.name); + } + else + 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 */ + + /* 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; + conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd; + + /* 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 */ + + free(conn->pathbuffer); /* free the newly allocated path pointer */ + conn->pathbuffer = old_conn->pathbuffer; /* use the old one */ + conn->path = old_conn->path; + + /* re-use init */ + conn->bits.reuse = TRUE; /* yes, we're re-using here */ + conn->bits.chunk = FALSE; /* always assume not chunked unless told + otherwise */ + conn->maxdownload = -1; /* might have been used previously! */ + + Curl_safefree(old_conn->user); + Curl_safefree(old_conn->passwd); + Curl_safefree(old_conn->proxyuser); + Curl_safefree(old_conn->proxypasswd); + + if(old_conn->bits.rangestringalloc) + free(old_conn->range); + + free(old_conn); /* we don't need this anymore */ + + /* + * If we're doing a resumed transfer, we need to setup our stuff + * properly. + */ + conn->resume_from = data->set.set_resume_from; + if (conn->resume_from) { + if (conn->bits.rangestringalloc == TRUE) + free(conn->range); + conn->range = aprintf("%" FORMAT_OFF_T "-", conn->resume_from); + if(!conn->range) + return CURLE_OUT_OF_MEMORY; + + /* tell ourselves to fetch this range */ + conn->bits.use_range = TRUE; /* enable range download */ + conn->bits.rangestringalloc = TRUE; /* mark range string allocated */ + } + else if (data->set.set_range) { + /* There is a range, but is not a resume, useful for random ftp access */ + conn->range = strdup(data->set.set_range); + if(!conn->range) + return CURLE_OUT_OF_MEMORY; + conn->bits.rangestringalloc = TRUE; /* mark range string allocated */ + conn->bits.use_range = TRUE; /* enable range download */ + } + else + conn->bits.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 persistant conncetion stuff */ + conn->fread = data->set.fread; + conn->fread_in = data->set.in; + + conn->bits.upload_chunky = + ((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 */ + TRUE: + /* else, no chunky upload */ + 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_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 + /* alarm() makes a signal get sent when the timeout fires off, and that + will abort system calls */ + prev_alarm = alarm(data->set.connecttimeout? + data->set.connecttimeout: + data->set.timeout); + /* We can expect the conn->created time to be "now", as that was just + recently set in the beginning of this function and nothing slow + has been done since then until now. */ +#endif +#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; + conn->dns_entry = NULL; /* we don't connect now so we don't have any fresh + dns entry struct to point to */ + + if (conn->bits.httpproxy) + fix_hostname(conn, &conn->host); + } + else { + /* this is a fresh connect */ + + /* set a pointer to the hostname we display */ + fix_hostname(conn, &conn->host); + + if(!data->change.proxy || !*data->change.proxy) { + /* If not connecting via a proxy, extract the port from the URL, if it is + * there, thus overriding any defaults that might have been set above. */ + conn->port = conn->remote_port; /* it is the same port */ + + /* Resolve target host right on */ + 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(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() should be called after the name resolve initiated in + * CreateConnection() is all done. + */ + +static CURLcode SetupConnection(struct connectdata *conn, + struct Curl_dns_entry *hostaddr) +{ + struct SessionHandle *data = conn->data; + CURLcode result=CURLE_OK; + + 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 */ + return result; + + /************************************************************* + * Send user-agent to HTTP proxies even if the target protocol + * isn't HTTP. + *************************************************************/ + if((conn->protocol&PROT_HTTP) || + (data->change.proxy && *data->change.proxy)) { + if(data->set.useragent) { + Curl_safefree(conn->allocptr.uagent); + conn->allocptr.uagent = + aprintf("User-Agent: %s\015\012", data->set.useragent); + if(!conn->allocptr.uagent) + return CURLE_OUT_OF_MEMORY; + } + } + + if(data->set.encoding) { + Curl_safefree(conn->allocptr.accept_encoding); + conn->allocptr.accept_encoding = + aprintf("Accept-Encoding: %s\015\012", data->set.encoding); + if(!conn->allocptr.accept_encoding) + return CURLE_OUT_OF_MEMORY; + } + + conn->bytecount = 0; + conn->headerbytecount = 0; + + if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) { + bool connected; + + /* Connect only if not already connected! */ + result = ConnectPlease(conn, hostaddr, &connected); + + if(connected) { + result = Curl_protocol_connect(conn); + if(CURLE_OK == result) + conn->bits.tcpconnect = TRUE; + } + else + conn->bits.tcpconnect = FALSE; + + + if(CURLE_OK != result) + return result; + } + else { + Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ + conn->bits.tcpconnect = TRUE; + if(data->set.verbose) + verboseconnect(conn); + } + + 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) +{ + 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 so => continue connecting from here */ + code = SetupConnection(*in_connect, dns); + /* 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 */ + } + } + + return code; +} + +/* Call this function after Curl_connect() has returned async=TRUE and + then a successful name resolve has been received */ +CURLcode Curl_async_resolved(struct connectdata *conn) +{ +#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \ + defined(USE_THREADING_GETADDRINFO) + CURLcode code = SetupConnection(conn, conn->async.dns); + + 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; + return CURLE_OK; +#endif +} + + +CURLcode Curl_done(struct connectdata **connp, + CURLcode status) /* an error if this is called after an + error was detected */ +{ + CURLcode result; + struct connectdata *conn = *connp; + struct SessionHandle *data=conn->data; + + /* cleanups done even if the connection is re-used */ + + if(conn->bits.rangestringalloc) { + free(conn->range); + conn->bits.rangestringalloc = FALSE; + } + + /* Cleanup possible redirect junk */ + if(conn->newurl) { + free(conn->newurl); + conn->newurl = NULL; + } + + if(conn->dns_entry) + Curl_resolv_unlock(conn->data, conn->dns_entry); /* done with this */ + +#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_hostcache_prune(data); /* kill old DNS cache entries */ + + /* this calls the protocol-specific function pointer previously set */ + if(conn->curl_done) + result = conn->curl_done(conn, status); + else + result = CURLE_OK; + + Curl_pgrsDone(conn); /* done with the operation */ + + /* if data->set.reuse_forbid is TRUE, it means the libcurl client has + forced us to close this no matter what we think. + + if conn->bits.close is TRUE, it means that the connection should be + closed in spite of all our efforts to be nice, due to protocol + restrictions in our or the server's end */ + if(data->set.reuse_forbid || conn->bits.close) { + CURLcode res2; + 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 + 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) +{ + CURLcode result=CURLE_OK; + struct connectdata *conn = *connp; + struct SessionHandle *data=conn->data; + + 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); + + /* 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); /* we are so done with this */ + + /* conn may no longer be a good pointer */ + + if(CURLE_OK == result) { + bool async; + /* Now, redo the connect and get a new connection */ + result = Curl_connect(data, connp, &async); + 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); + if(result) + return result; + } + + /* ... finally back to actually retry the DO phase */ + result = conn->curl_do(conn); + } + } + } + } + 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; +} + +static bool safe_strequal(char* str1, char* str2) +{ + if(str1 && str2) + /* both pointers point to something then compare them */ + return strequal(str1, str2); + else + /* if both pointers are NULL then treat them as equal */ + return (!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); +} + diff --git a/Utilities/cmcurl/url.h b/Utilities/cmcurl/url.h new file mode 100644 index 0000000..8fd4795 --- /dev/null +++ b/Utilities/cmcurl/url.h @@ -0,0 +1,47 @@ +#ifndef __URL_H +#define __URL_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$ + ***************************************************************************/ + +/* + * Prototypes for library-wide functions provided by url.c + */ + +CURLcode Curl_open(struct SessionHandle **curl); +CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...); +CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */ +CURLcode Curl_connect(struct SessionHandle *, struct connectdata **, + bool *async); +CURLcode Curl_async_resolved(struct connectdata *conn); +CURLcode Curl_do(struct connectdata **); +CURLcode Curl_do_more(struct connectdata *); +CURLcode Curl_done(struct connectdata **, CURLcode); +CURLcode Curl_disconnect(struct connectdata *); +CURLcode Curl_protocol_connect(struct connectdata *conn); +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); +void Curl_safefree(void *ptr); +#endif diff --git a/Utilities/cmcurl/urldata.h b/Utilities/cmcurl/urldata.h new file mode 100644 index 0000000..07aff9b --- /dev/null +++ b/Utilities/cmcurl/urldata.h @@ -0,0 +1,962 @@ +#ifndef __URLDATA_H +#define __URLDATA_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$ + ***************************************************************************/ + +/* This file is for lib internal stuff */ + +#include "setup.h" + +#define PORT_FTP 21 +#define PORT_FTPS 990 +#define PORT_TELNET 23 +#define PORT_GOPHER 70 +#define PORT_HTTP 80 +#define PORT_HTTPS 443 +#define PORT_DICT 2628 +#define PORT_LDAP 389 + +#define DICT_MATCH "/MATCH:" +#define DICT_MATCH2 "/M:" +#define DICT_MATCH3 "/FIND:" +#define DICT_DEFINE "/DEFINE:" +#define DICT_DEFINE2 "/D:" +#define DICT_DEFINE3 "/LOOKUP:" + +#define CURL_DEFAULT_USER "anonymous" +#define CURL_DEFAULT_PASSWORD "curl_by_daniel@haxx.se" + +#include "cookie.h" +#include "formdata.h" + +#ifdef USE_SSLEAY +/* SSLeay stuff usually in /usr/local/ssl/include */ +#ifdef USE_OPENSSL +#include "openssl/rsa.h" +#include "openssl/crypto.h" +#include "openssl/x509.h" +#include "openssl/pem.h" +#include "openssl/ssl.h" +#include "openssl/err.h" +#ifdef HAVE_OPENSSL_ENGINE_H +#include <openssl/engine.h> +#endif +#else +#include "rsa.h" +#include "crypto.h" +#include "x509.h" +#include "pem.h" +#include "ssl.h" +#include "err.h" +#endif +#endif + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#include "timeval.h" + +#ifdef HAVE_ZLIB_H +#include <zlib.h> /* for content-encoding */ +#endif + +#ifdef CURL_SPECIAL_ZLIB_H +#include CURL_SPECIAL_ZLIB_H +#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" + +#ifdef HAVE_GSSAPI +#ifdef HAVE_GSSMIT +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_generic.h> +#else +#include <gssapi.h> +#endif +#endif + +/* Download buffer size, keep it fairly big for speed reasons */ +#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 + +/* 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 + +/* 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; +#endif /* USE_SSLEAY */ +}; + +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 */ +}; + +/* 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 */ + 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; + +/* for 3rd party transfers to decide which side that issues PASV */ +typedef enum { + CURL_TARGET_PASV, + CURL_SOURCE_PASV +} curl_pasv_side; + +/* Struct used for NTLM challenge-response authentication */ +struct ntlmdata { + curlntlm state; + unsigned char nonce[8]; +}; + +#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 + ***************************************************************************/ +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 **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 *file; /* decoded file */ + + char *entrypath; /* the PWD reply when we logged on */ + + 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. */ + bool no_transfer; /* nothing was transfered, (possibly because a resumed + transfer already was complete) */ + 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() */ +}; + +/**************************************************************************** + * 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 remove site specified with pure IPv6 + IP address */ + bool use_range; + bool rangestringalloc; /* the range string is malloc()'ed */ + + 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 stream (or simimlar) is connected, this + is set the first time on the first connect function + call */ + 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 authprobe; /* set TRUE when this transfer is done to probe for auth + types, as when asking for "any" type when speaking + HTTP */ +}; + +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 */ +}; + +/* + * 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 { + 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; + + /* pointers to the actual descriptors we check */ + fd_set *readfdp; + fd_set *writefdp; + + /* the file descriptors to play with */ + fd_set readfd; + fd_set writefd; + fd_set rkeepfd; + fd_set wkeepfd; + 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! */ +}; + +#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 + +/* + * The connectdata struct contains all fields and variables that should be + * unique for an entire connection. + */ +struct connectdata { + /**** Fields set when inited and not modified again */ + struct SessionHandle *data; /* link to the root CURL struct */ + long connectindex; /* what index in the connects index this particular + struct has */ + + long protocol; /* PROT_* flags concerning the protocol set */ +#define PROT_MISSING (1<<0) +#define PROT_GOPHER (1<<1) +#define PROT_HTTP (1<<2) +#define PROT_HTTPS (1<<3) +#define PROT_FTP (1<<4) +#define PROT_TELNET (1<<5) +#define PROT_DICT (1<<6) +#define PROT_LDAP (1<<7) +#define PROT_FILE (1<<8) +#define PROT_FTPS (1<<9) +#define PROT_SSL (1<<10) /* protocol requires SSL */ + + /* the particular host we use, in two different ways */ + struct Curl_dns_entry *dns_entry; + Curl_addrinfo *ip_addr; /* the particular IP we connected to */ + + char protostr[16]; /* store the protocol string in this buffer */ + + struct hostname host; + struct hostname proxy; + + char *pathbuffer;/* allocated buffer to store the URL's path part in */ + char *path; /* path to use, points to somewhere within the pathbuffer + area */ + long port; /* which port to use locally */ + unsigned short remote_port; /* what remote port to connect to, + not the proxy port! */ + curl_off_t bytecount; + 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 *range; /* range, if used. See README for detailed specification on + this syntax. */ + curl_off_t resume_from; /* continue [ftp] transfer from here */ + + 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 */ + curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch, 0 + means unlimited */ + + 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 *); + CURLcode (*curl_done)(struct connectdata *, CURLcode); + + /* 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. + */ + CURLcode (*curl_do_more)(struct connectdata *); + + /* This function *MAY* be set to a protocol-dependent function that is run + * after the connect() and everything is done, as a step in the connection. + */ + CURLcode (*curl_connect)(struct connectdata *); + + /* 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 */ + + /* READ stuff */ + curl_socket_t sockfd; /* socket to read from or CURL_SOCKET_BAD */ + curl_off_t size; /* -1 if unknown at this point */ + curl_off_t *bytecountp; /* return number of bytes read or NULL */ + + /* WRITE stuff */ + curl_socket_t writesockfd; /* socket to write to, it may very + well be the same we read from. + CURL_SOCKET_BAD disables */ + curl_off_t *writebytecountp; /* return number of bytes written or NULL */ + + /** Dynamicly allocated strings, may need to be freed before this **/ + /** struct is killed. **/ + struct dynamically_allocated_data { + char *proxyuserpwd; /* free later if not NULL! */ + char *uagent; /* free later if not NULL! */ + char *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; + + char *newurl; /* This can only be set if a Location: was in the + document headers */ + +#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; + int sec_complete; + void *app_data; + + struct Curl_sec_client_mech *mech; + struct sockaddr_in local_addr; + +#endif + + /*************** Request - specific items ************/ + /* previously this was in the urldata struct */ + union { + struct HTTP *http; + struct HTTP *gopher; /* alias, just for the sake of being more readable */ + struct HTTP *https; /* alias, just for the sake of being more readable */ + struct FTP *ftp; + struct FILEPROTO *file; + void *telnet; /* private for telnet.c-eyes only */ + void *generic; + } proto; + + /* This struct is inited when needed */ + struct Curl_transfer_keeper keep; + + /* 'upload_present' is used to keep a byte counter of how much data there is + still left in the buffer, aimed for upload. */ + 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_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 + struct connectdata *sec_conn; /* secondary connection for 3rd party + transfer */ +}; + +/* 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; + + 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 */ +}; + +struct UrlState { + enum { + Curl_if_none, + Curl_if_easy, + Curl_if_multi + } used_interface; + + /* buffers to store authentication data in, as parsed from input options */ + struct timeval keeps_speed; /* for the progress meter really */ + + /* 'connects' will be an allocated array with pointers. If the pointer is + set, it holds an allocated connection. */ + struct connectdata **connects; + long numconnects; /* size of the 'connects' array */ + + char *headerbuff; /* allocated buffer to store headers in */ + 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 */ + + char *auth_host; /* if set, this should be the host name that we will + sent authorization to, no else. Used to make Location: + following not keep sending user+password... This is + strdup() data. + */ + + struct curl_ssl_session *session; /* array of 'numsessions' size */ + long sessionage; /* number of the most recent session */ + + char *scratch; /* 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. */ + +#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 +}; + + +/* + * 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 *proxy; /* work proxy, copied from UserDefined */ + bool proxy_alloc; /* http proxy string is malloc()'ed */ + char *referer; /* referer string */ + bool referer_alloc; /* referer sting is malloc()ed */ + 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 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 is non-NULL */ + char *set_url; /* what original URL to work on */ + char *set_proxy; /* proxy to use */ + long use_port; /* which port to use (when not using default) */ + char *userpwd; /* <user:password>, if used */ + 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 */ + 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; /* network interface to use */ + curl_write_callback fwrite; /* function that stores the output */ + curl_write_callback fwrite_header; /* function that stores headers */ + curl_read_callback fread; /* function that reads the input */ + curl_progress_callback fprogress; /* function for progress information */ + curl_debug_callback fdebug; /* function that write informational data */ + void *progress_client; /* pointer to pass to the progress 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 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 *crypto_engine; /* name of the crypto engine to use */ + char *cookiejar; /* dump all cookies to this file */ + bool cookiesession; /* new cookie session? */ + bool crlf; /* convert crlf on ftp upload(?) */ + struct curl_slist *quote; /* after connection is established */ + struct curl_slist *postquote; /* after the transfer */ + struct curl_slist *prequote; /* before the transfer, after type */ + 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_closepolicy closepolicy; /* connection cache close concept */ + Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */ + char *customrequest; /* HTTP/FTP request to use */ + long httpversion; /* when non-zero, a specific HTTP version requested to + be used in the library's request(s) */ + char *auth_host; /* if set, this is the allocated string to the host name + * to which to send the authorization data to, and no other + * host (which location-following otherwise could lead to) + */ + char *krb4_level; /* what security level */ + struct ssl_config_data ssl; /* user defined SSL stuff */ + + 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; /* Private data */ + + 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_host; /* for 3rd party transfer */ + char *source_port; /* for 3rd party transfer */ + char *source_userpwd; /* for 3rd party transfer */ + char *source_path; /* for 3rd party transfer */ + curl_pasv_side pasvHost; /* for 3rd party transfer indicates passive host */ + +/* 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 ftp_append; + bool ftp_ascii; + 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 expect100header; /* TRUE if we added Expect: 100-continue */ + bool ftp_use_epsv; /* if EPSV is to be attempted or not */ + bool ftp_use_eprt; /* if EPRT is to be attempted or not */ + curl_ftpssl ftp_ssl; /* if AUTH TLS is to be attempted etc */ + 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 */ + +}; + +/* + * In August 2001, this struct was redesigned and is since stricter than + * before. The 'connectdata' struct MUST have all the connection oriented + * stuff as we may now have several simultaneous connections and connection + * structs in memory. + * + * From now on, the 'SessionHandle' must only contain data that is set once to + * go for many (perhaps) independent connections. Values that are generated or + * calculated internally for the "session handle" must be defined within the + * 'struct urlstate' instead. */ + +struct SessionHandle { + curl_hash *hostcache; + struct Curl_share *share; /* Share, handles global variable mutexing */ + 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(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) + ENGINE* engine; +#endif /* USE_SSLEAY */ +}; + +#define LIBCURL_NAME "libcurl" + +#endif diff --git a/Utilities/cmcurl/version.c b/Utilities/cmcurl/version.c new file mode 100644 index 0000000..4af298b --- /dev/null +++ b/Utilities/cmcurl/version.c @@ -0,0 +1,261 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 <string.h> +#include <stdio.h> + +#include <curl/curl.h> +#include "urldata.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 + +#ifdef USE_SSLEAY +static int getssl_version(char *ptr, size_t left, long *num) +{ + +#if (SSLEAY_VERSION_NUMBER >= 0x905000) + { + char sub[2]; + unsigned long ssleay_value; + sub[1]='\0'; + ssleay_value=SSLeay(); + *num = (long)ssleay_value; + 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(ptr, left, " OpenSSL/%lx.%lx.%lx%s", + (ssleay_value>>28)&0xf, + (ssleay_value>>20)&0xff, + (ssleay_value>>12)&0xff, + sub); + } + +#else + *num = SSLEAY_VERSION_NUMBER; +#if (SSLEAY_VERSION_NUMBER >= 0x900000) + return snprintf(ptr, left, " OpenSSL/%lx.%lx.%lx", + (SSLEAY_VERSION_NUMBER>>28)&0xff, + (SSLEAY_VERSION_NUMBER>>20)&0xff, + (SSLEAY_VERSION_NUMBER>>12)&0xf); +#else + { + char sub[2]; + sub[1]='\0'; + if(SSLEAY_VERSION_NUMBER&0x0f) { + sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1; + } + else + sub[0]='\0'; + + return snprintf(ptr, left, " SSL/%x.%x.%x%s", + (SSLEAY_VERSION_NUMBER>>12)&0xff, + (SSLEAY_VERSION_NUMBER>>8)&0xf, + (SSLEAY_VERSION_NUMBER>>4)&0xf, sub); + } +#endif +#endif +} + +#endif + +char *curl_version(void) +{ + static char version[200]; + char *ptr=version; + /* to prevent compier warnings, we only declare len if we have code + that uses it */ +#if defined(USE_SSLEAY) || defined(HAVE_LIBZ) || defined(USE_ARES) || \ + defined(USE_LIBIDN) + int len; +#endif + size_t left = sizeof(version); + strcpy(ptr, LIBCURL_NAME "/" LIBCURL_VERSION ); + ptr=strchr(ptr, '\0'); + left -= strlen(ptr); + (void)left; + +#ifdef USE_SSLEAY + { + long num; + len = getssl_version(ptr, left, &num); + left -= len; + ptr += len; + } +#endif + +#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 + + return version; +} + +/* data for curl_version_info */ + +static const char *protocols[] = { +#ifndef CURL_DISABLE_FTP + "ftp", +#endif +#ifndef CURL_DISABLE_GOPHER + "gopher", +#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_SSLEAY +#ifndef CURL_DISABLE_HTTP + "https", +#endif +#ifndef CURL_DISABLE_FTP + "ftps", +#endif +#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_SSLEAY + | CURL_VERSION_SSL + | CURL_VERSION_NTLM /* since this requires OpenSSL */ +#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 + , + NULL, /* ssl_version */ + 0, /* ssl_version_num */ + NULL, /* zlib_version */ + protocols, + NULL, /* c-ares version */ + 0, /* c-ares version numerical */ + NULL, /* libidn version */ +}; + +curl_version_info_data *curl_version_info(CURLversion stamp) +{ +#ifdef USE_SSLEAY + static char ssl_buffer[80]; + long num; + getssl_version(ssl_buffer, sizeof(ssl_buffer), &num); + + version_info.ssl_version = ssl_buffer; + version_info.ssl_version_num = num; + /* SSL stuff is left zero if undefined */ +#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 + (void)stamp; /* avoid compiler warnings, we don't use this */ + + return &version_info; +} |