diff options
78 files changed, 24960 insertions, 0 deletions
diff --git a/Source/CTest/Curl/CMakeLists.txt b/Source/CTest/Curl/CMakeLists.txt new file mode 100644 index 0000000..4c14cae --- /dev/null +++ b/Source/CTest/Curl/CMakeLists.txt @@ -0,0 +1,309 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 1.5) +PROJECT(LIBCURL C) + +SET(PACKAGE "curl") +SET(VERSION "7.9.5") +SET(OPERATING_SYSTEM ${CMAKE_SYSTEM_NAME}) + +SET(CMAKE_C_FLAGS "${CMAKE_ANSI_CFLAGS} ${CMAKE_C_FLAGS}") +SET(CMAKE_REQUIRED_FLAGS ${CMAKE_ANSI_CFLAGS}) + +INCLUDE (${CMAKE_ROOT}/Modules/CheckLibraryExists.cmake) +INCLUDE (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake) +INCLUDE (${CMAKE_ROOT}/Modules/CheckTypeSize.cmake) +INCLUDE (${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake) +INCLUDE (${CMAKE_ROOT}/Modules/CheckIncludeFiles.cmake) + +SET(libCurl_SRCS + file.c + timeval.c + base64.c + hostip.c + progress.c + formdata.c + cookie.c + http.c + sendf.c + ftp.c + url.c + dict.c + if2ip.c + speedcheck.c + getdate.c + ldap.c + ssluse.c + version.c + getenv.c + escape.c + mprintf.c + telnet.c + getpass.c + netrc.c + getinfo.c + transfer.c + strequal.c + easy.c + security.c + krb4.c + memdebug.c + http_chunks.c + strtok.c + connect.c + llist.c + hash.c + multi.c + ) + +IF(WIN32) + INCLUDE(${LIBCURL_SOURCE_DIR}/WindowsCache.cmake) +ENDIF(WIN32) + +SET(CURL_LIBS "") + +CHECK_LIBRARY_EXISTS("nsl;${CURL_LIBS}" gethostname "" HAVE_LIBNSL) +IF(HAVE_LIBNSL) + SET(CURL_LIBS ${CURL_LIBS} nsl) +ENDIF(HAVE_LIBNSL) + +CHECK_LIBRARY_EXISTS("dl;${CURL_LIBS}" dlopen "" HAVE_LIBDL) +IF(HAVE_LIBDL) + SET(HAVE_DLOPEN 1) + SET(CURL_LIBS ${CURL_LIBS} dl) +ENDIF(HAVE_LIBDL) + +CHECK_LIBRARY_EXISTS("ucb;${CURL_LIBS}" gethostname "" HAVE_LIBUCB) +IF(HAVE_LIBUCB) + SET(CURL_LIBS ${CURL_LIBS} ucb) +ENDIF(HAVE_LIBUCB) + +CHECK_LIBRARY_EXISTS("socket;${CURL_LIBS}" connect "" HAVE_LIBSOCKET) +IF(HAVE_LIBSOCKET) + SET(CURL_LIBS ${CURL_LIBS} socket) +ENDIF(HAVE_LIBSOCKET) + +CHECK_LIBRARY_EXISTS("ws2_32;${CURL_LIBS}" printf "" HAVE_LIBWS2_32) +IF(HAVE_LIBWS2_32) + SET(CURL_LIBS ${CURL_LIBS} ws2_32) +ENDIF(HAVE_LIBWS2_32) + +SET(CMAKE_REQUIRED_LIBRARIES ${CURL_LIBS}) + +CHECK_INCLUDE_FILE("sys/types.h" HAVE_SYS_TYPES_H) +CHECK_INCLUDE_FILE("inttypes.h" HAVE_INTTYPES_H) +CHECK_INCLUDE_FILE("alloca.h" HAVE_ALLOCA_H) +CHECK_INCLUDE_FILE("arpa/inet.h" HAVE_ARPA_INET_H) +CHECK_INCLUDE_FILE("dlfcn.h" HAVE_DLFCN_H) +CHECK_INCLUDE_FILE("fcntl.h" HAVE_FCNTL_H) +CHECK_INCLUDE_FILE("malloc.h" HAVE_MALLOC_H) +CHECK_INCLUDE_FILE("memory.h" HAVE_MEMORY_H) +CHECK_INCLUDE_FILE("netdb.h" HAVE_NETDB_H) +CHECK_INCLUDE_FILE("netinet/in.h" HAVE_NETINET_IN_H) +CHECK_INCLUDE_FILE("sys/socket.h" HAVE_SYS_SOCKET_H) + +IF(HAVE_SYS_TYPES_H) + SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "sys/types.h") +ENDIF(HAVE_SYS_TYPES_H) +IF(HAVE_SYS_SOCKET_H) + SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "sys/socket.h") +ENDIF(HAVE_SYS_SOCKET_H) + +CHECK_INCLUDE_FILES("${TEST_NETWORK_INCLUDES};net/if.h" HAVE_NET_IF_H) + +IF(HAVE_NET_IF_H) + SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "net/if.h") +ENDIF(HAVE_NET_IF_H) +IF(HAVE_NETINET_IN_H) + SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "netinet/in.h") +ENDIF(HAVE_NETINET_IN_H) + +CHECK_INCLUDE_FILES("${TEST_NETWORK_INCLUDES};netinet/if_ether.h" HAVE_NETINET_IF_ETHER_H) + +CHECK_INCLUDE_FILE("netinet/in.h" HAVE_NETINET_IN_H) +CHECK_INCLUDE_FILE("pwd.h" HAVE_PWD_H) +CHECK_INCLUDE_FILE("sgtty.h" HAVE_SGTTY_H) +CHECK_INCLUDE_FILE("stdint.h" HAVE_STDINT_H) +CHECK_INCLUDE_FILE("stdlib.h" HAVE_STDLIB_H) +CHECK_INCLUDE_FILE("string.h" HAVE_STRING_H) +CHECK_INCLUDE_FILE("strings.h" HAVE_STRINGS_H) +CHECK_INCLUDE_FILE("sys/param.h" HAVE_SYS_PARAM_H) +CHECK_INCLUDE_FILE("sys/select.h" HAVE_SYS_SELECT_H) +CHECK_INCLUDE_FILE("sys/stat.h" HAVE_SYS_STAT_H) +CHECK_INCLUDE_FILE("sys/time.h" HAVE_SYS_TIME_H) +CHECK_INCLUDE_FILE("termios.h" HAVE_TERMIOS_H) +CHECK_INCLUDE_FILE("termio.h" HAVE_TERMIO_H) +CHECK_INCLUDE_FILE("io.h" HAVE_IO_H) +CHECK_INCLUDE_FILE("time.h" HAVE_TIME_H) +CHECK_INCLUDE_FILE("unistd.h" HAVE_UNISTD_H) +CHECK_INCLUDE_FILE("utime.h" HAVE_UTIME_H) +CHECK_INCLUDE_FILE("sys/utime.h" HAVE_SYS_UTIME_H) +CHECK_INCLUDE_FILE("winsock.h" HAVE_WINSOCK_H) +CHECK_INCLUDE_FILE("sockio.h" HAVE_SOCKIO_H) +CHECK_INCLUDE_FILE("sys/sockio.h" HAVE_SYS_SOCKIO_H) +CHECK_INCLUDE_FILE("x509.h" HAVE_X509_H) + +CHECK_TYPE_SIZE(ssize_t SIZEOF_SSIZE_T) +CHECK_TYPE_SIZE("long double" SIZEOF_LONG_DOUBLE) +IF(NOT HAVE_SIZEOF_SSIZE_T) + SET(ssize_t int) +ENDIF(NOT HAVE_SIZEOF_SSIZE_T) + +FIND_FILE(RANDOM_FILE urandom /dev) + +CHECK_FUNCTION_EXISTS(_doprnt HAVE_DOPRNT) +CHECK_FUNCTION_EXISTS(vprintf HAVE_VPRINTF) + +CHECK_FUNCTION_EXISTS(socket HAVE_SOCKET) +CHECK_FUNCTION_EXISTS(select HAVE_SELECT) +CHECK_FUNCTION_EXISTS(strdup HAVE_STRDUP) +CHECK_FUNCTION_EXISTS(strstr HAVE_STRSTR) +CHECK_FUNCTION_EXISTS(strtok_r HAVE_STRTOK_R) +CHECK_FUNCTION_EXISTS(strftime HAVE_STRFTIME) +CHECK_FUNCTION_EXISTS(uname HAVE_UNAME) +CHECK_FUNCTION_EXISTS(strcasecmp HAVE_STRCASECMP) +CHECK_FUNCTION_EXISTS(stricmp HAVE_STRICMP) +CHECK_FUNCTION_EXISTS(strcmpi HAVE_STRCMPI) +CHECK_FUNCTION_EXISTS(gethostname HAVE_GETHOSTNAME) +CHECK_FUNCTION_EXISTS(gethostbyaddr HAVE_GETHOSTBYADDR) +CHECK_FUNCTION_EXISTS(gettimeofday HAVE_GETTIMEOFDAY) +CHECK_FUNCTION_EXISTS(inet_addr HAVE_INET_ADDR) +CHECK_FUNCTION_EXISTS(inet_ntoa HAVE_INET_NTOA) +CHECK_FUNCTION_EXISTS(inet_ntoa_r HAVE_INET_NTOA_R) +CHECK_FUNCTION_EXISTS(tcsetattr HAVE_TCSETATTR) +CHECK_FUNCTION_EXISTS(tcgetattr HAVE_TCGETATTR) +CHECK_FUNCTION_EXISTS(perror HAVE_PERROR) +CHECK_FUNCTION_EXISTS(closesocket HAVE_CLOSESOCKET) +CHECK_FUNCTION_EXISTS(setvbuf HAVE_SETVBUF) +CHECK_FUNCTION_EXISTS(sigaction HAVE_SIGACTION) +CHECK_FUNCTION_EXISTS(signal HAVE_SIGNAL) +CHECK_FUNCTION_EXISTS(getpass_r HAVE_GETPASS_R) +CHECK_FUNCTION_EXISTS(strlcat HAVE_STRLCAT) +CHECK_FUNCTION_EXISTS(getpwuid HAVE_GETPWUID) +CHECK_FUNCTION_EXISTS(geteuid HAVE_GETEUID) +CHECK_FUNCTION_EXISTS(utime HAVE_UTIME) +CHECK_FUNCTION_EXISTS(RAND_status HAVE_RAND_STATUS) +CHECK_FUNCTION_EXISTS(RAND_screen HAVE_RAND_SCREEN) +CHECK_FUNCTION_EXISTS(RAND_egd HAVE_RAND_EGD) + +CHECK_FUNCTION_EXISTS(gethostbyname HAVE_GETHOSTBYNAME_R) +CHECK_FUNCTION_EXISTS(gethostbyaddr HAVE_GETHOSTBYADDR_R) +CHECK_FUNCTION_EXISTS(gmtime HAVE_GMTIME_R) +CHECK_FUNCTION_EXISTS(localtime HAVE_LOCALTIME_R) + +MACRO(CURL_INTERNAL_TEST CURL_TEST) + IF("${CURL_TEST}" MATCHES "^${CURL_TEST}$") + SET(MACRO_CHECK_FUNCTION_DEFINITIONS + "-D${CURL_TEST} ${CMAKE_REQUIRED_FLAGS}") + IF(CMAKE_REQUIRED_LIBRARIES) + SET(CURL_TEST_ADD_LIBRARIES + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") + ENDIF(CMAKE_REQUIRED_LIBRARIES) + + MESSAGE(STATUS "Performing Curl Test ${CURL_TEST}") + TRY_COMPILE(${CURL_TEST} + ${CMAKE_BINARY_DIR} + ${LIBCURL_SOURCE_DIR}/CMake/CurlTests.c + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + "${CURL_TEST_ADD_LIBRARIES}" + OUTPUT_VARIABLE OUTPUT) + IF(${CURL_TEST}) + SET(${CURL_TEST} 1 CACHE INTERNAL "Curl test ${FUNCTION}") + MESSAGE(STATUS "Performing Curl Test ${CURL_TEST} - Success") + ELSE(${CURL_TEST}) + MESSAGE(STATUS "Performing Curl Test ${CURL_TEST} - Failed") + SET(${CURL_TEST} "" CACHE INTERNAL "Curl test ${FUNCTION}") + WRITE_FILE(${CMAKE_BINARY_DIR}/CMakeError.log + "Performing Curl Test ${CURL_TEST} failed with the following output:\n" + "${OUTPUT}\n" APPEND) + ENDIF(${CURL_TEST}) + ENDIF("${CURL_TEST}" MATCHES "^${CURL_TEST}$") +ENDMACRO(CURL_INTERNAL_TEST) + +FOREACH(CURL_TEST + TIME_WITH_SYS_TIME + HAVE_O_NONBLOCK + HAVE_GETHOSTBYADDR_R_5 + HAVE_GETHOSTBYADDR_R_7 + HAVE_GETHOSTBYADDR_R_8 + HAVE_GETHOSTBYADDR_R_5_REENTRANT + HAVE_GETHOSTBYADDR_R_7_REENTRANT + HAVE_GETHOSTBYADDR_R_8_REENTRANT + HAVE_GETHOSTBYNAME_R_3 + HAVE_GETHOSTBYNAME_R_5 + HAVE_GETHOSTBYNAME_R_6 + HAVE_GETHOSTBYNAME_R_3_REENTRANT + HAVE_GETHOSTBYNAME_R_5_REENTRANT + HAVE_GETHOSTBYNAME_R_6_REENTRANT + HAVE_SOCKLEN_T + HAVE_IN_ADDR_T + STDC_HEADERS + RETSIGTYPE_TEST + HAVE_INET_NTOA_R_DECL + HAVE_INET_NTOA_R_DECL_REENTRANT + ) + CURL_INTERNAL_TEST(${CURL_TEST}) +ENDFOREACH(CURL_TEST) + +FOREACH(CURL_TEST + HAVE_GETHOSTBYADDR_R_5 + HAVE_GETHOSTBYADDR_R_7 + HAVE_GETHOSTBYADDR_R_8 + HAVE_GETHOSTBYNAME_R_3 + HAVE_GETHOSTBYNAME_R_5 + HAVE_GETHOSTBYNAME_R_6 + HAVE_INET_NTOA_R_DECL_REENTRANT) + IF(NOT ${CURL_TEST}) + IF(${CURL_TEST}_REENTRANT) + SET(NEED_REENTRANT 1) + ENDIF(${CURL_TEST}_REENTRANT) + ENDIF(NOT ${CURL_TEST}) +ENDFOREACH(CURL_TEST) + +IF(NEED_REENTRANT) + FOREACH(CURL_TEST + HAVE_GETHOSTBYADDR_R_5 + HAVE_GETHOSTBYADDR_R_7 + HAVE_GETHOSTBYADDR_R_8 + HAVE_GETHOSTBYNAME_R_3 + HAVE_GETHOSTBYNAME_R_5 + HAVE_GETHOSTBYNAME_R_6) + SET(${CURL_TEST} 0) + IF(${CURL_TEST}_REENTRANT) + SET(${CURL_TEST} 1) + ENDIF(${CURL_TEST}_REENTRANT) + ENDFOREACH(CURL_TEST) +ENDIF(NEED_REENTRANT) + +IF(HAVE_INET_NTOA_R_DECL_REENTRANT) + SET(HAVE_INET_NTOA_R_DECL 1) + SET(NEED_REENTRANT 1) +ENDIF(HAVE_INET_NTOA_R_DECL_REENTRANT) + +IF(NOT HAVE_SOCKLEN_T) + SET(socklen_t "int") +ENDIF(NOT HAVE_SOCKLEN_T) + +IF(NOT HAVE_IN_ADDR_T) + SET(in_addr_t "unsigned long") +ENDIF(NOT HAVE_IN_ADDR_T) + +IF(RETSIGTYPE_TEST) + SET(RETSIGTYPE void) +ELSE(RETSIGTYPE_TEST) + SET(RETSIGTYPE int) +ENDIF(RETSIGTYPE_TEST) + +INCLUDE_DIRECTORIES(${LIBCURL_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${LIBCURL_BINARY_DIR}) +ADD_DEFINITIONS(-DHAVE_CONFIG_H) +CONFIGURE_FILE(${LIBCURL_SOURCE_DIR}/config.h.in + ${LIBCURL_BINARY_DIR}/config.h) + +ADD_LIBRARY(Curl STATIC ${libCurl_SRCS}) + +TARGET_LINK_LIBRARIES(Curl ${CURL_LIBS}) + +OPTION(CURL_TESTING "Do libCurl testing" ON) +IF(CURL_TESTING) + SUBDIRS(Testing) +ENDIF(CURL_TESTING) + diff --git a/Source/CTest/Curl/WindowsCache.cmake b/Source/CTest/Curl/WindowsCache.cmake new file mode 100644 index 0000000..c361075 --- /dev/null +++ b/Source/CTest/Curl/WindowsCache.cmake @@ -0,0 +1,106 @@ +IF(WIN32)
+ SET(HAVE_GETHOSTNAME 1)
+ SET(HAVE_GETHOSTBYADDR 1)
+ SET(HAVE_GETSERVBYNAME 1)
+ SET(HAVE_INET_ADDR 1)
+ SET(HAVE_INET_NTOA 1)
+ SET(HAVE_SELECT 1)
+ SET(HAVE_SOCKET 1)
+ SET(HAVE_ARPA_INET 1)
+ SET(HAVE_NETDB_H 1)
+ SET(HAVE_SYS_SOCKET_H 0)
+ SET(HAVE_TERMIO_H 1)
+ SET(HAVE_TERMIOS_H 1)
+ SET(HAVE_ARPA_INET_H 1)
+ SET(HAVE_UTIME_H 0)
+ SET(HAVE_CLOSESOCKET 1)
+ SET(HAVE_RAND_SCREEN 1)
+ SET(HAVE_SIGNAL 0)
+ SET(OPERATING_SYSTEM "win32")
+ SET(HAVE_SYS_TYPES_H 1)
+ SET(HAVE_INTTYPES_H 0)
+ SET(HAVE_ALLOCA_H 0)
+ SET(HAVE_DLFCN_H 0)
+ SET(HAVE_FCNTL_H 1)
+ SET(HAVE_MALLOC_H 1)
+ SET(HAVE_MEMORY_H 1)
+ SET(HAVE_NETINET_IN_H 0)
+ SET(HAVE_NETINET_IF_ETHER_H 0)
+ SET(HAVE_NET_IF_H 0)
+ SET(HAVE_PWD_H 0)
+ SET(HAVE_SGTTY_H 0)
+ SET(HAVE_STDINT_H 0)
+ SET(HAVE_STDLIB_H 1)
+ SET(HAVE_STRING_H 1)
+ SET(HAVE_STRINGS_H 0)
+ SET(HAVE_SYS_PARAM_H 0)
+ SET(HAVE_SYS_SELECT_H 0)
+ SET(HAVE_SYS_SOCKIO_H 1)
+ SET(HAVE_SYS_STAT_H 1)
+ SET(HAVE_SYS_TIME_H 0)
+ SET(HAVE_IO_H 1)
+ SET(HAVE_TIME_H 1)
+ SET(HAVE_WINSOCK_H 1)
+ SET(HAVE_SOCK_H 0)
+ SET(HAVE_X509_H 0)
+ SET(SIZEOF_SSIZE_T 0)
+ SET(HAVE_DOPRNT 0)
+ SET(HAVE_VPRINTF 1)
+ SET(HAVE_STRDUP 1)
+ SET(HAVE_STRSTR 1)
+ SET(HAVE_STRTOK_R 0)
+ SET(HAVE_STRFTIME 1)
+ SET(HAVE_UNAME 0)
+ SET(HAVE_STRCASECMP 0)
+ SET(HAVE_STRICMP 1)
+ SET(HAVE_STRCMPI 0)
+ SET(HAVE_GETTIMEOFDAY 0)
+ SET(HAVE_TCGETATTR 0)
+ SET(HAVE_PERROR 1)
+ SET(HAVE_SETVBUF 1)
+ SET(HAVE_SIGACTION 0)
+ SET(HAVE_GETPASS_R 0)
+ SET(HAVE_STRLCAT 0)
+ SET(HAVE_GETPWUID 0)
+ SET(HAVE_GETEUID 0)
+ SET(HAVE_UTIME 0)
+ SET(HAVE_RAND_STATUS 0)
+ SET(HAVE_RAND_EGD 0)
+ SET(HAVE_DLOPEN 0)
+ SET(HAVE_CONNECT 0)
+ SET(HAVE_GETHOSTBYNAME 0)
+ SET(HAVE_LOCALTIME_R 0)
+ SET(HAVE_GMTIME_R 0)
+ SET(HAVE_LIBNSL 0)
+ SET(HAVE_LIBUCB 0)
+ SET(HAVE_LIBDL 0)
+ SET(HAVE_LIBSOCKET 0)
+ SET(HAVE_UNISTD_H 0)
+ SET(HAVE_SYS_UTIME_H 1)
+ SET(HAVE_SOCKIO_H 0)
+ SET(HAVE_INET_NTOA_R 0)
+ SET(HAVE_TCSETATTR 0)
+ SET(HAVE_GETHOSTBYNAME_R 0)
+ SET(HAVE_GETHOSTBYADDR_R 0)
+ SET(TIME_WITH_SYS_TIME 0)
+ SET(HAVE_O_NONBLOCK 0)
+ SET(HAVE_GETHOSTBYADDR_R_5 0)
+ SET(HAVE_GETHOSTBYADDR_R_7 0)
+ SET(HAVE_GETHOSTBYADDR_R_8 0)
+ SET(HAVE_GETHOSTBYADDR_R_5_REENTRANT 0)
+ SET(HAVE_GETHOSTBYADDR_R_7_REENTRANT 0)
+ SET(HAVE_GETHOSTBYADDR_R_8_REENTRANT 0)
+ SET(HAVE_GETHOSTBYNAME_R_3 0)
+ SET(HAVE_GETHOSTBYNAME_R_5 0)
+ SET(HAVE_GETHOSTBYNAME_R_6 0)
+ SET(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
+ SET(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
+ SET(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
+ SET(HAVE_SOCKLEN_T 0)
+ SET(HAVE_IN_ADDR_T 0)
+ SET(STDC_HEADERS 1)
+ SET(RETSIGTYPE_TEST 1)
+
+ELSE(WIN32)
+ MESSAGE("This file should be included on Windows platform only")
+ENDIF(WIN32)
\ No newline at end of file diff --git a/Source/CTest/Curl/arpa_telnet.h b/Source/CTest/Curl/arpa_telnet.h new file mode 100644 index 0000000..52ac66b --- /dev/null +++ b/Source/CTest/Curl/arpa_telnet.h @@ -0,0 +1,101 @@ +#ifndef __ARPA_TELNET_H +#define __ARPA_TELNET_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* + * Telnet option defines. Add more here if in need. + */ +#define TELOPT_BINARY 0 /* binary 8bit data */ +#define TELOPT_SGA 3 /* Supress Go Ahead */ +#define TELOPT_EXOPL 255 /* EXtended OPtions List */ +#define TELOPT_TTYPE 24 /* Terminal TYPE */ +#define TELOPT_XDISPLOC 35 /* X DISPlay LOCation */ + +#define TELOPT_NEW_ENVIRON 39 /* NEW ENVIRONment variables */ +#define NEW_ENV_VAR 0 +#define NEW_ENV_VALUE 1 + +/* + * The telnet options represented as strings + */ +static const char *telnetoptions[]= +{ + "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", + "NAME", "STATUS", "TIMING MARK", "RCTE", + "NAOL", "NAOP", "NAOCRD", "NAOHTS", + "NAOHTD", "NAOFFD", "NAOVTS", "NAOVTD", + "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO", + "DE TERMINAL", "SUPDUP", "SUPDUP OUTPUT", "SEND LOCATION", + "TERM TYPE", "END OF RECORD", "TACACS UID", "OUTPUT MARKING", + "TTYLOC", "3270 REGIME", "X3 PAD", "NAWS", + "TERM SPEED", "LFLOW", "LINEMODE", "XDISPLOC", + "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON" +}; + +#define TELOPT_MAXIMUM TELOPT_NEW_ENVIRON + +#define TELOPT_OK(x) ((x) <= TELOPT_MAXIMUM) +#define TELOPT(x) telnetoptions[x] + +#define NTELOPTS 40 + +/* + * First some defines + */ +#define xEOF 236 /* End Of File */ +#define SE 240 /* Sub negotiation End */ +#define NOP 241 /* No OPeration */ +#define DM 242 /* Data Mark */ +#define GA 249 /* Go Ahead, reverse the line */ +#define SB 250 /* SuBnegotiation */ +#define WILL 251 /* Our side WILL use this option */ +#define WONT 252 /* Our side WON'T use this option */ +#define DO 253 /* DO use this option! */ +#define DONT 254 /* DON'T use this option! */ +#define IAC 255 /* Interpret As Command */ + +/* + * Then those numbers represented as strings: + */ +static const char *telnetcmds[]= +{ + "EOF", "SUSP", "ABORT", "EOR", "SE", + "NOP", "DMARK", "BRK", "IP", "AO", + "AYT", "EC", "EL", "GA", "SB", + "WILL", "WONT", "DO", "DONT", "IAC" +}; + +#define TELCMD_MINIMUM xEOF /* the first one */ +#define TELCMD_MAXIMUM IAC /* surprise, 255 is the last one! ;-) */ + +#define TELQUAL_IS 0 +#define TELQUAL_SEND 1 +#define TELQUAL_INFO 2 +#define TELQUAL_NAME 3 + +#define TELCMD_OK(x) ( ((unsigned int)(x) >= TELCMD_MINIMUM) && \ + ((unsigned int)(x) <= TELCMD_MAXIMUM) ) +#define TELCMD(x) telnetcmds[(x)-TELCMD_MINIMUM] + +#endif diff --git a/Source/CTest/Curl/base64.c b/Source/CTest/Curl/base64.c new file mode 100644 index 0000000..af4beb2 --- /dev/null +++ b/Source/CTest/Curl/base64.c @@ -0,0 +1,270 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Andrew Francis and Daniel Stenberg + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* Base64 encoding/decoding + * + * Test harnesses down the bottom - compile with -DTEST_ENCODE for + * a program that will read in raw data from stdin and write out + * a base64-encoded version to stdout, and the length returned by the + * encoding function to stderr. Compile with -DTEST_DECODE for a program that + * will go the other way. + * + * This code will break if int is smaller than 32 bits + */ + +#include "setup.h" + +#include <stdlib.h> +#include <string.h> + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#include "base64.h" + +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +static void decodeQuantum(unsigned char *dest, char *src) +{ + unsigned int x = 0; + int i; + for(i = 0; i < 4; i++) { + if(src[i] >= 'A' && src[i] <= 'Z') + x = (x << 6) + (unsigned int)(src[i] - 'A' + 0); + else if(src[i] >= 'a' && src[i] <= 'z') + x = (x << 6) + (unsigned int)(src[i] - 'a' + 26); + else if(src[i] >= '0' && src[i] <= '9') + x = (x << 6) + (unsigned int)(src[i] - '0' + 52); + else if(src[i] == '+') + x = (x << 6) + 62; + else if(src[i] == '/') + x = (x << 6) + 63; + } + + dest[2] = (unsigned char)(x & 255); x >>= 8; + dest[1] = (unsigned char)(x & 255); x >>= 8; + dest[0] = (unsigned char)(x & 255); x >>= 8; +} + +/* base64Decode + * Given a base64 string at src, decode it into the memory pointed + * to by dest. If rawLength points to a valid address (ie not NULL), + * store the length of the decoded data to it. + */ +static void base64Decode(unsigned char *dest, char *src, int *rawLength) +{ + int length = 0; + int equalsTerm = 0; + int i; + unsigned char lastQuantum[3]; + + while((src[length] != '=') && src[length]) + length++; + while(src[length+equalsTerm] == '=') + equalsTerm++; + + if(rawLength) + *rawLength = (length * 3 / 4) - equalsTerm; + + for(i = 0; i < length/4 - 1; i++) { + decodeQuantum(dest, src); + dest += 3; src += 4; + } + + decodeQuantum(lastQuantum, src); + for(i = 0; i < 3 - equalsTerm; i++) dest[i] = lastQuantum[i]; + +} + +/* ---- Base64 Encoding --- */ +static char table64[]= + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* + * Curl_base64_encode() + * + * Returns the length of the newly created base64 string. The third argument + * is a pointer to an allocated area holding the base64 data. If something + * went wrong, -1 is returned. + * + */ +int Curl_base64_encode(const void *inp, int insize, char **outptr) +{ + unsigned char ibuf[3]; + unsigned char obuf[4]; + int i; + int inputparts; + char *output; + char *base64data; + + char *indata = (char *)inp; + + if(0 == insize) + insize = strlen(indata); + + base64data = output = (char*)malloc(insize*4/3+4); + if(NULL == output) + return -1; + + while(insize > 0) { + for (i = inputparts = 0; i < 3; i++) { + if(*indata) { + inputparts++; + ibuf[i] = *indata; + indata++; + insize--; + } + else + ibuf[i] = 0; + } + + obuf [0] = (ibuf [0] & 0xFC) >> 2; + obuf [1] = ((ibuf [0] & 0x03) << 4) | ((ibuf [1] & 0xF0) >> 4); + obuf [2] = ((ibuf [1] & 0x0F) << 2) | ((ibuf [2] & 0xC0) >> 6); + obuf [3] = ibuf [2] & 0x3F; + + switch(inputparts) { + case 1: /* only one byte read */ + sprintf(output, "%c%c==", + table64[obuf[0]], + table64[obuf[1]]); + break; + case 2: /* two bytes read */ + sprintf(output, "%c%c%c=", + table64[obuf[0]], + table64[obuf[1]], + table64[obuf[2]]); + break; + default: + sprintf(output, "%c%c%c%c", + table64[obuf[0]], + table64[obuf[1]], + table64[obuf[2]], + table64[obuf[3]] ); + break; + } + output += 4; + } + *output=0; + *outptr = base64data; /* make it return the actual data memory */ + + return strlen(base64data); /* return the length of the new data */ +} +/* ---- End of Base64 Encoding ---- */ + +int Curl_base64_decode(const char *str, void *data) +{ + int ret; + + base64Decode((unsigned char *)data, (char *)str, &ret); + return ret; +} + +/************* TEST HARNESS STUFF ****************/ + + +#ifdef TEST_ENCODE +/* encoding test harness. Read in standard input and write out the length + * returned by Curl_base64_encode, followed by the base64'd data itself + */ +#include <stdio.h> + +#define TEST_NEED_SUCK +void *suck(int *); + +int main(int argc, char **argv, char **envp) { + char *base64; + int base64Len; + unsigned char *data; + int dataLen; + + data = (unsigned char *)suck(&dataLen); + base64Len = Curl_base64_encode(data, dataLen, &base64); + + fprintf(stderr, "%d\n", base64Len); + fprintf(stdout, "%s", base64); + + free(base64); free(data); + return 0; +} +#endif + +#ifdef TEST_DECODE +/* decoding test harness. Read in a base64 string from stdin and write out the + * length returned by Curl_base64_decode, followed by the decoded data itself + */ +#include <stdio.h> + +#define TEST_NEED_SUCK +void *suck(int *); + +int main(int argc, char **argv, char **envp) { + char *base64; + int base64Len; + unsigned char *data; + int dataLen; + + base64 = (char *)suck(&base64Len); + data = (unsigned char *)malloc(base64Len * 3/4 + 8); + dataLen = Curl_base64_decode(base64, data); + + fprintf(stderr, "%d\n", dataLen); + fwrite(data,1,dataLen,stdout); + + + free(base64); free(data); + return 0; +} +#endif + +#ifdef TEST_NEED_SUCK +/* this function 'sucks' in as much as possible from stdin */ +void *suck(int *lenptr) { + int cursize = 8192; + unsigned char *buf = NULL; + int lastread; + int len = 0; + + do { + cursize *= 2; + buf = (unsigned char *)realloc(buf, cursize); + memset(buf + len, 0, cursize - len); + lastread = fread(buf + len, 1, cursize - len, stdin); + len += lastread; + } while(!feof(stdin)); + + lenptr[0] = len; + return (void *)buf; +} +#endif + + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/base64.h b/Source/CTest/Curl/base64.h new file mode 100644 index 0000000..55f86d5 --- /dev/null +++ b/Source/CTest/Curl/base64.h @@ -0,0 +1,27 @@ +#ifndef __BASE64_H +#define __BASE64_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +int Curl_base64_encode(const void *data, int size, char **str); +int Curl_base64_decode(const char *str, void *data); +#endif diff --git a/Source/CTest/Curl/config.h.in b/Source/CTest/Curl/config.h.in new file mode 100644 index 0000000..50d0a78 --- /dev/null +++ b/Source/CTest/Curl/config.h.in @@ -0,0 +1,409 @@ +/* lib/config.h.in. Generated automatically from configure.in by autoheader. */ +/* Name of this package! */ +#define PACKAGE "${PACKAGE}" + +/* Version number of this archive. */ +#define VERSION "${VERSION}" + +/* Define if you have the getpass function. */ +#cmakedefine HAVE_GETPASS ${HAVE_GETPASS} + +/* Define cpu-machine-OS */ +#define OS "${OPERATING_SYSTEM}" + +/* Define if you have the gethostbyaddr_r() function with 5 arguments */ +#cmakedefine HAVE_GETHOSTBYADDR_R_5 ${HAVE_GETHOSTBYADDR_R_5} + +/* Define if you have the gethostbyaddr_r() function with 7 arguments */ +#cmakedefine HAVE_GETHOSTBYADDR_R_7 ${HAVE_GETHOSTBYADDR_R_7} + +/* Define if you have the gethostbyaddr_r() function with 8 arguments */ +#cmakedefine HAVE_GETHOSTBYADDR_R_8 ${HAVE_GETHOSTBYADDR_R_8} + +/* Define if you have the gethostbyname_r() function with 3 arguments */ +#cmakedefine HAVE_GETHOSTBYNAME_R_3 ${HAVE_GETHOSTBYNAME_R_3} + +/* Define if you have the gethostbyname_r() function with 5 arguments */ +#cmakedefine HAVE_GETHOSTBYNAME_R_5 ${HAVE_GETHOSTBYNAME_R_5} + +/* Define if you have the gethostbyname_r() function with 6 arguments */ +#cmakedefine HAVE_GETHOSTBYNAME_R_6 ${HAVE_GETHOSTBYNAME_R_6} + +/* Define if you have the inet_ntoa_r function declared. */ +#cmakedefine HAVE_INET_NTOA_R_DECL ${HAVE_INET_NTOA_R_DECL} + +/* Define if you have the getservbyname function. */ +#cmakedefine HAVE_GETSERVBYNAME ${HAVE_GETSERVBYNAME} + +/* Define if you need the _REENTRANT define for some functions */ +#cmakedefine NEED_REENTRANT ${NEED_REENTRANT} + +/* Define if you have the Kerberos4 libraries (including -ldes) */ +#cmakedefine KRB4 ${KRB4} + +/* Define if you want to enable IPv6 support */ +#cmakedefine ENABLE_IPV6 ${ENABLE_IPV6} + +/* Define this to 'int' if ssize_t is not an available typedefed type */ +#cmakedefine ssize_t ${ssize_t} + +/* Define this to 'int' if socklen_t is not an available typedefed type */ +#cmakedefine socklen_t ${socklen_t} + +/* Define this as a suitable file to read random data from */ +#cmakedefine RANDOM_FILE "${RANDOM_FILE}" + +/* Define this to your Entropy Gathering Daemon socket pathname */ +#cmakedefine EGD_SOCKET ${EGD_SOCKET} + +/* Define if you have a working OpenSSL installation */ +#cmakedefine OPENSSL_ENABLED ${OPENSSL_ENABLED} + +/* Define the one correct non-blocking socket method below */ +#cmakedefine HAVE_FIONBIO ${HAVE_FIONBIO} +#cmakedefine HAVE_IOCTLSOCKET ${HAVE_IOCTLSOCKET} +#cmakedefine HAVE_IOCTLSOCKET_CASE ${HAVE_IOCTLSOCKET_CASE} +#cmakedefine HAVE_O_NONBLOCK ${HAVE_O_NONBLOCK} +#cmakedefine HAVE_DISABLED_NONBLOCKING ${HAVE_DISABLED_NONBLOCKING} + +/* Define this to 'int' if in_addr_t is not an available typedefed type */ +#cmakedefine in_addr_t ${in_addr_t} + +/* Set to explicitly specify we don't want to use thread-safe functions */ +#cmakedefine DISABLED_THREADSAFE ${DISABLED_THREADSAFE} + +/* Define if you want to enable IPv6 support */ +#cmakedefine ENABLE_IPV6 ${ENABLE_IPV6} + +/* Define if you have the <alloca.h> header file. */ +#cmakedefine HAVE_ALLOCA_H ${HAVE_ALLOCA_H} + +/* Define if you have the <arpa/inet.h> header file. */ +#cmakedefine HAVE_ARPA_INET_H ${HAVE_ARPA_INET_H} + +/* Define if you have the `closesocket' function. */ +#cmakedefine HAVE_CLOSESOCKET ${HAVE_CLOSESOCKET} + +/* Define if you have the <crypto.h> header file. */ +#cmakedefine HAVE_CRYPTO_H ${HAVE_CRYPTO_H} + +/* Define if you have the <des.h> header file. */ +#cmakedefine HAVE_DES_H ${HAVE_DES_H} + +/* Define if you have the <dlfcn.h> header file. */ +#cmakedefine HAVE_DLFCN_H ${HAVE_DLFCN_H} + +/* Define if you have the `dlopen' function. */ +#cmakedefine HAVE_DLOPEN ${HAVE_DLOPEN} + +/* Define if you have the <err.h> header file. */ +#cmakedefine HAVE_ERR_H ${HAVE_ERR_H} + +/* Define if you have the <fcntl.h> header file. */ +#cmakedefine HAVE_FCNTL_H ${HAVE_FCNTL_H} + +/* Define if getaddrinfo exists and works */ +#cmakedefine HAVE_GETADDRINFO ${HAVE_GETADDRINFO} + +/* Define if you have the `geteuid' function. */ +#cmakedefine HAVE_GETEUID ${HAVE_GETEUID} + +/* Define if you have the `gethostbyaddr' function. */ +#cmakedefine HAVE_GETHOSTBYADDR ${HAVE_GETHOSTBYADDR} + +/* Define if you have the `gethostbyaddr_r' function. */ +#cmakedefine HAVE_GETHOSTBYADDR_R ${HAVE_GETHOSTBYADDR_R} + +/* Define if you have the `gethostbyname_r' function. */ +#cmakedefine HAVE_GETHOSTBYNAME_R ${HAVE_GETHOSTBYNAME_R} + +/* Define if you have the `gethostname' function. */ +#cmakedefine HAVE_GETHOSTNAME ${HAVE_GETHOSTNAME} + +/* Define if you have the `getpass_r' function. */ +#cmakedefine HAVE_GETPASS_R ${HAVE_GETPASS_R} + +/* Define if you have the `getpwuid' function. */ +#cmakedefine HAVE_GETPWUID ${HAVE_GETPWUID} + +/* Define if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY ${HAVE_GETTIMEOFDAY} + +/* Define if you have the `gmtime_r' function. */ +#cmakedefine HAVE_GMTIME_R ${HAVE_GMTIME_R} + +/* Define if you have the `inet_addr' function. */ +#cmakedefine HAVE_INET_ADDR ${HAVE_INET_ADDR} + +/* Define if you have the `inet_ntoa' function. */ +#cmakedefine HAVE_INET_NTOA ${HAVE_INET_NTOA} + +/* Define if you have the `inet_ntoa_r' function. */ +#cmakedefine HAVE_INET_NTOA_R ${HAVE_INET_NTOA_R} + +/* Define if you have the <inttypes.h> header file. */ +#cmakedefine HAVE_INTTYPES_H ${HAVE_INTTYPES_H} + +/* Define if you have the <io.h> header file. */ +#cmakedefine HAVE_IO_H ${HAVE_IO_H} + +/* Define if you have the `krb_get_our_ip_for_realm' function. */ +#cmakedefine HAVE_KRB_GET_OUR_IP_FOR_REALM ${HAVE_KRB_GET_OUR_IP_FOR_REALM} +/* Define if you have the <krb.h> header file. */ +#cmakedefine HAVE_KRB_H ${HAVE_KRB_H} + +/* Define if you have the `crypto' library (-lcrypto). */ +#cmakedefine HAVE_LIBCRYPTO ${HAVE_LIBCRYPTO} + +/* Define if you have the `dl' library (-ldl). */ +#cmakedefine HAVE_LIBDL ${HAVE_LIBDL} + +/* Define if you have the `nsl' library (-lnsl). */ +#cmakedefine HAVE_LIBNSL ${HAVE_LIBNSL} + +/* Define if you have the `resolv' library (-lresolv). */ +#cmakedefine HAVE_LIBRESOLV ${HAVE_LIBRESOLV} + +/* Define if you have the `resolve' library (-lresolve). */ +#cmakedefine HAVE_LIBRESOLVE ${HAVE_LIBRESOLVE} + +/* Define if you have the `socket' library (-lsocket). */ +#cmakedefine HAVE_LIBSOCKET ${HAVE_LIBSOCKET} + +/* Define if you have the `ssl' library (-lssl). */ +#cmakedefine HAVE_LIBSSL ${HAVE_LIBSSL} + +/* Define if you have the `ucb' library (-lucb). */ +#cmakedefine HAVE_LIBUCB ${HAVE_LIBUCB} + +/* Define if you have the `localtime_r' function. */ +#cmakedefine HAVE_LOCALTIME_R ${HAVE_LOCALTIME_R} + +/* Define if you have the <malloc.h> header file. */ +#cmakedefine HAVE_MALLOC_H ${HAVE_MALLOC_H} + +/* Define if you have the <memory.h> header file. */ +#cmakedefine HAVE_MEMORY_H ${HAVE_MEMORY_H} + +/* Define if you have the <netdb.h> header file. */ +#cmakedefine HAVE_NETDB_H ${HAVE_NETDB_H} + +/* Define if you have the <netinet/if_ether.h> header file. */ +#cmakedefine HAVE_NETINET_IF_ETHER_H ${HAVE_NETINET_IF_ETHER_H} + +/* Define if you have the <netinet/in.h> header file. */ +#cmakedefine HAVE_NETINET_IN_H ${HAVE_NETINET_IN_H} + +/* Define if you have the <net/if.h> header file. */ +#cmakedefine HAVE_NET_IF_H ${HAVE_NET_IF_H} + +/* Define if you have the <openssl/crypto.h> header file. */ +#cmakedefine HAVE_OPENSSL_CRYPTO_H ${HAVE_OPENSSL_CRYPTO_H} + +/* Define if you have the <openssl/engine.h> header file. */ +#cmakedefine HAVE_OPENSSL_ENGINE_H ${HAVE_OPENSSL_ENGINE_H} + +/* Define if you have the <openssl/err.h> header file. */ +#cmakedefine HAVE_OPENSSL_ERR_H ${HAVE_OPENSSL_ERR_H} + +/* Define if you have the <openssl/pem.h> header file. */ +#cmakedefine HAVE_OPENSSL_PEM_H ${HAVE_OPENSSL_PEM_H} + +/* Define if you have the <openssl/rsa.h> header file. */ +#cmakedefine HAVE_OPENSSL_RSA_H ${HAVE_OPENSSL_RSA_H} + +/* Define if you have the <openssl/ssl.h> header file. */ +#cmakedefine HAVE_OPENSSL_SSL_H ${HAVE_OPENSSL_SSL_H} + +/* Define if you have the <openssl/x509.h> header file. */ +#cmakedefine HAVE_OPENSSL_X509_H ${HAVE_OPENSSL_X509_H} + +/* Define if you have the <pem.h> header file. */ +#cmakedefine HAVE_PEM_H ${HAVE_PEM_H} + +/* Define if you have the `perror' function. */ +#cmakedefine HAVE_PERROR ${HAVE_PERROR} + +/* Define if you have the <pwd.h> header file. */ +#cmakedefine HAVE_PWD_H ${HAVE_PWD_H} + +/* Define if you have the `RAND_egd' function. */ +#cmakedefine HAVE_RAND_EGD ${HAVE_RAND_EGD} + +/* Define if you have the `RAND_screen' function. */ +#cmakedefine HAVE_RAND_SCREEN ${HAVE_RAND_SCREEN} + +/* Define if you have the `RAND_status' function. */ +#cmakedefine HAVE_RAND_STATUS ${HAVE_RAND_STATUS} + +/* Define if you have the <rsa.h> header file. */ +#cmakedefine HAVE_RSA_H ${HAVE_RSA_H} + +/* Define if you have the `select' function. */ +#cmakedefine HAVE_SELECT ${HAVE_SELECT} + +/* Define if you have the `setvbuf' function. */ +#cmakedefine HAVE_SETVBUF ${HAVE_SETVBUF} + +/* Define if you have the <sgtty.h> header file. */ +#cmakedefine HAVE_SGTTY_H ${HAVE_SGTTY_H} + +/* Define if you have the `sigaction' function. */ +#cmakedefine HAVE_SIGACTION ${HAVE_SIGACTION} + +/* Define if you have the `signal' function. */ +#cmakedefine HAVE_SIGNAL ${HAVE_SIGNAL} + +/* Define if you have the `socket' function. */ +#cmakedefine HAVE_SOCKET ${HAVE_SOCKET} + +/* Define if you have the <ssl.h> header file. */ +#cmakedefine HAVE_SSL_H ${HAVE_SSL_H} + +/* Define if you have the <stdint.h> header file. */ +#cmakedefine HAVE_STDINT_H ${HAVE_STDINT_H} + +/* Define if you have the <stdlib.h> header file. */ +#cmakedefine HAVE_STDLIB_H ${HAVE_STDLIB_H} + +/* Define if you have the `strcasecmp' function. */ +#cmakedefine HAVE_STRCASECMP ${HAVE_STRCASECMP} + +/* Define if you have the `strcmpi' function. */ +#cmakedefine HAVE_STRCMPI ${HAVE_STRCMPI} + +/* Define if you have the `strdup' function. */ +#cmakedefine HAVE_STRDUP ${HAVE_STRDUP} + +/* Define if you have the `strftime' function. */ +#cmakedefine HAVE_STRFTIME ${HAVE_STRFTIME} + +/* Define if you have the `stricmp' function. */ +#cmakedefine HAVE_STRICMP ${HAVE_STRICMP} + +/* Define if you have the <strings.h> header file. */ +#cmakedefine HAVE_STRINGS_H ${HAVE_STRINGS_H} + +/* Define if you have the <string.h> header file. */ +#cmakedefine HAVE_STRING_H ${HAVE_STRING_H} + +/* Define if you have the `strlcat' function. */ +#cmakedefine HAVE_STRLCAT ${HAVE_STRLCAT} + +/* Define if you have the `strlcpy' function. */ +#cmakedefine HAVE_STRLCPY ${HAVE_STRLCPY} + +/* Define if you have the `strstr' function. */ +#cmakedefine HAVE_STRSTR ${HAVE_STRSTR} + +/* Define if you have the `strtok_r' function. */ +#cmakedefine HAVE_STRTOK_R ${HAVE_STRTOK_R} + +/* Define if you have the <sys/param.h> header file. */ +#cmakedefine HAVE_SYS_PARAM_H ${HAVE_SYS_PARAM_H} + +/* Define if you have the <sys/select.h> header file. */ +#cmakedefine HAVE_SYS_SELECT_H ${HAVE_SYS_SELECT_H} + +/* Define if you have the <sys/socket.h> header file. */ +#cmakedefine HAVE_SYS_SOCKET_H ${HAVE_SYS_SOCKET_H} + +/* Define if you have the <sys/sockio.h> header file. */ +#cmakedefine HAVE_SYS_SOCKIO_H ${HAVE_SYS_SOCKIO_H} + +/* Define if you have the <sys/stat.h> header file. */ +#cmakedefine HAVE_SYS_STAT_H ${HAVE_SYS_STAT_H} + +/* Define if you have the <sys/time.h> header file. */ +#cmakedefine HAVE_SYS_TIME_H ${HAVE_SYS_TIME_H} + +/* Define if you have the <sys/types.h> header file. */ +#cmakedefine HAVE_SYS_TYPES_H ${HAVE_SYS_TYPES_H} + +/* Define if you have the <sys/utime.h> header file. */ +#cmakedefine HAVE_SYS_UTIME_H ${HAVE_SYS_UTIME_H} + +/* Define if you have the `tcgetattr' function. */ +#cmakedefine HAVE_TCGETATTR ${HAVE_TCGETATTR} + +/* Define if you have the `tcsetattr' function. */ +#cmakedefine HAVE_TCSETATTR ${HAVE_TCSETATTR} + +/* Define if you have the <termios.h> header file. */ +#cmakedefine HAVE_TERMIOS_H ${HAVE_TERMIOS_H} + +/* Define if you have the <termio.h> header file. */ +#cmakedefine HAVE_TERMIO_H ${HAVE_TERMIO_H} + +/* Define if you have the <time.h> header file. */ +#cmakedefine HAVE_TIME_H ${HAVE_TIME_H} + +/* Define if you have the `uname' function. */ +#cmakedefine HAVE_UNAME ${HAVE_UNAME} + +/* Define if you have the <unistd.h> header file. */ +#cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H} + +/* Define if you have the `utime' function. */ +#cmakedefine HAVE_UTIME ${HAVE_UTIME} + +/* Define if you have the <utime.h> header file. */ +#cmakedefine HAVE_UTIME_H ${HAVE_UTIME_H} + +/* Define if you have the <winsock.h> header file. */ +#cmakedefine HAVE_WINSOCK_H ${HAVE_WINSOCK_H} + +/* Define if you have the <x509.h> header file. */ +#cmakedefine HAVE_X509_H ${HAVE_X509_H} + +/* Name of package */ +#cmakedefine PACKAGE "${PACKAGE}" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#cmakedefine RETSIGTYPE ${RETSIGTYPE} + +/* Define if you have the ANSI C header files. */ +#cmakedefine STDC_HEADERS ${STDC_HEADERS} + +/* Define if you can safely include both <sys/time.h> and <time.h>. */ +#cmakedefine TIME_WITH_SYS_TIME ${TIME_WITH_SYS_TIME} + +/* Version number of package */ +#cmakedefine VERSION "${VERSION}" + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS} + +/* Define for large files, on AIX-style hosts. */ +#cmakedefine _LARGE_FILES ${_LARGE_FILES} + +/* Define to empty if `const' does not conform to ANSI C. */ +#cmakedefine const ${const} + +/* type to use in place of in_addr_t if not defined */ +#cmakedefine in_addr_t ${in_addr_t} + +/* Define to `unsigned' if <sys/types.h> does not define. */ +#cmakedefine size_t ${size_t} + +/* type to use in place of socklen_t if not defined */ +#cmakedefine socklen_t ${socklen_t} + +/* Define to `int' if <sys/types.h> does not define. */ +#cmakedefine ssize_t ${ssize_t} + +/* Define if you don't have vprintf but do have _doprnt. */ +#cmakedefine HAVE_DOPRNT ${HAVE_DOPRNT} + +/* Define if you have the vprintf function. */ +#cmakedefine HAVE_VPRINTF ${HAVE_VPRINTF} + +/* The number of bytes in a long double. */ +#cmakedefine SIZEOF_LONG_DOUBLE ${SIZEOF_LONG_DOUBLE} diff --git a/Source/CTest/Curl/connect.c b/Source/CTest/Curl/connect.c new file mode 100644 index 0000000..77ceff0 --- /dev/null +++ b/Source/CTest/Curl/connect.c @@ -0,0 +1,581 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#ifndef WIN32 +/* headers for non-win32 */ +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* required for free() prototype, without it, this crashes + on macos 68K */ +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#endif + +#endif +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#ifdef WIN32 +#define HAVE_IOCTLSOCKET +#include <windows.h> +#include <winsock.h> +#define EINPROGRESS WSAEINPROGRESS +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EISCONN WSAEISCONN +#endif + +#include "urldata.h" +#include "sendf.h" +#include "if2ip.h" + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +static +int geterrno(void) +{ +#ifdef WIN32 + return (int)GetLastError(); +#else + return errno; +#endif +} + +/************************************************************************* + * Curl_nonblock + * + * Description: + * Set the socket to either blocking or non-blocking mode. + */ + +int Curl_nonblock(int socket, /* operate on this */ + int nonblock /* TRUE or FALSE */) +{ +#undef SETBLOCK +#ifdef HAVE_O_NONBLOCK + int flags; + + flags = fcntl(socket, F_GETFL, 0); + if (TRUE == nonblock) + return fcntl(socket, F_SETFL, flags | O_NONBLOCK); + else + return fcntl(socket, F_SETFL, flags & (~O_NONBLOCK)); +#define SETBLOCK 1 +#endif + +#ifdef HAVE_FIONBIO + int flags; + + flags = nonblock; + return ioctl(socket, FIONBIO, &flags); +#define SETBLOCK 2 +#endif + +#ifdef HAVE_IOCTLSOCKET + int flags; + flags = nonblock; + return ioctlsocket(socket, FIONBIO, &flags); +#define SETBLOCK 3 +#endif + +#ifdef HAVE_IOCTLSOCKET_CASE + return IoctlSocket(socket, FIONBIO, (long)nonblock); +#define SETBLOCK 4 +#endif + +#ifdef HAVE_DISABLED_NONBLOCKING + return 0; /* returns success */ +#define SETBLOCK 5 +#endif + +#ifndef SETBLOCK +#error "no non-blocking method was found/used/set" +#endif +} + +/* + * Return 0 on fine connect, -1 on error and 1 on timeout. + */ +static +int waitconnect(int sockfd, /* socket */ + int timeout_msec) +{ + fd_set fd; + fd_set errfd; + struct timeval interval; + int rc; + + /* now select() until we get connect or timeout */ + FD_ZERO(&fd); + FD_SET(sockfd, &fd); + + FD_ZERO(&errfd); + FD_SET(sockfd, &errfd); + + interval.tv_sec = timeout_msec/1000; + timeout_msec -= interval.tv_sec*1000; + + interval.tv_usec = timeout_msec*1000; + + rc = select(sockfd+1, NULL, &fd, &errfd, &interval); + if(-1 == rc) + /* error, no connect here, try next */ + return -1; + + else if(0 == rc) + /* timeout, no connect today */ + return 1; + + if(FD_ISSET(sockfd, &errfd)) { + /* error condition caught */ + return 2; + } + + /* we have a connect! */ + return 0; +} + +#ifndef ENABLE_IPV6 +static CURLcode bindlocal(struct connectdata *conn, + int sockfd) +{ +#if !defined(WIN32)||defined(__CYGWIN32__) + /* We don't generally like checking for OS-versions, we should make this + HAVE_XXXX based, although at the moment I don't have a decent test for + this! */ + +#ifdef HAVE_INET_NTOA + +#ifndef INADDR_NONE +#define INADDR_NONE (in_addr_t) ~0 +#endif + + struct SessionHandle *data = conn->data; + + /************************************************************* + * Select device to bind socket to + *************************************************************/ + if (strlen(data->set.device)<255) { + struct sockaddr_in sa; + struct hostent *h=NULL; + char *hostdataptr=NULL; + size_t size; + char myhost[256] = ""; + in_addr_t in; + + if(Curl_if2ip(data->set.device, myhost, sizeof(myhost))) { + h = Curl_resolv(data, myhost, 0, &hostdataptr); + } + else { + if(strlen(data->set.device)>1) { + h = Curl_resolv(data, data->set.device, 0, &hostdataptr); + } + if(h) { + /* we know data->set.device is shorter than the myhost array */ + strcpy(myhost, data->set.device); + } + } + + if(! *myhost) { + /* need to fix this + h=Curl_gethost(data, + getmyhost(*myhost,sizeof(myhost)), + hostent_buf, + sizeof(hostent_buf)); + */ + return CURLE_HTTP_PORT_FAILED; + } + + infof(data, "We bind local end to %s\n", myhost); + + in=inet_addr(myhost); + if (INADDR_NONE != in) { + + if ( h ) { + memset((char *)&sa, 0, sizeof(sa)); + memcpy((char *)&sa.sin_addr, + h->h_addr, + h->h_length); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = in; + sa.sin_port = 0; /* get any port */ + + if( bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) >= 0) { + /* we succeeded to bind */ + struct sockaddr_in add; + + size = sizeof(add); + if(getsockname(sockfd, (struct sockaddr *) &add, + (socklen_t *)&size)<0) { + failf(data, "getsockname() failed"); + return CURLE_HTTP_PORT_FAILED; + } + } + else { + switch(errno) { + case EBADF: + failf(data, "Invalid descriptor: %d", errno); + break; + case EINVAL: + failf(data, "Invalid request: %d", errno); + break; + case EACCES: + failf(data, "Address is protected, user not superuser: %d", errno); + break; + case ENOTSOCK: + failf(data, + "Argument is a descriptor for a file, not a socket: %d", + errno); + break; + case EFAULT: + failf(data, "Inaccessable memory error: %d", errno); + break; + case ENAMETOOLONG: + failf(data, "Address too long: %d", errno); + break; + case ENOMEM: + failf(data, "Insufficient kernel memory was available: %d", errno); + break; + default: + failf(data, "errno %d", errno); + break; + } /* end of switch(errno) */ + + return CURLE_HTTP_PORT_FAILED; + } /* end of else */ + + } /* end of if h */ + else { + failf(data,"could't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; + } + } /* end of inet_addr */ + + else { + failf(data, "could't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; + } + + return CURLE_OK; + + } /* end of device selection support */ +#endif /* end of HAVE_INET_NTOA */ +#endif /* end of not WIN32 */ + + return CURLE_HTTP_PORT_FAILED; +} +#endif /* end of ipv4-specific section */ + +static +int socketerror(int sockfd) +{ + int err = 0; + socklen_t errSize = sizeof(err); + + if( -1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR, + (void *)&err, &errSize)) + err = geterrno(); + + return err; +} + +/* + * TCP connect to the given host with timeout, proxy or remote doesn't matter. + * There might be more than one IP address to try out. Fill in the passed + * pointer with the connected socket. + */ + +CURLcode Curl_connecthost(struct connectdata *conn, /* context */ + Curl_addrinfo *remotehost, /* use one in here */ + int port, /* connect to this */ + int *sockconn, /* the connected socket */ + Curl_ipconnect **addr) /* the one we used */ +{ + struct SessionHandle *data = conn->data; + int rc; + int sockfd=-1; + int aliasindex=0; + + struct timeval after; + struct timeval before = Curl_tvnow(); + + /************************************************************* + * Figure out what maximum time we have left + *************************************************************/ + long timeout_ms=300000; /* milliseconds, default to five minutes */ + if(data->set.timeout || data->set.connecttimeout) { + double has_passed; + + /* Evaluate in milliseconds how much time that has passed */ + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + + /* get the most strict timeout of the ones converted to milliseconds */ + if(data->set.timeout && data->set.connecttimeout) { + if (data->set.timeout < data->set.connecttimeout) + timeout_ms = data->set.timeout*1000; + else + timeout_ms = data->set.connecttimeout*1000; + } + else if(data->set.timeout) + timeout_ms = data->set.timeout*1000; + else + timeout_ms = data->set.connecttimeout*1000; + + /* subtract the passed time */ + timeout_ms -= (long)has_passed; + + if(timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "Connection time-out"); + return CURLE_OPERATION_TIMEOUTED; + } + } + +#ifdef ENABLE_IPV6 + /* + * Connecting with IPv6 support is so much easier and cleanly done + */ + { + struct addrinfo *ai; + port =0; /* prevent compiler warning */ + + for (ai = remotehost; ai; ai = ai->ai_next, aliasindex++) { + sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sockfd < 0) + continue; + + /* set socket non-blocking */ + Curl_nonblock(sockfd, TRUE); + + rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen); + + if(-1 == rc) { + int error=geterrno(); + + switch (error) { + case EINPROGRESS: + case EWOULDBLOCK: +#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK + /* On some platforms EAGAIN and EWOULDBLOCK are the + * same value, and on others they are different, hence + * the odd #if + */ + case EAGAIN: +#endif + case EINTR: + + /* asynchronous connect, wait for connect or timeout */ + rc = waitconnect(sockfd, timeout_ms); + break; + case ECONNREFUSED: /* no one listening */ + default: + /* unknown error, fallthrough and try another address! */ + failf(data, "Failed to connect"); + break; + } + } + if(0 == rc) { + /* we might be connected, if the socket says it is OK! Ask it! */ + int err; + + err = socketerror(sockfd); + if ((0 == err) || (EISCONN == err)) { + /* we are connected, awesome! */ + break; + } + /* we are _not_ connected, it was a false alert, continue please */ + } + + /* connect failed or timed out */ + sclose(sockfd); + sockfd = -1; + + /* get a new timeout for next attempt */ + after = Curl_tvnow(); + timeout_ms -= Curl_tvdiff(after, before); + if(timeout_ms < 0) { + failf(data, "connect() timed out!"); + return CURLE_OPERATION_TIMEOUTED; + } + before = after; + continue; + } + if (sockfd < 0) { + failf(data, "connect() failed"); + return CURLE_COULDNT_CONNECT; + } + + /* leave the socket in non-blocking mode */ + + if(addr) + *addr = ai; /* the address we ended up connected to */ + } +#else + /* + * Connecting with IPv4-only support + */ + if(!remotehost->h_addr_list[0]) { + /* If there is no addresses in the address list, then we return + error right away */ + failf(data, "no address available"); + return CURLE_COULDNT_CONNECT; + } + /* create an IPv4 TCP socket */ + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if(-1 == sockfd) { + failf(data, "couldn't create socket"); + return CURLE_COULDNT_CONNECT; /* big time error */ + } + + if(conn->data->set.device) { + /* user selected to bind the outgoing socket to a specified "device" + before doing connect */ + CURLcode res = bindlocal(conn, sockfd); + if(res) + return res; + } + + /* Convert socket to non-blocking type */ + Curl_nonblock(sockfd, TRUE); + + /* This is the loop that attempts to connect to all IP-addresses we + know for the given host. One by one. */ + for(rc=-1, aliasindex=0; + rc && (struct in_addr *)remotehost->h_addr_list[aliasindex]; + aliasindex++) { + struct sockaddr_in serv_addr; + + /* do this nasty work to do the connect */ + memset((char *) &serv_addr, '\0', sizeof(serv_addr)); + memcpy((char *)&(serv_addr.sin_addr), + (struct in_addr *)remotehost->h_addr_list[aliasindex], + sizeof(struct in_addr)); + serv_addr.sin_family = remotehost->h_addrtype; + serv_addr.sin_port = htons(port); + + rc = connect(sockfd, (struct sockaddr *)&serv_addr, + sizeof(serv_addr)); + + if(-1 == rc) { + int error=geterrno(); + + switch (error) { + case EINPROGRESS: + case EWOULDBLOCK: +#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK + /* On some platforms EAGAIN and EWOULDBLOCK are the + * same value, and on others they are different, hence + * the odd #if + */ + case EAGAIN: +#endif + + /* asynchronous connect, wait for connect or timeout */ + rc = waitconnect(sockfd, timeout_ms); + break; + default: + /* unknown error, fallthrough and try another address! */ + failf(data, "Failed to connect to IP number %d", aliasindex+1); + break; + } + } + + if(0 == rc) { + int err = socketerror(sockfd); + if ((0 == err) || (EISCONN == err)) { + /* we are connected, awesome! */ + break; + } + /* nope, not connected for real */ + rc = -1; + } + + if(0 != rc) { + /* get a new timeout for next attempt */ + after = Curl_tvnow(); + timeout_ms -= Curl_tvdiff(after, before); + if(timeout_ms < 0) { + failf(data, "Connect timeout on IP number %d", aliasindex+1); + break; + } + before = after; + continue; /* try next address */ + } + break; + } + if(0 != rc) { + /* no good connect was made */ + sclose(sockfd); + *sockconn = -1; + failf(data, "Couldn't connect to host"); + return CURLE_COULDNT_CONNECT; + } + + /* leave the socket in non-blocking mode */ + + if(addr) + /* this is the address we've connected to */ + *addr = (struct in_addr *)remotehost->h_addr_list[aliasindex]; +#endif + + /* allow NULL-pointers to get passed in */ + if(sockconn) + *sockconn = sockfd; /* the socket descriptor we've connected */ + + return CURLE_OK; +} + diff --git a/Source/CTest/Curl/connect.h b/Source/CTest/Curl/connect.h new file mode 100644 index 0000000..f8c10db --- /dev/null +++ b/Source/CTest/Curl/connect.h @@ -0,0 +1,35 @@ +#ifndef __CONNECT_H +#define __CONNECT_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +int Curl_nonblock(int socket, /* operate on this */ + int nonblock /* TRUE or FALSE */); + +CURLcode Curl_connecthost(struct connectdata *conn, + Curl_addrinfo *host, /* connect to this */ + int port, /* connect to this port number */ + int *sockconn, /* not set if error is returned */ + Curl_ipconnect **addr /* the one we used */ + ); /* index we used */ +#endif diff --git a/Source/CTest/Curl/cookie.c b/Source/CTest/Curl/cookie.c new file mode 100644 index 0000000..4593530 --- /dev/null +++ b/Source/CTest/Curl/cookie.c @@ -0,0 +1,738 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2002, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/*** + + +RECEIVING COOKIE INFORMATION +============================ + +struct CookieInfo *cookie_init(char *file); + + Inits a cookie struct to store data in a local file. This is always + called before any cookies are set. + +int cookies_set(struct CookieInfo *cookie, char *cookie_line); + + The 'cookie_line' parameter is a full "Set-cookie:" line as + received from a server. + + The function need to replace previously stored lines that this new + line superceeds. + + It may remove lines that are expired. + + It should return an indication of success/error. + + +SENDING COOKIE INFORMATION +========================== + +struct Cookies *cookie_getlist(struct CookieInfo *cookie, + char *host, char *path, bool secure); + + For a given host and path, return a linked list of cookies that + the client should send to the server if used now. The secure + boolean informs the cookie if a secure connection is achieved or + not. + + It shall only return cookies that haven't expired. + + +Example set of cookies: + + Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure + Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; + domain=.fidelity.com; path=/ftgw; secure + Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; + domain=.fidelity.com; path=/; secure + Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; + domain=.fidelity.com; path=/; secure + Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; + domain=.fidelity.com; path=/; secure + Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; + domain=.fidelity.com; path=/; secure + Set-cookie: + Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday, + 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure +****/ + +#include "setup.h" + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "cookie.h" +#include "getdate.h" +#include "strequal.h" +#include "strtok.h" + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/**************************************************************************** + * + * Curl_cookie_add() + * + * Add a single cookie line to the cookie keeping object. + * + ***************************************************************************/ + +struct Cookie * +Curl_cookie_add(struct CookieInfo *c, + bool httpheader, /* TRUE if HTTP header-style line */ + char *lineptr, /* first non-space of the line */ + char *domain) /* default domain */ +{ + struct Cookie *clist; + char what[MAX_COOKIE_LINE]; + char name[MAX_NAME]; + char *ptr; + char *semiptr; + struct Cookie *co; + struct Cookie *lastc=NULL; + time_t now = time(NULL); + bool replace_old = FALSE; + + /* First, alloc and init a new struct for it */ + co = (struct Cookie *)malloc(sizeof(struct Cookie)); + if(!co) + return NULL; /* bail out if we're this low on memory */ + + /* clear the whole struct first */ + memset(co, 0, sizeof(struct Cookie)); + + if(httpheader) { + /* This line was read off a HTTP-header */ + char *sep; + semiptr=strchr(lineptr, ';'); /* first, find a semicolon */ + ptr = lineptr; + do { + /* we have a <what>=<this> pair or a 'secure' word here */ + sep = strchr(ptr, '='); + if(sep && (!semiptr || (semiptr>sep)) ) { + /* + * There is a = sign and if there was a semicolon too, which make sure + * that the semicolon comes _after_ the equal sign. + */ + + name[0]=what[0]=0; /* init the buffers */ + if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;=]=%" + MAX_COOKIE_LINE_TXT "[^;\r\n]", + name, what)) { + /* this is a <name>=<what> pair */ + + /* Strip off trailing whitespace from the 'what' */ + int len=strlen(what); + while(len && isspace((int)what[len-1])) { + what[len-1]=0; + len--; + } + + if(strequal("path", name)) { + co->path=strdup(what); + } + else if(strequal("domain", name)) { + co->domain=strdup(what); + co->field1= (what[0]=='.')?2:1; + } + else if(strequal("version", name)) { + co->version=strdup(what); + } + else if(strequal("max-age", name)) { + /* Defined in RFC2109: + + Optional. The Max-Age attribute defines the lifetime of the + cookie, in seconds. The delta-seconds value is a decimal non- + negative integer. After delta-seconds seconds elapse, the + client should discard the cookie. A value of zero means the + cookie should be discarded immediately. + + */ + co->maxage = strdup(what); + co->expires = + atoi((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0]) + now; + } + else if(strequal("expires", name)) { + co->expirestr=strdup(what); + co->expires = curl_getdate(what, &now); + } + else if(!co->name) { + co->name = strdup(name); + co->value = strdup(what); + } + /* + else this is the second (or more) name we don't know + about! */ + } + else { + /* this is an "illegal" <what>=<this> pair */ + } + } + else { + if(sscanf(ptr, "%" MAX_COOKIE_LINE_TXT "[^;\r\n]", + what)) { + if(strequal("secure", what)) + co->secure = TRUE; + /* else, + unsupported keyword without assign! */ + + } + } + if(!semiptr || !*semiptr) { + /* we already know there are no more cookies */ + semiptr = NULL; + continue; + } + + ptr=semiptr+1; + while(ptr && *ptr && isspace((int)*ptr)) + ptr++; + semiptr=strchr(ptr, ';'); /* now, find the next semicolon */ + + if(!semiptr && *ptr) + /* There are no more semicolons, but there's a final name=value pair + coming up */ + semiptr=strchr(ptr, '\0'); + } while(semiptr); + + if(NULL == co->name) { + /* we didn't get a cookie name, this is an illegal line, bail out */ + if(co->domain) + free(co->domain); + if(co->path) + free(co->path); + if(co->name) + free(co->name); + if(co->value) + free(co->value); + free(co); + return NULL; + } + + if(NULL == co->domain) + /* no domain given in the header line, set the default now */ + co->domain=domain?strdup(domain):NULL; + } + else { + /* This line is NOT a HTTP header style line, we do offer support for + reading the odd netscape cookies-file format here */ + char *firstptr; + char *tok_buf; + int fields; + + if(lineptr[0]=='#') { + /* don't even try the comments */ + free(co); + return NULL; + } + /* strip off the possible end-of-line characters */ + ptr=strchr(lineptr, '\r'); + if(ptr) + *ptr=0; /* clear it */ + ptr=strchr(lineptr, '\n'); + if(ptr) + *ptr=0; /* clear it */ + + firstptr=strtok_r(lineptr, "\t", &tok_buf); /* first tokenize it on the TAB */ + + /* Here's a quick check to eliminate normal HTTP-headers from this */ + if(!firstptr || strchr(firstptr, ':')) { + free(co); + return NULL; + } + + /* Now loop through the fields and init the struct we already have + allocated */ + for(ptr=firstptr, fields=0; ptr; ptr=strtok_r(NULL, "\t", &tok_buf), fields++) { + switch(fields) { + case 0: + co->domain = strdup(ptr); + break; + case 1: + /* This field got its explanation on the 23rd of May 2001 by + Andrés García: + + flag: A TRUE/FALSE value indicating if all machines within a given + domain can access the variable. This value is set automatically by + the browser, depending on the value you set for the domain. + + As far as I can see, it is set to true when the cookie says + .domain.com and to false when the domain is complete www.domain.com + + We don't currently take advantage of this knowledge. + */ + co->field1=strequal(ptr, "TRUE")+1; /* store information */ + break; + case 2: + /* It turns out, that sometimes the file format allows the path + field to remain not filled in, we try to detect this and work + around it! Andrés García made us aware of this... */ + if (strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { + /* only if the path doesn't look like a boolean option! */ + co->path = strdup(ptr); + break; + } + /* this doesn't look like a path, make one up! */ + co->path = strdup("/"); + fields++; /* add a field and fall down to secure */ + /* FALLTHROUGH */ + case 3: + co->secure = strequal(ptr, "TRUE"); + break; + case 4: + co->expires = atoi(ptr); + break; + case 5: + co->name = strdup(ptr); + break; + case 6: + co->value = strdup(ptr); + break; + } + } + + if(7 != fields) { + /* we did not find the sufficient number of fields to recognize this + as a valid line, abort and go home */ + + if(co->domain) + free(co->domain); + if(co->path) + free(co->path); + if(co->name) + free(co->name); + if(co->value) + free(co->value); + + free(co); + return NULL; + } + + } + + co->livecookie = c->running; + + /* now, we have parsed the incoming line, we must now check if this + superceeds an already existing cookie, which it may if the previous have + the same domain and path as this */ + + clist = c->cookies; + replace_old = FALSE; + while(clist) { + if(strequal(clist->name, co->name)) { + /* the names are identical */ + + if(clist->domain && co->domain) { + if(strequal(clist->domain, co->domain)) + replace_old=TRUE; + } + else if(!clist->domain && !co->domain) + replace_old = TRUE; + + if(replace_old) { + /* the domains were identical */ + + if(clist->path && co->path) { + if(strequal(clist->path, co->path)) { + replace_old = TRUE; + } + else + replace_old = FALSE; + } + else if(!clist->path && !co->path) + replace_old = TRUE; + else + replace_old = FALSE; + + } + + if(replace_old && !co->livecookie && clist->livecookie) { + /* Both cookies matched fine, except that the already present + cookie is "live", which means it was set from a header, while + the new one isn't "live" and thus only read from a file. We let + live cookies stay alive */ + + /* Free the newcomer and get out of here! */ + if(co->domain) + free(co->domain); + if(co->path) + free(co->path); + if(co->name) + free(co->name); + if(co->value) + free(co->value); + + free(co); + return NULL; + } + + if(replace_old) { + co->next = clist->next; /* get the next-pointer first */ + + /* then free all the old pointers */ + if(clist->name) + free(clist->name); + if(clist->value) + free(clist->value); + if(clist->domain) + free(clist->domain); + if(clist->path) + free(clist->path); + if(clist->expirestr) + free(clist->expirestr); + + if(clist->version) + free(clist->version); + if(clist->maxage) + free(clist->maxage); + + *clist = *co; /* then store all the new data */ + + free(co); /* free the newly alloced memory */ + co = clist; /* point to the previous struct instead */ + + /* We have replaced a cookie, now skip the rest of the list but + make sure the 'lastc' pointer is properly set */ + do { + lastc = clist; + clist = clist->next; + } while(clist); + break; + } + } + lastc = clist; + clist = clist->next; + } + + if(!replace_old) { + /* then make the last item point on this new one */ + if(lastc) + lastc->next = co; + else + c->cookies = co; + } + + c->numcookies++; /* one more cookie in the jar */ + + return co; +} + +/***************************************************************************** + * + * Curl_cookie_init() + * + * Inits a cookie struct to read data from a local file. This is always + * called before any cookies are set. File may be NULL. + * + ****************************************************************************/ +struct CookieInfo *Curl_cookie_init(char *file, struct CookieInfo *inc) +{ + char line[MAX_COOKIE_LINE]; + struct CookieInfo *c; + FILE *fp; + bool fromfile=TRUE; + + if(NULL == inc) { + /* we didn't get a struct, create one */ + c = (struct CookieInfo *)malloc(sizeof(struct CookieInfo)); + if(!c) + return NULL; /* failed to get memory */ + memset(c, 0, sizeof(struct CookieInfo)); + c->filename = strdup(file?file:"none"); /* copy the name just in case */ + } + else { + /* we got an already existing one, use that */ + c = inc; + } + c->running = FALSE; /* this is not running, this is init */ + + if(file && strequal(file, "-")) { + fp = stdin; + fromfile=FALSE; + } + else + fp = file?fopen(file, "r"):NULL; + + if(fp) { + char *lineptr; + bool headerline; + while(fgets(line, MAX_COOKIE_LINE, fp)) { + if(strnequal("Set-Cookie:", line, 11)) { + /* This is a cookie line, get it! */ + lineptr=&line[11]; + headerline=TRUE; + } + else { + lineptr=line; + headerline=FALSE; + } + while(*lineptr && isspace((int)*lineptr)) + lineptr++; + + Curl_cookie_add(c, headerline, lineptr, NULL); + } + if(fromfile) + fclose(fp); + } + + c->running = TRUE; /* now, we're running */ + + return c; +} + +/***************************************************************************** + * + * Curl_cookie_getlist() + * + * For a given host and path, return a linked list of cookies that the + * client should send to the server if used now. The secure boolean informs + * the cookie if a secure connection is achieved or not. + * + * It shall only return cookies that haven't expired. + * + ****************************************************************************/ + +struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, + char *host, char *path, bool secure) +{ + struct Cookie *newco; + struct Cookie *co; + time_t now = time(NULL); + int hostlen=strlen(host); + int domlen; + + struct Cookie *mainco=NULL; + + if(!c || !c->cookies) + return NULL; /* no cookie struct or no cookies in the struct */ + + co = c->cookies; + + while(co) { + /* only process this cookie if it is not expired or had no expire + date AND that if the cookie requires we're secure we must only + continue if we are! */ + if( (co->expires<=0 || (co->expires> now)) && + (co->secure?secure:TRUE) ) { + + /* now check if the domain is correct */ + domlen=co->domain?strlen(co->domain):0; + if(!co->domain || + ((domlen<=hostlen) && + strequal(host+(hostlen-domlen), co->domain)) ) { + /* the right part of the host matches the domain stuff in the + cookie data */ + + /* now check the left part of the path with the cookies path + requirement */ + if(!co->path || + strnequal(path, co->path, strlen(co->path))) { + + /* and now, we know this is a match and we should create an + entry for the return-linked-list */ + + newco = (struct Cookie *)malloc(sizeof(struct Cookie)); + if(newco) { + /* first, copy the whole source cookie: */ + memcpy(newco, co, sizeof(struct Cookie)); + + /* then modify our next */ + newco->next = mainco; + + /* point the main to us */ + mainco = newco; + } + } + } + } + co = co->next; + } + + return mainco; /* return the new list */ +} + + +/***************************************************************************** + * + * Curl_cookie_freelist() + * + * Free a list of cookies previously returned by Curl_cookie_getlist(); + * + ****************************************************************************/ + +void Curl_cookie_freelist(struct Cookie *co) +{ + struct Cookie *next; + if(co) { + while(co) { + next = co->next; + free(co); /* we only free the struct since the "members" are all + just copied! */ + co = next; + } + } +} + +/***************************************************************************** + * + * Curl_cookie_cleanup() + * + * Free a "cookie object" previous created with cookie_init(). + * + ****************************************************************************/ +void Curl_cookie_cleanup(struct CookieInfo *c) +{ + struct Cookie *co; + struct Cookie *next; + if(c) { + if(c->filename) + free(c->filename); + co = c->cookies; + + while(co) { + if(co->name) + free(co->name); + if(co->value) + free(co->value); + if(co->domain) + free(co->domain); + if(co->path) + free(co->path); + if(co->expirestr) + free(co->expirestr); + + if(co->version) + free(co->version); + if(co->maxage) + free(co->maxage); + + next = co->next; + free(co); + co = next; + } + free(c); /* free the base struct as well */ + } +} + +/* + * Curl_cookie_output() + * + * Writes all internally known cookies to the specified file. Specify + * "-" as file name to write to stdout. + * + * The function returns non-zero on write failure. + */ +int Curl_cookie_output(struct CookieInfo *c, char *dumphere) +{ + struct Cookie *co; + FILE *out; + bool use_stdout=FALSE; + + if((NULL == c) || (0 == c->numcookies)) + /* If there are no known cookies, we don't write or even create any + destination file */ + return 0; + + if(strequal("-", dumphere)) { + /* use stdout */ + out = stdout; + use_stdout=TRUE; + } + else { + out = fopen(dumphere, "w"); + if(!out) + return 1; /* failure */ + } + + if(c) { + fputs("# Netscape HTTP Cookie File\n" + "# http://www.netscape.com/newsref/std/cookie_spec.html\n" + "# This file was generated by libcurl! Edit at your own risk.\n\n", + out); + co = c->cookies; + + while(co) { + fprintf(out, + "%s\t" /* domain */ + "%s\t" /* field1 */ + "%s\t" /* path */ + "%s\t" /* secure */ + "%u\t" /* expires */ + "%s\t" /* name */ + "%s\n", /* value */ + co->domain?co->domain:"unknown", + co->field1==2?"TRUE":"FALSE", + co->path?co->path:"/", + co->secure?"TRUE":"FALSE", + (unsigned int)co->expires, + co->name, + co->value?co->value:""); + + co=co->next; + } + } + + if(!use_stdout) + fclose(out); + + return 0; +} + +#ifdef CURL_COOKIE_DEBUG + +/* + * On my Solaris box, this command line builds this test program: + * + * gcc -g -o cooktest -DCURL_COOKIE_DEBUG -DHAVE_CONFIG_H -I.. -I../include cookie.c strequal.o getdate.o memdebug.o mprintf.o strtok.o -lnsl -lsocket + * + */ + +int main(int argc, char **argv) +{ + struct CookieInfo *c=NULL; + if(argc>1) { + c = Curl_cookie_init(argv[1], c); + Curl_cookie_add(c, TRUE, "PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/ftgw; secure"); + Curl_cookie_add(c, TRUE, "foobar=yes; domain=.haxx.se; path=/looser;"); + c = Curl_cookie_init(argv[1], c); + + Curl_cookie_output(c); + Curl_cookie_cleanup(c); + return 0; + } + return 1; +} + +#endif + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/cookie.h b/Source/CTest/Curl/cookie.h new file mode 100644 index 0000000..f5eba37 --- /dev/null +++ b/Source/CTest/Curl/cookie.h @@ -0,0 +1,84 @@ +#ifndef __COOKIE_H +#define __COOKIE_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include <stdio.h> +#ifdef WIN32 +#include <time.h> +#else +#include <sys/time.h> +#endif + +#include <curl/curl.h> + +struct Cookie { + struct Cookie *next; /* next in the chain */ + char *name; /* <this> = value */ + char *value; /* name = <this> */ + char *path; /* path = <this> */ + char *domain; /* domain = <this> */ + long expires; /* expires = <this> */ + char *expirestr; /* the plain text version */ + + char field1; /* read from a cookie file, 1 => FALSE, 2=> TRUE */ + + /* RFC 2109 keywords. Version=1 means 2109-compliant cookie sending */ + char *version; /* Version = <value> */ + char *maxage; /* Max-Age = <value> */ + + bool secure; /* whether the 'secure' keyword was used */ + bool livecookie; /* updated from a server, not a stored file */ +}; + +struct CookieInfo { + /* linked list of cookies we know of */ + struct Cookie *cookies; + + char *filename; /* file we read from/write to */ + bool running; /* state info, for cookie adding information */ + long numcookies; /* number of cookies in the "jar" */ +}; + +/* This is the maximum line length we accept for a cookie line */ +#define MAX_COOKIE_LINE 2048 +#define MAX_COOKIE_LINE_TXT "2047" + +/* This is the maximum length of a cookie name we deal with: */ +#define MAX_NAME 256 +#define MAX_NAME_TXT "255" + +/* + * Add a cookie to the internal list of cookies. The domain argument is only + * used if the header boolean is TRUE. + */ +struct Cookie *Curl_cookie_add(struct CookieInfo *, bool header, char *line, + char *domain); + +struct CookieInfo *Curl_cookie_init(char *, struct CookieInfo *); +struct Cookie *Curl_cookie_getlist(struct CookieInfo *, char *, char *, bool); +void Curl_cookie_freelist(struct Cookie *); +void Curl_cookie_cleanup(struct CookieInfo *); +int Curl_cookie_output(struct CookieInfo *, char *); + +#endif diff --git a/Source/CTest/Curl/dict.c b/Source/CTest/Curl/dict.c new file mode 100644 index 0000000..d5068ff --- /dev/null +++ b/Source/CTest/Curl/dict.c @@ -0,0 +1,241 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <sys/time.h> +#include <sys/resource.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "transfer.h" +#include "sendf.h" + +#include "progress.h" +#include "strequal.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +CURLcode Curl_dict(struct connectdata *conn) +{ + int nth; + char *word; + char *ppath; + char *database = NULL; + char *strategy = NULL; + char *nthdef = NULL; /* This is not part of the protocol, but required + by RFC 2229 */ + CURLcode result=CURLE_OK; + struct SessionHandle *data=conn->data; + + char *path = conn->path; + long *bytecount = &conn->bytecount; + + if(conn->bits.user_passwd) { + /* AUTH is missing */ + } + + if (strnequal(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || + strnequal(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || + strnequal(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { + + word = strchr(path, ':'); + if (word) { + word++; + database = strchr(word, ':'); + if (database) { + *database++ = (char)0; + strategy = strchr(database, ':'); + if (strategy) { + *strategy++ = (char)0; + nthdef = strchr(strategy, ':'); + if (nthdef) { + *nthdef++ = (char)0; + } + } + } + } + + if ((word == NULL) || (*word == (char)0)) { + failf(data, "lookup word is missing"); + } + if ((database == NULL) || (*database == (char)0)) { + database = (char *)"!"; + } + if ((strategy == NULL) || (*strategy == (char)0)) { + strategy = (char *)"."; + } + if ((nthdef == NULL) || (*nthdef == (char)0)) { + nth = 0; + } + else { + nth = atoi(nthdef); + } + + result = Curl_sendf(conn->firstsocket, conn, + "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n" + "MATCH " + "%s " /* database */ + "%s " /* strategy */ + "%s\n" /* word */ + "QUIT\n", + + database, + strategy, + word + ); + if(result) + failf(data, "Failed sending DICT request"); + else + result = Curl_Transfer(conn, conn->firstsocket, -1, FALSE, bytecount, + -1, NULL); /* no upload */ + if(result) + return result; + } + else if (strnequal(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) || + strnequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) || + strnequal(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) { + + word = strchr(path, ':'); + if (word) { + word++; + database = strchr(word, ':'); + if (database) { + *database++ = (char)0; + nthdef = strchr(database, ':'); + if (nthdef) { + *nthdef++ = (char)0; + } + } + } + + if ((word == NULL) || (*word == (char)0)) { + failf(data, "lookup word is missing"); + } + if ((database == NULL) || (*database == (char)0)) { + database = (char *)"!"; + } + if ((nthdef == NULL) || (*nthdef == (char)0)) { + nth = 0; + } + else { + nth = atoi(nthdef); + } + + result = Curl_sendf(conn->firstsocket, conn, + "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n" + "DEFINE " + "%s " /* database */ + "%s\n" /* word */ + "QUIT\n", + database, + word); + if(result) + failf(data, "Failed sending DICT request"); + else + result = Curl_Transfer(conn, conn->firstsocket, -1, FALSE, bytecount, + -1, NULL); /* no upload */ + + if(result) + return result; + + } + else { + + ppath = strchr(path, '/'); + if (ppath) { + int i; + + ppath++; + for (i = 0; ppath[i]; i++) { + if (ppath[i] == ':') + ppath[i] = ' '; + } + result = Curl_sendf(conn->firstsocket, conn, + "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n" + "%s\n" + "QUIT\n", ppath); + if(result) + failf(data, "Failed sending DICT request"); + else + result = Curl_Transfer(conn, conn->firstsocket, -1, FALSE, bytecount, + -1, NULL); + if(result) + return result; + } + } + + return CURLE_OK; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/dict.h b/Source/CTest/Curl/dict.h new file mode 100644 index 0000000..348403d --- /dev/null +++ b/Source/CTest/Curl/dict.h @@ -0,0 +1,29 @@ +#ifndef __DICT_H +#define __DICT_H + +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +CURLcode Curl_dict(struct connectdata *conn); +CURLcode Curl_dict_done(struct connectdata *conn); + +#endif diff --git a/Source/CTest/Curl/dllinit.c b/Source/CTest/Curl/dllinit.c new file mode 100644 index 0000000..71b28bd --- /dev/null +++ b/Source/CTest/Curl/dllinit.c @@ -0,0 +1,96 @@ +#ifdef WIN32 +/* dllinit.c -- Portable DLL initialization. + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + Contributed by Mumit Khan (khan@xraylith.wisc.edu). + + I've used DllMain as the DLL "main" since that's the most common + usage. MSVC and Mingw32 both default to DllMain as the standard + callback from the linker entry point. Cygwin, as of b20.1, also + uses DllMain as the default callback from the entry point. + + The real entry point is typically always defined by the runtime + library, and usually never overridden by (casual) user. What you can + override however is the callback routine that the entry point calls, + and this file provides such a callback function, DllMain. + + Mingw32: The default entry point for mingw32 is DllMainCRTStartup + which is defined in libmingw32.a This in turn calls DllMain which is + defined here. If not defined, there is a stub in libmingw32.a which + does nothing. + + Cygwin: The default entry point for Cygwin b20.1 or newer is + __cygwin_dll_entry which is defined in libcygwin.a. This in turn + calls the routine DllMain. If not defined, there is a stub in + libcygwin.a which does nothing. + + MSVC: MSVC runtime calls DllMain, just like Mingw32. + + Summary: If you need to do anything special in DllMain, just add it + here. Otherwise, the default setup should be just fine for 99%+ of + the time. I strongly suggest that you *not* change the entry point, + but rather change DllMain as appropriate. + + */ + + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN +#include <stdio.h> + +BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, + LPVOID reserved /* Not used. */ ); + +/* + *---------------------------------------------------------------------- + * + * DllMain -- + * + * This routine is called by the Mingw32, Cygwin32 or VC++ C run + * time library init code, or the Borland DllEntryPoint routine. It + * is responsible for initializing various dynamically loaded + * libraries. + * + * Results: + * TRUE on sucess, FALSE on failure. + * + * Side effects: + * + *---------------------------------------------------------------------- + */ +BOOL APIENTRY +DllMain ( + HINSTANCE hInst /* Library instance handle. */ , + DWORD reason /* Reason this function is being called. */ , + LPVOID reserved /* Not used. */ ) +{ + + switch (reason) + { + case DLL_PROCESS_ATTACH: + break; + + case DLL_PROCESS_DETACH: + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + } + return TRUE; +} +#else +#ifdef VMS +int VOID_VAR_DLLINIT; +#endif +#endif + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/easy.c b/Source/CTest/Curl/easy.c new file mode 100644 index 0000000..45de7e8 --- /dev/null +++ b/Source/CTest/Curl/easy.c @@ -0,0 +1,340 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#include "strequal.h" + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <sys/time.h> +#include <sys/resource.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "transfer.h" +#include "ssluse.h" +#include "url.h" +#include "getinfo.h" +#include "hostip.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + + +/* Silly win32 socket initialization functions */ + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +static void win32_cleanup(void) +{ + WSACleanup(); +} + +static CURLcode win32_init(void) +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + wVersionRequested = MAKEWORD(2, 0); + + err = WSAStartup(wVersionRequested, &wsaData); + + if (err != 0) + /* Tell the user that we couldn't find a useable */ + /* winsock.dll. */ + return CURLE_FAILED_INIT; + + /* Confirm that the Windows Sockets DLL supports 2.0.*/ + /* Note that if the DLL supports versions greater */ + /* than 2.0 in addition to 2.0, it will still return */ + /* 2.0 in wVersion since that is the version we */ + /* requested. */ + + if ( LOBYTE( wsaData.wVersion ) != 2 || + HIBYTE( wsaData.wVersion ) != 0 ) { + /* Tell the user that we couldn't find a useable */ + + /* winsock.dll. */ + WSACleanup(); + return CURLE_FAILED_INIT; + } + return CURLE_OK; +} +/* The Windows Sockets DLL is acceptable. Proceed. */ +#else +/* These functions exist merely to prevent compiler warnings */ +static CURLcode win32_init(void) { return CURLE_OK; } +static void win32_cleanup(void) { } +#endif + + +/* true globals -- for curl_global_init() and curl_global_cleanup() */ +static unsigned int initialized = 0; +static long init_flags = 0; + +/** + * Globally initializes cURL given a bitwise set of + * the different features to initialize. + */ +CURLcode curl_global_init(long flags) +{ + if (initialized) + return CURLE_OK; + + if (flags & CURL_GLOBAL_SSL) + Curl_SSL_init(); + + if (flags & CURL_GLOBAL_WIN32) + if (win32_init() != CURLE_OK) + return CURLE_FAILED_INIT; + + initialized = 1; + init_flags = flags; + + return CURLE_OK; +} + +/** + * Globally cleanup cURL, uses the value of "init_flags" to determine + * what needs to be cleaned up and what doesn't + */ +void curl_global_cleanup(void) +{ + if (!initialized) + return; + + Curl_global_host_cache_dtor(); + + if (init_flags & CURL_GLOBAL_SSL) + Curl_SSL_cleanup(); + + if (init_flags & CURL_GLOBAL_WIN32) + win32_cleanup(); + + initialized = 0; + init_flags = 0; +} + +CURL *curl_easy_init(void) +{ + CURLcode res; + struct SessionHandle *data; + + /* Make sure we inited the global SSL stuff */ + if (!initialized) + curl_global_init(CURL_GLOBAL_DEFAULT); + + /* We use curl_open() with undefined URL so far */ + res = Curl_open(&data); + if(res != CURLE_OK) + return NULL; + + return data; +} + +typedef int (*func_T)(void); +CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) +{ + va_list arg; + func_T param_func = (func_T)0; + long param_long = 0; + void *param_obj = NULL; + struct SessionHandle *data = curl; + + va_start(arg, tag); + + /* PORTING NOTE: + Object pointers can't necessarily be casted to function pointers and + therefore we need to know what type it is and read the correct type + at once. This should also correct problems with different sizes of + the types. + */ + + if(tag < CURLOPTTYPE_OBJECTPOINT) { + /* This is a LONG type */ + param_long = va_arg(arg, long); + Curl_setopt(data, tag, param_long); + } + else if(tag < CURLOPTTYPE_FUNCTIONPOINT) { + /* This is a object pointer type */ + param_obj = va_arg(arg, void *); + Curl_setopt(data, tag, param_obj); + } + else { + param_func = va_arg(arg, func_T ); + Curl_setopt(data, tag, param_func); + } + + va_end(arg); + return CURLE_OK; +} + +CURLcode curl_easy_perform(CURL *curl) +{ + struct SessionHandle *data = (struct SessionHandle *)curl; + + if (!data->hostcache) { + if (Curl_global_host_cache_use(data)) { + data->hostcache = Curl_global_host_cache_get(); + } + else { + data->hostcache = curl_hash_alloc(7, Curl_freeaddrinfo); + } + } + + return Curl_perform(data); +} + +void curl_easy_cleanup(CURL *curl) +{ + struct SessionHandle *data = (struct SessionHandle *)curl; + if (!Curl_global_host_cache_use(data)) { + curl_hash_destroy(data->hostcache); + } + Curl_close(data); +} + +CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) +{ + va_list arg; + void *paramp; + struct SessionHandle *data = (struct SessionHandle *)curl; + + va_start(arg, info); + paramp = va_arg(arg, void *); + + return Curl_getinfo(data, info, paramp); +} + +CURL *curl_easy_duphandle(CURL *incurl) +{ + struct SessionHandle *data=(struct SessionHandle *)incurl; + + struct SessionHandle *outcurl = (struct SessionHandle *) + malloc(sizeof(struct SessionHandle)); + + if(NULL == outcurl) + return NULL; /* failure */ + + /* start with clearing the entire new struct */ + memset(outcurl, 0, sizeof(struct SessionHandle)); + + /* + * We setup a few buffers we need. We should probably make them + * get setup on-demand in the code, as that would probably decrease + * the likeliness of us forgetting to init a buffer here in the future. + */ + outcurl->state.headerbuff=(char*)malloc(HEADERSIZE); + if(!outcurl->state.headerbuff) { + free(outcurl); /* free the memory again */ + return NULL; + } + outcurl->state.headersize=HEADERSIZE; + + /* copy all userdefined values */ + outcurl->set = data->set; + outcurl->state.numconnects = data->state.numconnects; + outcurl->state.connects = (struct connectdata **) + malloc(sizeof(struct connectdata *) * outcurl->state.numconnects); + + if(!outcurl->state.connects) { + free(outcurl->state.headerbuff); + free(outcurl); + return NULL; + } + memset(outcurl->state.connects, 0, + sizeof(struct connectdata *)*outcurl->state.numconnects); + + outcurl->progress.flags = data->progress.flags; + outcurl->progress.callback = data->progress.callback; + + if(data->cookies) + /* If cookies are enabled in the parent handle, we enable them + in the clone as well! */ + outcurl->cookies = Curl_cookie_init(data->cookies->filename, + outcurl->cookies); + + /* duplicate all values in 'change' */ + if(data->change.url) { + outcurl->change.url = strdup(data->change.url); + outcurl->change.url_alloc = TRUE; + } + if(data->change.proxy) { + outcurl->change.proxy = strdup(data->change.proxy); + outcurl->change.proxy_alloc = TRUE; + } + if(data->change.referer) { + outcurl->change.referer = strdup(data->change.referer); + outcurl->change.referer_alloc = TRUE; + } + + return outcurl; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/escape.c b/Source/CTest/Curl/escape.c new file mode 100644 index 0000000..88f4359 --- /dev/null +++ b/Source/CTest/Curl/escape.c @@ -0,0 +1,120 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* Escape and unescape URL encoding in strings. The functions return a new + * allocated string or NULL if an error occurred. */ + +#include "setup.h" +#include <ctype.h> +#include <curl/curl.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +char *curl_escape(const char *string, int length) +{ + int alloc = (length?length:(int)strlen(string))+1; + char *ns = malloc(alloc); + unsigned char in; + int newlen = alloc; + int index=0; + + length = alloc-1; + while(length--) { + in = *string; + if(' ' == in) + ns[index++] = '+'; + else if(!(in >= 'a' && in <= 'z') && + !(in >= 'A' && in <= 'Z') && + !(in >= '0' && in <= '9')) { + /* encode it */ + newlen += 2; /* the size grows with two, since this'll become a %XX */ + if(newlen > alloc) { + alloc *= 2; + ns = realloc(ns, alloc); + if(!ns) + return NULL; + } + sprintf(&ns[index], "%%%02X", in); + + index+=3; + } + else { + /* just copy this */ + ns[index++]=in; + } + string++; + } + ns[index]=0; /* terminate it */ + return ns; +} + +char *curl_unescape(const char *string, int length) +{ + int alloc = (length?length:(int)strlen(string))+1; + char *ns = malloc(alloc); + unsigned char in; + int index=0; + unsigned int hex; + char querypart=FALSE; /* everything to the right of a '?' letter is + the "query part" where '+' should become ' '. + RFC 2316, section 3.10 */ + + while(--alloc > 0) { + in = *string; + if(querypart && ('+' == in)) + in = ' '; + else if(!querypart && ('?' == in)) { + /* we have "walked in" to the query part */ + querypart=TRUE; + } + else if('%' == in) { + /* encoded part */ + if(sscanf(string+1, "%02X", &hex)) { + in = hex; + string+=2; + alloc-=2; + } + } + + ns[index++] = in; + string++; + } + ns[index]=0; /* terminate it */ + return ns; + +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/escape.h b/Source/CTest/Curl/escape.h new file mode 100644 index 0000000..cda6a65 --- /dev/null +++ b/Source/CTest/Curl/escape.h @@ -0,0 +1,32 @@ +#ifndef __ESCAPE_H +#define __ESCAPE_H + +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +/* Escape and unescape URL encoding in strings. The functions return a new + * allocated string or NULL if an error occurred. */ + +char *curl_escape(const char *string, int length); +char *curl_unescape(const char *string, int length); + +#endif diff --git a/Source/CTest/Curl/file.c b/Source/CTest/Curl/file.c new file mode 100644 index 0000000..b2270aa --- /dev/null +++ b/Source/CTest/Curl/file.c @@ -0,0 +1,206 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#include <time.h> +#include <io.h> +#include <fcntl.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#include <sys/time.h> +#include <sys/resource.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "progress.h" +#include "sendf.h" +#include "escape.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* Emulate a connect-then-transfer protocol. We connect to the file here */ +CURLcode Curl_file_connect(struct connectdata *conn) +{ + char *actual_path = curl_unescape(conn->path, 0); + struct FILE *file; + int fd; +#if defined(WIN32) || defined(__EMX__) + int i; +#endif + + file = (struct FILE *)malloc(sizeof(struct FILE)); + if(!file) + return CURLE_OUT_OF_MEMORY; + + memset(file, 0, sizeof(struct FILE)); + conn->proto.file = file; + +#if defined(WIN32) || defined(__EMX__) + /* change path separators from '/' to '\\' for Windows and OS/2 */ + for (i=0; actual_path[i] != '\0'; ++i) + if (actual_path[i] == '/') + actual_path[i] = '\\'; + + fd = open(actual_path, O_RDONLY | O_BINARY); /* no CR/LF translation! */ +#else + fd = open(actual_path, O_RDONLY); +#endif + free(actual_path); + + if(fd == -1) { + failf(conn->data, "Couldn't open file %s", conn->path); + return CURLE_FILE_COULDNT_READ_FILE; + } + file->fd = fd; + + return CURLE_OK; +} + +/* This is the do-phase, separated from the connect-phase above */ + +CURLcode Curl_file(struct connectdata *conn) +{ + /* This implementation ignores the host name in conformance with + RFC 1738. Only local files (reachable via the standard file system) + are supported. This means that files on remotely mounted directories + (via NFS, Samba, NT sharing) can be accessed through a file:// URL + */ + CURLcode res = CURLE_OK; + struct stat statbuf; + ssize_t expected_size=-1; + ssize_t nread; + struct SessionHandle *data = conn->data; + char *buf = data->state.buffer; + int bytecount = 0; + struct timeval start = Curl_tvnow(); + struct timeval now = start; + int fd; + + /* get the fd from the connection phase */ + fd = conn->proto.file->fd; + +/*VMS?? -- This only works reliable for STREAMLF files */ + if( -1 != fstat(fd, &statbuf)) { + /* we could stat it, then read out the size */ + expected_size = statbuf.st_size; + } + + /* The following is a shortcut implementation of file reading + this is both more efficient than the former call to download() and + it avoids problems with select() and recv() on file descriptors + in Winsock */ + if(expected_size != -1) + Curl_pgrsSetDownloadSize(data, expected_size); + + while (res == CURLE_OK) { + nread = read(fd, buf, BUFSIZE-1); + + if ( nread > 0) + buf[nread] = 0; + + if (nread <= 0) + break; + + bytecount += nread; + /* NOTE: The following call to fwrite does CR/LF translation on + Windows systems if the target is stdout. Use -O or -o parameters + to prevent CR/LF translation (this then goes to a binary mode + file descriptor). */ + + res = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread); + if(res) + return res; + + now = Curl_tvnow(); + if(Curl_pgrsUpdate(conn)) + res = CURLE_ABORTED_BY_CALLBACK; + } + now = Curl_tvnow(); + if(Curl_pgrsUpdate(conn)) + res = CURLE_ABORTED_BY_CALLBACK; + + close(fd); + + return res; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/file.h b/Source/CTest/Curl/file.h new file mode 100644 index 0000000..83ffa39 --- /dev/null +++ b/Source/CTest/Curl/file.h @@ -0,0 +1,28 @@ +#ifndef __FILE_H +#define __FILE_H + +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +CURLcode Curl_file(struct connectdata *conn); +CURLcode Curl_file_connect(struct connectdata *conn); +#endif diff --git a/Source/CTest/Curl/formdata.c b/Source/CTest/Curl/formdata.c new file mode 100644 index 0000000..e464559 --- /dev/null +++ b/Source/CTest/Curl/formdata.c @@ -0,0 +1,1501 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* + Debug the form generator stand-alone by compiling this source file with: + + gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -o formdata -I../include formdata.c strequal.c + + run the 'formdata' executable the output should end with: + All Tests seem to have worked ... + and the following parts should be there: + +Content-Disposition: form-data; name="simple_COPYCONTENTS" +value for simple COPYCONTENTS + +Content-Disposition: form-data; name="COPYCONTENTS_+_CONTENTTYPE" +Content-Type: image/gif +value for COPYCONTENTS + CONTENTTYPE + +Content-Disposition: form-data; name="PRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH" +vlue for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH +(or you might see P^@RNAME and v^@lue at the start) + +Content-Disposition: form-data; name="simple_PTRCONTENTS" +value for simple PTRCONTENTS + +Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH" +vlue for PTRCONTENTS + CONTENTSLENGTH +(or you might see v^@lue at the start) + +Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE" +Content-Type: text/plain +vlue for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE +(or you might see v^@lue at the start) + +Content-Disposition: form-data; name="FILE1_+_CONTENTTYPE"; filename="inet_ntoa_r.h" +Content-Type: text/html +... + +Content-Disposition: form-data; name="FILE1_+_FILE2" +Content-Type: multipart/mixed, boundary=curlz1s0dkticx49MV1KGcYP5cvfSsz +... +Content-Disposition: attachment; filename="inet_ntoa_r.h" +Content-Type: text/plain +... +Content-Disposition: attachment; filename="Makefile.b32.resp" +Content-Type: text/plain +... + +Content-Disposition: form-data; name="FILE1_+_FILE2_+_FILE3" +Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1 +... +Content-Disposition: attachment; filename="inet_ntoa_r.h" +Content-Type: text/plain +... +Content-Disposition: attachment; filename="Makefile.b32.resp" +Content-Type: text/plain +... +Content-Disposition: attachment; filename="inet_ntoa_r.h" +Content-Type: text/plain +... + + +Content-Disposition: form-data; name="ARRAY: FILE1_+_FILE2_+_FILE3" +Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1 +... +Content-Disposition: attachment; filename="inet_ntoa_r.h" +Content-Type: text/plain +... +Content-Disposition: attachment; filename="Makefile.b32.resp" +Content-Type: text/plain +... +Content-Disposition: attachment; filename="inet_ntoa_r.h" +Content-Type: text/plain +... + +Content-Disposition: form-data; name="FILECONTENT" +... + + For the old FormParse used by curl_formparse use: + + gcc -DHAVE_CONFIG_H -I../ -g -D_OLD_FORM_DEBUG -o formdata -I../include formdata.c strequal.c + + run the 'formdata' executable and make sure the output is ok! + + try './formdata "name=Daniel" "poo=noo" "foo=bar"' and similarly + + */ + +#include "setup.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include <time.h> + +#include <curl/curl.h> +#include "formdata.h" + +#include "strequal.h" + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* Length of the random boundary string. The risk of this being used + in binary data is very close to zero, 64^32 makes + 6277101735386680763835789423207666416102355444464034512896 + combinations... */ +#define BOUNDARY_LENGTH 32 + +/* What kind of Content-Type to use on un-specified files with unrecognized + extensions. */ +#define HTTPPOST_CONTENTTYPE_DEFAULT "text/plain" + +/* This is a silly duplicate of the function in main.c to enable this source + to compile stand-alone for better debugging */ +static void GetStr(char **string, + const char *value) +{ + if(*string) + free(*string); + *string = strdup(value); +} + +/*************************************************************************** + * + * FormParse() + * + * Reads a 'name=value' paramter and builds the appropriate linked list. + * + * Specify files to upload with 'name=@filename'. Supports specified + * given Content-Type of the files. Such as ';type=<content-type>'. + * + * You may specify more than one file for a single name (field). Specify + * multiple files by writing it like: + * + * 'name=@filename,filename2,filename3' + * + * If you want content-types specified for each too, write them like: + * + * 'name=@filename;type=image/gif,filename2,filename3' + * + ***************************************************************************/ + +#define FORM_FILE_SEPARATOR ',' +#define FORM_TYPE_SEPARATOR ';' + +static +int FormParse(char *input, + struct HttpPost **httppost, + struct HttpPost **last_post) +{ + /* nextarg MUST be a string in the format 'name=contents' and we'll + build a linked list with the info */ + char name[256]; + char *contents; + char major[128]; + char minor[128]; + long flags = 0; + char *contp; + const char *type = NULL; + char *prevtype = NULL; + char *sep; + char *sep2; + struct HttpPost *post; + struct HttpPost *subpost; /* a sub-node */ + unsigned int i; + + /* Preallocate contents to the length of input to make sure we don't + overwrite anything. */ + contents = malloc(strlen(input)); + contents[0] = '\000'; + + if(1 <= sscanf(input, "%255[^=]=%[^\n]", name, contents)) { + /* the input was using the correct format */ + contp = contents; + + if('@' == contp[0]) { + /* we use the @-letter to indicate file name(s) */ + + flags = HTTPPOST_FILENAME; + contp++; + + post=NULL; + + do { + /* since this was a file, it may have a content-type specifier + at the end too */ + + sep=strchr(contp, FORM_TYPE_SEPARATOR); + sep2=strchr(contp, FORM_FILE_SEPARATOR); + + /* pick the closest */ + if(sep2 && (sep2 < sep)) { + sep = sep2; + + /* no type was specified! */ + } + if(sep) { + + /* if we got here on a comma, don't do much */ + if(FORM_FILE_SEPARATOR != *sep) + type = strstr(sep+1, "type="); + else + type=NULL; + + *sep=0; /* terminate file name at separator */ + + if(type) { + type += strlen("type="); + + if(2 != sscanf(type, "%127[^/]/%127[^,\n]", + major, minor)) { + free(contents); + return 2; /* illegal content-type syntax! */ + } + /* now point beyond the content-type specifier */ + sep = (char *)type + strlen(major)+strlen(minor)+1; + + /* find the following comma */ + sep=strchr(sep, FORM_FILE_SEPARATOR); + } + } + else { + type=NULL; + sep=strchr(contp, FORM_FILE_SEPARATOR); + } + if(sep) { + /* the next file name starts here */ + *sep =0; + sep++; + } + if(!type) { + /* + * No type was specified, we scan through a few well-known + * extensions and pick the first we match! + */ + struct ContentType { + const char *extension; + const char *type; + }; + static struct ContentType ctts[]={ + {".gif", "image/gif"}, + {".jpg", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".txt", "text/plain"}, + {".html", "text/plain"} + }; + + if(prevtype) + /* default to the previously set/used! */ + type = prevtype; + else + /* It seems RFC1867 defines no Content-Type to default to + text/plain so we don't actually need to set this: */ + type = HTTPPOST_CONTENTTYPE_DEFAULT; + + for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) { + if(strlen(contp) >= strlen(ctts[i].extension)) { + if(strequal(contp + + strlen(contp) - strlen(ctts[i].extension), + ctts[i].extension)) { + type = ctts[i].type; + break; + } + } + } + /* we have a type by now */ + } + + if(NULL == post) { + /* For the first file name, we allocate and initiate the main list + node */ + + post = (struct HttpPost *)malloc(sizeof(struct HttpPost)); + if(post) { + memset(post, 0, sizeof(struct HttpPost)); + GetStr(&post->name, name); /* get the name */ + GetStr(&post->contents, contp); /* get the contents */ + post->contentslength = 0; + post->flags = flags; + if(type) { + GetStr(&post->contenttype, (char *)type); /* get type */ + prevtype=post->contenttype; /* point to the allocated string! */ + } + /* make the previous point to this */ + if(*last_post) + (*last_post)->next = post; + else + (*httppost) = post; + + (*last_post) = post; + } + + } + else { + /* we add a file name to the previously allocated node, known as + 'post' now */ + subpost =(struct HttpPost *)malloc(sizeof(struct HttpPost)); + if(subpost) { + memset(subpost, 0, sizeof(struct HttpPost)); + GetStr(&subpost->name, name); /* get the name */ + GetStr(&subpost->contents, contp); /* get the contents */ + subpost->contentslength = 0; + subpost->flags = flags; + if(type) { + GetStr(&subpost->contenttype, (char *)type); /* get type */ + prevtype=subpost->contenttype; /* point to allocated string! */ + } + /* now, point our 'more' to the original 'more' */ + subpost->more = post->more; + + /* then move the original 'more' to point to ourselves */ + post->more = subpost; + } + } + contp = sep; /* move the contents pointer to after the separator */ + } while(sep && *sep); /* loop if there's another file name */ + } + else { + post = (struct HttpPost *)malloc(sizeof(struct HttpPost)); + if(post) { + memset(post, 0, sizeof(struct HttpPost)); + GetStr(&post->name, name); /* get the name */ + if( contp[0]=='<' ) { + GetStr(&post->contents, contp+1); /* get the contents */ + post->contentslength = 0; + post->flags = HTTPPOST_READFILE; + } + else { + GetStr(&post->contents, contp); /* get the contents */ + post->contentslength = 0; + post->flags = 0; + } + + /* make the previous point to this */ + if(*last_post) + (*last_post)->next = post; + else + (*httppost) = post; + + (*last_post) = post; + } + + } + + } + else { + free(contents); + return 1; + } + free(contents); + return 0; +} + +int curl_formparse(char *input, + struct HttpPost **httppost, + struct HttpPost **last_post) +{ + return FormParse(input, httppost, last_post); +} + +/*************************************************************************** + * + * AddHttpPost() + * + * Adds a HttpPost structure to the list, if parent_post is given becomes + * a subpost of parent_post instead of a direct list element. + * + * Returns newly allocated HttpPost on success and NULL if malloc failed. + * + ***************************************************************************/ +static struct HttpPost * AddHttpPost(char * name, + long namelength, + char * value, + long contentslength, + char *contenttype, + long flags, + struct curl_slist* contentHeader, + struct HttpPost *parent_post, + struct HttpPost **httppost, + struct HttpPost **last_post) +{ + struct HttpPost *post; + post = (struct HttpPost *)malloc(sizeof(struct HttpPost)); + if(post) { + memset(post, 0, sizeof(struct HttpPost)); + post->name = name; + post->namelength = name?(namelength?namelength:(long)strlen(name)):0; + post->contents = value; + post->contentslength = contentslength; + post->contenttype = contenttype; + post->contentheader = contentHeader; + post->flags = flags; + } + else + return NULL; + + if (parent_post) { + /* now, point our 'more' to the original 'more' */ + post->more = parent_post->more; + + /* then move the original 'more' to point to ourselves */ + parent_post->more = post; + } + else { + /* make the previous point to this */ + if(*last_post) + (*last_post)->next = post; + else + (*httppost) = post; + + (*last_post) = post; + } + return post; +} + +/*************************************************************************** + * + * AddFormInfo() + * + * Adds a FormInfo structure to the list presented by parent_form_info. + * + * Returns newly allocated FormInfo on success and NULL if malloc failed/ + * parent_form_info is NULL. + * + ***************************************************************************/ +static FormInfo * AddFormInfo(char *value, + char *contenttype, + FormInfo *parent_form_info) +{ + FormInfo *form_info; + form_info = (FormInfo *)malloc(sizeof(FormInfo)); + if(form_info) { + memset(form_info, 0, sizeof(FormInfo)); + if (value) + form_info->value = value; + if (contenttype) + form_info->contenttype = contenttype; + form_info->flags = HTTPPOST_FILENAME; + } + else + return NULL; + + if (parent_form_info) { + /* now, point our 'more' to the original 'more' */ + form_info->more = parent_form_info->more; + + /* then move the original 'more' to point to ourselves */ + parent_form_info->more = form_info; + } + else + return NULL; + + return form_info; +} + +/*************************************************************************** + * + * ContentTypeForFilename() + * + * Provides content type for filename if one of the known types (else + * (either the prevtype or the default is returned). + * + * Returns some valid contenttype for filename. + * + ***************************************************************************/ +static const char * ContentTypeForFilename (const char *filename, + const char *prevtype) +{ + const char *contenttype = NULL; + unsigned int i; + /* + * No type was specified, we scan through a few well-known + * extensions and pick the first we match! + */ + struct ContentType { + const char *extension; + const char *type; + }; + static struct ContentType ctts[]={ + {".gif", "image/gif"}, + {".jpg", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".txt", "text/plain"}, + {".html", "text/plain"} + }; + + if(prevtype) + /* default to the previously set/used! */ + contenttype = prevtype; + else + /* It seems RFC1867 defines no Content-Type to default to + text/plain so we don't actually need to set this: */ + contenttype = HTTPPOST_CONTENTTYPE_DEFAULT; + + for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) { + if(strlen(filename) >= strlen(ctts[i].extension)) { + if(strequal(filename + + strlen(filename) - strlen(ctts[i].extension), + ctts[i].extension)) { + contenttype = ctts[i].type; + break; + } + } + } + /* we have a contenttype by now */ + return contenttype; +} + +/*************************************************************************** + * + * AllocAndCopy() + * + * Copies the data currently available under *buffer using newly allocated + * buffer (that becomes *buffer). Uses buffer_length if not null, else + * uses strlen to determine the length of the buffer to be copied + * + * Returns 0 on success and 1 if the malloc failed. + * + ***************************************************************************/ +static int AllocAndCopy (char **buffer, int buffer_length) +{ + const char *src = *buffer; + int length, add = 0; + if (buffer_length) + length = buffer_length; + else { + length = strlen(*buffer); + add = 1; + } + *buffer = (char*)malloc(length+add); + if (!*buffer) + return 1; + memcpy(*buffer, src, length); + /* if length unknown do null termination */ + if (add) + (*buffer)[length] = '\0'; + return 0; +} + +/*************************************************************************** + * + * FormAdd() + * + * Stores a 'name=value' formpost parameter and builds the appropriate + * linked list. + * + * Has two principal functionalities: using files and byte arrays as + * post parts. Byte arrays are either copied or just the pointer is stored + * (as the user requests) while for files only the filename and not the + * content is stored. + * + * While you may have only one byte array for each name, multiple filenames + * are allowed (and because of this feature CURLFORM_END is needed after + * using CURLFORM_FILE). + * + * Examples: + * + * Simple name/value pair with copied contents: + * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", + * CURLFORM_COPYCONTENTS, "value", CURLFORM_END); + * + * name/value pair where only the content pointer is remembered: + * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", + * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END); + * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used) + * + * storing a filename (CONTENTTYPE is optional!): + * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", + * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text", + * CURLFORM_END); + * + * storing multiple filenames: + * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", + * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END); + * + * Returns: + * FORMADD_OK on success + * FORMADD_MEMORY if the FormInfo allocation fails + * FORMADD_OPTION_TWICE if one option is given twice for one Form + * FORMADD_NULL if a null pointer was given for a char + * FORMADD_MEMORY if the allocation of a FormInfo struct failed + * FORMADD_UNKNOWN_OPTION if an unknown option was used + * FORMADD_INCOMPLETE if the some FormInfo is not complete (or an error) + * FORMADD_MEMORY if a HttpPost struct cannot be allocated + * FORMADD_MEMORY if some allocation for string copying failed. + * FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + ***************************************************************************/ + +typedef enum { + FORMADD_OK, /* first, no error */ + + FORMADD_MEMORY, + FORMADD_OPTION_TWICE, + FORMADD_NULL, + FORMADD_UNKNOWN_OPTION, + FORMADD_INCOMPLETE, + FORMADD_ILLEGAL_ARRAY, + + FORMADD_LAST /* last */ +} FORMcode; + +static +FORMcode FormAdd(struct HttpPost **httppost, + struct HttpPost **last_post, + va_list params) +{ + FormInfo *first_form, *current_form, *form; + FORMcode return_value = FORMADD_OK; + const char *prevtype = NULL; + struct HttpPost *post = NULL; + CURLformoption option; + struct curl_forms *forms = NULL; + const char *array_value; /* value read from an array */ + + /* This is a state variable, that if TRUE means that we're parsing an + array that we got passed to us. If FALSE we're parsing the input + va_list arguments. */ + bool array_state = FALSE; + + /* + * We need to allocate the first struct to fill in. + */ + first_form = (FormInfo *)malloc(sizeof(struct FormInfo)); + if(first_form) { + memset(first_form, 0, sizeof(FormInfo)); + current_form = first_form; + } + else + return FORMADD_MEMORY; + + /* + * Loop through all the options set. + */ + while (1) { + + /* break if we have an error to report */ + if (return_value != FORMADD_OK) + break; + + /* first see if we have more parts of the array param */ + if ( array_state ) { + /* get the upcoming option from the given array */ + option = forms->option; + array_value = forms->value; + + forms++; /* advance this to next entry */ + if (CURLFORM_END == option) { + /* end of array state */ + array_state = FALSE; + continue; + } + else { + /* check that the option is OK in an array */ + + /* Daniel's note: do we really need to do this? */ + if ( (option <= CURLFORM_ARRAY_START) || + (option >= CURLFORM_ARRAY_END) ) { + return_value = FORMADD_ILLEGAL_ARRAY; + break; + } + } + } + else { + /* This is not array-state, get next option */ + option = va_arg(params, CURLformoption); + if (CURLFORM_END == option) + break; + } + + switch (option) { + case CURLFORM_ARRAY: + forms = va_arg(params, struct curl_forms *); + if (forms) + array_state = TRUE; + else + return_value = FORMADD_NULL; + break; + + /* + * Set the Name property. + */ + case CURLFORM_PTRNAME: + current_form->flags |= HTTPPOST_PTRNAME; /* fall through */ + case CURLFORM_COPYNAME: + if (current_form->name) + return_value = FORMADD_OPTION_TWICE; + else { + char *name = va_arg(params, char *); + if (name) + current_form->name = name; /* store for the moment */ + else + return_value = FORMADD_NULL; + } + break; + case CURLFORM_NAMELENGTH: + if (current_form->namelength) + return_value = FORMADD_OPTION_TWICE; + else + current_form->namelength = va_arg(params, long); + break; + + /* + * Set the contents property. + */ + case CURLFORM_PTRCONTENTS: + current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */ + case CURLFORM_COPYCONTENTS: + if (current_form->value) + return_value = FORMADD_OPTION_TWICE; + else { + char *value = va_arg(params, char *); + if (value) + current_form->value = value; /* store for the moment */ + else + return_value = FORMADD_NULL; + } + break; + case CURLFORM_CONTENTSLENGTH: + if (current_form->contentslength) + return_value = FORMADD_OPTION_TWICE; + else + current_form->contentslength = va_arg(params, long); + break; + + /* Get contents from a given file name */ + case CURLFORM_FILECONTENT: + if (current_form->flags != 0) + return_value = FORMADD_OPTION_TWICE; + else { + char *filename = va_arg(params, char *); + if (filename) { + current_form->value = strdup(filename); + current_form->flags |= HTTPPOST_READFILE; + } + else + return_value = FORMADD_NULL; + } + break; + + /* We upload a file */ + case CURLFORM_FILE: + { + const char *filename = NULL; + if (array_state) + filename = array_value; + else + filename = va_arg(params, const char *); + if (current_form->value) { + if (current_form->flags & HTTPPOST_FILENAME) { + if (filename) { + if (!(current_form = AddFormInfo(strdup(filename), + NULL, current_form))) + return_value = FORMADD_MEMORY; + } + else + return_value = FORMADD_NULL; + } + else + return_value = FORMADD_OPTION_TWICE; + } + else { + if (filename) + current_form->value = strdup(filename); + else + return_value = FORMADD_NULL; + current_form->flags |= HTTPPOST_FILENAME; + } + break; + } + case CURLFORM_CONTENTTYPE: + { + const char *contenttype = NULL; + if (array_state) + contenttype = array_value; + else + contenttype = va_arg(params, const char *); + if (current_form->contenttype) { + if (current_form->flags & HTTPPOST_FILENAME) { + if (contenttype) { + if (!(current_form = AddFormInfo(NULL, + strdup(contenttype), + current_form))) + return_value = FORMADD_MEMORY; + } + else + return_value = FORMADD_NULL; + } + else + return_value = FORMADD_OPTION_TWICE; + } + else { + if (contenttype) + current_form->contenttype = strdup(contenttype); + else + return_value = FORMADD_NULL; + } + break; + } + case CURLFORM_CONTENTHEADER: + { + struct curl_slist* list = NULL; + if( array_state ) + list = (struct curl_slist*)array_value; + else + list = va_arg(params,struct curl_slist*); + + if( current_form->contentheader ) + return_value = FORMADD_OPTION_TWICE; + else + current_form->contentheader = list; + + break; + } + default: + return_value = FORMADD_UNKNOWN_OPTION; + } + } + + if(FORMADD_OK == return_value) { + /* go through the list, check for copleteness and if everything is + * alright add the HttpPost item otherwise set return_value accordingly */ + + post = NULL; + for(form = first_form; + form != NULL; + form = form->more) { + if ( ((!form->name || !form->value) && !post) || + ( (form->contentslength) && + (form->flags & HTTPPOST_FILENAME) ) || + ( (form->flags & HTTPPOST_FILENAME) && + (form->flags & HTTPPOST_PTRCONTENTS) ) || + ( (form->flags & HTTPPOST_READFILE) && + (form->flags & HTTPPOST_PTRCONTENTS) ) + ) { + return_value = FORMADD_INCOMPLETE; + break; + } + else { + if ( (form->flags & HTTPPOST_FILENAME) && + !form->contenttype ) { + /* our contenttype is missing */ + form->contenttype + = strdup(ContentTypeForFilename(form->value, prevtype)); + } + if ( !(form->flags & HTTPPOST_PTRNAME) && + (form == first_form) ) { + /* copy name (without strdup; possibly contains null characters) */ + if (AllocAndCopy(&form->name, form->namelength)) { + return_value = FORMADD_MEMORY; + break; + } + } + if ( !(form->flags & HTTPPOST_FILENAME) && + !(form->flags & HTTPPOST_READFILE) && + !(form->flags & HTTPPOST_PTRCONTENTS) ) { + /* copy value (without strdup; possibly contains null characters) */ + if (AllocAndCopy(&form->value, form->contentslength)) { + return_value = FORMADD_MEMORY; + break; + } + } + post = AddHttpPost(form->name, form->namelength, + form->value, form->contentslength, + form->contenttype, form->flags, + form->contentheader, + post, httppost, + last_post); + + if(!post) + return_value = FORMADD_MEMORY; + + if (form->contenttype) + prevtype = form->contenttype; + } + } + } + + /* always delete the allocated memory before returning */ + form = first_form; + while (form != NULL) { + FormInfo *delete_form; + + delete_form = form; + form = form->more; + free (delete_form); + } + + return return_value; +} + +int curl_formadd(struct HttpPost **httppost, + struct HttpPost **last_post, + ...) +{ + va_list arg; + int result; + va_start(arg, last_post); + result = FormAdd(httppost, last_post, arg); + va_end(arg); + return result; +} + +static int AddFormData(struct FormData **formp, + const void *line, + long length) +{ + struct FormData *newform = (struct FormData *) + malloc(sizeof(struct FormData)); + newform->next = NULL; + + /* we make it easier for plain strings: */ + if(!length) + length = strlen((char *)line); + + newform->line = (char *)malloc(length+1); + memcpy(newform->line, line, length); + newform->length = length; + newform->line[length]=0; /* zero terminate for easier debugging */ + + if(*formp) { + (*formp)->next = newform; + *formp = newform; + } + else + *formp = newform; + + return length; +} + + +static int AddFormDataf(struct FormData **formp, + const char *fmt, ...) +{ + char s[4096]; + va_list ap; + va_start(ap, fmt); + vsprintf(s, fmt, ap); + va_end(ap); + + return AddFormData(formp, s, 0); +} + + +char *Curl_FormBoundary(void) +{ + char *retstring; + static int randomizer=0; /* this is just so that two boundaries within + the same form won't be identical */ + int i; + + static char table64[]= + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + retstring = (char *)malloc(BOUNDARY_LENGTH); + + if(!retstring) + return NULL; /* failed */ + + srand(time(NULL)+randomizer++); /* seed */ + + strcpy(retstring, "curl"); /* bonus commercials 8*) */ + + for(i=4; i<(BOUNDARY_LENGTH-1); i++) { + retstring[i] = table64[rand()%64]; + } + retstring[BOUNDARY_LENGTH-1]=0; /* zero terminate */ + + return retstring; +} + +/* Used from http.c, this cleans a built FormData linked list */ +void Curl_formclean(struct FormData *form) +{ + struct FormData *next; + + do { + next=form->next; /* the following form line */ + free(form->line); /* free the line */ + free(form); /* free the struct */ + + } while((form=next)); /* continue */ +} + +/* external function to free up a whole form post chain */ +void curl_formfree(struct HttpPost *form) +{ + struct HttpPost *next; + + if(!form) + /* no form to free, just get out of this */ + return; + + do { + next=form->next; /* the following form line */ + + /* recurse to sub-contents */ + if(form->more) + curl_formfree(form->more); + + if( !(form->flags & HTTPPOST_PTRNAME) && form->name) + free(form->name); /* free the name */ + if( !(form->flags & HTTPPOST_PTRCONTENTS) && form->contents) + free(form->contents); /* free the contents */ + if(form->contenttype) + free(form->contenttype); /* free the content type */ + free(form); /* free the struct */ + + } while((form=next)); /* continue */ +} + +struct FormData *Curl_getFormData(struct HttpPost *post, + int *sizep) +{ + struct FormData *form = NULL; + struct FormData *firstform; + + struct HttpPost *file; + + int size =0; + char *boundary; + char *fileboundary=NULL; + struct curl_slist* curList; + + + if(!post) + return NULL; /* no input => no output! */ + + boundary = Curl_FormBoundary(); + + /* Make the first line of the output */ + AddFormDataf(&form, + "Content-Type: multipart/form-data;" + " boundary=%s\r\n", + boundary); + /* we DO NOT count that line since that'll be part of the header! */ + + firstform = form; + + do { + + if(size) + size += AddFormDataf(&form, "\r\n"); + + /* boundary */ + size += AddFormDataf(&form, "--%s\r\n", boundary); + + size += AddFormData(&form, + "Content-Disposition: form-data; name=\"", 0); + + size += AddFormData(&form, post->name, post->namelength); + + size += AddFormData(&form, "\"", 0); + + if(post->more) { + /* If used, this is a link to more file names, we must then do + the magic to include several files with the same field name */ + + fileboundary = Curl_FormBoundary(); + + size += AddFormDataf(&form, + "\r\nContent-Type: multipart/mixed," + " boundary=%s\r\n", + fileboundary); + } + + file = post; + + do { + if(post->more) { + /* if multiple-file */ + size += AddFormDataf(&form, + "\r\n--%s\r\nContent-Disposition: attachment; filename=\"%s\"", + fileboundary, file->contents); + } + else if(post->flags & HTTPPOST_FILENAME) { + size += AddFormDataf(&form, + "; filename=\"%s\"", + post->contents); + } + + if(file->contenttype) { + /* we have a specified type */ + size += AddFormDataf(&form, + "\r\nContent-Type: %s", + file->contenttype); + } + + curList = file->contentheader; + while( curList ) { + /* Process the additional headers specified for this form */ + size += AddFormDataf( &form, "\r\n%s", curList->data ); + curList = curList->next; + } + +#if 0 + /* The header Content-Transfer-Encoding: seems to confuse some receivers + * (like the built-in PHP engine). While I can't see any reason why it + * should, I can just as well skip this to the benefit of the users who + * are using such confused receivers. + */ + + if(file->contenttype && + !strnequal("text/", file->contenttype, 5)) { + /* this is not a text content, mention our binary encoding */ + size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0); + } +#endif + + size += AddFormData(&form, "\r\n\r\n", 0); + + if((post->flags & HTTPPOST_FILENAME) || + (post->flags & HTTPPOST_READFILE)) { + /* we should include the contents from the specified file */ + FILE *fileread; + char buffer[1024]; + int nread; + + fileread = strequal("-", file->contents)?stdin: + /* binary read for win32 crap */ +/*VMS??*/ fopen(file->contents, "rb"); /* ONLY ALLOWS FOR STREAM FILES ON VMS */ +/*VMS?? Stream files are OK, as are FIXED & VAR files WITHOUT implied CC */ +/*VMS?? For implied CC, every record needs to have a \n appended & 1 added to SIZE */ + if(fileread) { + while((nread = fread(buffer, 1, 1024, fileread))) { + size += AddFormData(&form, + buffer, + nread); + } + if(fileread != stdin) + fclose(fileread); + } + else { + /* File wasn't found, add a nothing field! */ + size += AddFormData(&form, "", 0); + } + } + else { + /* include the contents we got */ + size += AddFormData(&form, post->contents, post->contentslength); + } + } while((file = file->more)); /* for each specified file for this field */ + + if(post->more) { + /* this was a multiple-file inclusion, make a termination file + boundary: */ + size += AddFormDataf(&form, + "\r\n--%s--", + fileboundary); + free(fileboundary); + } + + } while((post=post->next)); /* for each field */ + + /* end-boundary for everything */ + size += AddFormDataf(&form, + "\r\n--%s--\r\n", + boundary); + + *sizep = size; + + free(boundary); + + return firstform; +} + +int Curl_FormInit(struct Form *form, struct FormData *formdata ) +{ + if(!formdata) + return 1; /* error */ + + form->data = formdata; + form->sent = 0; + + return 0; +} + +/* fread() emulation */ +int Curl_FormReader(char *buffer, + size_t size, + size_t nitems, + FILE *mydata) +{ + struct Form *form; + int wantedsize; + int gotsize = 0; + + form=(struct Form *)mydata; + + wantedsize = size * nitems; + + if(!form->data) + return -1; /* nothing, error, empty */ + + do { + + if( (form->data->length - form->sent ) > wantedsize - gotsize) { + + memcpy(buffer + gotsize , form->data->line + form->sent, + wantedsize - gotsize); + + form->sent += wantedsize-gotsize; + + return wantedsize; + } + + memcpy(buffer+gotsize, + form->data->line + form->sent, + (form->data->length - form->sent) ); + gotsize += form->data->length - form->sent; + + form->sent = 0; + + form->data = form->data->next; /* advance */ + + } while(form->data); + /* If we got an empty line and we have more data, we proceed to the next + line immediately to avoid returning zero before we've reached the end. + This is the bug reported November 22 1999 on curl 6.3. (Daniel) */ + + return gotsize; +} + +/* possible (old) fread() emulation that copies at most one line */ +int Curl_FormReadOneLine(char *buffer, + size_t size, + size_t nitems, + FILE *mydata) +{ + struct Form *form; + int wantedsize; + int gotsize; + + form=(struct Form *)mydata; + + wantedsize = size * nitems; + + if(!form->data) + return -1; /* nothing, error, empty */ + + do { + + if( (form->data->length - form->sent ) > wantedsize ) { + + memcpy(buffer, form->data->line + form->sent, wantedsize); + + form->sent += wantedsize; + + return wantedsize; + } + + memcpy(buffer, + form->data->line + form->sent, + gotsize = (form->data->length - form->sent) ); + + form->sent = 0; + + form->data = form->data->next; /* advance */ + + } while(!gotsize && form->data); + /* If we got an empty line and we have more data, we proceed to the next + line immediately to avoid returning zero before we've reached the end. + This is the bug reported November 22 1999 on curl 6.3. (Daniel) */ + + return gotsize; +} + + +#ifdef _FORM_DEBUG +int FormAddTest(const char * errormsg, + struct HttpPost **httppost, + struct HttpPost **last_post, + ...) +{ + int result; + va_list arg; + va_start(arg, last_post); + if ((result = FormAdd(httppost, last_post, arg))) + fprintf (stderr, "ERROR doing FormAdd ret: %d action: %s\n", result, + errormsg); + va_end(arg); + return result; +} + + +int main() +{ + char name1[] = "simple_COPYCONTENTS"; + char name2[] = "COPYCONTENTS_+_CONTENTTYPE"; + char name3[] = "PTRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH"; + char name4[] = "simple_PTRCONTENTS"; + char name5[] = "PTRCONTENTS_+_CONTENTSLENGTH"; + char name6[] = "PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE"; + char name7[] = "FILE1_+_CONTENTTYPE"; + char name8[] = "FILE1_+_FILE2"; + char name9[] = "FILE1_+_FILE2_+_FILE3"; + char name10[] = "ARRAY: FILE1_+_FILE2_+_FILE3"; + char name11[] = "FILECONTENT"; + char value1[] = "value for simple COPYCONTENTS"; + char value2[] = "value for COPYCONTENTS + CONTENTTYPE"; + char value3[] = "value for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH"; + char value4[] = "value for simple PTRCONTENTS"; + char value5[] = "value for PTRCONTENTS + CONTENTSLENGTH"; + char value6[] = "value for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE"; + char value7[] = "inet_ntoa_r.h"; + char value8[] = "Makefile.b32.resp"; + char type2[] = "image/gif"; + char type6[] = "text/plain"; + char type7[] = "text/html"; + int name3length = strlen(name3); + int value3length = strlen(value3); + int value5length = strlen(value4); + int value6length = strlen(value5); + int errors = 0; + int size; + int nread; + char buffer[4096]; + struct HttpPost *httppost=NULL; + struct HttpPost *last_post=NULL; + struct curl_forms forms[4]; + + struct FormData *form; + struct Form formread; + + if (FormAddTest("simple COPYCONTENTS test", &httppost, &last_post, + CURLFORM_COPYNAME, name1, CURLFORM_COPYCONTENTS, value1, + CURLFORM_END)) + ++errors; + if (FormAddTest("COPYCONTENTS + CONTENTTYPE test", &httppost, &last_post, + CURLFORM_COPYNAME, name2, CURLFORM_COPYCONTENTS, value2, + CURLFORM_CONTENTTYPE, type2, CURLFORM_END)) + ++errors; + /* make null character at start to check that contentslength works + correctly */ + name3[1] = '\0'; + value3[1] = '\0'; + if (FormAddTest("PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH test", + &httppost, &last_post, + CURLFORM_PTRNAME, name3, CURLFORM_COPYCONTENTS, value3, + CURLFORM_CONTENTSLENGTH, value3length, + CURLFORM_NAMELENGTH, name3length, CURLFORM_END)) + ++errors; + if (FormAddTest("simple PTRCONTENTS test", &httppost, &last_post, + CURLFORM_COPYNAME, name4, CURLFORM_PTRCONTENTS, value4, + CURLFORM_END)) + ++errors; + /* make null character at start to check that contentslength works + correctly */ + value5[1] = '\0'; + if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH test", &httppost, &last_post, + CURLFORM_COPYNAME, name5, CURLFORM_PTRCONTENTS, value5, + CURLFORM_CONTENTSLENGTH, value5length, CURLFORM_END)) + ++errors; + /* make null character at start to check that contentslength works + correctly */ + value6[1] = '\0'; + if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE test", + &httppost, &last_post, + CURLFORM_COPYNAME, name6, CURLFORM_PTRCONTENTS, value6, + CURLFORM_CONTENTSLENGTH, value6length, + CURLFORM_CONTENTTYPE, type6, CURLFORM_END)) + ++errors; + if (FormAddTest("FILE + CONTENTTYPE test", &httppost, &last_post, + CURLFORM_COPYNAME, name7, CURLFORM_FILE, value7, + CURLFORM_CONTENTTYPE, type7, CURLFORM_END)) + ++errors; + if (FormAddTest("FILE1 + FILE2 test", &httppost, &last_post, + CURLFORM_COPYNAME, name8, CURLFORM_FILE, value7, + CURLFORM_FILE, value8, CURLFORM_END)) + ++errors; + if (FormAddTest("FILE1 + FILE2 + FILE3 test", &httppost, &last_post, + CURLFORM_COPYNAME, name9, CURLFORM_FILE, value7, + CURLFORM_FILE, value8, CURLFORM_FILE, value7, CURLFORM_END)) + ++errors; + forms[0].option = CURLFORM_FILE; + forms[0].value = value7; + forms[1].option = CURLFORM_FILE; + forms[1].value = value8; + forms[2].option = CURLFORM_FILE; + forms[2].value = value7; + forms[3].option = CURLFORM_END; + if (FormAddTest("FILE1 + FILE2 + FILE3 ARRAY test", &httppost, &last_post, + CURLFORM_COPYNAME, name10, CURLFORM_ARRAY, forms, + CURLFORM_END)) + ++errors; + if (FormAddTest("FILECONTENT test", &httppost, &last_post, + CURLFORM_COPYNAME, name11, CURLFORM_FILECONTENT, value7, + CURLFORM_END)) + ++errors; + + form=Curl_getFormData(httppost, &size); + + Curl_FormInit(&formread, form); + + do { + nread = Curl_FormReader(buffer, 1, sizeof(buffer), + (FILE *)&formread); + + if(-1 == nread) + break; + fwrite(buffer, nread, 1, stdout); + } while(1); + + fprintf(stdout, "size: %d\n", size); + if (errors) + fprintf(stdout, "\n==> %d Test(s) failed!\n", errors); + else + fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n"); + + return 0; +} + +#endif + +#ifdef _OLD_FORM_DEBUG + +int main(int argc, char **argv) +{ +#if 0 + char *testargs[]={ + "name1 = data in number one", + "name2 = number two data", + "test = @upload" + }; +#endif + int i; + char *nextarg; + struct HttpPost *httppost=NULL; + struct HttpPost *last_post=NULL; + struct HttpPost *post; + int size; + int nread; + char buffer[4096]; + + struct FormData *form; + struct Form formread; + + for(i=1; i<argc; i++) { + + if( FormParse( argv[i], + &httppost, + &last_post)) { + fprintf(stderr, "Illegally formatted input field: '%s'!\n", + argv[i]); + return 1; + } + } + + form=Curl_getFormData(httppost, &size); + + Curl_FormInit(&formread, form); + + do { + nread = Curl_FormReader(buffer, 1, sizeof(buffer), + (FILE *)&formread); + + if(-1 == nread) + break; + fwrite(buffer, nread, 1, stderr); + } while(1); + + fprintf(stderr, "size: %d\n", size); + + return 0; +} + +#endif + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/formdata.h b/Source/CTest/Curl/formdata.h new file mode 100644 index 0000000..817c0d6 --- /dev/null +++ b/Source/CTest/Curl/formdata.h @@ -0,0 +1,73 @@ +#ifndef __FORMDATA_H +#define __FORMDATA_H + +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +/* plain and simple linked list with lines to send */ +struct FormData { + struct FormData *next; + char *line; + long length; +}; + +struct Form { + struct FormData *data; /* current form line to send */ + int sent; /* number of bytes of the current line that has already + been sent in a previous invoke */ +}; + +/* used by FormAdd for temporary storage */ +typedef struct FormInfo { + char *name; + long namelength; + char *value; + long contentslength; + char *contenttype; + long flags; + struct curl_slist* contentheader; + struct FormInfo *more; +} FormInfo; + +int Curl_FormInit(struct Form *form, struct FormData *formdata ); + +struct FormData *Curl_getFormData(struct HttpPost *post, + int *size); + +/* fread() emulation */ +int Curl_FormReader(char *buffer, + size_t size, + size_t nitems, + FILE *mydata); + +/* possible (old) fread() emulation that copies at most one line */ +int Curl_FormReadOneLine(char *buffer, + size_t size, + size_t nitems, + FILE *mydata); + +char *Curl_FormBoundary(void); + +void Curl_formclean(struct FormData *); + +#endif + diff --git a/Source/CTest/Curl/ftp.c b/Source/CTest/Curl/ftp.c new file mode 100644 index 0000000..127971b --- /dev/null +++ b/Source/CTest/Curl/ftp.c @@ -0,0 +1,2138 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <errno.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#else /* some kind of unix */ +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <sys/types.h> +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#include <sys/utsname.h> +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#endif +#endif + +#if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__) +#include <errno.h> +#endif + +#include <curl/curl.h> +#include "urldata.h" +#include "sendf.h" + +#include "if2ip.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "escape.h" +#include "http.h" /* for HTTP proxy tunnel stuff */ +#include "ftp.h" + +#ifdef KRB4 +#include "security.h" +#include "krb4.h" +#endif + +#include "strequal.h" +#include "ssluse.h" +#include "connect.h" + +#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) +#include "inet_ntoa_r.h" +#endif + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* Local API functions */ +static CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote); +static CURLcode ftp_cwd(struct connectdata *conn, char *path); + +/* easy-to-use macro: */ +#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result + +/*********************************************************************** + * + * AllowServerConnect() + * + * When we've issue the PORT command, we have told the server to connect + * to us. This function will sit and wait here until the server has + * connected. + * + */ +static CURLcode AllowServerConnect(struct SessionHandle *data, + struct connectdata *conn, + int sock) +{ + fd_set rdset; + struct timeval dt; + + FD_ZERO(&rdset); + + FD_SET(sock, &rdset); + + /* we give the server 10 seconds to connect to us */ + dt.tv_sec = 10; + dt.tv_usec = 0; + + switch (select(sock+1, &rdset, NULL, NULL, &dt)) { + case -1: /* error */ + /* let's die here */ + failf(data, "Error while waiting for server connect"); + return CURLE_FTP_PORT_FAILED; + case 0: /* timeout */ + /* let's die here */ + failf(data, "Timeout while waiting for server connect"); + return CURLE_FTP_PORT_FAILED; + default: + /* we have received data here */ + { + int s; + size_t size = sizeof(struct sockaddr_in); + struct sockaddr_in add; + + getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size); + s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size); + + sclose(sock); /* close the first socket */ + + if (-1 == s) { + /* DIE! */ + failf(data, "Error accept()ing server connect"); + return CURLE_FTP_PORT_FAILED; + } + infof(data, "Connection accepted from server\n"); + + conn->secondarysocket = s; + } + break; + } + return CURLE_OK; +} + + +/* --- parse FTP server responses --- */ + +/* + * Curl_GetFTPResponse() is supposed to be invoked after each command sent to + * a remote FTP server. This function will wait and read all lines of the + * response and extract the relevant return code for the invoking function. + */ + +int Curl_GetFTPResponse(char *buf, + struct connectdata *conn, + int *ftpcode) +{ + /* Brand new implementation. + * We cannot read just one byte per read() and then go back to select() + * as it seems that the OpenSSL read() stuff doesn't grok that properly. + * + * Alas, read as much as possible, split up into lines, use the ending + * line in a response or continue reading. */ + + int sockfd = conn->firstsocket; + int nread; /* total size read */ + int perline; /* count bytes per line */ + bool keepon=TRUE; + ssize_t gotbytes; + char *ptr; + int timeout = 3600; /* default timeout in seconds */ + struct timeval interval; + fd_set rkeepfd; + fd_set readfd; + struct SessionHandle *data = conn->data; + char *line_start; + int code=0; /* default "error code" to return */ + +#define SELECT_OK 0 +#define SELECT_ERROR 1 /* select() problems */ +#define SELECT_TIMEOUT 2 /* took too long */ +#define SELECT_MEMORY 3 /* no available memory */ +#define SELECT_CALLBACK 4 /* aborted by callback */ + + int error = SELECT_OK; + + struct FTP *ftp = conn->proto.ftp; + + if (ftpcode) + *ftpcode = 0; /* 0 for errors */ + + if(data->set.timeout) { + /* if timeout is requested, find out how much remaining time we have */ + timeout = data->set.timeout - /* timeout time */ + Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */ + if(timeout <=0 ) { + failf(data, "Transfer aborted due to timeout"); + return -SELECT_TIMEOUT; /* already too little time */ + } + } + + FD_ZERO (&readfd); /* clear it */ + FD_SET (sockfd, &readfd); /* read socket */ + + /* get this in a backup variable to be able to restore it on each lap in the + select() loop */ + rkeepfd = readfd; + + ptr=buf; + line_start = buf; + + nread=0; + perline=0; + keepon=TRUE; + + while((nread<BUFSIZE) && (keepon && !error)) { + readfd = rkeepfd; /* set every lap */ + interval.tv_sec = timeout; + interval.tv_usec = 0; + + if(!ftp->cache) + switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) { + case -1: /* select() error, stop reading */ + error = SELECT_ERROR; + failf(data, "Transfer aborted due to select() error"); + break; + case 0: /* timeout */ + error = SELECT_TIMEOUT; + failf(data, "Transfer aborted due to timeout"); + break; + default: + error = SELECT_OK; + break; + } + if(SELECT_OK == error) { + /* + * This code previously didn't use the kerberos sec_read() code + * to read, but when we use Curl_read() it may do so. Do confirm + * that this is still ok and then remove this comment! + */ + if(ftp->cache) { + /* we had data in the "cache", copy that instead of doing an actual + read */ + memcpy(ptr, ftp->cache, ftp->cache_size); + gotbytes = ftp->cache_size; + free(ftp->cache); /* free the cache */ + ftp->cache = NULL; /* clear the pointer */ + ftp->cache_size = 0; /* zero the size just in case */ + } + else { + int res = Curl_read(conn, sockfd, ptr, + BUFSIZE-nread, &gotbytes); + if(res < 0) + /* EWOULDBLOCK */ + continue; /* go looping again */ + + if(CURLE_OK != res) + keepon = FALSE; + } + + if(!keepon) + ; + else if(gotbytes <= 0) { + keepon = FALSE; + error = SELECT_ERROR; + failf(data, "Connection aborted"); + } + else { + /* we got a whole chunk of data, which can be anything from one + * byte to a set of lines and possible just a piece of the last + * line */ + int i; + + nread += gotbytes; + for(i = 0; i < gotbytes; ptr++, i++) { + perline++; + if(*ptr=='\n') { + /* a newline is CRLF in ftp-talk, so the CR is ignored as + the line isn't really terminated until the LF comes */ + CURLcode result; + + /* output debug output if that is requested */ + if(data->set.verbose) { + fputs("< ", data->set.err); + fwrite(line_start, perline, 1, data->set.err); + /* no need to output LF here, it is part of the data */ + } + + /* + * We pass all response-lines to the callback function registered + * for "headers". The response lines can be seen as a kind of + * headers. + */ + result = Curl_client_write(data, CLIENTWRITE_HEADER, + line_start, perline); + if(result) + return -SELECT_CALLBACK; + +#define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \ + isdigit((int)line[2]) && (' ' == line[3])) + + if(perline>3 && lastline(line_start)) { + /* This is the end of the last line, copy the last + * line to the start of the buffer and zero terminate, + * for old times sake (and krb4)! */ + char *meow; + int n; + for(meow=line_start, n=0; meow<ptr; meow++, n++) + buf[n] = *meow; + *meow=0; /* zero terminate */ + keepon=FALSE; + line_start = ptr+1; /* advance pointer */ + i++; /* skip this before getting out */ + break; + } + perline=0; /* line starts over here */ + line_start = ptr+1; + } + } + if(!keepon && (i != gotbytes)) { + /* We found the end of the response lines, but we didn't parse the + full chunk of data we have read from the server. We therefore + need to store the rest of the data to be checked on the next + invoke as it may actually contain another end of response + already! Cleverly figured out by Eric Lavigne in December + 2001. */ + ftp->cache_size = gotbytes - i; + ftp->cache = (char *)malloc(ftp->cache_size); + if(ftp->cache) + memcpy(ftp->cache, line_start, ftp->cache_size); + else + return -SELECT_MEMORY; /**BANG**/ + } + } /* there was data */ + } /* if(no error) */ + } /* while there's buffer left and loop is requested */ + + if(!error) + code = atoi(buf); + +#ifdef KRB4 + /* handle the security-oriented responses 6xx ***/ + /* FIXME: some errorchecking perhaps... ***/ + switch(code) { + case 631: + Curl_sec_read_msg(conn, buf, prot_safe); + break; + case 632: + Curl_sec_read_msg(conn, buf, prot_private); + break; + case 633: + Curl_sec_read_msg(conn, buf, prot_confidential); + break; + default: + /* normal ftp stuff we pass through! */ + break; + } +#endif + + if(error) + return -error; + + if(ftpcode) + *ftpcode=code; /* return the initial number like this */ + + return nread; /* total amount of bytes read */ +} + +#ifndef ENABLE_IPV6 +/* + * This function is only used by code that works on IPv4. When we add proper + * support for that functionality with IPv6, this function can go in again. + */ +/* -- who are we? -- */ +static char *getmyhost(char *buf, int buf_size) +{ +#if defined(HAVE_GETHOSTNAME) + gethostname(buf, buf_size); +#elif defined(HAVE_UNAME) + struct utsname ugnm; + strncpy(buf, uname(&ugnm) < 0 ? "localhost" : ugnm.nodename, buf_size - 1); + buf[buf_size - 1] = '\0'; +#else + /* We have no means of finding the local host name! */ + strncpy(buf, "localhost", buf_size); + buf[buf_size - 1] = '\0'; +#endif + return buf; +} + +#endif /* ipv4-only function */ + + +/* ftp_connect() should do everything that is to be considered a part + of the connection phase. */ +CURLcode Curl_ftp_connect(struct connectdata *conn) +{ + /* this is FTP and no proxy */ + int nread; + struct SessionHandle *data=conn->data; + char *buf = data->state.buffer; /* this is our buffer */ + struct FTP *ftp; + CURLcode result; + int ftpcode; + + ftp = (struct FTP *)malloc(sizeof(struct FTP)); + if(!ftp) + return CURLE_OUT_OF_MEMORY; + + memset(ftp, 0, sizeof(struct FTP)); + conn->proto.ftp = ftp; + + /* We always support persistant connections on ftp */ + conn->bits.close = FALSE; + + /* get some initial data into the ftp struct */ + ftp->bytecountp = &conn->bytecount; + + /* no need to duplicate them, the data struct won't change */ + ftp->user = data->state.user; + ftp->passwd = data->state.passwd; + + if (data->set.tunnel_thru_httpproxy) { + /* We want "seamless" FTP operations through HTTP proxy tunnel */ + result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket, + conn->hostname, conn->remote_port); + if(CURLE_OK != result) + return result; + } + + if(conn->protocol & PROT_FTPS) { + /* FTPS is simply ftp with SSL for the control channel */ + /* now, perform the SSL initialization for this socket */ + result = Curl_SSLConnect(conn); + if(result) + return result; + } + + + /* The first thing we do is wait for the "220*" line: */ + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if(ftpcode != 220) { + failf(data, "This doesn't seem like a nice ftp-server response"); + return CURLE_FTP_WEIRD_SERVER_REPLY; + } + +#ifdef KRB4 + /* if not anonymous login, try a secure login */ + if(data->set.krb4) { + + /* request data protection level (default is 'clear') */ + Curl_sec_request_prot(conn, "private"); + + /* We set private first as default, in case the line below fails to + set a valid level */ + Curl_sec_request_prot(conn, data->set.krb4_level); + + if(Curl_sec_login(conn) != 0) + infof(data, "Logging in with password in cleartext!\n"); + else + infof(data, "Authentication successful\n"); + } +#endif + + /* send USER */ + FTPSENDF(conn, "USER %s", ftp->user); + + /* wait for feedback */ + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if(ftpcode == 530) { + /* 530 User ... access denied + (the server denies to log the specified user) */ + failf(data, "Access denied: %s", &buf[4]); + return CURLE_FTP_ACCESS_DENIED; + } + else if(ftpcode == 331) { + /* 331 Password required for ... + (the server requires to send the user's password too) */ + FTPSENDF(conn, "PASS %s", ftp->passwd); + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if(ftpcode == 530) { + /* 530 Login incorrect. + (the username and/or the password are incorrect) */ + failf(data, "the username and/or the password are incorrect"); + return CURLE_FTP_USER_PASSWORD_INCORRECT; + } + else if(ftpcode == 230) { + /* 230 User ... logged in. + (user successfully logged in) */ + + infof(data, "We have successfully logged in\n"); + } + else { + failf(data, "Odd return code after PASS"); + return CURLE_FTP_WEIRD_PASS_REPLY; + } + } + else if(buf[0] == '2') { + /* 230 User ... logged in. + (the user logged in without password) */ + infof(data, "We have successfully logged in\n"); +#ifdef KRB4 + /* we are logged in (with Kerberos) + * now set the requested protection level + */ + if(conn->sec_complete) + Curl_sec_set_protection_level(conn); + + /* we may need to issue a KAUTH here to have access to the files + * do it if user supplied a password + */ + if(data->state.passwd && *data->state.passwd) + Curl_krb_kauth(conn); +#endif + } + else { + failf(data, "Odd return code after USER"); + return CURLE_FTP_WEIRD_USER_REPLY; + } + + /* send PWD to discover our entry point */ + FTPSENDF(conn, "PWD", NULL); + + /* wait for feedback */ + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if(ftpcode == 257) { + char *dir = (char *)malloc(nread+1); + char *store=dir; + char *ptr=&buf[4]; /* start on the first letter */ + + /* Reply format is like + 257<space>"<directory-name>"<space><commentary> and the RFC959 says + + The directory name can contain any character; embedded double-quotes + should be escaped by double-quotes (the "quote-doubling" convention). + */ + if('\"' == *ptr) { + /* it started good */ + ptr++; + while(ptr && *ptr) { + if('\"' == *ptr) { + if('\"' == ptr[1]) { + /* "quote-doubling" */ + *store = ptr[1]; + ptr++; + } + else { + /* end of path */ + *store = '\0'; /* zero terminate */ + break; /* get out of this loop */ + } + } + else + *store = *ptr; + store++; + ptr++; + } + ftp->entrypath =dir; /* remember this */ + infof(data, "Entry path is '%s'\n", ftp->entrypath); + } + else { + /* couldn't get the path */ + } + + } + else { + /* We couldn't read the PWD response! */ + } + + return CURLE_OK; +} + +/*********************************************************************** + * + * Curl_ftp_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +CURLcode Curl_ftp_done(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + struct FTP *ftp = conn->proto.ftp; + ssize_t nread; + char *buf = data->state.buffer; /* this is our buffer */ + int ftpcode; + CURLcode result=CURLE_OK; + + if(data->set.upload) { + if((-1 != data->set.infilesize) && (data->set.infilesize != *ftp->bytecountp)) { + failf(data, "Wrote only partial file (%d out of %d bytes)", + *ftp->bytecountp, data->set.infilesize); + return CURLE_PARTIAL_FILE; + } + } + else { + if((-1 != conn->size) && (conn->size != *ftp->bytecountp) && + (conn->maxdownload != *ftp->bytecountp)) { + failf(data, "Received only partial file: %d bytes", *ftp->bytecountp); + return CURLE_PARTIAL_FILE; + } + else if(!conn->bits.resume_done && + !data->set.no_body && + (0 == *ftp->bytecountp)) { + /* We consider this an error, but there's no true FTP error received + why we need to continue to "read out" the server response too. + We don't want to leave a "waiting" server reply if we'll get told + to make a second request on this same connection! */ + failf(data, "No data was received!"); + result = CURLE_FTP_COULDNT_RETR_FILE; + } + } + +#ifdef KRB4 + Curl_sec_fflush_fd(conn, conn->secondarysocket); +#endif + /* shut down the socket to inform the server we're done */ + sclose(conn->secondarysocket); + conn->secondarysocket = -1; + + if(!data->set.no_body && !conn->bits.resume_done) { + /* now let's see what the server says about the transfer we + just performed: */ + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + /* 226 Transfer complete, 250 Requested file action okay, completed. */ + if((ftpcode != 226) && (ftpcode != 250)) { + failf(data, "server did not report OK, got %d", ftpcode); + return CURLE_FTP_WRITE_ERROR; + } + } + + conn->bits.resume_done = FALSE; /* clean this for next connection */ + + /* Send any post-transfer QUOTE strings? */ + if(!result && data->set.postquote) + result = ftp_sendquote(conn, data->set.postquote); + + return result; +} + +/*********************************************************************** + * + * ftp_sendquote() + * + * Where a 'quote' means a list of custom commands to send to the server. + * The quote list is passed as an argument. + */ + +static +CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote) +{ + struct curl_slist *item; + ssize_t nread; + int ftpcode; + CURLcode result; + + item = quote; + while (item) { + if (item->data) { + FTPSENDF(conn, "%s", item->data); + + nread = Curl_GetFTPResponse(conn->data->state.buffer, conn, &ftpcode); + if (nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if (ftpcode >= 400) { + failf(conn->data, "QUOT string not accepted: %s", item->data); + return CURLE_FTP_QUOTE_ERROR; + } + } + + item = item->next; + } + + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_cwd() + * + * Send 'CWD' to the remote server to Change Working Directory. + * It is the ftp version of the unix 'cd' command. + */ +static +CURLcode ftp_cwd(struct connectdata *conn, char *path) +{ + ssize_t nread; + int ftpcode; + CURLcode result; + + FTPSENDF(conn, "CWD %s", path); + nread = Curl_GetFTPResponse( + conn->data->state.buffer, conn, &ftpcode); + if (nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if (ftpcode != 250) { + failf(conn->data, "Couldn't cd to %s", path); + return CURLE_FTP_ACCESS_DENIED; + } + + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_getfiletime() + * + * Get the timestamp of the given file. + */ +static +CURLcode ftp_getfiletime(struct connectdata *conn, char *file) +{ + CURLcode result=CURLE_OK; + int ftpcode; /* for ftp status */ + ssize_t nread; + char *buf = conn->data->state.buffer; + + /* we have requested to get the modified-time of the file, this is yet + again a grey area as the MDTM is not kosher RFC959 */ + FTPSENDF(conn, "MDTM %s", file); + + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if(ftpcode == 213) { + /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the + last .sss part is optional and means fractions of a second */ + int year, month, day, hour, minute, second; + if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d", + &year, &month, &day, &hour, &minute, &second)) { + /* we have a time, reformat it */ + time_t secs=time(NULL); + sprintf(buf, "%04d%02d%02d %02d:%02d:%02d", + year, month, day, hour, minute, second); + /* now, convert this into a time() value: */ + conn->data->info.filetime = curl_getdate(buf, &secs); + } + else { + infof(conn->data, "unsupported MDTM reply format\n"); + } + } + return result; +} + +/*********************************************************************** + * + * ftp_transfertype() + * + * Set transfer type. We only deal with ASCII or BINARY so this function + * sets one of them. + */ +static CURLcode ftp_transfertype(struct connectdata *conn, + bool ascii) +{ + struct SessionHandle *data = conn->data; + int ftpcode; + ssize_t nread; + char *buf=data->state.buffer; + CURLcode result; + + FTPSENDF(conn, "TYPE %s", ascii?"A":"I"); + + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if(ftpcode != 200) { + failf(data, "Couldn't set %s mode", + ascii?"ASCII":"binary"); + return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY; + } + + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_getsize() + * + * Returns the file size (in bytes) of the given remote file. + */ + +static +CURLcode ftp_getsize(struct connectdata *conn, char *file, + ssize_t *size) +{ + struct SessionHandle *data = conn->data; + int ftpcode; + ssize_t nread; + char *buf=data->state.buffer; + CURLcode result; + + FTPSENDF(conn, "SIZE %s", file); + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if(ftpcode == 213) { + /* get the size from the ascii string: */ + *size = atoi(buf+4); + } + else + return CURLE_FTP_COULDNT_GET_SIZE; + + return CURLE_OK; +} + +/*************************************************************************** + * + * ftp_pasv_verbose() + * + * This function only outputs some informationals about this second connection + * when we've issued a PASV command before and thus we have connected to a + * possibly new IP address. + * + */ +static void +ftp_pasv_verbose(struct connectdata *conn, + Curl_ipconnect *addr, + char *newhost, /* ascii version */ + int port) +{ +#ifndef ENABLE_IPV6 + /***************************************************************** + * + * IPv4-only code section + */ + + struct in_addr in; + struct hostent * answer; + +#ifdef HAVE_INET_NTOA_R + char ntoa_buf[64]; +#endif + /* The array size trick below is to make this a large chunk of memory + suitably 8-byte aligned on 64-bit platforms. This was thoughtfully + suggested by Philip Gladstone. */ + long bigbuf[9000 / sizeof(long)]; + +#if defined(HAVE_INET_ADDR) + in_addr_t address; +# if defined(HAVE_GETHOSTBYADDR_R) + int h_errnop; +# endif + char *hostent_buf = (char *)bigbuf; /* get a char * to the buffer */ + + address = inet_addr(newhost); +# ifdef HAVE_GETHOSTBYADDR_R + +# ifdef HAVE_GETHOSTBYADDR_R_5 + /* AIX, Digital Unix (OSF1, Tru64) style: + extern int gethostbyaddr_r(char *addr, size_t len, int type, + struct hostent *htent, struct hostent_data *ht_data); */ + + /* Fred Noz helped me try this out, now it at least compiles! */ + + /* Bjorn Reese (November 28 2001): + The Tru64 man page on gethostbyaddr_r() says that + the hostent struct must be filled with zeroes before the call to + gethostbyaddr_r(). */ + + memset(hostent_buf, 0, sizeof(struct hostent)); + + if(gethostbyaddr_r((char *) &address, + sizeof(address), AF_INET, + (struct hostent *)hostent_buf, + hostent_buf + sizeof(*answer))) + answer=NULL; + +# endif +# ifdef HAVE_GETHOSTBYADDR_R_7 + /* Solaris and IRIX */ + answer = gethostbyaddr_r((char *) &address, sizeof(address), AF_INET, + (struct hostent *)bigbuf, + hostent_buf + sizeof(*answer), + sizeof(hostent_buf) - sizeof(*answer), + &h_errnop); +# endif +# ifdef HAVE_GETHOSTBYADDR_R_8 + /* Linux style */ + if(gethostbyaddr_r((char *) &address, sizeof(address), AF_INET, + (struct hostent *)hostent_buf, + hostent_buf + sizeof(*answer), + sizeof(hostent_buf) - sizeof(*answer), + &answer, + &h_errnop)) + answer=NULL; /* error */ +# endif + +# else + answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET); +# endif +#else + answer = NULL; +#endif + (void) memcpy(&in.s_addr, addr, sizeof (Curl_ipconnect)); + infof(conn->data, "Connecting to %s (%s) port %u\n", + answer?answer->h_name:newhost, +#if defined(HAVE_INET_NTOA_R) + inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)), +#else + inet_ntoa(in), +#endif + port); + +#else + /***************************************************************** + * + * IPv6-only code section + */ + char hbuf[NI_MAXHOST]; /* ~1KB */ + char nbuf[NI_MAXHOST]; /* ~1KB */ + char sbuf[NI_MAXSERV]; /* around 32 */ +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; +#endif + port = 0; /* unused, prevent warning */ + if (getnameinfo(addr->ai_addr, addr->ai_addrlen, + nbuf, sizeof(nbuf), sbuf, sizeof(sbuf), niflags)) { + snprintf(nbuf, sizeof(nbuf), "?"); + snprintf(sbuf, sizeof(sbuf), "?"); + } + + if (getnameinfo(addr->ai_addr, addr->ai_addrlen, + hbuf, sizeof(hbuf), NULL, 0, 0)) { + infof(conn->data, "Connecting to %s (%s) port %s\n", nbuf, newhost, sbuf); + } + else { + infof(conn->data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf); + } +#endif +} + +/*********************************************************************** + * + * ftp_use_port() + * + * Send the proper PORT command. PORT is the ftp client's way of telling the + * server that *WE* open a port that we listen on an awaits the server to + * connect to. This is the opposite of PASV. + */ + +static +CURLcode ftp_use_port(struct connectdata *conn) +{ + struct SessionHandle *data=conn->data; + int portsock=-1; + ssize_t nread; + char *buf = data->state.buffer; /* this is our buffer */ + int ftpcode; /* receive FTP response codes in this */ + CURLcode result; + +#ifdef ENABLE_IPV6 + /****************************************************************** + * + * Here's a piece of IPv6-specific code coming up + * + */ + + struct addrinfo hints, *res, *ai; + struct sockaddr_storage ss; + socklen_t sslen; + char hbuf[NI_MAXHOST]; + + struct sockaddr *sa=(struct sockaddr *)&ss; +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; +#endif + unsigned char *ap; + unsigned char *pp; + int alen, plen; + char portmsgbuf[4096], tmp[4096]; + + const char *mode[] = { "EPRT", "LPRT", "PORT", NULL }; + char **modep; + + /* + * we should use Curl_if2ip? given pickiness of recent ftpd, + * I believe we should use the same address as the control connection. + */ + sslen = sizeof(ss); + if (getsockname(conn->firstsocket, (struct sockaddr *)&ss, &sslen) < 0) + return CURLE_FTP_PORT_FAILED; + + if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0, + niflags)) + return CURLE_FTP_PORT_FAILED; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = sa->sa_family; + /*hints.ai_family = ss.ss_family; + this way can be used if sockaddr_storage is properly defined, as glibc + 2.1.X doesn't do*/ + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + if (getaddrinfo(hbuf, (char *)"0", &hints, &res)) + return CURLE_FTP_PORT_FAILED; + + portsock = -1; + for (ai = res; ai; ai = ai->ai_next) { + portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (portsock < 0) + continue; + + if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) { + sclose(portsock); + portsock = -1; + continue; + } + + if (listen(portsock, 1) < 0) { + sclose(portsock); + portsock = -1; + continue; + } + + break; + } + freeaddrinfo(res); + if (portsock < 0) { + failf(data, strerror(errno)); + return CURLE_FTP_PORT_FAILED; + } + + sslen = sizeof(ss); + if (getsockname(portsock, sa, &sslen) < 0) { + failf(data, strerror(errno)); + return CURLE_FTP_PORT_FAILED; + } + + for (modep = (char **)mode; modep && *modep; modep++) { + int lprtaf, eprtaf; + + switch (sa->sa_family) { + case AF_INET: + ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr; + alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr); + pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port; + plen = sizeof(((struct sockaddr_in *)&ss)->sin_port); + lprtaf = 4; + eprtaf = 1; + break; + case AF_INET6: + ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr; + alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr); + pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port; + plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port); + lprtaf = 6; + eprtaf = 2; + break; + default: + ap = pp = NULL; + lprtaf = eprtaf = -1; + break; + } + + if (strcmp(*modep, "EPRT") == 0) { + if (eprtaf < 0) + continue; + if (getnameinfo((struct sockaddr *)&ss, sslen, + portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags)) + continue; + + /* do not transmit IPv6 scope identifier to the wire */ + if (sa->sa_family == AF_INET6) { + char *q = strchr(portmsgbuf, '%'); + if (q) + *q = '\0'; + } + + result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf, + portmsgbuf, tmp); + if(result) + return result; + } else if (strcmp(*modep, "LPRT") == 0 || + strcmp(*modep, "PORT") == 0) { + int i; + + if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0) + continue; + if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET) + continue; + + portmsgbuf[0] = '\0'; + if (strcmp(*modep, "LPRT") == 0) { + snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen); + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= + sizeof(portmsgbuf)) { + continue; + } + } + + for (i = 0; i < alen; i++) { + if (portmsgbuf[0]) + snprintf(tmp, sizeof(tmp), ",%u", ap[i]); + else + snprintf(tmp, sizeof(tmp), "%u", ap[i]); + + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= + sizeof(portmsgbuf)) { + continue; + } + } + + if (strcmp(*modep, "LPRT") == 0) { + snprintf(tmp, sizeof(tmp), ",%d", plen); + + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) + continue; + } + + for (i = 0; i < plen; i++) { + snprintf(tmp, sizeof(tmp), ",%u", pp[i]); + + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= + sizeof(portmsgbuf)) { + continue; + } + } + + result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf); + if(result) + return result; + } + + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if (ftpcode != 200) { + failf(data, "Server does not grok %s", *modep); + continue; + } + else + break; + } + + if (!*modep) { + sclose(portsock); + return CURLE_FTP_PORT_FAILED; + } + /* we set the secondary socket variable to this for now, it + is only so that the cleanup function will close it in case + we fail before the true secondary stuff is made */ + conn->secondarysocket = portsock; + +#else + /****************************************************************** + * + * Here's a piece of IPv4-specific code coming up + * + */ + struct sockaddr_in sa; + struct hostent *h=NULL; + char *hostdataptr=NULL; + unsigned short porttouse; + char myhost[256] = ""; + + if(data->set.ftpport) { + if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) { + h = Curl_resolv(data, myhost, 0, &hostdataptr); + } + else { + int len = strlen(data->set.ftpport); + if(len>1) + h = Curl_resolv(data, data->set.ftpport, 0, &hostdataptr); + if(h) + strcpy(myhost, data->set.ftpport); /* buffer overflow risk */ + } + } + if(! *myhost) { + char *tmp_host = getmyhost(myhost, sizeof(myhost)); + h=Curl_resolv(data, tmp_host, 0, &hostdataptr); + } + infof(data, "We connect from %s\n", myhost); + + if ( h ) { + if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) { + int size; + + /* we set the secondary socket variable to this for now, it + is only so that the cleanup function will close it in case + we fail before the true secondary stuff is made */ + conn->secondarysocket = portsock; + + memset((char *)&sa, 0, sizeof(sa)); + memcpy((char *)&sa.sin_addr, + h->h_addr, + h->h_length); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = INADDR_ANY; + sa.sin_port = 0; + size = sizeof(sa); + + if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) { + /* we succeeded to bind */ + struct sockaddr_in add; + socklen_t socksize = sizeof(add); + + if(getsockname(portsock, (struct sockaddr *) &add, + &socksize)<0) { + failf(data, "getsockname() failed"); + return CURLE_FTP_PORT_FAILED; + } + porttouse = ntohs(add.sin_port); + + if ( listen(portsock, 1) < 0 ) { + failf(data, "listen(2) failed on socket"); + free(hostdataptr); + return CURLE_FTP_PORT_FAILED; + } + } + else { + failf(data, "bind(2) failed on socket"); + free(hostdataptr); + return CURLE_FTP_PORT_FAILED; + } + } + else { + failf(data, "socket(2) failed (%s)"); + free(hostdataptr); + return CURLE_FTP_PORT_FAILED; + } + } + else { + failf(data, "could't find my own IP address (%s)", myhost); + return CURLE_FTP_PORT_FAILED; + } + { +#ifdef HAVE_INET_NTOA_R + char ntoa_buf[64]; +#endif + struct in_addr in; + unsigned short ip[5]; + (void) memcpy(&in.s_addr, *h->h_addr_list, sizeof (in.s_addr)); +#ifdef HAVE_INET_NTOA_R + /* ignore the return code from inet_ntoa_r() as it is int or + char * depending on system */ + inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)); + sscanf( ntoa_buf, "%hu.%hu.%hu.%hu", + &ip[0], &ip[1], &ip[2], &ip[3]); +#else + sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu", + &ip[0], &ip[1], &ip[2], &ip[3]); +#endif + result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d", + ip[0], ip[1], ip[2], ip[3], + porttouse >> 8, + porttouse & 255); + if(result) + return result; + } + + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if(ftpcode != 200) { + failf(data, "Server does not grok PORT, try without it!"); + return CURLE_FTP_PORT_FAILED; + } +#endif /* end of ipv4-specific code */ + + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_use_pasv() + * + * Send the PASV command. PASV is the ftp client's way of asking the server to + * open a second port that we can connect to (for the data transfer). This is + * the opposite of PORT. + */ + +static +CURLcode ftp_use_pasv(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + ssize_t nread; + char *buf = data->state.buffer; /* this is our buffer */ + int ftpcode; /* receive FTP response codes in this */ + CURLcode result; + Curl_addrinfo *addr=NULL; + Curl_ipconnect *conninfo; + + /* + Here's the excecutive summary on what to do: + + PASV is RFC959, expect: + 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2) + + LPSV is RFC1639, expect: + 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2) + + EPSV is RFC2428, expect: + 229 Entering Extended Passive Mode (|||port|) + + */ + +#if 1 + const char *mode[] = { "EPSV", "PASV", NULL }; + int results[] = { 229, 227, 0 }; +#else +#if 0 + char *mode[] = { "EPSV", "LPSV", "PASV", NULL }; + int results[] = { 229, 228, 227, 0 }; +#else + const char *mode[] = { "PASV", NULL }; + int results[] = { 227, 0 }; +#endif +#endif + int modeoff; + unsigned short connectport; /* the local port connect() should use! */ + unsigned short newport; /* remote port, not necessary the local one */ + char *hostdataptr=NULL; + + /* newhost must be able to hold a full IP-style address in ASCII, which + in the IPv6 case means 5*8-1 = 39 letters */ + char newhost[48]; + char *newhostp=NULL; + + for (modeoff = (data->set.ftp_use_epsv?0:1); + mode[modeoff]; modeoff++) { + result = Curl_ftpsendf(conn, mode[modeoff]); + if(result) + return result; + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + if (ftpcode == results[modeoff]) + break; + } + + if (!mode[modeoff]) { + failf(data, "Odd return code after PASV"); + return CURLE_FTP_WEIRD_PASV_REPLY; + } + else if (227 == results[modeoff]) { + int ip[4]; + int port[2]; + char *str=buf; + + /* + * New 227-parser June 3rd 1999. + * It now scans for a sequence of six comma-separated numbers and + * will take them as IP+port indicators. + * + * Found reply-strings include: + * "227 Entering Passive Mode (127,0,0,1,4,51)" + * "227 Data transfer will passively listen to 127,0,0,1,4,51" + * "227 Entering passive mode. 127,0,0,1,4,51" + */ + + while(*str) { + if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d", + &ip[0], &ip[1], &ip[2], &ip[3], + &port[0], &port[1])) + break; + str++; + } + + if(!*str) { + failf(data, "Couldn't interpret this 227-reply: %s", buf); + return CURLE_FTP_WEIRD_227_FORMAT; + } + + sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + newhostp = newhost; + newport = (port[0]<<8) + port[1]; + } +#if 1 + else if (229 == results[modeoff]) { + char *ptr = strchr(buf, '('); + if(ptr) { + unsigned int num; + char separator[4]; + ptr++; + if(5 == sscanf(ptr, "%c%c%c%u%c", + &separator[0], + &separator[1], + &separator[2], + &num, + &separator[3])) { + /* the four separators should be identical */ + newport = num; + + /* we should use the same host we already are connected to */ + newhostp = conn->name; + } + else + ptr=NULL; + } + if(!ptr) { + failf(data, "Weirdly formatted EPSV reply"); + return CURLE_FTP_WEIRD_PASV_REPLY; + } + } +#endif + else + return CURLE_FTP_CANT_RECONNECT; + + if(data->change.proxy) { + /* + * This is a tunnel through a http proxy and we need to connect to the + * proxy again here. We already have the name info for it since the + * previous lookup. + */ + addr = conn->hostaddr; + connectport = + (unsigned short)conn->port; /* we connect to the proxy's port */ + } + else { + /* normal, direct, ftp connection */ + addr = Curl_resolv(data, newhostp, newport, &hostdataptr); + if(!addr) { + failf(data, "Can't resolve new host %s", newhost); + return CURLE_FTP_CANT_GET_HOST; + } + connectport = newport; /* we connect to the remote port */ + } + + result = Curl_connecthost(conn, + addr, + connectport, + &conn->secondarysocket, + &conninfo); + + if((CURLE_OK == result) && + data->set.verbose) + /* this just dumps information about this second connection */ + ftp_pasv_verbose(conn, conninfo, newhost, connectport); + + if(CURLE_OK != result) + return result; + + if (data->set.tunnel_thru_httpproxy) { + /* We want "seamless" FTP operations through HTTP proxy tunnel */ + result = Curl_ConnectHTTPProxyTunnel(conn, conn->secondarysocket, + newhost, newport); + if(CURLE_OK != result) + return result; + } + + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_perform() + * + * This is the actual DO function for FTP. Get a file/directory according to + * the options previously setup. + */ + +static +CURLcode ftp_perform(struct connectdata *conn) +{ + /* this is FTP and no proxy */ + ssize_t nread; + CURLcode result; + struct SessionHandle *data=conn->data; + char *buf = data->state.buffer; /* this is our buffer */ + + /* the ftp struct is already inited in ftp_connect() */ + struct FTP *ftp = conn->proto.ftp; + + long *bytecountp = ftp->bytecountp; + int ftpcode; /* for ftp status */ + + /* Send any QUOTE strings? */ + if(data->set.quote) { + if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) + return result; + } + + /* This is a re-used connection. Since we change directory to where the + transfer is taking place, we must now get back to the original dir + where we ended up after login: */ + if (conn->bits.reuse) { + if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) + return result; + } + + /* change directory first! */ + if(ftp->dir && ftp->dir[0]) { + if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK) + return result; + } + + /* Requested time of file? */ + if(data->set.get_filetime && ftp->file) { + result = ftp_getfiletime(conn, ftp->file); + if(result) + return result; + } + + /* If we have selected NOBODY and HEADER, it means that we only want file + information. Which in FTP can't be much more than the file size and + date. */ + if(data->set.no_body && data->set.include_header) { + /* The SIZE command is _not_ RFC 959 specified, and therefor many servers + may not support it! It is however the only way we have to get a file's + size! */ + ssize_t filesize; + + /* Some servers return different sizes for different modes, and thus we + must set the proper type before we check the size */ + result = ftp_transfertype(conn, data->set.ftp_ascii); + if(result) + return result; + + /* failing to get size is not a serious error */ + result = ftp_getsize(conn, ftp->file, &filesize); + + if(CURLE_OK == result) { + sprintf(buf, "Content-Length: %d\r\n", filesize); + result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + if(result) + return result; + } + + /* If we asked for a time of the file and we actually got one as + well, we "emulate" a HTTP-style header in our output. */ + +#ifdef HAVE_STRFTIME + if(data->set.get_filetime && data->info.filetime) { + struct tm *tm; +#ifdef HAVE_LOCALTIME_R + struct tm buffer; + tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); +#else + tm = localtime((unsigned long *)&data->info.filetime); +#endif + /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ + strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", + tm); + result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + if(result) + return result; + } +#endif + + return CURLE_OK; + } + + if(data->set.no_body) + /* don't transfer the data */ + ; + /* Get us a second connection up and connected */ + else if(data->set.ftp_use_port) { + /* We have chosen to use the PORT command */ + result = ftp_use_port(conn); + if(CURLE_OK == result) + /* we have the data connection ready */ + infof(data, "Connected the data stream with PORT!\n"); + } + else { + /* We have chosen (this is default) to use the PASV command */ + result = ftp_use_pasv(conn); + if(CURLE_OK == result) + infof(data, "Connected the data stream with PASV!\n"); + } + + if(result) + return result; + + if(data->set.upload) { + + /* Set type to binary (unless specified ASCII) */ + result = ftp_transfertype(conn, data->set.ftp_ascii); + if(result) + return result; + + /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/ + if(data->set.prequote) { + if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK) + return result; + } + + if(conn->resume_from) { + /* we're about to continue the uploading of a file */ + /* 1. get already existing file's size. We use the SIZE + command for this which may not exist in the server! + The SIZE command is not in RFC959. */ + + /* 2. This used to set REST. But since we can do append, we + don't another ftp command. We just skip the source file + offset and then we APPEND the rest on the file instead */ + + /* 3. pass file-size number of bytes in the source file */ + /* 4. lower the infilesize counter */ + /* => transfer as usual */ + + if(conn->resume_from < 0 ) { + /* we could've got a specified offset from the command line, + but now we know we didn't */ + ssize_t gottensize; + + if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) { + failf(data, "Couldn't get remote file size"); + return CURLE_FTP_COULDNT_GET_SIZE; + } + conn->resume_from = gottensize; + } + + if(conn->resume_from) { + /* do we still game? */ + int passed=0; + /* enable append instead */ + data->set.ftp_append = 1; + + /* Now, let's read off the proper amount of bytes from the + input. If we knew it was a proper file we could've just + fseek()ed but we only have a stream here */ + do { + int readthisamountnow = (conn->resume_from - passed); + int actuallyread; + + if(readthisamountnow > BUFSIZE) + readthisamountnow = BUFSIZE; + + actuallyread = + data->set.fread(data->state.buffer, 1, readthisamountnow, + data->set.in); + + passed += actuallyread; + if(actuallyread != readthisamountnow) { + failf(data, "Could only read %d bytes from the input", passed); + return CURLE_FTP_COULDNT_USE_REST; + } + } + while(passed != conn->resume_from); + + /* now, decrease the size of the read */ + if(data->set.infilesize>0) { + data->set.infilesize -= conn->resume_from; + + if(data->set.infilesize <= 0) { + infof(data, "File already completely uploaded\n"); + + /* no data to transfer */ + result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + + /* Set resume done so that we won't get any error in + * Curl_ftp_done() because we didn't transfer the amount of bytes + * that the local file file obviously is */ + conn->bits.resume_done = TRUE; + + return CURLE_OK; + } + } + /* we've passed, proceed as normal */ + } + } + + /* Send everything on data->set.in to the socket */ + if(data->set.ftp_append) { + /* we append onto the file instead of rewriting it */ + FTPSENDF(conn, "APPE %s", ftp->file); + } + else { + FTPSENDF(conn, "STOR %s", ftp->file); + } + + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if(ftpcode>=400) { + failf(data, "Failed FTP upload:%s", buf+3); + /* oops, we never close the sockets! */ + return CURLE_FTP_COULDNT_STOR_FILE; + } + + if(data->set.ftp_use_port) { + /* PORT means we are now awaiting the server to connect to us. */ + result = AllowServerConnect(data, conn, conn->secondarysocket); + if( result ) + return result; + } + + *bytecountp=0; + + /* When we know we're uploading a specified file, we can get the file + size prior to the actual upload. */ + + Curl_pgrsSetUploadSize(data, data->set.infilesize); + + result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */ + conn->secondarysocket, bytecountp); + if(result) + return result; + + } + else if(!data->set.no_body) { + /* Retrieve file or directory */ + bool dirlist=FALSE; + long downloadsize=-1; + + if(conn->bits.use_range && conn->range) { + long from, to; + int totalsize=-1; + char *ptr; + char *ptr2; + + from=strtol(conn->range, &ptr, 0); + while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-'))) + ptr++; + to=strtol(ptr, &ptr2, 0); + if(ptr == ptr2) { + /* we didn't get any digit */ + to=-1; + } + if((-1 == to) && (from>=0)) { + /* X - */ + conn->resume_from = from; + infof(data, "FTP RANGE %d to end of file\n", from); + } + else if(from < 0) { + /* -Y */ + totalsize = -from; + conn->maxdownload = -from; + conn->resume_from = from; + infof(data, "FTP RANGE the last %d bytes\n", totalsize); + } + else { + /* X-Y */ + totalsize = to-from; + conn->maxdownload = totalsize+1; /* include the last mentioned byte */ + conn->resume_from = from; + infof(data, "FTP RANGE from %d getting %d bytes\n", from, + conn->maxdownload); + } + infof(data, "range-download from %d to %d, totally %d bytes\n", + from, to, totalsize); + } + + if((data->set.ftp_list_only) || !ftp->file) { + /* The specified path ends with a slash, and therefore we think this + is a directory that is requested, use LIST. But before that we + need to set ASCII transfer mode. */ + dirlist = TRUE; + + /* Set type to ASCII */ + result = ftp_transfertype(conn, TRUE /* ASCII enforced */); + if(result) + return result; + + /* if this output is to be machine-parsed, the NLST command will be + better used since the LIST command output is not specified or + standard in any way */ + + FTPSENDF(conn, "%s", + data->set.customrequest?data->set.customrequest: + (data->set.ftp_list_only?"NLST":"LIST")); + } + else { + ssize_t foundsize; + + /* Set type to binary (unless specified ASCII) */ + result = ftp_transfertype(conn, data->set.ftp_ascii); + if(result) + return result; + + /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/ + if(data->set.prequote) { + if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK) + return result; + } + + /* Attempt to get the size, it'll be useful in some cases: for resumed + downloads and when talking to servers that don't give away the size + in the RETR response line. */ + result = ftp_getsize(conn, ftp->file, &foundsize); + if(CURLE_OK == result) + downloadsize = foundsize; + + if(conn->resume_from) { + + /* Daniel: (August 4, 1999) + * + * We start with trying to use the SIZE command to figure out the size + * of the file we're gonna get. If we can get the size, this is by far + * the best way to know if we're trying to resume beyond the EOF. + * + * Daniel, November 28, 2001. We *always* get the size on downloads + * now, so it is done before this even when not doing resumes. I saved + * the comment above for nostalgical reasons! ;-) + */ + if(CURLE_OK != result) { + infof(data, "ftp server doesn't support SIZE\n"); + /* We couldn't get the size and therefore we can't know if there + really is a part of the file left to get, although the server + will just close the connection when we start the connection so it + won't cause us any harm, just not make us exit as nicely. */ + } + else { + /* We got a file size report, so we check that there actually is a + part of the file left to get, or else we go home. */ + if(conn->resume_from< 0) { + /* We're supposed to download the last abs(from) bytes */ + if(foundsize < -conn->resume_from) { + failf(data, "Offset (%d) was beyond file size (%d)", + conn->resume_from, foundsize); + return CURLE_FTP_BAD_DOWNLOAD_RESUME; + } + /* convert to size to download */ + downloadsize = -conn->resume_from; + /* download from where? */ + conn->resume_from = foundsize - downloadsize; + } + else { + if(foundsize < conn->resume_from) { + failf(data, "Offset (%d) was beyond file size (%d)", + conn->resume_from, foundsize); + return CURLE_FTP_BAD_DOWNLOAD_RESUME; + } + /* Now store the number of bytes we are expected to download */ + downloadsize = foundsize-conn->resume_from; + } + } + + if (downloadsize == 0) { + /* no data to transfer */ + result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + infof(data, "File already completely downloaded\n"); + + /* Set resume done so that we won't get any error in Curl_ftp_done() + * because we didn't transfer the amount of bytes that the remote + * file obviously is */ + conn->bits.resume_done = TRUE; + + return CURLE_OK; + } + + /* Set resume file transfer offset */ + infof(data, "Instructs server to resume from offset %d\n", + conn->resume_from); + + FTPSENDF(conn, "REST %d", conn->resume_from); + + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if(ftpcode != 350) { + failf(data, "Couldn't use REST: %s", buf+4); + return CURLE_FTP_COULDNT_USE_REST; + } + } + + FTPSENDF(conn, "RETR %s", ftp->file); + } + + nread = Curl_GetFTPResponse(buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if((ftpcode == 150) || (ftpcode == 125)) { + + /* + A; + 150 Opening BINARY mode data connection for /etc/passwd (2241 + bytes). (ok, the file is being transfered) + + B: + 150 Opening ASCII mode data connection for /bin/ls + + C: + 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes). + + D: + 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes). + + E: + 125 Data connection already open; Transfer starting. */ + + int size=-1; /* default unknown size */ + + if(!dirlist && + !data->set.ftp_ascii && + (-1 == downloadsize)) { + /* + * It seems directory listings either don't show the size or very + * often uses size 0 anyway. ASCII transfers may very well turn out + * that the transfered amount of data is not the same as this line + * tells, why using this number in those cases only confuses us. + * + * Example D above makes this parsing a little tricky */ + char *bytes; + bytes=strstr(buf, " bytes"); + if(bytes--) { + int index=bytes-buf; + /* this is a hint there is size information in there! ;-) */ + while(--index) { + /* scan for the parenthesis and break there */ + if('(' == *bytes) + break; + /* if only skip digits, or else we're in deep trouble */ + if(!isdigit((int)*bytes)) { + bytes=NULL; + break; + } + /* one more estep backwards */ + bytes--; + } + /* only if we have nothing but digits: */ + if(bytes++) { + /* get the number! */ + size = atoi(bytes); + } + + } + } + else if(downloadsize > -1) + size = downloadsize; + + if(data->set.ftp_use_port) { + result = AllowServerConnect(data, conn, conn->secondarysocket); + if( result ) + return result; + } + + infof(data, "Getting file with size: %d\n", size); + + /* FTP download: */ + result=Curl_Transfer(conn, conn->secondarysocket, size, FALSE, + bytecountp, + -1, NULL); /* no upload here */ + if(result) + return result; + } + else { + failf(data, "%s", buf+4); + return CURLE_FTP_COULDNT_RETR_FILE; + } + + } + /* end of transfer */ + + return CURLE_OK; +} + +/*********************************************************************** + * + * Curl_ftp() + * + * This function is registered as 'curl_do' function. It decodes the path + * parts etc as a wrapper to the actual DO function (ftp_perform). + * + * The input argument is already checked for validity. + */ +CURLcode Curl_ftp(struct connectdata *conn) +{ + CURLcode retcode; + + struct SessionHandle *data = conn->data; + struct FTP *ftp; + int dirlength=0; /* 0 forces strlen() */ + + /* the ftp struct is already inited in ftp_connect() */ + ftp = conn->proto.ftp; + + /* We split the path into dir and file parts *before* we URLdecode + it */ + ftp->file = strrchr(conn->ppath, '/'); + if(ftp->file) { + if(ftp->file != conn->ppath) + dirlength=ftp->file-conn->ppath; /* don't count the traling slash */ + + ftp->file++; /* point to the first letter in the file name part or + remain NULL */ + } + else { + ftp->file = conn->ppath; /* there's only a file part */ + } + + if(*ftp->file) { + ftp->file = curl_unescape(ftp->file, 0); + if(NULL == ftp->file) { + failf(data, "no memory"); + return CURLE_OUT_OF_MEMORY; + } + } + else + ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL + pointer */ + + ftp->urlpath = conn->ppath; + if(dirlength) { + ftp->dir = curl_unescape(ftp->urlpath, dirlength); + if(NULL == ftp->dir) { + if(ftp->file) + free(ftp->file); + failf(data, "no memory"); + return CURLE_OUT_OF_MEMORY; /* failure */ + } + } + else + ftp->dir = NULL; + + retcode = ftp_perform(conn); + + /* clean up here, success or error doesn't matter */ + if(ftp->file) + free(ftp->file); + if(ftp->dir) + free(ftp->dir); + + ftp->file = ftp->dir = NULL; /* zero */ + + return retcode; +} + +/*********************************************************************** + * + * Curl_ftpsendf() + * + * Sends the formated string as a ftp command to a ftp server + * + * NOTE: we build the command in a fixed-length buffer, which sets length + * restrictions on the command! + */ +CURLcode Curl_ftpsendf(struct connectdata *conn, + const char *fmt, ...) +{ + ssize_t bytes_written; + char s[256]; + ssize_t write_len; + char *sptr=s; + CURLcode res = CURLE_OK; + + va_list ap; + va_start(ap, fmt); + vsnprintf(s, 250, fmt, ap); + va_end(ap); + + if(conn->data->set.verbose) + fprintf(conn->data->set.err, "> %s\n", s); + + strcat(s, "\r\n"); /* append a trailing CRLF */ + + bytes_written=0; + write_len = strlen(s); + + do { + res = Curl_write(conn, conn->firstsocket, sptr, write_len, + &bytes_written); + + if(CURLE_OK != res) + break; + + if(bytes_written != write_len) { + write_len -= bytes_written; + sptr += bytes_written; + } + else + break; + } while(1); + + return res; +} + +/*********************************************************************** + * + * Curl_ftp_disconnect() + * + * Disconnect from an FTP server. Cleanup protocol-specific per-connection + * resources + */ +CURLcode Curl_ftp_disconnect(struct connectdata *conn) +{ + struct FTP *ftp= conn->proto.ftp; + + /* The FTP session may or may not have been allocated/setup at this point! */ + if(ftp) { + if(ftp->entrypath) + free(ftp->entrypath); + if(ftp->cache) + free(ftp->cache); + } + return CURLE_OK; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/ftp.h b/Source/CTest/Curl/ftp.h new file mode 100644 index 0000000..a88cc72 --- /dev/null +++ b/Source/CTest/Curl/ftp.h @@ -0,0 +1,37 @@ +#ifndef __FTP_H +#define __FTP_H + +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +CURLcode Curl_ftp(struct connectdata *conn); +CURLcode Curl_ftp_done(struct connectdata *conn); +CURLcode Curl_ftp_connect(struct connectdata *conn); +CURLcode Curl_ftp_disconnect(struct connectdata *conn); + +CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...); + +/* The kerberos stuff needs this: */ +int Curl_GetFTPResponse(char *buf, struct connectdata *conn, + int *ftpcode); + +#endif diff --git a/Source/CTest/Curl/getdate.c b/Source/CTest/Curl/getdate.c new file mode 100644 index 0000000..dab41be --- /dev/null +++ b/Source/CTest/Curl/getdate.c @@ -0,0 +1,2149 @@ + +/* A Bison parser, made from getdate.y + by GNU Bison version 1.28 */ + +#define YYBISON 1 /* Identify Bison output. */ + +#define tAGO 257 +#define tDAY 258 +#define tDAY_UNIT 259 +#define tDAYZONE 260 +#define tDST 261 +#define tHOUR_UNIT 262 +#define tID 263 +#define tMERIDIAN 264 +#define tMINUTE_UNIT 265 +#define tMONTH 266 +#define tMONTH_UNIT 267 +#define tSEC_UNIT 268 +#define tSNUMBER 269 +#define tUNUMBER 270 +#define tYEAR_UNIT 271 +#define tZONE 272 + +#line 1 "getdate.y" + +/* +** Originally written by Steven M. Bellovin <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" + +# ifdef HAVE_ALLOCA_H +# include <alloca.h> +# endif + +# ifdef HAVE_TIME_H +# include <time.h> +# endif + +#ifndef YYDEBUG + /* to satisfy gcc -Wundef, we set this to 0 */ +#define YYDEBUG 0 +#endif + +/* Since the code of getdate.y is not included in the Emacs executable + itself, there is no need to #define static in this file. Even if + the code were included in the Emacs executable, it probably + wouldn't do any harm to #undef it here; this will only cause + problems if we try to write to a static variable, which I don't + think this code needs to do. */ +#ifdef emacs +# undef static +#endif + +#ifdef __APPLE__ +#include <sys/types.h> +#include <sys/malloc.h> +#else + +#endif +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#if HAVE_STDLIB_H +# include <stdlib.h> /* for `free'; used by Bison 1.27 */ +#else + +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif + +#endif + +#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) +# define IN_CTYPE_DOMAIN(c) 1 +#else +# define IN_CTYPE_DOMAIN(c) isascii(c) +#endif + +#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) +#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) +#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) +#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) + +/* ISDIGIT differs from ISDIGIT_LOCALE, as follows: + - Its arg may be any int or unsigned int; it need not be an unsigned char. + - It's guaranteed to evaluate its argument exactly once. + - It's typically faster. + Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that + only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless + it's important to use the locale's definition of `digit' even when the + host does not conform to Posix. */ +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) + +#if defined (STDC_HEADERS) || defined (USG) +# include <string.h> +#endif + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __attribute__(x) +#endif + +#ifndef ATTRIBUTE_UNUSED +# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +# define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), + as well as gratuitiously global symbol names, so we can have multiple + yacc generated parsers in the same program. Note that these are only + the variables produced by yacc. If other parser generators (bison, + byacc, etc) produce additional global names that conflict at link time, + then those parser generators need to be fixed instead of adding those + names to this list. */ + +#define yymaxdepth Curl_gd_maxdepth +#define yyparse Curl_gd_parse +#define yylex Curl_gd_lex +#define yyerror Curl_gd_error +#define yylval Curl_gd_lval +#define yychar Curl_gd_char +#define yydebug Curl_gd_debug +#define yypact Curl_gd_pact +#define yyr1 Curl_gd_r1 +#define yyr2 Curl_gd_r2 +#define yydef Curl_gd_def +#define yychk Curl_gd_chk +#define yypgo Curl_gd_pgo +#define yyact Curl_gd_act +#define yyexca Curl_gd_exca +#define yyerrflag Curl_gd_errflag +#define yynerrs Curl_gd_nerrs +#define yyps Curl_gd_ps +#define yypv Curl_gd_pv +#define yys Curl_gd_s +#define yy_yys Curl_gd_yys +#define yystate Curl_gd_state +#define yytmp Curl_gd_tmp +#define yyv Curl_gd_v +#define yy_yyv Curl_gd_yyv +#define yyval Curl_gd_val +#define yylloc Curl_gd_lloc +#define yyreds Curl_gd_reds /* With YYDEBUG defined */ +#define yytoks Curl_gd_toks /* With YYDEBUG defined */ +#define yylhs Curl_gd_yylhs +#define yylen Curl_gd_yylen +#define yydefred Curl_gd_yydefred +#define yydgoto Curl_gd_yydgoto +#define yysindex Curl_gd_yysindex +#define yyrindex Curl_gd_yyrindex +#define yygindex Curl_gd_yygindex +#define yytable Curl_gd_yytable +#define yycheck Curl_gd_yycheck + +static int yylex (); +static int yyerror (); + +#define EPOCH 1970 +#define HOUR(x) ((x) * 60) + +#define MAX_BUFF_LEN 128 /* size of buffer to read the date into */ + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + const char *name; + int type; + int value; +} TABLE; + + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + +/* parse results and input string */ +typedef struct _CONTEXT { + const char *yyInput; + int yyDayOrdinal; + int yyDayNumber; + int yyHaveDate; + int yyHaveDay; + int yyHaveRel; + int yyHaveTime; + int yyHaveZone; + int yyTimezone; + int yyDay; + int yyHour; + int yyMinutes; + int yyMonth; + int yySeconds; + int yyYear; + MERIDIAN yyMeridian; + int yyRelDay; + int yyRelHour; + int yyRelMinutes; + int yyRelMonth; + int yyRelSeconds; + int yyRelYear; +} CONTEXT; + +/* enable use of extra argument to yyparse and yylex which can be used to pass +** in a user defined value (CONTEXT struct in our case) +*/ +#define YYPARSE_PARAM cookie +#define YYLEX_PARAM cookie +#define context ((CONTEXT *) cookie) + +#line 215 "getdate.y" +typedef union { + int Number; + enum _MERIDIAN Meridian; +} YYSTYPE; +#include <stdio.h> + +#ifndef __cplusplus +#ifndef __STDC__ +#define const +#endif +#endif + + + +#define YYFINAL 61 +#define YYFLAG -32768 +#define YYNTBASE 22 + +#define YYTRANSLATE(x) ((unsigned)(x) <= 272 ? yytranslate[x] : 32) + +static const char yytranslate[] = { 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 20, 2, 2, 21, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 19, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18 +}; + +#if YYDEBUG != 0 +static const short yyprhs[] = { 0, + 0, 1, 4, 6, 8, 10, 12, 14, 16, 19, + 24, 29, 36, 43, 45, 47, 50, 52, 55, 58, + 62, 68, 72, 76, 79, 84, 87, 91, 94, 96, + 99, 102, 104, 107, 110, 112, 115, 118, 120, 123, + 126, 128, 131, 134, 136, 139, 142, 144, 146, 147 +}; + +static const short yyrhs[] = { -1, + 22, 23, 0, 24, 0, 25, 0, 27, 0, 26, + 0, 28, 0, 30, 0, 16, 10, 0, 16, 19, + 16, 31, 0, 16, 19, 16, 15, 0, 16, 19, + 16, 19, 16, 31, 0, 16, 19, 16, 19, 16, + 15, 0, 18, 0, 6, 0, 18, 7, 0, 4, + 0, 4, 20, 0, 16, 4, 0, 16, 21, 16, + 0, 16, 21, 16, 21, 16, 0, 16, 15, 15, + 0, 16, 12, 15, 0, 12, 16, 0, 12, 16, + 20, 16, 0, 16, 12, 0, 16, 12, 16, 0, + 29, 3, 0, 29, 0, 16, 17, 0, 15, 17, + 0, 17, 0, 16, 13, 0, 15, 13, 0, 13, + 0, 16, 5, 0, 15, 5, 0, 5, 0, 16, + 8, 0, 15, 8, 0, 8, 0, 16, 11, 0, + 15, 11, 0, 11, 0, 16, 14, 0, 15, 14, + 0, 14, 0, 16, 0, 0, 10, 0 +}; + +#endif + +#if YYDEBUG != 0 +static const short yyrline[] = { 0, + 231, 232, 235, 238, 241, 244, 247, 250, 253, 259, + 265, 274, 280, 292, 295, 298, 304, 308, 312, 318, + 322, 340, 346, 352, 356, 361, 365, 372, 380, 383, + 386, 389, 392, 395, 398, 401, 404, 407, 410, 413, + 416, 419, 422, 425, 428, 431, 434, 439, 473, 477 +}; +#endif + + +#if YYDEBUG != 0 || defined (YYERROR_VERBOSE) + +static const char * const yytname[] = { "$","error","$undefined.","tAGO","tDAY", +"tDAY_UNIT","tDAYZONE","tDST","tHOUR_UNIT","tID","tMERIDIAN","tMINUTE_UNIT", +"tMONTH","tMONTH_UNIT","tSEC_UNIT","tSNUMBER","tUNUMBER","tYEAR_UNIT","tZONE", +"':'","','","'/'","spec","item","time","zone","day","date","rel","relunit","number", +"o_merid", NULL +}; +#endif + +static const short yyr1[] = { 0, + 22, 22, 23, 23, 23, 23, 23, 23, 24, 24, + 24, 24, 24, 25, 25, 25, 26, 26, 26, 27, + 27, 27, 27, 27, 27, 27, 27, 28, 28, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 30, 31, 31 +}; + +static const short yyr2[] = { 0, + 0, 2, 1, 1, 1, 1, 1, 1, 2, 4, + 4, 6, 6, 1, 1, 2, 1, 2, 2, 3, + 5, 3, 3, 2, 4, 2, 3, 2, 1, 2, + 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, + 1, 2, 2, 1, 2, 2, 1, 1, 0, 1 +}; + +static const short yydefact[] = { 1, + 0, 17, 38, 15, 41, 44, 0, 35, 47, 0, + 48, 32, 14, 2, 3, 4, 6, 5, 7, 29, + 8, 18, 24, 37, 40, 43, 34, 46, 31, 19, + 36, 39, 9, 42, 26, 33, 45, 0, 30, 0, + 0, 16, 28, 0, 23, 27, 22, 49, 20, 25, + 50, 11, 0, 10, 0, 49, 21, 13, 12, 0, + 0 +}; + +static const short yydefgoto[] = { 1, + 14, 15, 16, 17, 18, 19, 20, 21, 54 +}; + +static const short yypact[] = {-32768, + 0, -19,-32768,-32768,-32768,-32768, -13,-32768,-32768, 30, + 15,-32768, 14,-32768,-32768,-32768,-32768,-32768,-32768, 19, +-32768,-32768, 4,-32768,-32768,-32768,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768, -6,-32768,-32768, 16,-32768, 17, + 23,-32768,-32768, 24,-32768,-32768,-32768, 27, 28,-32768, +-32768,-32768, 29,-32768, 32, -8,-32768,-32768,-32768, 50, +-32768 +}; + +static const short yypgoto[] = {-32768, +-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, -5 +}; + + +#define YYLAST 51 + + +static const short yytable[] = { 60, + 22, 51, 23, 2, 3, 4, 58, 5, 45, 46, + 6, 7, 8, 9, 10, 11, 12, 13, 30, 31, + 42, 43, 32, 44, 33, 34, 35, 36, 37, 38, + 47, 39, 48, 40, 24, 41, 51, 25, 49, 50, + 26, 52, 27, 28, 56, 53, 29, 57, 55, 61, + 59 +}; + +static const short yycheck[] = { 0, + 20, 10, 16, 4, 5, 6, 15, 8, 15, 16, + 11, 12, 13, 14, 15, 16, 17, 18, 4, 5, + 7, 3, 8, 20, 10, 11, 12, 13, 14, 15, + 15, 17, 16, 19, 5, 21, 10, 8, 16, 16, + 11, 15, 13, 14, 16, 19, 17, 16, 21, 0, + 56 +}; +#define YYPURE 1 + +/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ +#line 3 "/usr/local/share/bison.simple" +/* This file comes from bison-1.28. */ + +/* Skeleton output parser for bison, + Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* This is the parser code that is written into each bison parser + when the %semantic_parser declaration is not specified in the grammar. + It was written by Richard Stallman by simplifying the hairy parser + used when %semantic_parser is specified. */ + +#ifndef YYSTACK_USE_ALLOCA +#ifdef alloca +#define YYSTACK_USE_ALLOCA +#else /* alloca not defined */ +#ifdef __GNUC__ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#else /* not GNU C. */ +#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386)) +#define YYSTACK_USE_ALLOCA +#include <alloca.h> +#else /* not sparc */ +/* We think this test detects Watcom and Microsoft C. */ +/* This used to test MSDOS, but that is a bad idea + since that symbol is in the user namespace. */ +#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__) +#if 0 /* No need for malloc.h, which pollutes the namespace; + instead, just don't use alloca. */ +#include <malloc.h> +#endif +#else /* not MSDOS, or __TURBOC__ */ +#if defined(_AIX) +/* I don't know what this was needed for, but it pollutes the namespace. + So I turned it off. rms, 2 May 1997. */ +/* #include <malloc.h> */ + #pragma alloca +#define YYSTACK_USE_ALLOCA +#else /* not MSDOS, or __TURBOC__, or _AIX */ +#if 0 +#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up, + and on HPUX 10. Eventually we can turn this on. */ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#endif /* __hpux */ +#endif +#endif /* not _AIX */ +#endif /* not MSDOS, or __TURBOC__ */ +#endif /* not sparc */ +#endif /* not GNU C */ +#endif /* alloca not defined */ +#endif /* YYSTACK_USE_ALLOCA not defined */ + +#ifdef YYSTACK_USE_ALLOCA +#define YYSTACK_ALLOC alloca +#else +#define YYSTACK_ALLOC malloc +#endif + +/* Note: there must be only one dollar sign in this file. + It is replaced by the list of actions, each action + as one case of the switch. */ + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY -2 +#define YYEOF 0 +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrlab1 +/* Like YYERROR except do call yyerror. + This remains here temporarily to ease the + transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ +#define YYFAIL goto yyerrlab +#define YYRECOVERING() (!!yyerrstatus) +#define YYBACKUP(token, value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { yychar = (token), yylval = (value); \ + yychar1 = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { yyerror ("syntax error: cannot back up"); YYERROR; } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +#ifndef YYPURE +#define YYLEX yylex() +#endif + +#ifdef YYPURE +#ifdef YYLSP_NEEDED +#ifdef YYLEX_PARAM +#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM) +#else +#define YYLEX yylex(&yylval, &yylloc) +#endif +#else /* not YYLSP_NEEDED */ +#ifdef YYLEX_PARAM +#define YYLEX yylex(&yylval, YYLEX_PARAM) +#else +#define YYLEX yylex(&yylval) +#endif +#endif /* not YYLSP_NEEDED */ +#endif + +/* If nonreentrant, generate the variables here */ + +#ifndef YYPURE + +int yychar; /* the lookahead symbol */ +YYSTYPE yylval; /* the semantic value of the */ + /* lookahead symbol */ + +#ifdef YYLSP_NEEDED +YYLTYPE yylloc; /* location data for the lookahead */ + /* symbol */ +#endif + +int yynerrs; /* number of parse errors so far */ +#endif /* not YYPURE */ + +#if YYDEBUG != 0 +int yydebug; /* nonzero means print parse trace */ +/* Since this is uninitialized, it does not stop multiple parsers + from coexisting. */ +#endif + +/* YYINITDEPTH indicates the initial size of the parser's stacks */ + +#ifndef YYINITDEPTH +#define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH is the maximum size the stacks can grow to + (effective only if the built-in stack extension method is used). */ + +#if YYMAXDEPTH == 0 +#undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 10000 +#endif + +/* Define __yy_memcpy. Note that the size argument + should be passed with type unsigned int, because that is what the non-GCC + definitions require. With GCC, __builtin_memcpy takes an arg + of type size_t, but it can handle unsigned int. */ + +#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */ +#define __yy_memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT) +#else /* not GNU C or C++ */ +#ifndef __cplusplus + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__yy_memcpy (to, from, count) + char *to; + char *from; + unsigned int count; +{ + register char *f = from; + register char *t = to; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#else /* __cplusplus */ + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__yy_memcpy (char *to, char *from, unsigned int count) +{ + register char *t = to; + register char *f = from; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#endif +#endif + +#line 217 "/usr/local/share/bison.simple" + +/* The user can define YYPARSE_PARAM as the name of an argument to be passed + into yyparse. The argument should have type void *. + It should actually point to an object. + Grammar actions can access the variable by casting it + to the proper pointer type. */ + +#ifdef YYPARSE_PARAM +#ifdef __cplusplus +#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM +#define YYPARSE_PARAM_DECL +#else /* not __cplusplus */ +#define YYPARSE_PARAM_ARG YYPARSE_PARAM +#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM; +#endif /* not __cplusplus */ +#else /* not YYPARSE_PARAM */ +#define YYPARSE_PARAM_ARG +#define YYPARSE_PARAM_DECL +#endif /* not YYPARSE_PARAM */ + +/* Prevent warning if -Wstrict-prototypes. */ +#ifdef __GNUC__ +#ifdef YYPARSE_PARAM +int yyparse (void *); +#else +int yyparse (void); +#endif +#endif + +int +yyparse(YYPARSE_PARAM_ARG) + YYPARSE_PARAM_DECL +{ + register int yystate; + register int yyn; + register short *yyssp; + register YYSTYPE *yyvsp; + int yyerrstatus; /* number of tokens to shift before error messages enabled */ + int yychar1 = 0; /* lookahead token as an internal (translated) token number */ + + short yyssa[YYINITDEPTH]; /* the state stack */ + YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */ + + short *yyss = yyssa; /* refer to the stacks thru separate pointers */ + YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */ + +#ifdef YYLSP_NEEDED + YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */ + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + +#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--) +#else +#define YYPOPSTACK (yyvsp--, yyssp--) +#endif + + int yystacksize = YYINITDEPTH; + int yyfree_stacks = 0; + +#ifdef YYPURE + int yychar; + YYSTYPE yylval; + int yynerrs; +#ifdef YYLSP_NEEDED + YYLTYPE yylloc; +#endif +#endif + + YYSTYPE yyval; /* the variable used to return */ + /* semantic values from the action */ + /* routines */ + + int yylen; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Starting parse\n"); +#endif + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss - 1; + yyvsp = yyvs; +#ifdef YYLSP_NEEDED + yylsp = yyls; +#endif + +/* Push a new state, which is found in yystate . */ +/* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. */ +yynewstate: + + *++yyssp = yystate; + + if (yyssp >= yyss + yystacksize - 1) + { + /* Give user a chance to reallocate the stack */ + /* Use copies of these so that the &'s don't force the real ones into memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; +#ifdef YYLSP_NEEDED + YYLTYPE *yyls1 = yyls; +#endif + + /* Get the current used size of the three stacks, in elements. */ + int size = yyssp - yyss + 1; + +#ifdef yyoverflow + /* Each stack pointer address is followed by the size of + the data in use in that stack, in bytes. */ +#ifdef YYLSP_NEEDED + /* This used to be a conditional around just the two extra args, + but that might be undefined if yyoverflow is a macro. */ + yyoverflow("parser stack overflow", + &yyss1, size * sizeof (*yyssp), + &yyvs1, size * sizeof (*yyvsp), + &yyls1, size * sizeof (*yylsp), + &yystacksize); +#else + yyoverflow("parser stack overflow", + &yyss1, size * sizeof (*yyssp), + &yyvs1, size * sizeof (*yyvsp), + &yystacksize); +#endif + + yyss = yyss1; yyvs = yyvs1; +#ifdef YYLSP_NEEDED + yyls = yyls1; +#endif +#else /* no yyoverflow */ + /* Extend the stack our own way. */ + if (yystacksize >= YYMAXDEPTH) + { + yyerror("parser stack overflow"); + if (yyfree_stacks) + { + free (yyss); + free (yyvs); +#ifdef YYLSP_NEEDED + free (yyls); +#endif + } + return 2; + } + yystacksize *= 2; + if (yystacksize > YYMAXDEPTH) + yystacksize = YYMAXDEPTH; +#ifndef YYSTACK_USE_ALLOCA + yyfree_stacks = 1; +#endif + yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp)); + __yy_memcpy ((char *)yyss, (char *)yyss1, + size * (unsigned int) sizeof (*yyssp)); + yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp)); + __yy_memcpy ((char *)yyvs, (char *)yyvs1, + size * (unsigned int) sizeof (*yyvsp)); +#ifdef YYLSP_NEEDED + yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp)); + __yy_memcpy ((char *)yyls, (char *)yyls1, + size * (unsigned int) sizeof (*yylsp)); +#endif +#endif /* no yyoverflow */ + + yyssp = yyss + size - 1; + yyvsp = yyvs + size - 1; +#ifdef YYLSP_NEEDED + yylsp = yyls + size - 1; +#endif + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Stack size increased to %d\n", yystacksize); +#endif + + if (yyssp >= yyss + yystacksize - 1) + YYABORT; + } + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Entering state %d\n", yystate); +#endif + + goto yybackup; + yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* yychar is either YYEMPTY or YYEOF + or a valid token in external form. */ + + if (yychar == YYEMPTY) + { +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Reading a token: "); +#endif + yychar = YYLEX; + } + + /* Convert token to internal form (in yychar1) for indexing tables with */ + + if (yychar <= 0) /* This means end of input. */ + { + yychar1 = 0; + yychar = YYEOF; /* Don't call YYLEX any more */ + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Now at end of input.\n"); +#endif + } + else + { + yychar1 = YYTRANSLATE(yychar); + +#if YYDEBUG != 0 + if (yydebug) + { + fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]); + /* Give the individual parser a way to print the precise meaning + of a token, for further debugging info. */ +#ifdef YYPRINT + YYPRINT (stderr, yychar, yylval); +#endif + fprintf (stderr, ")\n"); + } +#endif + } + + yyn += yychar1; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1) + goto yydefault; + + yyn = yytable[yyn]; + + /* yyn is what to do for this token type in this state. + Negative => reduce, -yyn is rule number. + Positive => shift, yyn is new state. + New state is final state => don't bother to shift, + just return success. + 0, or most negative number => error. */ + + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrlab; + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]); +#endif + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; +#ifdef YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + /* count tokens shifted since error; after three, turn off error status. */ + if (yyerrstatus) yyerrstatus--; + + yystate = yyn; + goto yynewstate; + +/* Do the default action for the current state. */ +yydefault: + + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + +/* Do a reduction. yyn is the number of a rule to reduce with. */ +yyreduce: + yylen = yyr2[yyn]; + if (yylen > 0) + yyval = yyvsp[1-yylen]; /* implement default value of the action */ + +#if YYDEBUG != 0 + if (yydebug) + { + int i; + + fprintf (stderr, "Reducing via rule %d (line %d), ", + yyn, yyrline[yyn]); + + /* Print the symbols being reduced, and their result. */ + for (i = yyprhs[yyn]; yyrhs[i] > 0; i++) + fprintf (stderr, "%s ", yytname[yyrhs[i]]); + fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]); + } +#endif + + + switch (yyn) { + +case 3: +#line 235 "getdate.y" +{ + context->yyHaveTime++; + ; + break;} +case 4: +#line 238 "getdate.y" +{ + context->yyHaveZone++; + ; + break;} +case 5: +#line 241 "getdate.y" +{ + context->yyHaveDate++; + ; + break;} +case 6: +#line 244 "getdate.y" +{ + context->yyHaveDay++; + ; + break;} +case 7: +#line 247 "getdate.y" +{ + context->yyHaveRel++; + ; + break;} +case 9: +#line 253 "getdate.y" +{ + context->yyHour = yyvsp[-1].Number; + context->yyMinutes = 0; + context->yySeconds = 0; + context->yyMeridian = yyvsp[0].Meridian; + ; + break;} +case 10: +#line 259 "getdate.y" +{ + context->yyHour = yyvsp[-3].Number; + context->yyMinutes = yyvsp[-1].Number; + context->yySeconds = 0; + context->yyMeridian = yyvsp[0].Meridian; + ; + break;} +case 11: +#line 265 "getdate.y" +{ + context->yyHour = yyvsp[-3].Number; + context->yyMinutes = yyvsp[-1].Number; + context->yyMeridian = MER24; + context->yyHaveZone++; + context->yyTimezone = (yyvsp[0].Number < 0 + ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60 + : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60)); + ; + break;} +case 12: +#line 274 "getdate.y" +{ + context->yyHour = yyvsp[-5].Number; + context->yyMinutes = yyvsp[-3].Number; + context->yySeconds = yyvsp[-1].Number; + context->yyMeridian = yyvsp[0].Meridian; + ; + break;} +case 13: +#line 280 "getdate.y" +{ + context->yyHour = yyvsp[-5].Number; + context->yyMinutes = yyvsp[-3].Number; + context->yySeconds = yyvsp[-1].Number; + context->yyMeridian = MER24; + context->yyHaveZone++; + context->yyTimezone = (yyvsp[0].Number < 0 + ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60 + : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60)); + ; + break;} +case 14: +#line 292 "getdate.y" +{ + context->yyTimezone = yyvsp[0].Number; + ; + break;} +case 15: +#line 295 "getdate.y" +{ + context->yyTimezone = yyvsp[0].Number - 60; + ; + break;} +case 16: +#line 299 "getdate.y" +{ + context->yyTimezone = yyvsp[-1].Number - 60; + ; + break;} +case 17: +#line 304 "getdate.y" +{ + context->yyDayOrdinal = 1; + context->yyDayNumber = yyvsp[0].Number; + ; + break;} +case 18: +#line 308 "getdate.y" +{ + context->yyDayOrdinal = 1; + context->yyDayNumber = yyvsp[-1].Number; + ; + break;} +case 19: +#line 312 "getdate.y" +{ + context->yyDayOrdinal = yyvsp[-1].Number; + context->yyDayNumber = yyvsp[0].Number; + ; + break;} +case 20: +#line 318 "getdate.y" +{ + context->yyMonth = yyvsp[-2].Number; + context->yyDay = yyvsp[0].Number; + ; + break;} +case 21: +#line 322 "getdate.y" +{ + /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. + The goal in recognizing YYYY/MM/DD is solely to support legacy + machine-generated dates like those in an RCS log listing. If + you want portability, use the ISO 8601 format. */ + if (yyvsp[-4].Number >= 1000) + { + context->yyYear = yyvsp[-4].Number; + context->yyMonth = yyvsp[-2].Number; + context->yyDay = yyvsp[0].Number; + } + else + { + context->yyMonth = yyvsp[-4].Number; + context->yyDay = yyvsp[-2].Number; + context->yyYear = yyvsp[0].Number; + } + ; + break;} +case 22: +#line 340 "getdate.y" +{ + /* ISO 8601 format. yyyy-mm-dd. */ + context->yyYear = yyvsp[-2].Number; + context->yyMonth = -yyvsp[-1].Number; + context->yyDay = -yyvsp[0].Number; + ; + break;} +case 23: +#line 346 "getdate.y" +{ + /* e.g. 17-JUN-1992. */ + context->yyDay = yyvsp[-2].Number; + context->yyMonth = yyvsp[-1].Number; + context->yyYear = -yyvsp[0].Number; + ; + break;} +case 24: +#line 352 "getdate.y" +{ + context->yyMonth = yyvsp[-1].Number; + context->yyDay = yyvsp[0].Number; + ; + break;} +case 25: +#line 356 "getdate.y" +{ + context->yyMonth = yyvsp[-3].Number; + context->yyDay = yyvsp[-2].Number; + context->yyYear = yyvsp[0].Number; + ; + break;} +case 26: +#line 361 "getdate.y" +{ + context->yyMonth = yyvsp[0].Number; + context->yyDay = yyvsp[-1].Number; + ; + break;} +case 27: +#line 365 "getdate.y" +{ + context->yyMonth = yyvsp[-1].Number; + context->yyDay = yyvsp[-2].Number; + context->yyYear = yyvsp[0].Number; + ; + break;} +case 28: +#line 372 "getdate.y" +{ + context->yyRelSeconds = -context->yyRelSeconds; + context->yyRelMinutes = -context->yyRelMinutes; + context->yyRelHour = -context->yyRelHour; + context->yyRelDay = -context->yyRelDay; + context->yyRelMonth = -context->yyRelMonth; + context->yyRelYear = -context->yyRelYear; + ; + break;} +case 30: +#line 383 "getdate.y" +{ + context->yyRelYear += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 31: +#line 386 "getdate.y" +{ + context->yyRelYear += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 32: +#line 389 "getdate.y" +{ + context->yyRelYear += yyvsp[0].Number; + ; + break;} +case 33: +#line 392 "getdate.y" +{ + context->yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 34: +#line 395 "getdate.y" +{ + context->yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 35: +#line 398 "getdate.y" +{ + context->yyRelMonth += yyvsp[0].Number; + ; + break;} +case 36: +#line 401 "getdate.y" +{ + context->yyRelDay += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 37: +#line 404 "getdate.y" +{ + context->yyRelDay += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 38: +#line 407 "getdate.y" +{ + context->yyRelDay += yyvsp[0].Number; + ; + break;} +case 39: +#line 410 "getdate.y" +{ + context->yyRelHour += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 40: +#line 413 "getdate.y" +{ + context->yyRelHour += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 41: +#line 416 "getdate.y" +{ + context->yyRelHour += yyvsp[0].Number; + ; + break;} +case 42: +#line 419 "getdate.y" +{ + context->yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 43: +#line 422 "getdate.y" +{ + context->yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 44: +#line 425 "getdate.y" +{ + context->yyRelMinutes += yyvsp[0].Number; + ; + break;} +case 45: +#line 428 "getdate.y" +{ + context->yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 46: +#line 431 "getdate.y" +{ + context->yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number; + ; + break;} +case 47: +#line 434 "getdate.y" +{ + context->yyRelSeconds += yyvsp[0].Number; + ; + break;} +case 48: +#line 440 "getdate.y" +{ + if (context->yyHaveTime && context->yyHaveDate && + !context->yyHaveRel) + context->yyYear = yyvsp[0].Number; + else + { + if (yyvsp[0].Number>10000) + { + context->yyHaveDate++; + context->yyDay= (yyvsp[0].Number)%100; + context->yyMonth= (yyvsp[0].Number/100)%100; + context->yyYear = yyvsp[0].Number/10000; + } + else + { + context->yyHaveTime++; + if (yyvsp[0].Number < 100) + { + context->yyHour = yyvsp[0].Number; + context->yyMinutes = 0; + } + else + { + context->yyHour = yyvsp[0].Number / 100; + context->yyMinutes = yyvsp[0].Number % 100; + } + context->yySeconds = 0; + context->yyMeridian = MER24; + } + } + ; + break;} +case 49: +#line 474 "getdate.y" +{ + yyval.Meridian = MER24; + ; + break;} +case 50: +#line 478 "getdate.y" +{ + yyval.Meridian = yyvsp[0].Meridian; + ; + break;} +} + /* the action file gets copied in in place of this dollarsign */ +#line 543 "/usr/local/share/bison.simple" + + yyvsp -= yylen; + yyssp -= yylen; +#ifdef YYLSP_NEEDED + yylsp -= yylen; +#endif + +#if YYDEBUG != 0 + if (yydebug) + { + short *ssp1 = yyss - 1; + fprintf (stderr, "state stack now"); + while (ssp1 != yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + + *++yyvsp = yyval; + +#ifdef YYLSP_NEEDED + yylsp++; + if (yylen == 0) + { + yylsp->first_line = yylloc.first_line; + yylsp->first_column = yylloc.first_column; + yylsp->last_line = (yylsp-1)->last_line; + yylsp->last_column = (yylsp-1)->last_column; + yylsp->text = 0; + } + else + { + yylsp->last_line = (yylsp+yylen-1)->last_line; + yylsp->last_column = (yylsp+yylen-1)->last_column; + } +#endif + + /* Now "shift" the result of the reduction. + Determine what state that goes to, + based on the state we popped back to + and the rule number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTBASE] + *yyssp; + if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTBASE]; + + goto yynewstate; + +yyerrlab: /* here on detecting error */ + + if (! yyerrstatus) + /* If not already recovering from an error, report this error. */ + { + ++yynerrs; + +#ifdef YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (yyn > YYFLAG && yyn < YYLAST) + { + int size = 0; + char *msg; + int x, count; + + count = 0; + /* Start X at -yyn if nec to avoid negative indexes in yycheck. */ + for (x = (yyn < 0 ? -yyn : 0); + x < (sizeof(yytname) / sizeof(char *)); x++) + if (yycheck[x + yyn] == x) + size += strlen(yytname[x]) + 15, count++; + msg = (char *) malloc(size + 15); + if (msg != 0) + { + strcpy(msg, "parse error"); + + if (count < 5) + { + count = 0; + for (x = (yyn < 0 ? -yyn : 0); + x < (sizeof(yytname) / sizeof(char *)); x++) + if (yycheck[x + yyn] == x) + { + strcat(msg, count == 0 ? ", expecting `" : " or `"); + strcat(msg, yytname[x]); + strcat(msg, "'"); + count++; + } + } + yyerror(msg); + free(msg); + } + else + yyerror ("parse error; also virtual memory exceeded"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror("parse error"); + } + + goto yyerrlab1; +yyerrlab1: /* here on error raised explicitly by an action */ + + if (yyerrstatus == 3) + { + /* if just tried and failed to reuse lookahead token after an error, discard it. */ + + /* return failure if at end of input */ + if (yychar == YYEOF) + YYABORT; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]); +#endif + + yychar = YYEMPTY; + } + + /* Else will try to reuse lookahead token + after shifting the error token. */ + + yyerrstatus = 3; /* Each real token shifted decrements this */ + + goto yyerrhandle; + +yyerrdefault: /* current state does not do anything special for the error token. */ + +#if 0 + /* This is wrong; only states that explicitly want error tokens + should shift them. */ + yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/ + if (yyn) goto yydefault; +#endif + +yyerrpop: /* pop the current state because it cannot handle the error token */ + + if (yyssp == yyss) YYABORT; + yyvsp--; + yystate = *--yyssp; +#ifdef YYLSP_NEEDED + yylsp--; +#endif + +#if YYDEBUG != 0 + if (yydebug) + { + short *ssp1 = yyss - 1; + fprintf (stderr, "Error: state stack now"); + while (ssp1 != yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + +yyerrhandle: + + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yyerrdefault; + + yyn += YYTERROR; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR) + goto yyerrdefault; + + yyn = yytable[yyn]; + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrpop; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrpop; + + if (yyn == YYFINAL) + YYACCEPT; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Shifting error token, "); +#endif + + *++yyvsp = yylval; +#ifdef YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + yystate = yyn; + goto yynewstate; + + yyacceptlab: + /* YYACCEPT comes here. */ + if (yyfree_stacks) + { + free (yyss); + free (yyvs); +#ifdef YYLSP_NEEDED + free (yyls); +#endif + } + return 0; + + yyabortlab: + /* YYABORT comes here. */ + if (yyfree_stacks) + { + free (yyss); + free (yyvs); +#ifdef YYLSP_NEEDED + free (yyls); +#endif + } + return 1; +} +#line 483 "getdate.y" + + +/* Include this file down here because bison inserts code above which + may define-away `const'. We want the prototype for get_date to have + the same signature as the function definition does. */ +#include "getdate.h" + +#ifndef WIN32 /* the windows dudes don't need these, does anyone really? */ +extern struct tm *gmtime (); +extern struct tm *localtime (); +extern time_t mktime (); +#endif + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL, 0, 0 } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tYEAR_UNIT, 1 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tDAY_UNIT, 14 }, + { "week", tDAY_UNIT, 7 }, + { "day", tDAY_UNIT, 1 }, + { "hour", tHOUR_UNIT, 1 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL, 0, 0 } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 1 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL, 0, 0 } +}; + +/* The timezone table. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR ( 0) }, + { "wet", tZONE, HOUR ( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */ + { "wat", tZONE, HOUR ( 1) }, /* West Africa */ + { "at", tZONE, HOUR ( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR ( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR (10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR (11) }, /* Nome */ + { "idlw", tZONE, HOUR (12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR (1) }, /* Central European */ + { "met", tZONE, -HOUR (1) }, /* Middle European */ + { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ + { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR (1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */ + { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR (3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Standard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */ +#if 0 + { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR (12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR (12) }, /* International Date Line East */ + { NULL, 0, 0 } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR ( 1) }, + { "b", tZONE, HOUR ( 2) }, + { "c", tZONE, HOUR ( 3) }, + { "d", tZONE, HOUR ( 4) }, + { "e", tZONE, HOUR ( 5) }, + { "f", tZONE, HOUR ( 6) }, + { "g", tZONE, HOUR ( 7) }, + { "h", tZONE, HOUR ( 8) }, + { "i", tZONE, HOUR ( 9) }, + { "k", tZONE, HOUR ( 10) }, + { "l", tZONE, HOUR ( 11) }, + { "m", tZONE, HOUR ( 12) }, + { "n", tZONE, HOUR (- 1) }, + { "o", tZONE, HOUR (- 2) }, + { "p", tZONE, HOUR (- 3) }, + { "q", tZONE, HOUR (- 4) }, + { "r", tZONE, HOUR (- 5) }, + { "s", tZONE, HOUR (- 6) }, + { "t", tZONE, HOUR (- 7) }, + { "u", tZONE, HOUR (- 8) }, + { "v", tZONE, HOUR (- 9) }, + { "w", tZONE, HOUR (-10) }, + { "x", tZONE, HOUR (-11) }, + { "y", tZONE, HOUR (-12) }, + { "z", tZONE, HOUR ( 0) }, + { NULL, 0, 0 } +}; + + + + +/* ARGSUSED */ +static int +yyerror (s) + char *s ATTRIBUTE_UNUSED; +{ + return 0; +} + +static int +ToHour (Hours, Meridian) + int Hours; + MERIDIAN Meridian; +{ + switch (Meridian) + { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return Hours; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return Hours; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return Hours + 12; + default: + abort (); + } + /* NOTREACHED */ +} + +static int +ToYear (Year) + int Year; +{ + if (Year < 0) + Year = -Year; + + /* XPG4 suggests that years 00-68 map to 2000-2068, and + years 69-99 map to 1969-1999. */ + if (Year < 69) + Year += 2000; + else if (Year < 100) + Year += 1900; + + return Year; +} + +static int +LookupWord (yylval, buff) + YYSTYPE *yylval; + char *buff; +{ + register char *p; + register char *q; + register const TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (ISUPPER ((unsigned char) *p)) + *p = tolower (*p); + + if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) + { + yylval->Meridian = MERam; + return tMERIDIAN; + } + if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) + { + yylval->Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen (buff) == 3) + abbrev = 1; + else if (strlen (buff) == 4 && buff[3] == '.') + { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) + { + if (abbrev) + { + if (strncmp (buff, tp->name, 3) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + } + else if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + + if (strcmp (buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen (buff) - 1; + if (buff[i] == 's') + { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff)) + { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval->Number = tp->value; + return tp->type; + } + + return tID; +} + +static int +yylex (yylval, cookie) + YYSTYPE *yylval; + void *cookie; +{ + register unsigned char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for (;;) + { + while (ISSPACE ((unsigned char) *context->yyInput)) + context->yyInput++; + + if (ISDIGIT (c = *context->yyInput) || c == '-' || c == '+') + { + if (c == '-' || c == '+') + { + sign = c == '-' ? -1 : 1; + if (!ISDIGIT (*++context->yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval->Number = 0; ISDIGIT (c = *context->yyInput++);) + yylval->Number = 10 * yylval->Number + c - '0'; + context->yyInput--; + if (sign < 0) + yylval->Number = -yylval->Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (ISALPHA (c)) + { + for (p = buff; (c = *context->yyInput++, ISALPHA (c)) || c == '.';) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + context->yyInput--; + return LookupWord (yylval, buff); + } + if (c != '(') + return *context->yyInput++; + Count = 0; + do + { + c = *context->yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } + while (Count > 0); + } +} + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static long +difftm (struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + long days = ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay / 100 - by / 100) + + ((ay / 100 >> 2) - (by / 100 >> 2)) + /* + difference in years * 365 */ + + (long) (ay - by) * 365 + ); + return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} + +time_t +curl_getdate (const char *p, const time_t *now) +{ + struct tm tm, tm0, *tmp; + time_t Start; + CONTEXT cookie; +#ifdef HAVE_LOCALTIME_R + struct tm keeptime; +#endif + cookie.yyInput = p; + Start = now ? *now : time ((time_t *) NULL); +#ifdef HAVE_LOCALTIME_R + tmp = (struct tm *)localtime_r(&Start, &keeptime); +#else + tmp = localtime (&Start); +#endif + if (!tmp) + return -1; + cookie.yyYear = tmp->tm_year + TM_YEAR_ORIGIN; + cookie.yyMonth = tmp->tm_mon + 1; + cookie.yyDay = tmp->tm_mday; + cookie.yyHour = tmp->tm_hour; + cookie.yyMinutes = tmp->tm_min; + cookie.yySeconds = tmp->tm_sec; + tm.tm_isdst = tmp->tm_isdst; + cookie.yyMeridian = MER24; + cookie.yyRelSeconds = 0; + cookie.yyRelMinutes = 0; + cookie.yyRelHour = 0; + cookie.yyRelDay = 0; + cookie.yyRelMonth = 0; + cookie.yyRelYear = 0; + cookie.yyHaveDate = 0; + cookie.yyHaveDay = 0; + cookie.yyHaveRel = 0; + cookie.yyHaveTime = 0; + cookie.yyHaveZone = 0; + + if (yyparse (&cookie) + || cookie.yyHaveTime > 1 || cookie.yyHaveZone > 1 || + cookie.yyHaveDate > 1 || cookie.yyHaveDay > 1) + return -1; + + tm.tm_year = ToYear (cookie.yyYear) - TM_YEAR_ORIGIN + cookie.yyRelYear; + tm.tm_mon = cookie.yyMonth - 1 + cookie.yyRelMonth; + tm.tm_mday = cookie.yyDay + cookie.yyRelDay; + if (cookie.yyHaveTime || + (cookie.yyHaveRel && !cookie.yyHaveDate && !cookie.yyHaveDay)) + { + tm.tm_hour = ToHour (cookie.yyHour, cookie.yyMeridian); + if (tm.tm_hour < 0) + return -1; + tm.tm_min = cookie.yyMinutes; + tm.tm_sec = cookie.yySeconds; + } + else + { + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + } + tm.tm_hour += cookie.yyRelHour; + tm.tm_min += cookie.yyRelMinutes; + tm.tm_sec += cookie.yyRelSeconds; + + /* Let mktime deduce tm_isdst if we have an absolute timestamp, + or if the relative timestamp mentions days, months, or years. */ + if (cookie.yyHaveDate | cookie.yyHaveDay | cookie.yyHaveTime | + cookie.yyRelDay | cookie.yyRelMonth | cookie.yyRelYear) + tm.tm_isdst = -1; + + tm0 = tm; + + Start = mktime (&tm); + + if (Start == (time_t) -1) + { + + /* Guard against falsely reporting errors near the time_t boundaries + when parsing times in other time zones. For example, if the min + time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead + of UTC, then the min localtime value is 1970-01-01 08:00:00; if + we apply mktime to 1970-01-01 00:00:00 we will get an error, so + we apply mktime to 1970-01-02 08:00:00 instead and adjust the time + zone by 24 hours to compensate. This algorithm assumes that + there is no DST transition within a day of the time_t boundaries. */ + if (cookie.yyHaveZone) + { + tm = tm0; + if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) + { + tm.tm_mday++; + cookie.yyTimezone -= 24 * 60; + } + else + { + tm.tm_mday--; + cookie.yyTimezone += 24 * 60; + } + Start = mktime (&tm); + } + + if (Start == (time_t) -1) + return Start; + } + + if (cookie.yyHaveDay && !cookie.yyHaveDate) + { + tm.tm_mday += ((cookie.yyDayNumber - tm.tm_wday + 7) % 7 + + 7 * (cookie.yyDayOrdinal - (0 < cookie.yyDayOrdinal))); + Start = mktime (&tm); + if (Start == (time_t) -1) + return Start; + } + + if (cookie.yyHaveZone) + { + long delta; + struct tm *gmt; +#ifdef HAVE_GMTIME_R + /* thread-safe version */ + struct tm keeptime; + gmt = (struct tm *)gmtime_r(&Start, &keeptime); +#else + gmt = gmtime(&Start); +#endif + if (!gmt) + return -1; + delta = cookie.yyTimezone * 60L + difftm (&tm, gmt); + if ((Start + delta < Start) != (delta < 0)) + return -1; /* time_t overflow */ + Start += delta; + } + + return Start; +} + +#if defined (TEST) + +/* ARGSUSED */ +int +main (ac, av) + int ac; + char *av[]; +{ + char buff[MAX_BUFF_LEN + 1]; + time_t d; + + (void) printf ("Enter date, or blank line to exit.\n\t> "); + (void) fflush (stdout); + + buff[MAX_BUFF_LEN] = 0; + while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0]) + { + d = curl_getdate (buff, (time_t *) NULL); + if (d == -1) + (void) printf ("Bad format - couldn't convert.\n"); + else + (void) printf ("%s", ctime (&d)); + (void) printf ("\t> "); + (void) fflush (stdout); + } + exit (0); + /* NOTREACHED */ +} +#endif /* defined (TEST) */ diff --git a/Source/CTest/Curl/getdate.h b/Source/CTest/Curl/getdate.h new file mode 100644 index 0000000..85650e3 --- /dev/null +++ b/Source/CTest/Curl/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/Source/CTest/Curl/getenv.c b/Source/CTest/Curl/getenv.c new file mode 100644 index 0000000..66bca30 --- /dev/null +++ b/Source/CTest/Curl/getenv.c @@ -0,0 +1,77 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef WIN32 +#include <windows.h> +#endif + +#ifdef VMS +#include <unixlib.h> +#endif + +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +static +char *GetEnv(const char *variable) +{ +#ifdef WIN32 + /* This shit requires windows.h (HUGE) to be included */ + char env[MAX_PATH]; /* MAX_PATH is from windef.h */ + char *temp = getenv(variable); + env[0] = '\0'; + if (temp != NULL) + ExpandEnvironmentStrings(temp, env, sizeof(env)); +#else +#ifdef VMS + char *env = getenv(variable); + if (env && strcmp("HOME",variable) == 0) { + env = decc$translate_vms(env); + } +#else + /* no length control */ + char *env = getenv(variable); +#endif +#endif + return (env && env[0])?strdup(env):NULL; +} + +char *curl_getenv(const char *v) +{ + return GetEnv(v); +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/getinfo.c b/Source/CTest/Curl/getinfo.c new file mode 100644 index 0000000..b05e51b --- /dev/null +++ b/Source/CTest/Curl/getinfo.c @@ -0,0 +1,161 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <curl/curl.h> + +#include "urldata.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +#ifdef VMS +#include <stdlib.h> +#endif + +/* + * This is supposed to be called in the beginning of a permform() session + * and should reset all session-info variables + */ +CURLcode Curl_initinfo(struct SessionHandle *data) +{ + struct Progress *pro = &data->progress; + struct PureInfo *info =&data->info; + + pro->t_nslookup = 0; + pro->t_connect = 0; + pro->t_pretransfer = 0; + pro->t_starttransfer = 0; + pro->timespent = 0; + + info->httpcode = 0; + info->httpversion=0; + info->filetime=-1; /* -1 is an illegal time and thus means unknown */ + + if (info->contenttype) + free(info->contenttype); + info->contenttype = NULL; + + info->header_size = 0; + info->request_size = 0; + return CURLE_OK; +} + +CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) +{ + va_list arg; + long *param_longp; + double *param_doublep; + char **param_charp; + va_start(arg, info); + + switch(info&CURLINFO_TYPEMASK) { + default: + return CURLE_BAD_FUNCTION_ARGUMENT; + case CURLINFO_STRING: + param_charp = va_arg(arg, char **); + if(NULL == param_charp) + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + case CURLINFO_LONG: + param_longp = va_arg(arg, long *); + if(NULL == param_longp) + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + case CURLINFO_DOUBLE: + param_doublep = va_arg(arg, double *); + if(NULL == param_doublep) + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + } + + switch(info) { + case CURLINFO_EFFECTIVE_URL: + *param_charp = data->change.url?data->change.url:(char *)""; + break; + case CURLINFO_HTTP_CODE: + *param_longp = data->info.httpcode; + break; + case CURLINFO_FILETIME: + *param_longp = data->info.filetime; + break; + case CURLINFO_HEADER_SIZE: + *param_longp = data->info.header_size; + break; + case CURLINFO_REQUEST_SIZE: + *param_longp = data->info.request_size; + break; + case CURLINFO_TOTAL_TIME: + *param_doublep = data->progress.timespent; + break; + case CURLINFO_NAMELOOKUP_TIME: + *param_doublep = data->progress.t_nslookup; + break; + case CURLINFO_CONNECT_TIME: + *param_doublep = data->progress.t_connect; + break; + case CURLINFO_PRETRANSFER_TIME: + *param_doublep = data->progress.t_pretransfer; + break; + case CURLINFO_STARTTRANSFER_TIME: + *param_doublep = data->progress.t_starttransfer; + break; + case CURLINFO_SIZE_UPLOAD: + *param_doublep = data->progress.uploaded; + break; + case CURLINFO_SIZE_DOWNLOAD: + *param_doublep = data->progress.downloaded; + break; + case CURLINFO_SPEED_DOWNLOAD: + *param_doublep = data->progress.dlspeed; + break; + case CURLINFO_SPEED_UPLOAD: + *param_doublep = data->progress.ulspeed; + break; + case CURLINFO_SSL_VERIFYRESULT: + *param_longp = data->set.ssl.certverifyresult; + break; + case CURLINFO_CONTENT_LENGTH_DOWNLOAD: + *param_doublep = data->progress.size_dl; + break; + case CURLINFO_CONTENT_LENGTH_UPLOAD: + *param_doublep = data->progress.size_ul; + break; + case CURLINFO_CONTENT_TYPE: + *param_charp = data->info.contenttype; + break; + default: + return CURLE_BAD_FUNCTION_ARGUMENT; + } + return CURLE_OK; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/getinfo.h b/Source/CTest/Curl/getinfo.h new file mode 100644 index 0000000..23fdff1 --- /dev/null +++ b/Source/CTest/Curl/getinfo.h @@ -0,0 +1,28 @@ +#ifndef __GETINFO_H +#define __GETINFO_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...); +CURLcode Curl_initinfo(struct SessionHandle *data); + +#endif diff --git a/Source/CTest/Curl/getpass.c b/Source/CTest/Curl/getpass.c new file mode 100644 index 0000000..fd0d6fe --- /dev/null +++ b/Source/CTest/Curl/getpass.c @@ -0,0 +1,252 @@ +/* ============================================================================ + * Copyright (C) 1998 Angus Mackay. All rights reserved; + * + * Redistribution and use are freely permitted provided that: + * + * 1) This header remain in tact. + * 2) The prototypes for getpass and getpass_r are not changed from: + * char *getpass(const char *prompt) + * char *getpass_r(const char *prompt, char* buffer, int buflen) + * 3) This source code is not used outside of this(getpass.c) file. + * 4) Any changes to this(getpass.c) source code are made publicly available. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * $Id$ + * + * The spirit of this license is to allow use of this source code in any + * project be it open or closed but still encourage the use of the open, + * library based equivilents. + * + * Author(s): + * Angus Mackay <amackay@gus.ml.org> + * + * Contributor(s): + * Daniel Stenberg <daniel@haxx.se> + */ + +#include "setup.h" /* setup.h is required for read() prototype */ + +#ifndef HAVE_GETPASS_R + +#ifndef WIN32 +#ifdef VMS +#include <stdio.h> +#include <string.h> +#include descrip +#include starlet +#include iodef +#include iosbdef +char *getpass_r(const char *prompt, char *buffer, size_t buflen) +{ + long sts; + short chan; + struct _iosb iosb; + $DESCRIPTOR(ttdesc, "TT"); + + buffer[0]='\0'; + if ((sts = sys$assign(&ttdesc, &chan,0,0)) & 1) { + if (((sts = sys$qiow(0, chan, IO$_READPROMPT | IO$M_NOECHO, &iosb, 0, 0, buffer, buflen, 0, 0, prompt, strlen(prompt))) & 1) && (iosb.iosb$w_status&1)) { + buffer[iosb.iosb$w_bcnt] = '\0'; + } + sts = sys$dassgn(chan); + } + return buffer; /* we always return success */ +} +#else /* VMS */ +#ifdef HAVE_TERMIOS_H +# if !defined(HAVE_TCGETATTR) && !defined(HAVE_TCSETATTR) +# undef HAVE_TERMIOS_H +# endif +#endif + +#ifndef RETSIGTYPE +# define RETSIGTYPE void +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdio.h> +#include <signal.h> +#ifdef HAVE_TERMIOS_H +# include <termios.h> +#else +# ifdef HAVE_TERMIO_H +# include <termio.h> +# else +# endif +#endif + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* no perror? make an fprintf! */ +#ifndef HAVE_PERROR +# define perror(x) fprintf(stderr, "Error in: %s\n", x) +#endif + +char *getpass_r(const char *prompt, char *buffer, size_t buflen) +{ + FILE *infp; + FILE *outfp; + RETSIGTYPE (*sigint)(); +#ifndef __EMX__ + RETSIGTYPE (*sigtstp)(); +#endif + size_t bytes_read; + int infd; + int outfd; +#ifdef HAVE_TERMIOS_H + struct termios orig; + struct termios noecho; +#else +# ifdef HAVE_TERMIO_H + struct termio orig; + struct termio noecho; +# else +# endif +#endif + + sigint = signal(SIGINT, SIG_IGN); + /* 20000318 mgs + * this is needed by the emx system, SIGTSTP is not a supported signal */ +#ifndef __EMX__ + sigtstp = signal(SIGTSTP, SIG_IGN); +#endif + + if( (infp=fopen("/dev/tty", "r")) == NULL ) + { + infp = stdin; + } + if( (outfp=fopen("/dev/tty", "w")) == NULL ) + { + outfp = stderr; + } + infd = fileno(infp); + outfd = fileno(outfp); + + /* dissable echo */ +#ifdef HAVE_TERMIOS_H + if(tcgetattr(outfd, &orig) != 0) + { + ; /*perror("tcgetattr");*/ + } + noecho = orig; + noecho.c_lflag &= ~ECHO; + if(tcsetattr(outfd, TCSANOW, &noecho) != 0) + { + ; /*perror("tcgetattr");*/ + } +#else +# ifdef HAVE_TERMIO_H + if(ioctl(outfd, TCGETA, &orig) != 0) + { + ; /*perror("ioctl");*/ + } + noecho = orig; + noecho.c_lflag &= ~ECHO; + if(ioctl(outfd, TCSETA, &noecho) != 0) + { + ; /*perror("ioctl");*/ + } +# else +# endif +#endif + + fputs(prompt, outfp); + fflush(outfp); + + bytes_read=read(infd, buffer, buflen); + buffer[bytes_read > 0 ? (bytes_read -1) : 0] = '\0'; + + /* print a new line if needed */ +#ifdef HAVE_TERMIOS_H + fputs("\n", outfp); +#else +# ifdef HAVE_TERMIO_H + fputs("\n", outfp); +# else +# endif +#endif + + /* + * reset term charectaristics, use TCSAFLUSH incase the + * user types more than buflen + */ +#ifdef HAVE_TERMIOS_H + if(tcsetattr(outfd, TCSAFLUSH, &orig) != 0) + { + ; /*perror("tcgetattr");*/ + } +#else +# ifdef HAVE_TERMIO_H + if(ioctl(outfd, TCSETA, &orig) != 0) + { + ; /*perror("ioctl");*/ + } +# else +# endif +#endif + + signal(SIGINT, sigint); +#ifndef __EMX__ + signal(SIGTSTP, sigtstp); +#endif + + return buffer; /* we always return success */ +} +#endif /* VMS */ +#else /* WIN32 */ +#include <stdio.h> +#include <conio.h> +char *getpass_r(const char *prompt, char *buffer, int buflen) +{ + int i; + printf("%s", prompt); + + for(i=0; i<buflen; i++) { + buffer[i] = getch(); + if ( buffer[i] == '\r' ) { + buffer[i] = 0; + break; + } + } + /* if user didn't hit ENTER, terminate buffer */ + if (i==buflen) + buffer[buflen-1]=0; + + return buffer; /* we always return success */ +} +#endif + +#endif /* ifndef HAVE_GETPASS_R */ + +#if 0 +/* for consistensy, here's the old-style function: */ +char *getpass(const char *prompt) +{ + static char buf[256]; + return getpass_r(prompt, buf, sizeof(buf)); +} +#endif + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/getpass.h b/Source/CTest/Curl/getpass.h new file mode 100644 index 0000000..249a26e --- /dev/null +++ b/Source/CTest/Curl/getpass.h @@ -0,0 +1,35 @@ +#ifndef __GETPASS_H +#define __GETPASS_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +#ifndef HAVE_GETPASS_R +/* If there's a system-provided function named like this, we trust it is + also found in one of the standard headers. */ + +/* + * Returning NULL will abort the continued operation! + */ +char* getpass_r(const char *prompt, char* buffer, size_t buflen ); +#endif + +#endif diff --git a/Source/CTest/Curl/hash.c b/Source/CTest/Curl/hash.c new file mode 100644 index 0000000..584c30e --- /dev/null +++ b/Source/CTest/Curl/hash.c @@ -0,0 +1,285 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2002, Daniel Stenberg, <daniel@haxx.se>, et al + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <string.h> +#include <stdlib.h> +#include "hash.h" +#include "llist.h" + +#ifdef MALLOCDEBUG +/* this must be the last include file */ +#include "memdebug.h" +#endif + + +static unsigned long +curl_hash_str(const char *key, unsigned int key_length) +{ + register unsigned long h = 0; + register unsigned long g; + register char *p = (char *) key; + register char *end = (char *) key + key_length; + + while (p < end) { + h = (h << 4) + *p++; + if ((g = (h & 0xF0000000))) { + h = h ^ (g >> 24); + h = h ^ g; + } + } + + return h; +} + +static unsigned long +curl_hash_num(unsigned long key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += (key << 11); + key ^= (key >> 16); + + return key; +} + +static void +hash_element_dtor(void *u, void *ele) +{ + curl_hash_element *e = (curl_hash_element *) ele; + curl_hash *h = (curl_hash *) u; + + if (e->key.type == CURL_HASH_KEY_IS_STRING) { + free(e->key.value.str.val); + } + h->dtor(e->ptr); + + free(e); + e = NULL; +} + +void +curl_hash_init(curl_hash *h, int slots, curl_hash_dtor dtor) +{ + int i; + + h->dtor = dtor; + h->size = 0; + h->slots = slots; + + h->table = (curl_llist **) malloc(slots * sizeof(curl_llist *)); + for (i = 0; i < h->slots; ++i) { + h->table[i] = curl_llist_alloc((curl_llist_dtor) hash_element_dtor); + } +} + +curl_hash * +curl_hash_alloc(int slots, curl_hash_dtor dtor) +{ + curl_hash *h; + + h = (curl_hash *)malloc(sizeof(curl_hash)); + if(NULL == h) + return NULL; + + curl_hash_init(h, slots, dtor); + + return h; +} + +#define FIND_SLOT(__h, __s_key, __s_key_len, __n_key) \ + ((__s_key ? curl_hash_str(__s_key, __s_key_len) : curl_hash_num(__n_key)) % (__h)->slots) + +#define KEY_CREATE(__k, __s_key, __s_key_len, __n_key, __dup) \ + if (__s_key) { \ + if (__dup) { \ + (__k)->value.str.val = (char *) malloc(__s_key_len); \ + memcpy((__k)->value.str.val, __s_key, __s_key_len); \ + } else { \ + (__k)->value.str.val = __s_key; \ + } \ + (__k)->value.str.len = __s_key_len; \ + (__k)->type = CURL_HASH_KEY_IS_STRING; \ + } else { \ + (__k)->value.num = __n_key; \ + (__k)->type = CURL_HASH_KEY_IS_NUM; \ + } + +#define MIN(a, b) (a > b ? b : a) + +static int +curl_hash_key_compare(curl_hash_key *key1, curl_hash_key *key2) +{ + if (key1->type == CURL_HASH_KEY_IS_NUM) { + if (key2->type == CURL_HASH_KEY_IS_STRING) + return 0; + + if (key1->value.num == key2->value.num) + return 1; + } else { + if (key2->type == CURL_HASH_KEY_IS_NUM) + return 0; + + if (memcmp(key1->value.str.val, key2->value.str.val, + MIN(key1->value.str.len, key2->value.str.len)) == 0) + return 1; + } + + return 0; +} + +int +curl_hash_add_or_update(curl_hash *h, char *str_key, unsigned int str_key_len, + unsigned long num_key, const void *p) +{ + curl_hash_element *e; + curl_hash_key tmp; + curl_llist *l; + curl_llist_element *le; + int slot; + + slot = FIND_SLOT(h, str_key, str_key_len, num_key); + l = h->table[slot]; + KEY_CREATE(&tmp, str_key, str_key_len, num_key, 0); + for (le = CURL_LLIST_HEAD(l); le != NULL; le = CURL_LLIST_NEXT(le)) { + if (curl_hash_key_compare(&tmp, &((curl_hash_element *) CURL_LLIST_VALP(le))->key)) { + curl_hash_element *to_update = CURL_LLIST_VALP(le); + h->dtor(to_update->ptr); + to_update->ptr = (void *) p; + return 1; + } + } + + e = (curl_hash_element *) malloc(sizeof(curl_hash_element)); + KEY_CREATE(&e->key, str_key, str_key_len, num_key, 1); + e->ptr = (void *) p; + + if (curl_llist_insert_next(l, CURL_LLIST_TAIL(l), e)) { + ++h->size; + return 1; + } else { + return 0; + } +} + +int +curl_hash_extended_delete(curl_hash *h, char *str_key, unsigned int str_key_len, + unsigned long num_key) +{ + curl_llist *l; + curl_llist_element *le; + curl_hash_key tmp; + int slot; + + slot = FIND_SLOT(h, str_key, str_key_len, num_key); + l = h->table[slot]; + + KEY_CREATE(&tmp, str_key, str_key_len, num_key, 0); + for (le = CURL_LLIST_HEAD(l); le != NULL; le = CURL_LLIST_NEXT(le)) { + if (curl_hash_key_compare(&tmp, &((curl_hash_element *) CURL_LLIST_VALP(le))->key)) { + curl_llist_remove(l, le, (void *) h); + --h->size; + return 1; + } + } + + return 0; +} + +int +curl_hash_extended_find(curl_hash *h, char *str_key, unsigned int str_key_len, + unsigned long num_key, void **p) +{ + curl_llist *l; + curl_llist_element *le; + curl_hash_key tmp; + int slot; + + slot = FIND_SLOT(h, str_key, str_key_len, num_key); + l = h->table[slot]; + + KEY_CREATE(&tmp, str_key, str_key_len, num_key, 0); + for (le = CURL_LLIST_HEAD(l); le != NULL; le = CURL_LLIST_NEXT(le)) { + if (curl_hash_key_compare(&tmp, &((curl_hash_element *) CURL_LLIST_VALP(le))->key)) { + *p = ((curl_hash_element *) CURL_LLIST_VALP(le))->ptr; + return 1; + } + } + + return 0; +} + +void +curl_hash_apply(curl_hash *h, void *user, void (*cb)(void *, curl_hash_element *)) +{ + curl_llist_element *le; + int i; + + for (i = 0; i < h->slots; ++i) { + for (le = CURL_LLIST_HEAD(h->table[i]); le != NULL; le = CURL_LLIST_NEXT(le)) { + cb(user, (curl_hash_element *) CURL_LLIST_VALP(le)); + } + } +} + +void +curl_hash_clean(curl_hash *h) +{ + int i; + + for (i = 0; i < h->slots; ++i) { + curl_llist_destroy(h->table[i], (void *) h); + } + + free(h->table); + h->table = NULL; +} + +size_t +curl_hash_count(curl_hash *h) +{ + return h->size; +} + +void +curl_hash_destroy(curl_hash *h) +{ + if (!h) { + return; + } + + curl_hash_clean(h); + free(h); + h = NULL; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/hash.h b/Source/CTest/Curl/hash.h new file mode 100644 index 0000000..d6117ce --- /dev/null +++ b/Source/CTest/Curl/hash.h @@ -0,0 +1,85 @@ +#ifndef __HASH_H +#define __HASH_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <stddef.h> + +#include "llist.h" + +#define CURL_HASH_KEY_IS_STRING 0 +#define CURL_HASH_KEY_IS_NUM 1 + +typedef void (*curl_hash_dtor)(void *); + +typedef struct _curl_hash { + curl_llist **table; + curl_hash_dtor dtor; + int slots; + size_t size; +} curl_hash; + +typedef struct _curl_hash_key { + union { + struct { + char *val; + unsigned int len; + } str; + + unsigned long num; + } value; + + int type; +} curl_hash_key; + +typedef struct _curl_hash_element { + curl_hash_key key; + void *ptr; +} curl_hash_element; + + +void curl_hash_init(curl_hash *h, int slots, curl_hash_dtor dtor); +curl_hash *curl_hash_alloc(int slots, curl_hash_dtor dtor); +int curl_hash_add_or_update(curl_hash *h, char *str_key, unsigned int str_key_len, + unsigned long num_key, const void *p); +int curl_hash_extended_delete(curl_hash *h, char *str_key, unsigned int str_key_len, + unsigned long num_key); +int curl_hash_extended_find(curl_hash *h, char *str_key, unsigned int str_key_len, + unsigned long num_key, void **p); +void curl_hash_apply(curl_hash *h, void *user, void (*cb)(void *, curl_hash_element *)); +size_t curl_hash_count(curl_hash *h); +void curl_hash_clean(curl_hash *h); +void curl_hash_destroy(curl_hash *h); + +#define curl_hash_find(h, key, key_len, p) curl_hash_extended_find(h, key, key_len, 0, p) +#define curl_hash_delete(h, key, key_len) curl_hash_extended_delete(h, key, key_len, 0) +#define curl_hash_add(h, key, key_len, p) curl_hash_add_or_update(h, key, key_len, 0, p) +#define curl_hash_update curl_hash_add +#define curl_hash_index_find(h, key, p) curl_hash_extended_find(h, NULL, 0, key, p) +#define curl_hash_index_delete(h, key) curl_hash_extended_delete(h, NULL, 0, key) +#define curl_hash_index_add(h, key, p) curl_hash_add_or_update(h, NULL, 0, key, p) +#define curl_hash_index_update curl_hash_index_add + +#endif diff --git a/Source/CTest/Curl/hostip.c b/Source/CTest/Curl/hostip.c new file mode 100644 index 0000000..489dfab --- /dev/null +++ b/Source/CTest/Curl/hostip.c @@ -0,0 +1,529 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <string.h> +#include <errno.h> + +#define _REENTRANT + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#else +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* required for free() prototypes */ +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#include <stdlib.h> +#endif +#endif + +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "hash.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) +#include "inet_ntoa_r.h" +#endif + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +static curl_hash hostname_cache; +static int host_cache_initialized; + +void Curl_global_host_cache_init(void) +{ + if (!host_cache_initialized) { + curl_hash_init(&hostname_cache, 7, Curl_freeaddrinfo); + host_cache_initialized = 1; + } +} + +curl_hash *Curl_global_host_cache_get(void) +{ + return &hostname_cache; +} + +void Curl_global_host_cache_dtor(void) +{ + if (host_cache_initialized) { + curl_hash_clean(&hostname_cache); + host_cache_initialized = 0; + } +} + +struct curl_dns_cache_entry { + Curl_addrinfo *addr; + time_t timestamp; +}; + +/* count the number of characters that an integer takes up */ +static int _num_chars(int i) +{ + int chars = 0; + + /* While the number divided by 10 is greater than one, + * re-divide the number by 10, and increment the number of + * characters by 1. + * + * this relies on the fact that for every multiple of 10, + * a new digit is added onto every number + */ + do { + chars++; + + i = (int) i / 10; + } while (i >= 1); + + return chars; +} + +/* Create a hostcache id */ +static char * +_create_hostcache_id(char *server, int port, ssize_t *entry_len) +{ + char *id = NULL; + + /* Get the length of the new entry id */ + *entry_len = *entry_len + /* Hostname length */ + 1 + /* The ':' seperator */ + _num_chars(port); /* The number of characters the port will take up */ + + /* Allocate the new entry id */ + id = malloc(*entry_len + 1); + if (!id) { + return NULL; + } + + /* Create the new entry */ + /* If sprintf() doesn't return the entry length, that signals failure */ + if (sprintf(id, "%s:%d", server, port) != *entry_len) { + /* Free the allocated id, set length to zero and return NULL */ + *entry_len = 0; + free(id); + return NULL; + } + + return id; +} + +/* Macro to save redundant free'ing of entry_id */ +#define _hostcache_return(__v) \ +{ \ + free(entry_id); \ + return (__v); \ +} + +Curl_addrinfo *Curl_resolv(struct SessionHandle *data, + char *hostname, + int port, + char **bufp) +{ + char *entry_id = NULL; + struct curl_dns_cache_entry *p = NULL; + ssize_t entry_len; + time_t now; + + /* If the host cache timeout is 0, we don't do DNS cach'ing + so fall through */ + if (data->set.dns_cache_timeout == 0) { + return Curl_getaddrinfo(data, hostname, port, bufp); + } + + /* Create an entry id, based upon the hostname and port */ + entry_len = strlen(hostname); + entry_id = _create_hostcache_id(hostname, port, &entry_len); + /* If we can't create the entry id, don't cache, just fall-through + to the plain Curl_getaddrinfo() */ + if (!entry_id) { + return Curl_getaddrinfo(data, hostname, port, bufp); + } + + time(&now); + /* See if its already in our dns cache */ + if (entry_id && curl_hash_find(data->hostcache, entry_id, entry_len+1, (void **) &p)) { + /* Do we need to check for a cache timeout? */ + if (data->set.dns_cache_timeout != -1) { + /* Return if the entry has not timed out */ + if ((now - p->timestamp) < data->set.dns_cache_timeout) { + _hostcache_return(p->addr); + } + } + else { + _hostcache_return(p->addr); + } + } + + /* Create a new cache entry */ + p = (struct curl_dns_cache_entry *) malloc(sizeof(struct curl_dns_cache_entry)); + if (!p) { + _hostcache_return(NULL); + } + + p->addr = Curl_getaddrinfo(data, hostname, port, bufp); + if (!p->addr) { + free(p); + _hostcache_return(NULL); + } + p->timestamp = now; + + /* Save it in our host cache */ + curl_hash_update(data->hostcache, entry_id, entry_len+1, (const void *) p); + + _hostcache_return(p->addr); +} + +/* + * This is a wrapper function for freeing name information in a protocol + * independent way. This takes care of using the appropriate underlaying + * proper function. + */ +void Curl_freeaddrinfo(void *freethis) +{ + struct curl_dns_cache_entry *p = (struct curl_dns_cache_entry *) freethis; + +#ifdef ENABLE_IPV6 + freeaddrinfo(p->addr); +#else + free(p->addr); +#endif + + free(p); +} + +/* --- resolve name or IP-number --- */ + +#ifdef ENABLE_IPV6 + +#ifdef MALLOCDEBUG +/* These two are strictly for memory tracing and are using the same + * style as the family otherwise present in memdebug.c. I put these ones + * here since they require a bunch of struct types I didn't wanna include + * in memdebug.c + */ +int curl_getaddrinfo(char *hostname, char *service, + struct addrinfo *hints, + struct addrinfo **result, + int line, const char *source) +{ + int res=(getaddrinfo)(hostname, service, hints, result); + if(0 == res) { + /* success */ + if(logfile) + fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n", + source, line, (void *)*result); + } + else { + if(logfile) + fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n", + source, line); + } + return res; +} + +void curl_freeaddrinfo(struct addrinfo *freethis, + int line, const char *source) +{ + (freeaddrinfo)(freethis); + if(logfile) + fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n", + source, line, (void *)freethis); +} + +#endif + +/* + * Return name information about the given hostname and port number. If + * successful, the 'addrinfo' is returned and the forth argument will point to + * memory we need to free after use. That meory *MUST* be freed with + * Curl_freeaddrinfo(), nothing else. + */ +Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, + char *hostname, + int port, + char **bufp) +{ + struct addrinfo hints, *res; + int error; + char sbuf[NI_MAXSERV]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + snprintf(sbuf, sizeof(sbuf), "%d", port); + error = getaddrinfo(hostname, sbuf, &hints, &res); + if (error) { + infof(data, "getaddrinfo(3) failed for %s\n", hostname); + return NULL; + } + *bufp=(char *)res; /* make it point to the result struct */ + + return res; +} +#else /* following code is IPv4-only */ + +#ifndef HAVE_GETHOSTBYNAME_R +/** + * Performs a "deep" copy of a hostent into a buffer (returns a pointer to the + * copy). Make absolutely sure the destination buffer is big enough! + * + * Keith McGuigan + * 10/3/2001 */ +static struct hostent* pack_hostent(char* buf, struct hostent* orig) +{ + char* bufptr; + struct hostent* copy; + + int i; + char* str; + int len; + + bufptr = buf; + copy = (struct hostent*)bufptr; + + bufptr += sizeof(struct hostent); + copy->h_name = bufptr; + len = strlen(orig->h_name) + 1; + strncpy(bufptr, orig->h_name, len); + bufptr += len; + + /* we align on even 64bit boundaries for safety */ +#define MEMALIGN(x) (((unsigned long)(x)&0xfffffff8)+8) + + /* This must be aligned properly to work on many CPU architectures! */ + copy->h_aliases = (char**)MEMALIGN(bufptr); + + /* Figure out how many aliases there are */ + for (i = 0; orig->h_aliases[i] != NULL; ++i); + + /* Reserve room for the array */ + bufptr += (i + 1) * sizeof(char*); + + /* Clone all known aliases */ + for(i = 0; (str = orig->h_aliases[i]); i++) { + len = strlen(str) + 1; + strncpy(bufptr, str, len); + copy->h_aliases[i] = bufptr; + bufptr += len; + } + /* Terminate the alias list with a NULL */ + copy->h_aliases[i] = NULL; + + copy->h_addrtype = orig->h_addrtype; + copy->h_length = orig->h_length; + + /* align it for (at least) 32bit accesses */ + bufptr = (char *)MEMALIGN(bufptr); + + copy->h_addr_list = (char**)bufptr; + + /* Figure out how many addresses there are */ + for (i = 0; orig->h_addr_list[i] != NULL; ++i); + + /* Reserve room for the array */ + bufptr += (i + 1) * sizeof(char*); + + i = 0; + len = orig->h_length; + str = orig->h_addr_list[i]; + while (str != NULL) { + memcpy(bufptr, str, len); + copy->h_addr_list[i] = bufptr; + bufptr += len; + str = orig->h_addr_list[++i]; + } + copy->h_addr_list[i] = NULL; + + return copy; +} +#endif + +static char *MakeIP(unsigned long num,char *addr, int addr_len) +{ +#if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R) + struct in_addr in; + in.s_addr = htonl(num); + +#if defined(HAVE_INET_NTOA_R) + inet_ntoa_r(in,addr,addr_len); +#else + strncpy(addr,inet_ntoa(in),addr_len); +#endif +#else + unsigned char *paddr; + + num = htonl(num); /* htonl() added to avoid endian probs */ + paddr = (unsigned char *)# + sprintf(addr, "%u.%u.%u.%u", paddr[0], paddr[1], paddr[2], paddr[3]); +#endif + return (addr); +} + +/* The original code to this function was once stolen from the Dancer source + code, written by Bjorn Reese, it has since been patched and modified + considerably. */ + +#ifndef INADDR_NONE +#define INADDR_NONE (in_addr_t) ~0 +#endif + +Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, + char *hostname, + int port, + char **bufp) +{ + struct hostent *h = NULL; + in_addr_t in; + int ret; /* this variable is unused on several platforms but used on some */ + +#define CURL_NAMELOOKUP_SIZE 9000 + /* Allocate enough memory to hold the full name information structs and + * everything. OSF1 is known to require at least 8872 bytes. The buffer + * required for storing all possible aliases and IP numbers is according to + * Stevens' Unix Network Programming 2nd editor, p. 304: 8192 bytes! */ + int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE); + if(!buf) + return NULL; /* major failure */ + *bufp = (char *)buf; + + port=0; /* unused in IPv4 code */ + ret = 0; /* to prevent the compiler warning */ + + if ( (in=inet_addr(hostname)) != INADDR_NONE ) { + struct in_addr *addrentry; + + h = (struct hostent*)buf; + h->h_addr_list = (char**)(buf + sizeof(*h)); + addrentry = (struct in_addr*)(h->h_addr_list + 2); + addrentry->s_addr = in; + h->h_addr_list[0] = (char*)addrentry; + h->h_addr_list[1] = NULL; + h->h_addrtype = AF_INET; + h->h_length = sizeof(*addrentry); + h->h_name = *(h->h_addr_list) + h->h_length; + /* bad one h->h_name = (char*)(h->h_addr_list + h->h_length); */ + MakeIP(ntohl(in),h->h_name, CURL_NAMELOOKUP_SIZE - (long)(h->h_name) + (long)buf); + } +#if defined(HAVE_GETHOSTBYNAME_R) + else { + int h_errnop; + /* Workaround for gethostbyname_r bug in qnx nto. It is also _required_ + for some of these functions. */ + memset(buf, 0, CURL_NAMELOOKUP_SIZE); +#ifdef HAVE_GETHOSTBYNAME_R_5 + /* Solaris, IRIX and more */ + if ((h = gethostbyname_r(hostname, + (struct hostent *)buf, + (char *)buf + sizeof(struct hostent), + CURL_NAMELOOKUP_SIZE - sizeof(struct hostent), + &h_errnop)) == NULL ) +#endif +#ifdef HAVE_GETHOSTBYNAME_R_6 + /* Linux */ + if( gethostbyname_r(hostname, + (struct hostent *)buf, + (char *)buf + sizeof(struct hostent), + CURL_NAMELOOKUP_SIZE - sizeof(struct hostent), + &h, /* DIFFERENCE */ + &h_errnop)) +#endif +#ifdef HAVE_GETHOSTBYNAME_R_3 + /* AIX, Digital Unix, HPUX 10, more? */ + + if(CURL_NAMELOOKUP_SIZE >= + (sizeof(struct hostent)+sizeof(struct hostent_data))) + + /* August 22nd, 2000: Albert Chin-A-Young brought an updated version + * that should work! September 20: Richard Prescott worked on the buffer + * size dilemma. */ + + ret = gethostbyname_r(hostname, + (struct hostent *)buf, + (struct hostent_data *)(buf + sizeof(struct hostent))); + else + ret = -1; /* failure, too smallish buffer size */ + + /* result expected in h */ + h = (struct hostent*)buf; + h_errnop= errno; /* we don't deal with this, but set it anyway */ + if(ret) +#endif + { + infof(data, "gethostbyname_r(2) failed for %s\n", hostname); + h = NULL; /* set return code to NULL */ + free(buf); + *bufp=NULL; + } +#else + else { + if ((h = gethostbyname(hostname)) == NULL ) { + infof(data, "gethostbyname(2) failed for %s\n", hostname); + free(buf); + *bufp=NULL; + } + else + /* we make a copy of the hostent right now, right here, as the + static one we got a pointer to might get removed when we don't + want/expect that */ + h = pack_hostent((char *)buf, h); +#endif + } + return (h); +} + +#endif /* end of IPv4-specific code */ + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ + diff --git a/Source/CTest/Curl/hostip.h b/Source/CTest/Curl/hostip.h new file mode 100644 index 0000000..6910071 --- /dev/null +++ b/Source/CTest/Curl/hostip.h @@ -0,0 +1,60 @@ +#ifndef __HOSTIP_H +#define __HOSTIP_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "hash.h" + +struct addrinfo; +struct hostent; +struct SessionHandle; + +void Curl_global_host_cache_init(void); +void Curl_global_host_cache_dtor(void); +curl_hash *Curl_global_host_cache_get(void); + +#define Curl_global_host_cache_use(__p) ((__p)->set.global_dns_cache) + +Curl_addrinfo *Curl_resolv(struct SessionHandle *data, + char *hostname, + int port, + char **bufp); + +/* Get name info */ +Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, + char *hostname, + int port, + char **bufp); +/* free name info */ +void Curl_freeaddrinfo(void *freethis); + +#ifdef MALLOCDEBUG +void curl_freeaddrinfo(struct addrinfo *freethis, + int line, const char *source); +int curl_getaddrinfo(char *hostname, char *service, + struct addrinfo *hints, + struct addrinfo **result, + int line, const char *source); +#endif + +#endif diff --git a/Source/CTest/Curl/http.c b/Source/CTest/Curl/http.c new file mode 100644 index 0000000..1413618 --- /dev/null +++ b/Source/CTest/Curl/http.c @@ -0,0 +1,985 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#include <sys/time.h> + +#ifdef HAVE_TIME_H +#ifdef TIME_WITH_SYS_TIME +#include <time.h> +#endif +#endif + +#include <sys/resource.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "transfer.h" +#include "sendf.h" +#include "formdata.h" +#include "progress.h" +#include "base64.h" +#include "cookie.h" +#include "strequal.h" +#include "ssluse.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* ------------------------------------------------------------------------- */ +/* + * The add_buffer series of functions are used to build one large memory chunk + * from repeated function invokes. Used so that the entire HTTP request can + * be sent in one go. + */ +static CURLcode + add_buffer(send_buffer *in, const void *inptr, size_t size); + +/* + * add_buffer_init() returns a fine buffer struct + */ +static +send_buffer *add_buffer_init(void) +{ + send_buffer *blonk; + blonk=(send_buffer *)malloc(sizeof(send_buffer)); + if(blonk) { + memset(blonk, 0, sizeof(send_buffer)); + return blonk; + } + return NULL; /* failed, go home */ +} + +/* + * add_buffer_send() sends a buffer and frees all associated memory. + */ +static +CURLcode add_buffer_send(int sockfd, struct connectdata *conn, send_buffer *in, + long *bytes_written) +{ + ssize_t amount; + CURLcode res; + char *ptr; + int size; + + if(conn->data->set.verbose) { + fputs("> ", conn->data->set.err); + /* this data _may_ contain binary stuff */ + fwrite(in->buffer, in->size_used, 1, conn->data->set.err); + } + + /* The looping below is required since we use non-blocking sockets, but due + to the circumstances we will just loop and try again and again etc */ + + ptr = in->buffer; + size = in->size_used; + do { + res = Curl_write(conn, sockfd, ptr, size, &amount); + + if(CURLE_OK != res) + break; + + if(amount != size) { + size -= amount; + ptr += amount; + } + else + break; + + } while(1); + + if(in->buffer) + free(in->buffer); + free(in); + + *bytes_written = amount; + + return res; +} + + +/* + * add_bufferf() builds a buffer from the formatted input + */ +static +CURLcode add_bufferf(send_buffer *in, const char *fmt, ...) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + char *s; + va_list ap; + va_start(ap, fmt); + s = vaprintf(fmt, ap); /* this allocs a new string to append */ + va_end(ap); + + if(s) { + result = add_buffer(in, s, strlen(s)); + free(s); + } + return result; +} + +/* + * add_buffer() appends a memory chunk to the existing one + */ +static +CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size) +{ + char *new_rb; + int new_size; + + if(!in->buffer || + ((in->size_used + size) > (in->size_max - 1))) { + new_size = (in->size_used+size)*2; + if(in->buffer) + /* we have a buffer, enlarge the existing one */ + new_rb = (char *)realloc(in->buffer, new_size); + else + /* create a new buffer */ + new_rb = (char *)malloc(new_size); + + if(!new_rb) + return CURLE_OUT_OF_MEMORY; + + in->buffer = new_rb; + in->size_max = new_size; + } + memcpy(&in->buffer[in->size_used], inptr, size); + + in->size_used += size; + + return CURLE_OK; +} + +/* end of the add_buffer functions */ +/* ------------------------------------------------------------------------- */ + +/* + * This function checks the linked list of custom HTTP headers for a particular + * header (prefix). + */ +static bool checkheaders(struct SessionHandle *data, const char *thisheader) +{ + struct curl_slist *head; + size_t thislen = strlen(thisheader); + + for(head = data->set.headers; head; head=head->next) { + if(strnequal(head->data, thisheader, thislen)) { + return TRUE; + } + } + return FALSE; +} + +/* + * ConnectHTTPProxyTunnel() requires that we're connected to a HTTP proxy. This + * function will issue the necessary commands to get a seamless tunnel through + * this proxy. After that, the socket can be used just as a normal socket. + */ + +CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, + int tunnelsocket, + char *hostname, int remote_port) +{ + int httperror=0; + int subversion=0; + struct SessionHandle *data=conn->data; + CURLcode result; + int res; + + int nread; /* total size read */ + int perline; /* count bytes per line */ + bool keepon=TRUE; + ssize_t gotbytes; + char *ptr; + int timeout = 3600; /* default timeout in seconds */ + struct timeval interval; + fd_set rkeepfd; + fd_set readfd; + char *line_start; + +#define SELECT_OK 0 +#define SELECT_ERROR 1 +#define SELECT_TIMEOUT 2 + int error = SELECT_OK; + + infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port); + + /* OK, now send the connect request to the proxy */ + result = + Curl_sendf(tunnelsocket, conn, + "CONNECT %s:%d HTTP/1.0\015\012" + "%s" + "%s" + "\r\n", + hostname, remote_port, + (conn->bits.proxy_user_passwd)?conn->allocptr.proxyuserpwd:"", + (data->set.useragent?conn->allocptr.uagent:"") + ); + if(result) { + failf(data, "Failed sending CONNECT to proxy"); + return result; + } + + /* Now, read the full reply we get from the proxy */ + + + if(data->set.timeout) { + /* if timeout is requested, find out how much remaining time we have */ + timeout = data->set.timeout - /* timeout time */ + Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */ + if(timeout <=0 ) { + failf(data, "Transfer aborted due to timeout"); + return -SELECT_TIMEOUT; /* already too little time */ + } + } + + FD_ZERO (&readfd); /* clear it */ + FD_SET (tunnelsocket, &readfd); /* read socket */ + + /* get this in a backup variable to be able to restore it on each lap in the + select() loop */ + rkeepfd = readfd; + + ptr=data->state.buffer; + line_start = ptr; + + nread=0; + perline=0; + keepon=TRUE; + + while((nread<BUFSIZE) && (keepon && !error)) { + readfd = rkeepfd; /* set every lap */ + interval.tv_sec = timeout; + interval.tv_usec = 0; + + switch (select (tunnelsocket+1, &readfd, NULL, NULL, &interval)) { + case -1: /* select() error, stop reading */ + error = SELECT_ERROR; + failf(data, "Transfer aborted due to select() error"); + break; + case 0: /* timeout */ + error = SELECT_TIMEOUT; + failf(data, "Transfer aborted due to timeout"); + break; + default: + /* + * This code previously didn't use the kerberos sec_read() code + * to read, but when we use Curl_read() it may do so. Do confirm + * that this is still ok and then remove this comment! + */ + res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, + &gotbytes); + if(res< 0) + /* EWOULDBLOCK */ + continue; /* go loop yourself */ + else if(res) + keepon = FALSE; + else if(gotbytes <= 0) { + keepon = FALSE; + error = SELECT_ERROR; + failf(data, "Connection aborted"); + } + else { + /* we got a whole chunk of data, which can be anything from one + * byte to a set of lines and possibly just a piece of the last + * line */ + int i; + + nread += gotbytes; + for(i = 0; i < gotbytes; ptr++, i++) { + perline++; /* amount of bytes in this line so far */ + if(*ptr=='\n') { + /* a newline is CRLF in ftp-talk, so the CR is ignored as + the line isn't really terminated until the LF comes */ + + /* output debug output if that is requested */ + if(data->set.verbose) { + fputs("< ", data->set.err); + fwrite(line_start, perline, 1, data->set.err); + /* no need to output LF here, it is part of the data */ + } + + if('\r' == line_start[0]) { + /* end of headers */ + keepon=FALSE; + break; /* breaks out of loop, not switch */ + } + + if(2 == sscanf(line_start, "HTTP/1.%d %d", + &subversion, + &httperror)) { + ; + } + + perline=0; /* line starts over here */ + line_start = ptr+1; + } + } + } + break; + } /* switch */ + } /* while there's buffer left and loop is requested */ + + if(error) + return CURLE_READ_ERROR; + + if(200 != httperror) { + if(407 == httperror) + /* Added Nov 6 1998 */ + failf(data, "Proxy requires authorization!"); + else + failf(data, "Received error code %d from proxy", httperror); + return CURLE_READ_ERROR; + } + + infof (data, "Proxy replied to CONNECT request\n"); + return CURLE_OK; +} + +/* + * HTTP stuff to do at connect-time. + */ +CURLcode Curl_http_connect(struct connectdata *conn) +{ + struct SessionHandle *data; + CURLcode result; + + data=conn->data; + + /* If we are not using a proxy and we want a secure connection, + * perform SSL initialization & connection now. + * If using a proxy with https, then we must tell the proxy to CONNECT + * us to the host we want to talk to. Only after the connect + * has occured, can we start talking SSL + */ + + if(data->change.proxy && + ((conn->protocol & PROT_HTTPS) || data->set.tunnel_thru_httpproxy)) { + + /* either HTTPS over proxy, OR explicitly asked for a tunnel */ + result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket, + conn->hostname, conn->remote_port); + if(CURLE_OK != result) + return result; + } + + if(conn->protocol & PROT_HTTPS) { + /* now, perform the SSL initialization for this socket */ + result = Curl_SSLConnect(conn); + if(result) + return result; + } + + if(conn->bits.user_passwd && !data->state.this_is_a_follow) { + /* Authorization: is requested, this is not a followed location, get the + original host name */ + data->state.auth_host = strdup(conn->hostname); + } + + return CURLE_OK; +} + +CURLcode Curl_http_done(struct connectdata *conn) +{ + struct SessionHandle *data; + long *bytecount = &conn->bytecount; + struct HTTP *http; + + data=conn->data; + http=conn->proto.http; + + if(HTTPREQ_POST_FORM == data->set.httpreq) { + *bytecount = http->readbytecount + http->writebytecount; + + Curl_formclean(http->sendit); /* Now free that whole lot */ + + data->set.fread = http->storefread; /* restore */ + data->set.in = http->in; /* restore */ + } + else if(HTTPREQ_PUT == data->set.httpreq) { + *bytecount = http->readbytecount + http->writebytecount; + } + + if(0 == (http->readbytecount + conn->headerbytecount)) { + /* nothing was read from the HTTP server, this can't be right + so we return an error here */ + failf(data, "Empty reply from server"); + return CURLE_GOT_NOTHING; + } + + return CURLE_OK; +} + + +CURLcode Curl_http(struct connectdata *conn) +{ + struct SessionHandle *data=conn->data; + char *buf = data->state.buffer; /* this is a short cut to the buffer */ + CURLcode result=CURLE_OK; + struct HTTP *http; + struct Cookie *co=NULL; /* no cookies from start */ + char *ppath = conn->ppath; /* three previous function arguments */ + char *host = conn->name; + long *bytecount = &conn->bytecount; + + if(!conn->proto.http) { + /* Only allocate this struct if we don't already have it! */ + + http = (struct HTTP *)malloc(sizeof(struct HTTP)); + if(!http) + return CURLE_OUT_OF_MEMORY; + memset(http, 0, sizeof(struct HTTP)); + conn->proto.http = http; + } + else + http = conn->proto.http; + + /* We default to persistant connections */ + conn->bits.close = FALSE; + + if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) && + data->set.upload) { + data->set.httpreq = HTTPREQ_PUT; + } + + /* The User-Agent string has been built in url.c already, because it might + have been used in the proxy connect, but if we have got a header with + the user-agent string specified, we erase the previously made string + here. */ + if(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) { + free(conn->allocptr.uagent); + conn->allocptr.uagent=NULL; + } + + if((conn->bits.user_passwd) && !checkheaders(data, "Authorization:")) { + char *authorization; + + /* To prevent the user+password to get sent to other than the original + host due to a location-follow, we do some weirdo checks here */ + if(!data->state.this_is_a_follow || + !data->state.auth_host || + strequal(data->state.auth_host, conn->hostname)) { + sprintf(data->state.buffer, "%s:%s", + data->state.user, data->state.passwd); + if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer), + &authorization) >= 0) { + if(conn->allocptr.userpwd) + free(conn->allocptr.userpwd); + conn->allocptr.userpwd = aprintf( "Authorization: Basic %s\015\012", + authorization); + free(authorization); + } + } + } + if((data->change.referer) && !checkheaders(data, "Referer:")) { + if(conn->allocptr.ref) + free(conn->allocptr.ref); + conn->allocptr.ref = aprintf("Referer: %s\015\012", data->change.referer); + } + if(data->set.cookie && !checkheaders(data, "Cookie:")) { + if(conn->allocptr.cookie) + free(conn->allocptr.cookie); + conn->allocptr.cookie = aprintf("Cookie: %s\015\012", data->set.cookie); + } + + if(data->cookies) { + co = Curl_cookie_getlist(data->cookies, + host, ppath, + conn->protocol&PROT_HTTPS?TRUE:FALSE); + } + if (data->change.proxy && + !data->set.tunnel_thru_httpproxy && + !(conn->protocol&PROT_HTTPS)) { + /* The path sent to the proxy is in fact the entire URL */ + ppath = data->change.url; + } + if(HTTPREQ_POST_FORM == data->set.httpreq) { + /* we must build the whole darned post sequence first, so that we have + a size of the whole shebang before we start to send it */ + http->sendit = Curl_getFormData(data->set.httppost, &http->postsize); + } + + if(!checkheaders(data, "Host:")) { + /* if ptr_host is already set, it is almost OK since we only re-use + connections to the very same host and port, but when we use a HTTP + proxy we have a persistant connect and yet we must change the Host: + header! */ + + if(conn->allocptr.host) + free(conn->allocptr.host); + + if(((conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTPS)) || + (!(conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTP)) ) + /* If (HTTPS on port 443) OR (non-HTTPS on port 80) then don't include + the port number in the host string */ + conn->allocptr.host = aprintf("Host: %s\r\n", host); + else + conn->allocptr.host = aprintf("Host: %s:%d\r\n", host, + conn->remote_port); + } + + if(!checkheaders(data, "Pragma:")) + http->p_pragma = "Pragma: no-cache\r\n"; + + if(!checkheaders(data, "Accept:")) + http->p_accept = "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"; + + if(( (HTTPREQ_POST == data->set.httpreq) || + (HTTPREQ_POST_FORM == data->set.httpreq) || + (HTTPREQ_PUT == data->set.httpreq) ) && + conn->resume_from) { + /********************************************************************** + * Resuming upload in HTTP means that we PUT or POST and that we have + * got a resume_from value set. The resume value has already created + * a Range: header that will be passed along. We need to "fast forward" + * the file the given number of bytes and decrease the assume upload + * file size before we continue this venture in the dark lands of HTTP. + *********************************************************************/ + + if(conn->resume_from < 0 ) { + /* + * This is meant to get the size of the present remote-file by itself. + * We don't support this now. Bail out! + */ + conn->resume_from = 0; + } + + if(conn->resume_from) { + /* do we still game? */ + int passed=0; + + /* Now, let's read off the proper amount of bytes from the + input. If we knew it was a proper file we could've just + fseek()ed but we only have a stream here */ + do { + int readthisamountnow = (conn->resume_from - passed); + int actuallyread; + + if(readthisamountnow > BUFSIZE) + readthisamountnow = BUFSIZE; + + actuallyread = + data->set.fread(data->state.buffer, 1, readthisamountnow, + data->set.in); + + passed += actuallyread; + if(actuallyread != readthisamountnow) { + failf(data, "Could only read %d bytes from the input", + passed); + return CURLE_READ_ERROR; + } + } while(passed != conn->resume_from); /* loop until done */ + + /* now, decrease the size of the read */ + if(data->set.infilesize>0) { + data->set.infilesize -= conn->resume_from; + + if(data->set.infilesize <= 0) { + failf(data, "File already completely uploaded"); + return CURLE_PARTIAL_FILE; + } + } + /* we've passed, proceed as normal */ + } + } + if(conn->bits.use_range) { + /* + * A range is selected. We use different headers whether we're downloading + * or uploading and we always let customized headers override our internal + * ones if any such are specified. + */ + if((data->set.httpreq == HTTPREQ_GET) && + !checkheaders(data, "Range:")) { + conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", conn->range); + } + else if((data->set.httpreq != HTTPREQ_GET) && + !checkheaders(data, "Content-Range:")) { + + if(conn->resume_from) { + /* This is because "resume" was selected */ + long total_expected_size= conn->resume_from + data->set.infilesize; + conn->allocptr.rangeline = aprintf("Content-Range: bytes %s%ld/%ld\r\n", + conn->range, total_expected_size-1, + total_expected_size); + } + else { + /* Range was selected and then we just pass the incoming range and + append total size */ + conn->allocptr.rangeline = aprintf("Content-Range: bytes %s/%d\r\n", + conn->range, data->set.infilesize); + } + } + } + + do { + /* Use 1.1 unless the use specificly asked for 1.0 */ + const char *httpstring= + data->set.httpversion==CURL_HTTP_VERSION_1_0?"1.0":"1.1"; + + send_buffer *req_buffer; + struct curl_slist *headers=data->set.headers; + + /* initialize a dynamic send-buffer */ + req_buffer = add_buffer_init(); + + /* add the main request stuff */ + add_bufferf(req_buffer, + "%s " /* GET/HEAD/POST/PUT */ + "%s HTTP/%s\r\n" /* path */ + "%s" /* proxyuserpwd */ + "%s" /* userpwd */ + "%s" /* range */ + "%s" /* user agent */ + "%s" /* cookie */ + "%s" /* host */ + "%s" /* pragma */ + "%s" /* accept */ + "%s", /* referer */ + + data->set.customrequest?data->set.customrequest: + (data->set.no_body?"HEAD": + ((HTTPREQ_POST == data->set.httpreq) || + (HTTPREQ_POST_FORM == data->set.httpreq))?"POST": + (HTTPREQ_PUT == data->set.httpreq)?"PUT":"GET"), + ppath, httpstring, + (conn->bits.proxy_user_passwd && + conn->allocptr.proxyuserpwd)?conn->allocptr.proxyuserpwd:"", + (conn->bits.user_passwd && conn->allocptr.userpwd)? + conn->allocptr.userpwd:"", + (conn->bits.use_range && conn->allocptr.rangeline)? + conn->allocptr.rangeline:"", + (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)? + conn->allocptr.uagent:"", + (conn->allocptr.cookie?conn->allocptr.cookie:""), /* Cookie: <data> */ + (conn->allocptr.host?conn->allocptr.host:""), /* Host: host */ + http->p_pragma?http->p_pragma:"", + http->p_accept?http->p_accept:"", + (data->change.referer && conn->allocptr.ref)?conn->allocptr.ref:"" /* Referer: <data> <CRLF> */ + ); + + if(co) { + int count=0; + struct Cookie *store=co; + /* now loop through all cookies that matched */ + while(co) { + if(co->value && strlen(co->value)) { + if(0 == count) { + add_bufferf(req_buffer, "Cookie: "); + } + add_bufferf(req_buffer, + "%s%s=%s", count?"; ":"", co->name, co->value); + count++; + } + co = co->next; /* next cookie please */ + } + if(count) { + add_buffer(req_buffer, "\r\n", 2); + } + Curl_cookie_freelist(store); /* free the cookie list */ + co=NULL; + } + + if(data->set.timecondition) { + struct tm *thistime; + + /* Phil Karn (Fri, 13 Apr 2001) pointed out that the If-Modified-Since + * header family should have their times set in GMT as RFC2616 defines: + * "All HTTP date/time stamps MUST be represented in Greenwich Mean Time + * (GMT), without exception. For the purposes of HTTP, GMT is exactly + * equal to UTC (Coordinated Universal Time)." (see page 20 of RFC2616). + */ + +#ifdef HAVE_GMTIME_R + /* thread-safe version */ + struct tm keeptime; + thistime = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime); +#else + thistime = gmtime(&data->set.timevalue); +#endif + if(NULL == thistime) { + failf(data, "localtime() failed!"); + return CURLE_OUT_OF_MEMORY; + } + +#ifdef HAVE_STRFTIME + /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ + strftime(buf, BUFSIZE-1, "%a, %d %b %Y %H:%M:%S GMT", thistime); +#else + /* TODO: Right, we *could* write a replacement here */ + strcpy(buf, "no strftime() support"); +#endif + switch(data->set.timecondition) { + case TIMECOND_IFMODSINCE: + default: + add_bufferf(req_buffer, + "If-Modified-Since: %s\r\n", buf); + break; + case TIMECOND_IFUNMODSINCE: + add_bufferf(req_buffer, + "If-Unmodified-Since: %s\r\n", buf); + break; + case TIMECOND_LASTMOD: + add_bufferf(req_buffer, + "Last-Modified: %s\r\n", buf); + break; + } + } + + while(headers) { + char *ptr = strchr(headers->data, ':'); + if(ptr) { + /* we require a colon for this to be a true header */ + + ptr++; /* pass the colon */ + while(*ptr && isspace((int)*ptr)) + ptr++; + + if(*ptr) { + /* only send this if the contents was non-blank */ + + add_bufferf(req_buffer, "%s\r\n", headers->data); + } + } + headers = headers->next; + } + + if(HTTPREQ_POST_FORM == data->set.httpreq) { + if(Curl_FormInit(&http->form, http->sendit)) { + failf(data, "Internal HTTP POST error!"); + return CURLE_HTTP_POST_ERROR; + } + + http->storefread = data->set.fread; /* backup */ + http->in = data->set.in; /* backup */ + + data->set.fread = (curl_read_callback) + Curl_FormReader; /* set the read function to read from the + generated form data */ + data->set.in = (FILE *)&http->form; + + add_bufferf(req_buffer, + "Content-Length: %d\r\n", http->postsize); + + if(!checkheaders(data, "Expect:")) { + /* if not disabled explicitly we add a Expect: 100-continue + to the headers which actually speeds up post operations (as + there is one packet coming back from the web server) */ + add_bufferf(req_buffer, + "Expect: 100-continue\r\n"); + data->set.expect100header = TRUE; + } + + if(!checkheaders(data, "Content-Type:")) { + /* Get Content-Type: line from Curl_FormReadOneLine, which happens + to always be the first line. We can know this for sure since + we always build the formpost linked list the same way! + + The Content-Type header line also contains the MIME boundary + string etc why disabling this header is likely to not make things + work, but we support it anyway. + */ + char contentType[256]; + int linelength=0; + linelength = Curl_FormReadOneLine (contentType, + sizeof(contentType), + 1, + (FILE *)&http->form); + if(linelength == -1) { + failf(data, "Could not get Content-Type header line!"); + return CURLE_HTTP_POST_ERROR; + } + add_buffer(req_buffer, contentType, linelength); + } + + /* make the request end in a true CRLF */ + add_buffer(req_buffer, "\r\n", 2); + + /* set upload size to the progress meter */ + Curl_pgrsSetUploadSize(data, http->postsize); + + /* fire away the whole request to the server */ + result = add_buffer_send(conn->firstsocket, conn, req_buffer, + &data->info.request_size); + if(result) + failf(data, "Failed sending POST request"); + else + /* setup variables for the upcoming transfer */ + result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE, + &http->readbytecount, + conn->firstsocket, + &http->writebytecount); + if(result) { + Curl_formclean(http->sendit); /* free that whole lot */ + return result; + } + } + else if(HTTPREQ_PUT == data->set.httpreq) { + /* Let's PUT the data to the server! */ + + if(data->set.infilesize>0) { + add_bufferf(req_buffer, + "Content-Length: %d\r\n\r\n", /* file size */ + data->set.infilesize ); + } + else + add_bufferf(req_buffer, "\015\012"); + + /* set the upload size to the progress meter */ + Curl_pgrsSetUploadSize(data, data->set.infilesize); + + /* this sends the buffer and frees all the buffer resources */ + result = add_buffer_send(conn->firstsocket, conn, req_buffer, + &data->info.request_size); + if(result) + failf(data, "Faied sending POST request"); + else + /* prepare for transfer */ + result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE, + &http->readbytecount, + conn->firstsocket, + &http->writebytecount); + if(result) + return result; + + } + else { + if(HTTPREQ_POST == data->set.httpreq) { + /* this is the simple POST, using x-www-form-urlencoded style */ + + if(!data->set.postfields) { + /* + * This is an attempt to do a POST without having anything to + * actually send. Let's make a NULL pointer equal "" here. Good/bad + * ? + */ + data->set.postfields = (char *)""; + data->set.postfieldsize = 0; /* it might been set to something illegal, + anything > 0 would be! */ + } + + if(!checkheaders(data, "Content-Length:")) + /* we allow replacing this header, although it isn't very wise to + actually set your own */ + add_bufferf(req_buffer, + "Content-Length: %d\r\n", + (data->set.postfieldsize?data->set.postfieldsize: + strlen(data->set.postfields)) ); + + if(!checkheaders(data, "Content-Type:")) + add_bufferf(req_buffer, + "Content-Type: application/x-www-form-urlencoded\r\n"); + + /* and here comes the actual data */ + if(data->set.postfieldsize) { + add_buffer(req_buffer, "\r\n", 2); + add_buffer(req_buffer, data->set.postfields, + data->set.postfieldsize); + } + else { + add_bufferf(req_buffer, + "\r\n" + "%s", + data->set.postfields ); + } + } + else + add_buffer(req_buffer, "\r\n", 2); + + /* issue the request */ + result = add_buffer_send(conn->firstsocket, conn, req_buffer, + &data->info.request_size); + + if(result) + failf(data, "Failed sending HTTP request"); + else + /* HTTP GET/HEAD download: */ + result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE, bytecount, + -1, NULL); /* nothing to upload */ + } + if(result) + return result; + } while (0); /* this is just a left-over from the multiple document download + attempts */ + + return CURLE_OK; +} + + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/http.h b/Source/CTest/Curl/http.h new file mode 100644 index 0000000..abd96ba --- /dev/null +++ b/Source/CTest/Curl/http.h @@ -0,0 +1,42 @@ +#ifndef __HTTP_H +#define __HTTP_H + +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* ftp can use this as well */ +CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, + int tunnelsocket, + char *hostname, int remote_port); + +/* protocol-specific functions set up to be called by the main engine */ +CURLcode Curl_http(struct connectdata *conn); +CURLcode Curl_http_done(struct connectdata *conn); +CURLcode Curl_http_connect(struct connectdata *conn); + +/* The following functions are defined in http_chunks.c */ +void Curl_httpchunk_init(struct connectdata *conn); +CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap, + ssize_t length, ssize_t *wrote); + +#endif diff --git a/Source/CTest/Curl/http_chunks.c b/Source/CTest/Curl/http_chunks.c new file mode 100644 index 0000000..1754079 --- /dev/null +++ b/Source/CTest/Curl/http_chunks.c @@ -0,0 +1,230 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +#include "setup.h" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> + +#include "urldata.h" /* it includes http_chunks.h */ +#include "sendf.h" /* for the client write stuff */ + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* + * Chunk format (simplified): + * + * <HEX SIZE>[ chunk extension ] CRLF + * <DATA> CRLF + * + * Highlights from RFC2616 section 3.6 say: + + The chunked encoding modifies the body of a message in order to + transfer it as a series of chunks, each with its own size indicator, + followed by an OPTIONAL trailer containing entity-header fields. This + allows dynamically produced content to be transferred along with the + information necessary for the recipient to verify that it has + received the full message. + + Chunked-Body = *chunk + last-chunk + trailer + CRLF + + chunk = chunk-size [ chunk-extension ] CRLF + chunk-data CRLF + chunk-size = 1*HEX + last-chunk = 1*("0") [ chunk-extension ] CRLF + + chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) + chunk-ext-name = token + chunk-ext-val = token | quoted-string + chunk-data = chunk-size(OCTET) + trailer = *(entity-header CRLF) + + The chunk-size field is a string of hex digits indicating the size of + the chunk. The chunked encoding is ended by any chunk whose size is + zero, followed by the trailer, which is terminated by an empty line. + + */ + + +void Curl_httpchunk_init(struct connectdata *conn) +{ + struct Curl_chunker *chunk = &conn->proto.http->chunk; + chunk->hexindex=0; /* start at 0 */ + chunk->dataleft=0; /* no data left yet! */ + chunk->state = CHUNK_HEX; /* we get hex first! */ +} + +/* + * chunk_read() returns a OK for normal operations, or a positive return code + * for errors. STOP means this sequence of chunks is complete. The 'wrote' + * argument is set to tell the caller how many bytes we actually passed to the + * client (for byte-counting and whatever). + * + * The states and the state-machine is further explained in the header file. + */ +CHUNKcode Curl_httpchunk_read(struct connectdata *conn, + char *datap, + size_t length, + size_t *wrote) +{ + CURLcode result; + struct Curl_chunker *ch = &conn->proto.http->chunk; + int piece; + *wrote = 0; /* nothing yet */ + + while(length) { + switch(ch->state) { + case CHUNK_HEX: + if(isxdigit((int)*datap)) { + if(ch->hexindex < MAXNUM_SIZE) { + ch->hexbuffer[ch->hexindex] = *datap; + datap++; + length--; + ch->hexindex++; + } + else { + return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */ + } + } + else { + if(0 == ch->hexindex) { + /* This is illegal data, we received junk where we expected + a hexadecimal digit. */ + return CHUNKE_ILLEGAL_HEX; + } + /* length and datap are unmodified */ + ch->hexbuffer[ch->hexindex]=0; + ch->datasize=strtoul(ch->hexbuffer, NULL, 16); + ch->state = CHUNK_POSTHEX; + } + break; + + case CHUNK_POSTHEX: + /* In this state, we're waiting for CRLF to arrive. We support + this to allow so called chunk-extensions to show up here + before the CRLF comes. */ + if(*datap == '\r') + ch->state = CHUNK_CR; + length--; + datap++; + break; + + case CHUNK_CR: + /* waiting for the LF */ + if(*datap == '\n') { + /* we're now expecting data to come, unless size was zero! */ + if(0 == ch->datasize) { + ch->state = CHUNK_STOP; /* stop reading! */ + if(1 == length) { + /* This was the final byte, return right now */ + return CHUNKE_STOP; + } + } + else + ch->state = CHUNK_DATA; + } + else + /* previously we got a fake CR, go back to CR waiting! */ + ch->state = CHUNK_CR; + datap++; + length--; + break; + + case CHUNK_DATA: + /* we get pure and fine data + + We expect another 'datasize' of data. We have 'length' right now, + it can be more or less than 'datasize'. Get the smallest piece. + */ + piece = (ch->datasize >= length)?length:ch->datasize; + + /* Write the data portion available */ + result = Curl_client_write(conn->data, CLIENTWRITE_BODY, datap, piece); + if(result) + return CHUNKE_WRITE_ERROR; + *wrote += piece; + + ch->datasize -= piece; /* decrease amount left to expect */ + datap += piece; /* move read pointer forward */ + length -= piece; /* decrease space left in this round */ + + if(0 == ch->datasize) + /* end of data this round, we now expect a trailing CRLF */ + ch->state = CHUNK_POSTCR; + break; + + case CHUNK_POSTCR: + if(*datap == '\r') { + ch->state = CHUNK_POSTLF; + datap++; + length--; + } + else + return CHUNKE_BAD_CHUNK; + break; + + case CHUNK_POSTLF: + if(*datap == '\n') { + /* + * The last one before we go back to hex state and start all + * over. + */ + Curl_httpchunk_init(conn); + datap++; + length--; + } + else + return CHUNKE_BAD_CHUNK; + break; + + case CHUNK_STOP: + /* If we arrive here, there is data left in the end of the buffer + even if there's no more chunks to read */ + ch->dataleft = length; + return CHUNKE_STOP; /* return stop */ + default: + return CHUNKE_STATE_ERROR; + } + } + return CHUNKE_OK; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/http_chunks.h b/Source/CTest/Curl/http_chunks.h new file mode 100644 index 0000000..c3e54a3 --- /dev/null +++ b/Source/CTest/Curl/http_chunks.h @@ -0,0 +1,87 @@ +#ifndef __HTTP_CHUNKS_H +#define __HTTP_CHUNKS_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +/* + * The longest possible hexadecimal number we support in a chunked transfer. + * Weird enough, RFC2616 doesn't set a maximum size! Since we use strtoul() + * to convert it, we "only" support 2^32 bytes chunk data. + */ +#define MAXNUM_SIZE 16 + +typedef enum { + CHUNK_FIRST, /* never use */ + + /* In this we await and buffer all hexadecimal digits until we get one + that isn't a hexadecimal digit. When done, we go POSTHEX */ + CHUNK_HEX, + + /* We have received the hexadecimal digit and we eat all characters until + we get a CRLF pair. When we see a CR we go to the CR state. */ + CHUNK_POSTHEX, + + /* A single CR has been found and we should get a LF right away in this + state or we go back to POSTHEX. When LF is received, we go to DATA. + If the size given was zero, we set state to STOP and return. */ + CHUNK_CR, + + /* We eat the amount of data specified. When done, we move on to the + POST_CR state. */ + CHUNK_DATA, + + /* POSTCR should get a CR and nothing else, then move to POSTLF */ + CHUNK_POSTCR, + + /* POSTLF should get a LF and nothing else, then move back to HEX as + the CRLF combination marks the end of a chunk */ + CHUNK_POSTLF, + + /* This is mainly used to really mark that we're out of the game. + NOTE: that there's a 'dataleft' field in the struct that will tell how + many bytes that were not passed to the client in the end of the last + buffer! */ + CHUNK_STOP, + + CHUNK_LAST /* never use */ +} ChunkyState; + +typedef enum { + CHUNKE_STOP = -1, + CHUNKE_OK = 0, + CHUNKE_TOO_LONG_HEX = 1, + CHUNKE_ILLEGAL_HEX, + CHUNKE_BAD_CHUNK, + CHUNKE_WRITE_ERROR, + CHUNKE_STATE_ERROR, + CHUNKE_LAST +} CHUNKcode; + +struct Curl_chunker { + char hexbuffer[ MAXNUM_SIZE + 1]; + int hexindex; + ChunkyState state; + size_t datasize; + size_t dataleft; /* untouched data amount at the end of the last buffer */ +}; + +#endif diff --git a/Source/CTest/Curl/if2ip.c b/Source/CTest/Curl/if2ip.c new file mode 100644 index 0000000..0a63730 --- /dev/null +++ b/Source/CTest/Curl/if2ip.c @@ -0,0 +1,134 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#if ! defined(WIN32) && ! defined(__BEOS__) && !defined(__CYGWIN32__) + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SYS_TIME_H +/* This must be before net/if.h for AIX 3.2 to enjoy life */ +#include <sys/time.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> + +/* -- if2ip() -- */ +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif + +#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) +#include "inet_ntoa_r.h" +#endif + +#ifdef VMS +#define IOCTL_3_ARGS +#include <inet.h> +#endif + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +#define SYS_ERROR -1 + +char *Curl_if2ip(char *interface, char *buf, int buf_size) +{ + int dummy; + char *ip=NULL; + + if(!interface) + return NULL; + + dummy = socket(AF_INET, SOCK_STREAM, 0); + if (SYS_ERROR == dummy) { + return NULL; + } + else { + struct ifreq req; + memset(&req, 0, sizeof(req)); + strcpy(req.ifr_name, interface); + req.ifr_addr.sa_family = AF_INET; +#ifdef IOCTL_3_ARGS + if (SYS_ERROR == ioctl(dummy, SIOCGIFADDR, &req)) { +#else + if (SYS_ERROR == ioctl(dummy, SIOCGIFADDR, &req, sizeof(req))) { +#endif + sclose(dummy); + return NULL; + } + else { + struct in_addr in; + + struct sockaddr_in *s = (struct sockaddr_in *)&req.ifr_dstaddr; + memcpy(&in, &(s->sin_addr.s_addr), sizeof(in)); +#if defined(HAVE_INET_NTOA_R) + ip = inet_ntoa_r(in,buf,buf_size); +#else + ip = strncpy(buf,inet_ntoa(in),buf_size); + ip[buf_size - 1] = 0; +#endif + } + sclose(dummy); + } + return ip; +} + +/* -- end of if2ip() -- */ +#else +#define if2ip(x) NULL +#endif + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/if2ip.h b/Source/CTest/Curl/if2ip.h new file mode 100644 index 0000000..2fbb471 --- /dev/null +++ b/Source/CTest/Curl/if2ip.h @@ -0,0 +1,33 @@ +#ifndef __IF2IP_H +#define __IF2IP_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +#include "setup.h" + +#if ! defined(WIN32) && ! defined(__BEOS__) && !defined(__CYGWIN32__) +extern char *Curl_if2ip(char *interface, char *buf, int buf_size); +#else +#define Curl_if2ip(a,b,c) NULL +#endif + +#endif diff --git a/Source/CTest/Curl/inet_ntoa_r.h b/Source/CTest/Curl/inet_ntoa_r.h new file mode 100644 index 0000000..7959c49 --- /dev/null +++ b/Source/CTest/Curl/inet_ntoa_r.h @@ -0,0 +1,9 @@ +#ifndef __INET_NTOA_R_H +#define __INET_NTOA_R_H +/* + * My solaris 5.6 system running gcc 2.8.1 does *not* have this prototype + * in any system include file! Isn't that weird? + */ +char *inet_ntoa_r(const struct in_addr in, char *buffer, int buflen); + +#endif diff --git a/Source/CTest/Curl/krb4.c b/Source/CTest/Curl/krb4.c new file mode 100644 index 0000000..a182bea --- /dev/null +++ b/Source/CTest/Curl/krb4.c @@ -0,0 +1,401 @@ +/* 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" + +#ifdef KRB4 + +#include "security.h" +#include "base64.h" +#include <stdlib.h> +#include <netdb.h> +#include <syslog.h> +#include <string.h> +#include <krb.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for getpid() */ +#endif + +#include "ftp.h" +#include "sendf.h" + +#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) +#include "inet_ntoa_r.h" +#endif + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +#define LOCAL_ADDR (&conn->local_addr) +#define REMOTE_ADDR (&conn->serv_addr) +#define myctladdr LOCAL_ADDR +#define hisctladdr REMOTE_ADDR + +struct krb4_data { + des_cblock key; + des_key_schedule schedule; + char name[ANAME_SZ]; + char instance[INST_SZ]; + char realm[REALM_SZ]; +}; + +#ifndef HAVE_STRLCPY +/* if it ever goes non-static, make it Curl_ prefixed! */ +static size_t +strlcpy (char *dst, const char *src, size_t dst_sz) +{ + size_t n; + char *p; + + for (p = dst, n = 0; + n + 1 < dst_sz && *src != '\0'; + ++p, ++src, ++n) + *p = *src; + *p = '\0'; + if (*src == '\0') + return n; + else + return n + strlen (src); +} +#else +size_t strlcpy (char *dst, const char *src, size_t dst_sz); +#endif + +static int +krb4_check_prot(void *app_data, int level) +{ + app_data = NULL; /* prevent compiler warning */ + if(level == prot_confidential) + return -1; + return 0; +} + +static int +krb4_decode(void *app_data, void *buf, int len, int level, + struct connectdata *conn) +{ + MSG_DAT m; + int e; + struct krb4_data *d = app_data; + + if(level == prot_safe) + e = krb_rd_safe(buf, len, &d->key, + (struct sockaddr_in *)REMOTE_ADDR, + (struct sockaddr_in *)LOCAL_ADDR, &m); + else + e = krb_rd_priv(buf, len, d->schedule, &d->key, + (struct sockaddr_in *)REMOTE_ADDR, + (struct sockaddr_in *)LOCAL_ADDR, &m); + if(e){ + syslog(LOG_ERR, "krb4_decode: %s", krb_get_err_text(e)); + return -1; + } + memmove(buf, m.app_data, m.app_length); + return m.app_length; +} + +static int +krb4_overhead(void *app_data, int level, int len) +{ + /* no arguments are used, just init them to prevent compiler warnings */ + app_data = NULL; + level = 0; + len = 0; + return 31; +} + +static int +krb4_encode(void *app_data, void *from, int length, int level, void **to, + struct connectdata *conn) +{ + struct krb4_data *d = app_data; + *to = malloc(length + 31); + if(level == prot_safe) + return krb_mk_safe(from, *to, length, &d->key, + (struct sockaddr_in *)LOCAL_ADDR, + (struct sockaddr_in *)REMOTE_ADDR); + else if(level == prot_private) + return krb_mk_priv(from, *to, length, d->schedule, &d->key, + (struct sockaddr_in *)LOCAL_ADDR, + (struct sockaddr_in *)REMOTE_ADDR); + else + return -1; +} + +static int +mk_auth(struct krb4_data *d, KTEXT adat, + const char *service, char *host, int checksum) +{ + int ret; + CREDENTIALS cred; + char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ]; + + strlcpy(sname, service, sizeof(sname)); + strlcpy(inst, krb_get_phost(host), sizeof(inst)); + strlcpy(realm, krb_realmofhost(host), sizeof(realm)); + ret = krb_mk_req(adat, sname, inst, realm, checksum); + if(ret) + return ret; + strlcpy(sname, service, sizeof(sname)); + strlcpy(inst, krb_get_phost(host), sizeof(inst)); + strlcpy(realm, krb_realmofhost(host), sizeof(realm)); + ret = krb_get_cred(sname, inst, realm, &cred); + memmove(&d->key, &cred.session, sizeof(des_cblock)); + des_key_sched(&d->key, d->schedule); + memset(&cred, 0, sizeof(cred)); + return ret; +} + +static int +krb4_auth(void *app_data, struct connectdata *conn) +{ + int ret; + char *p; + int len; + KTEXT_ST adat; + MSG_DAT msg_data; + int checksum; + u_int32_t cs; + struct krb4_data *d = app_data; + char *host = conn->hostaddr->h_name; + ssize_t nread; + int l = sizeof(conn->local_addr); + struct SessionHandle *data = conn->data; + + if(getsockname(conn->firstsocket, + (struct sockaddr *)LOCAL_ADDR, &l) < 0) + perror("getsockname()"); + + checksum = getpid(); + ret = mk_auth(d, &adat, "ftp", host, checksum); + if(ret == KDC_PR_UNKNOWN) + ret = mk_auth(d, &adat, "rcmd", host, checksum); + if(ret) { + Curl_infof(data, "%s\n", krb_get_err_text(ret)); + return AUTH_CONTINUE; + } + +#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM + if (krb_get_config_bool("nat_in_use")) { + struct sockaddr_in *localaddr = (struct sockaddr_in *)LOCAL_ADDR; + struct in_addr natAddr; + + if (krb_get_our_ip_for_realm(krb_realmofhost(host), + &natAddr) != KSUCCESS + && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS) + Curl_infof(data, "Can't get address for realm %s\n", + krb_realmofhost(host)); + else { + if (natAddr.s_addr != localaddr->sin_addr.s_addr) { +#ifdef HAVE_INET_NTOA_R + char ntoa_buf[64]; + char *ip = (char *)inet_ntoa_r(natAddr, ntoa_buf, sizeof(ntoa_buf)); +#else + char *ip = (char *)inet_ntoa(natAddr); +#endif + Curl_infof(data, "Using NAT IP address (%s) for kerberos 4\n", ip); + localaddr->sin_addr = natAddr; + } + } + } +#endif + + if(Curl_base64_encode(adat.dat, adat.length, &p) < 0) { + Curl_failf(data, "Out of memory base64-encoding"); + return AUTH_CONTINUE; + } + + if(Curl_ftpsendf(conn, "ADAT %s", p)) + return -2; + + nread = Curl_GetFTPResponse(data->state.buffer, conn, NULL); + if(nread < 0) + return -1; + free(p); + + if(data->state.buffer[0] != '2'){ + Curl_failf(data, "Server didn't accept auth data"); + return AUTH_ERROR; + } + + p = strstr(data->state.buffer, "ADAT="); + if(!p) { + Curl_failf(data, "Remote host didn't send adat reply"); + return AUTH_ERROR; + } + p += 5; + len = Curl_base64_decode(p, adat.dat); + if(len < 0) { + Curl_failf(data, "Failed to decode base64 from server"); + return AUTH_ERROR; + } + adat.length = len; + ret = krb_rd_safe(adat.dat, adat.length, &d->key, + (struct sockaddr_in *)hisctladdr, + (struct sockaddr_in *)myctladdr, &msg_data); + if(ret) { + Curl_failf(data, "Error reading reply from server: %s", + krb_get_err_text(ret)); + return AUTH_ERROR; + } + krb_get_int(msg_data.app_data, &cs, 4, 0); + if(cs - checksum != 1) { + Curl_failf(data, "Bad checksum returned from server"); + return AUTH_ERROR; + } + return AUTH_OK; +} + +struct Curl_sec_client_mech Curl_krb4_client_mech = { + "KERBEROS_V4", + sizeof(struct krb4_data), + NULL, /* init */ + krb4_auth, + NULL, /* end */ + krb4_check_prot, + krb4_overhead, + krb4_encode, + krb4_decode +}; + +void Curl_krb_kauth(struct connectdata *conn) +{ + des_cblock key; + des_key_schedule schedule; + KTEXT_ST tkt, tktcopy; + char *name; + char *p; + char passwd[100]; + int tmp; + ssize_t nread; + + int save; + + save = Curl_set_command_prot(conn, prot_private); + + if(Curl_ftpsendf(conn, "SITE KAUTH %s", conn->data->state.user)) + return; + + nread = Curl_GetFTPResponse(conn->data->state.buffer, + conn, NULL); + if(nread < 0) + return /*CURLE_OPERATION_TIMEOUTED*/; + + if(conn->data->state.buffer[0] != '3'){ + Curl_set_command_prot(conn, save); + return; + } + + p = strstr(conn->data->state.buffer, "T="); + if(!p) { + Curl_failf(conn->data, "Bad reply from server"); + Curl_set_command_prot(conn, save); + return; + } + + p += 2; + tmp = Curl_base64_decode(p, &tkt.dat); + if(tmp < 0) { + Curl_failf(conn->data, "Failed to decode base64 in reply.\n"); + Curl_set_command_prot(conn, save); + return; + } + tkt.length = tmp; + tktcopy.length = tkt.length; + + p = strstr(conn->data->state.buffer, "P="); + if(!p) { + Curl_failf(conn->data, "Bad reply from server"); + Curl_set_command_prot(conn, save); + return; + } + name = p + 2; + for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++); + *p = 0; + + des_string_to_key (conn->data->state.passwd, &key); + des_key_sched(&key, schedule); + + des_pcbc_encrypt((des_cblock*)tkt.dat, (des_cblock*)tktcopy.dat, + tkt.length, + schedule, &key, DES_DECRYPT); + if (strcmp ((char*)tktcopy.dat + 8, + KRB_TICKET_GRANTING_TICKET) != 0) { + afs_string_to_key (passwd, + krb_realmofhost(conn->hostaddr->h_name), + &key); + des_key_sched (&key, schedule); + des_pcbc_encrypt((des_cblock*)tkt.dat, (des_cblock*)tktcopy.dat, + tkt.length, + schedule, &key, DES_DECRYPT); + } + memset(key, 0, sizeof(key)); + memset(schedule, 0, sizeof(schedule)); + memset(passwd, 0, sizeof(passwd)); + if(Curl_base64_encode(tktcopy.dat, tktcopy.length, &p) < 0) { + failf(conn->data, "Out of memory base64-encoding."); + Curl_set_command_prot(conn, save); + return; + } + memset (tktcopy.dat, 0, tktcopy.length); + + if(Curl_ftpsendf(conn, "SITE KAUTH %s %s", name, p)) + return; + + nread = Curl_GetFTPResponse(conn->data->state.buffer, + conn, NULL); + if(nread < 0) + return /*CURLE_OPERATION_TIMEOUTED*/; + free(p); + Curl_set_command_prot(conn, save); +} + +#endif /* KRB4 */ + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/krb4.h b/Source/CTest/Curl/krb4.h new file mode 100644 index 0000000..6880486 --- /dev/null +++ b/Source/CTest/Curl/krb4.h @@ -0,0 +1,27 @@ +#ifndef __KRB4_H +#define __KRB4_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +void Curl_krb_kauth(struct connectdata *conn); + +#endif diff --git a/Source/CTest/Curl/ldap.c b/Source/CTest/Curl/ldap.c new file mode 100644 index 0000000..9080669 --- /dev/null +++ b/Source/CTest/Curl/ldap.c @@ -0,0 +1,231 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) +#else +# ifdef HAVE_UNISTD_H +# include <unistd.h> +# endif +# ifdef HAVE_DLFCN_H +# include <dlfcn.h> +# endif +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "sendf.h" +#include "escape.h" +#include "transfer.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + + +#define DYNA_GET_FUNCTION(type, fnc) \ + (fnc) = (type)DynaGetFunction(#fnc); \ + if ((fnc) == NULL) { \ + return CURLE_FUNCTION_NOT_FOUND; \ + } \ + +/*********************************************************************** + */ +static void *libldap = NULL; +static void *liblber = NULL; + +static void DynaOpen(void) +{ +#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) + if (libldap == NULL) { + /* + * libldap.so should be able to resolve its dependency on + * liblber.so automatically, but since it does not we will + * handle it here by opening liblber.so as global. + */ + dlopen("liblber.so", +#ifdef RTLD_LAZY_GLOBAL /* It turns out some systems use this: */ + RTLD_LAZY_GLOBAL +#else +#ifdef RTLD_GLOBAL + RTLD_LAZY | RTLD_GLOBAL +#else + /* and some systems don't have the RTLD_GLOBAL symbol */ + RTLD_LAZY +#endif +#endif + ); + libldap = dlopen("libldap.so", RTLD_LAZY); + } +#endif +} + +static void DynaClose(void) +{ +#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) + if (libldap) { + dlclose(libldap); + libldap=NULL; + } + if (liblber) { + dlclose(liblber); + liblber=NULL; + } +#endif +} + +static void * DynaGetFunction(const char *name) +{ + void *func = NULL; + +#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) + if (libldap) { + func = dlsym(libldap, name); + } +#endif + + return func; +} + +static int WriteProc(void *param, char *text, int len) +{ + struct SessionHandle *data = (struct SessionHandle *)param; + len = 0; /* prevent compiler warning */ + Curl_client_write(data, CLIENTWRITE_BODY, text, 0); + return 0; +} + +/*********************************************************************** + */ +CURLcode Curl_ldap(struct connectdata *conn) +{ + CURLcode status = CURLE_OK; + int rc; + void *(*ldap_open)(char *, int); + int (*ldap_simple_bind_s)(void *, char *, char *); + int (*ldap_unbind_s)(void *); + int (*ldap_url_search_s)(void *, char *, int, void **); + void *(*ldap_first_entry)(void *, void *); + void *(*ldap_next_entry)(void *, void *); + char *(*ldap_err2string)(int); + int (*ldap_entry2text)(void *, char *, void *, void *, char **, char **, int (*)(void *, char *, int), void *, char *, int, unsigned long); + int (*ldap_entry2html)(void *, char *, void *, void *, char **, char **, int (*)(void *, char *, int), void *, char *, int, unsigned long, char *, char *); + void *server; + void *result; + void *entryIterator; + + int ldaptext; + struct SessionHandle *data=conn->data; + + infof(data, "LDAP: %s %s\n", data->change.url); + + DynaOpen(); + if (libldap == NULL) { + failf(data, "The needed LDAP library/libraries couldn't be opened"); + return CURLE_LIBRARY_NOT_FOUND; + } + + ldaptext = data->set.ftp_ascii; /* This is a dirty hack */ + + /* The types are needed because ANSI C distinguishes between + * pointer-to-object (data) and pointer-to-function. + */ + DYNA_GET_FUNCTION(void *(*)(char *, int), ldap_open); + DYNA_GET_FUNCTION(int (*)(void *, char *, char *), ldap_simple_bind_s); + DYNA_GET_FUNCTION(int (*)(void *), ldap_unbind_s); + DYNA_GET_FUNCTION(int (*)(void *, char *, int, void **), ldap_url_search_s); + DYNA_GET_FUNCTION(void *(*)(void *, void *), ldap_first_entry); + DYNA_GET_FUNCTION(void *(*)(void *, void *), ldap_next_entry); + DYNA_GET_FUNCTION(char *(*)(int), ldap_err2string); + DYNA_GET_FUNCTION(int (*)(void *, char *, void *, void *, char **, char **, int (*)(void *, char *, int), void *, char *, int, unsigned long), ldap_entry2text); + DYNA_GET_FUNCTION(int (*)(void *, char *, void *, void *, char **, char **, int (*)(void *, char *, int), void *, char *, int, unsigned long, char *, char *), ldap_entry2html); + + server = ldap_open(conn->hostname, conn->port); + if (server == NULL) { + failf(data, "LDAP: Cannot connect to %s:%d", + conn->hostname, conn->port); + status = CURLE_COULDNT_CONNECT; + } else { + rc = ldap_simple_bind_s(server, + conn->bits.user_passwd?data->state.user:NULL, + conn->bits.user_passwd?data->state.passwd:NULL); + if (rc != 0) { + failf(data, "LDAP: %s", ldap_err2string(rc)); + status = CURLE_LDAP_CANNOT_BIND; + } else { + rc = ldap_url_search_s(server, data->change.url, 0, &result); + if (rc != 0) { + failf(data, "LDAP: %s", ldap_err2string(rc)); + status = CURLE_LDAP_SEARCH_FAILED; + } else { + for (entryIterator = ldap_first_entry(server, result); + entryIterator; + entryIterator = ldap_next_entry(server, entryIterator)) + { + if (ldaptext) { + rc = ldap_entry2text(server, NULL, entryIterator, NULL, + NULL, NULL, WriteProc, data, + (char *)"", 0, 0); + if (rc != 0) { + failf(data, "LDAP: %s", ldap_err2string(rc)); + status = CURLE_LDAP_SEARCH_FAILED; + } + } else { + rc = ldap_entry2html(server, NULL, entryIterator, NULL, + NULL, NULL, WriteProc, data, + (char *)"", 0, 0, NULL, NULL); + if (rc != 0) { + failf(data, "LDAP: %s", ldap_err2string(rc)); + status = CURLE_LDAP_SEARCH_FAILED; + } + } + } + } + ldap_unbind_s(server); + } + } + DynaClose(); + + /* no data to transfer */ + Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + + return status; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/ldap.h b/Source/CTest/Curl/ldap.h new file mode 100644 index 0000000..c9ab7c8 --- /dev/null +++ b/Source/CTest/Curl/ldap.h @@ -0,0 +1,29 @@ +#ifndef __LDAP_H +#define __LDAP_H + +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +CURLcode Curl_ldap(struct connectdata *conn); +CURLcode Curl_ldap_done(struct connectdata *conn); + +#endif /* __LDAP_H */ diff --git a/Source/CTest/Curl/llist.c b/Source/CTest/Curl/llist.c new file mode 100644 index 0000000..0d969ae --- /dev/null +++ b/Source/CTest/Curl/llist.c @@ -0,0 +1,168 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2002, Daniel Stenberg, <daniel@haxx.se>, et al + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <string.h> +#include <stdlib.h> + +#include "llist.h" + +#ifdef MALLOCDEBUG +/* this must be the last include file */ +#include "memdebug.h" +#endif +void +curl_llist_init(curl_llist *l, curl_llist_dtor dtor) +{ + l->size = 0; + l->dtor = dtor; + l->head = NULL; + l->tail = NULL; +} + +curl_llist * +curl_llist_alloc(curl_llist_dtor dtor) +{ + curl_llist *list; + + list = (curl_llist *)malloc(sizeof(curl_llist)); + if(NULL == list) + return NULL; + + curl_llist_init(list, dtor); + + return list; +} + +int +curl_llist_insert_next(curl_llist *list, curl_llist_element *e, const void *p) +{ + curl_llist_element *ne; + + ne = (curl_llist_element *) malloc(sizeof(curl_llist_element)); + ne->ptr = (void *) p; + if (list->size == 0) { + list->head = ne; + list->head->prev = NULL; + list->head->next = NULL; + list->tail = ne; + } else { + ne->next = e->next; + ne->prev = e; + if (e->next) { + e->next->prev = ne; + } else { + list->tail = ne; + } + e->next = ne; + } + + ++list->size; + + return 1; +} + +int +curl_llist_insert_prev(curl_llist *list, curl_llist_element *e, const void *p) +{ + curl_llist_element *ne; + + ne = (curl_llist_element *) malloc(sizeof(curl_llist_element)); + ne->ptr = (void *) p; + if (list->size == 0) { + list->head = ne; + list->head->prev = NULL; + list->head->next = NULL; + list->tail = ne; + } else { + ne->next = e; + ne->prev = e->prev; + if (e->prev) + e->prev->next = ne; + else + list->head = ne; + e->prev = ne; + } + + ++list->size; + + return 1; +} + +int +curl_llist_remove(curl_llist *list, curl_llist_element *e, void *user) +{ + if (e == NULL || list->size == 0) + return 1; + + if (e == list->head) { + list->head = e->next; + + if (list->head == NULL) + list->tail = NULL; + else + e->next->prev = NULL; + } else { + e->prev->next = e->next; + if (!e->next) + list->tail = e->prev; + else + e->next->prev = e->prev; + } + + list->dtor(user, e->ptr); + free(e); + --list->size; + + return 1; +} + +int +curl_llist_remove_next(curl_llist *list, curl_llist_element *e, void *user) +{ + return curl_llist_remove(list, e->next, user); +} + +int +curl_llist_remove_prev(curl_llist *list, curl_llist_element *e, void *user) +{ + return curl_llist_remove(list, e->prev, user); +} + +size_t +curl_llist_count(curl_llist *list) +{ + return list->size; +} + +void +curl_llist_destroy(curl_llist *list, void *user) +{ + while (list->size > 0) { + curl_llist_remove(list, CURL_LLIST_TAIL(list), user); + } + + free(list); + list = NULL; +} diff --git a/Source/CTest/Curl/llist.h b/Source/CTest/Curl/llist.h new file mode 100644 index 0000000..f598a1c --- /dev/null +++ b/Source/CTest/Curl/llist.h @@ -0,0 +1,64 @@ +#ifndef __LLIST_H +#define __LLIST_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" +#include <stddef.h> + +typedef void (*curl_llist_dtor)(void *, void *); + +typedef struct _curl_llist_element { + void *ptr; + + struct _curl_llist_element *prev; + struct _curl_llist_element *next; +} curl_llist_element; + +typedef struct _curl_llist { + curl_llist_element *head; + curl_llist_element *tail; + + curl_llist_dtor dtor; + + size_t size; +} curl_llist; + +void curl_llist_init(curl_llist *, curl_llist_dtor); +curl_llist *curl_llist_alloc(curl_llist_dtor); +int curl_llist_insert_next(curl_llist *, curl_llist_element *, const void *); +int curl_llist_insert_prev(curl_llist *, curl_llist_element *, const void *); +int curl_llist_remove(curl_llist *, curl_llist_element *, void *); +int curl_llist_remove_next(curl_llist *, curl_llist_element *, void *); +size_t curl_llist_count(curl_llist *); +void curl_llist_destroy(curl_llist *, void *); + +#define CURL_LLIST_HEAD(__l) ((__l)->head) +#define CURL_LLIST_TAIL(__l) ((__l)->tail) +#define CURL_LLIST_NEXT(__e) ((__e)->next) +#define CURL_LLIST_PREV(__e) ((__e)->prev) +#define CURL_LLIST_VALP(__e) ((__e)->ptr) +#define CURL_LLIST_IS_TAIL(__e) ((__e)->next ? 0 : 1) +#define CURL_LLIST_IS_HEAD(__e) ((__e)->prev ? 0 : 1) + +#endif diff --git a/Source/CTest/Curl/memdebug.c b/Source/CTest/Curl/memdebug.c new file mode 100644 index 0000000..9cb22d5 --- /dev/null +++ b/Source/CTest/Curl/memdebug.c @@ -0,0 +1,221 @@ +#ifdef MALLOCDEBUG +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <curl/curl.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#else /* some kind of unix */ +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#endif + +#define _MPRINTF_REPLACE +#include <curl/mprintf.h> +#include "urldata.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/* DONT include memdebug.h here! */ + +struct memdebug { + int size; + char mem[1]; +}; + +/* + * Note that these debug functions are very simple and they are meant to + * remain so. For advanced analysis, record a log file and write perl scripts + * to analyze them! + * + * Don't use these with multithreaded test programs! + */ + +FILE *logfile; + +/* this sets the log file name */ +void curl_memdebug(const char *logname) +{ + if(logname) + logfile = fopen(logname, "w"); + else + logfile = stderr; +} + + +void *curl_domalloc(size_t wantedsize, int line, const char *source) +{ + struct memdebug *mem; + size_t size; + + /* alloc at least 64 bytes */ + size = sizeof(struct memdebug)+wantedsize; + + mem=(struct memdebug *)(malloc)(size); + if(mem) { + /* fill memory with junk */ + memset(mem->mem, 0xA5, wantedsize); + mem->size = wantedsize; + } + + if(logfile && source) + fprintf(logfile, "MEM %s:%d malloc(%d) = %p\n", + source, line, wantedsize, mem->mem); + return mem->mem; +} + +char *curl_dostrdup(const char *str, int line, const char *source) +{ + char *mem; + size_t len; + + if(NULL ==str) { + fprintf(stderr, "ILLEGAL strdup() on NULL at %s:%d\n", + source, line); + exit(2); + } + + len=strlen(str)+1; + + mem=curl_domalloc(len, 0, NULL); /* NULL prevents logging */ + memcpy(mem, str, len); + + if(logfile) + fprintf(logfile, "MEM %s:%d strdup(%p) (%d) = %p\n", + source, line, str, len, mem); + + return mem; +} + +void *curl_dorealloc(void *ptr, size_t wantedsize, + int line, const char *source) +{ + struct memdebug *mem; + + size_t size = sizeof(struct memdebug)+wantedsize; + + mem = (struct memdebug *)((char *)ptr - offsetof(struct memdebug, mem)); + + mem=(struct memdebug *)(realloc)(mem, size); + if(logfile) + fprintf(logfile, "MEM %s:%d realloc(%p, %d) = %p\n", + source, line, ptr, wantedsize, mem?mem->mem:NULL); + + if(mem) { + mem->size = wantedsize; + return mem->mem; + } + + return NULL; +} + +void curl_dofree(void *ptr, int line, const char *source) +{ + struct memdebug *mem; + + if(NULL == ptr) { + fprintf(stderr, "ILLEGAL free() on NULL at %s:%d\n", + source, line); + exit(2); + } + mem = (struct memdebug *)((char *)ptr - offsetof(struct memdebug, mem)); + + /* destroy */ + memset(mem->mem, 0x13, mem->size); + + /* free for real */ + (free)(mem); + + if(logfile) + fprintf(logfile, "MEM %s:%d free(%p)\n", source, line, ptr); +} + +int curl_socket(int domain, int type, int protocol, int line, char *source) +{ + int sockfd=(socket)(domain, type, protocol); + if(logfile) + fprintf(logfile, "FD %s:%d socket() = %d\n", + source, line, sockfd); + return sockfd; +} + +int curl_accept(int s, struct sockaddr *addr, socklen_t *addrlen, + int line, const char *source) +{ + int sockfd=(accept)(s, addr, addrlen); + if(logfile) + fprintf(logfile, "FD %s:%d accept() = %d\n", + source, line, sockfd); + return sockfd; +} + +/* this is our own defined way to close sockets on *ALL* platforms */ +int curl_sclose(int sockfd, int line, char *source) +{ + int res=sclose(sockfd); + if(logfile) + fprintf(logfile, "FD %s:%d sclose(%d)\n", + source, line, sockfd); + return res; +} + +FILE *curl_fopen(const char *file, const char *mode, + int line, const char *source) +{ + FILE *res=(fopen)(file, mode); + if(logfile) + fprintf(logfile, "FILE %s:%d fopen(\"%s\") = %p\n", + source, line, file, res); + return res; +} + +int curl_fclose(FILE *file, int line, const char *source) +{ + int res=(fclose)(file); + if(logfile) + fprintf(logfile, "FILE %s:%d fclose(%p)\n", + source, line, file); + return res; +} +#else +#ifdef VMS +int VOID_VAR_MEMDEBUG; +#endif +#endif /* MALLOCDEBUG */ + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/memdebug.h b/Source/CTest/Curl/memdebug.h new file mode 100644 index 0000000..3c5c090 --- /dev/null +++ b/Source/CTest/Curl/memdebug.h @@ -0,0 +1,61 @@ +#ifdef MALLOCDEBUG + +#include "setup.h" + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <stdio.h> +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +extern FILE *logfile; + +/* memory functions */ +void *curl_domalloc(size_t size, int line, const char *source); +void *curl_dorealloc(void *ptr, size_t size, int line, const char *source); +void curl_dofree(void *ptr, int line, const char *source); +char *curl_dostrdup(const char *str, int line, const char *source); +void curl_memdebug(const char *logname); + +/* file descriptor manipulators */ +int curl_socket(int domain, int type, int protocol, int, const char *); +int curl_sclose(int sockfd, int, const char *source); +int curl_accept(int s, struct sockaddr *addr, socklen_t *addrlen, + int line, const char *source); + +/* FILE functions */ +FILE *curl_fopen(const char *file, const char *mode, int line, + const char *source); +int curl_fclose(FILE *file, int line, const char *source); + +/* Set this symbol on the command-line, recompile all lib-sources */ +#define strdup(ptr) curl_dostrdup(ptr, __LINE__, __FILE__) +#define malloc(size) curl_domalloc(size, __LINE__, __FILE__) +#define realloc(ptr,size) curl_dorealloc(ptr, size, __LINE__, __FILE__) +#define free(ptr) curl_dofree(ptr, __LINE__, __FILE__) + +#define socket(domain,type,protocol)\ + curl_socket(domain,type,protocol,__LINE__,__FILE__) +#define accept(sock,addr,len)\ + curl_accept(sock,addr,len,__LINE__,__FILE__) + +#define getaddrinfo(host,serv,hint,res) \ + curl_getaddrinfo(host,serv,hint,res,__LINE__,__FILE__) +#define freeaddrinfo(data) \ + curl_freeaddrinfo(data,__LINE__,__FILE__) + +/* sclose is probably already defined, redefine it! */ +#undef sclose +#define sclose(sockfd) curl_sclose(sockfd,__LINE__,__FILE__) + +#undef fopen +#define fopen(file,mode) curl_fopen(file,mode,__LINE__,__FILE__) +#define fclose(file) curl_fclose(file,__LINE__,__FILE__) + +#endif diff --git a/Source/CTest/Curl/mprintf.c b/Source/CTest/Curl/mprintf.c new file mode 100644 index 0000000..b039d8d --- /dev/null +++ b/Source/CTest/Curl/mprintf.c @@ -0,0 +1,1181 @@ +/**************************************************************************** + * + * $Id$ + * + ************************************************************************* + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + * Purpose: + * A merge of Bjorn Reese's format() function and Daniel's dsprintf() + * 1.0. A full blooded printf() clone with full support for <num>$ + * everywhere (parameters, widths and precisions) including variabled + * sized parameters (like doubles, long longs, long doubles and even + * void * in 64-bit architectures). + * + * Current restrictions: + * - Max 128 parameters + * - No 'long double' support. + * + * If you ever want truly portable and good *printf() clones, the project that + * took on from here is named 'Trio' and you find more details on the trio web + * page at http://daniel.haxx.se/trio/ + */ + + +#include "setup.h" +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <string.h> + +#ifndef SIZEOF_LONG_LONG +/* prevents warnings on picky compilers */ +#define SIZEOF_LONG_LONG 0 +#endif +#ifndef SIZEOF_LONG_DOUBLE +#define SIZEOF_LONG_DOUBLE 0 +#endif + + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +#define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */ +#define MAX_PARAMETERS 128 /* lame static limit */ + +#undef TRUE +#undef FALSE +#undef BOOL +#ifdef __cplusplus +# define TRUE true +# define FALSE false +# define BOOL bool +#else +# define TRUE ((char)(1 == 1)) +# define FALSE ((char)(0 == 1)) +# define BOOL char +#endif + + +/* Lower-case digits. */ +static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +/* Upper-case digits. */ +static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +#define OUTCHAR(x) done+=(stream(x, (FILE *)data)==-1?0:1) + +/* Data type to read from the arglist */ +typedef enum { + FORMAT_UNKNOWN = 0, + FORMAT_STRING, + FORMAT_PTR, + FORMAT_INT, + FORMAT_INTPTR, + FORMAT_LONG, + FORMAT_LONGLONG, + FORMAT_DOUBLE, + FORMAT_LONGDOUBLE, + FORMAT_WIDTH /* For internal use */ +} FormatType; + +/* convertion and display flags */ +enum { + FLAGS_NEW = 0, + FLAGS_SPACE = 1<<0, + FLAGS_SHOWSIGN = 1<<1, + FLAGS_LEFT = 1<<2, + FLAGS_ALT = 1<<3, + FLAGS_SHORT = 1<<4, + FLAGS_LONG = 1<<5, + FLAGS_LONGLONG = 1<<6, + FLAGS_LONGDOUBLE = 1<<7, + FLAGS_PAD_NIL = 1<<8, + FLAGS_UNSIGNED = 1<<9, + FLAGS_OCTAL = 1<<10, + FLAGS_HEX = 1<<11, + FLAGS_UPPER = 1<<12, + FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */ + FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */ + FLAGS_PREC = 1<<15, /* precision was specified */ + FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */ + FLAGS_CHAR = 1<<17, /* %c story */ + FLAGS_FLOATE = 1<<18, /* %e or %E */ + FLAGS_FLOATG = 1<<19 /* %g or %G */ +}; + +typedef struct { + FormatType type; + int flags; + int width; /* width OR width parameter number */ + int precision; /* precision OR precision parameter number */ + union { + char *str; + void *ptr; + long num; +#if SIZEOF_LONG_LONG /* if this is non-zero */ + long long lnum; +#endif + double dnum; +#if SIZEOF_LONG_DOUBLE + long double ldnum; +#endif + } data; +} va_stack_t; + +struct nsprintf { + char *buffer; + size_t length; + size_t max; +}; + +struct asprintf { + char *buffer; /* allocated buffer */ + size_t len; /* length of string */ + size_t alloc; /* length of alloc */ +}; + +int curl_msprintf(char *buffer, const char *format, ...); + +static int dprintf_DollarString(char *input, char **end) +{ + int number=0; + while(isdigit((int)*input)) { + number *= 10; + number += *input-'0'; + input++; + } + if(number && ('$'==*input++)) { + *end = input; + return number; + } + return 0; +} + +static BOOL dprintf_IsQualifierNoDollar(char c) +{ + switch (c) { + case '-': case '+': case ' ': case '#': case '.': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'h': case 'l': case 'L': case 'Z': case 'q': + return TRUE; + default: + return FALSE; + } +} + +#ifdef DPRINTF_DEBUG2 +int dprintf_Pass1Report(va_stack_t *vto, int max) +{ + int i; + char buffer[128]; + int bit; + int flags; + + for(i=0; i<max; i++) { + char *type; + switch(vto[i].type) { + case FORMAT_UNKNOWN: + type = "unknown"; + break; + case FORMAT_STRING: + type ="string"; + break; + case FORMAT_PTR: + type ="pointer"; + break; + case FORMAT_INT: + type = "int"; + break; + case FORMAT_LONG: + type = "long"; + break; + case FORMAT_LONGLONG: + type = "long long"; + break; + case FORMAT_DOUBLE: + type = "double"; + break; + case FORMAT_LONGDOUBLE: + type = "long double"; + break; + } + + + buffer[0]=0; + + for(bit=0; bit<31; bit++) { + flags = vto[i].flags & (1<<bit); + + if(flags & FLAGS_SPACE) + strcat(buffer, "space "); + else if(flags & FLAGS_SHOWSIGN) + strcat(buffer, "plus "); + else if(flags & FLAGS_LEFT) + strcat(buffer, "left "); + else if(flags & FLAGS_ALT) + strcat(buffer, "alt "); + else if(flags & FLAGS_SHORT) + strcat(buffer, "short "); + else if(flags & FLAGS_LONG) + strcat(buffer, "long "); + else if(flags & FLAGS_LONGLONG) + strcat(buffer, "longlong "); + else if(flags & FLAGS_LONGDOUBLE) + strcat(buffer, "longdouble "); + else if(flags & FLAGS_PAD_NIL) + strcat(buffer, "padnil "); + else if(flags & FLAGS_UNSIGNED) + strcat(buffer, "unsigned "); + else if(flags & FLAGS_OCTAL) + strcat(buffer, "octal "); + else if(flags & FLAGS_HEX) + strcat(buffer, "hex "); + else if(flags & FLAGS_UPPER) + strcat(buffer, "upper "); + else if(flags & FLAGS_WIDTH) + strcat(buffer, "width "); + else if(flags & FLAGS_WIDTHPARAM) + strcat(buffer, "widthparam "); + else if(flags & FLAGS_PREC) + strcat(buffer, "precision "); + else if(flags & FLAGS_PRECPARAM) + strcat(buffer, "precparam "); + else if(flags & FLAGS_CHAR) + strcat(buffer, "char "); + else if(flags & FLAGS_FLOATE) + strcat(buffer, "floate "); + else if(flags & FLAGS_FLOATG) + strcat(buffer, "floatg "); + } + printf("REPORT: %d. %s [%s]\n", i, type, buffer); + + } + + +} +#endif + +/****************************************************************** + * + * Pass 1: + * Create an index with the type of each parameter entry and its + * value (may vary in size) + * + ******************************************************************/ + +static int dprintf_Pass1(char *format, va_stack_t *vto, char **endpos, va_list arglist) +{ + char *fmt = format; + int param_num = 0; + int this_param; + int width; + int precision; + int flags; + int max_param=0; + int i; + + while (*fmt) { + if (*fmt++ == '%') { + if (*fmt == '%') { + fmt++; + continue; /* while */ + } + + flags = FLAGS_NEW; + + /* Handle the positional case (N$) */ + + param_num++; + + this_param = dprintf_DollarString(fmt, &fmt); + if (0 == this_param) + /* we got no positional, get the next counter */ + this_param = param_num; + + if (this_param > max_param) + max_param = this_param; + + /* + * The parameter with number 'i' should be used. Next, we need + * to get SIZE and TYPE of the parameter. Add the information + * to our array. + */ + + width = 0; + precision = 0; + + /* Handle the flags */ + + while (dprintf_IsQualifierNoDollar(*fmt)) { + switch (*fmt++) { + case ' ': + flags |= FLAGS_SPACE; + break; + case '+': + flags |= FLAGS_SHOWSIGN; + break; + case '-': + flags |= FLAGS_LEFT; + flags &= ~FLAGS_PAD_NIL; + break; + case '#': + flags |= FLAGS_ALT; + break; + case '.': + flags |= FLAGS_PREC; + if ('*' == *fmt) { + /* The precision is picked from a specified parameter */ + + flags |= FLAGS_PRECPARAM; + fmt++; + param_num++; + + i = dprintf_DollarString(fmt, &fmt); + if (i) + precision = i; + else + precision = param_num; + + if (precision > max_param) + max_param = precision; + } + else { + flags |= FLAGS_PREC; + precision = strtol(fmt, &fmt, 10); + } + break; + case 'h': + flags |= FLAGS_SHORT; + break; + case 'l': + if (flags & FLAGS_LONG) + flags |= FLAGS_LONGLONG; + else + flags |= FLAGS_LONG; + break; + case 'L': + flags |= FLAGS_LONGDOUBLE; + break; + case 'q': + flags |= FLAGS_LONGLONG; + break; + case 'Z': + if (sizeof(size_t) > sizeof(unsigned long int)) + flags |= FLAGS_LONGLONG; + if (sizeof(size_t) > sizeof(unsigned int)) + flags |= FLAGS_LONG; + break; + case '0': + if (!(flags & FLAGS_LEFT)) + flags |= FLAGS_PAD_NIL; + /* FALLTHROUGH */ + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + flags |= FLAGS_WIDTH; + width = strtol(fmt-1, &fmt, 10); + break; + case '*': /* Special case */ + flags |= FLAGS_WIDTHPARAM; + param_num++; + + i = dprintf_DollarString(fmt, &fmt); + if(i) + width = i; + else + width = param_num; + if(width > max_param) + max_param=width; + break; + default: + break; + } + } /* switch */ + + /* Handle the specifier */ + + i = this_param - 1; + + switch (*fmt) { + case 'S': + flags |= FLAGS_ALT; + /* FALLTHROUGH */ + case 's': + vto[i].type = FORMAT_STRING; + break; + case 'n': + vto[i].type = FORMAT_INTPTR; + break; + case 'p': + vto[i].type = FORMAT_PTR; + break; + case 'd': case 'i': + vto[i].type = FORMAT_INT; + break; + case 'u': + vto[i].type = FORMAT_INT; + flags |= FLAGS_UNSIGNED; + break; + case 'o': + vto[i].type = FORMAT_INT; + flags |= FLAGS_OCTAL; + break; + case 'x': + vto[i].type = FORMAT_INT; + flags |= FLAGS_HEX; + break; + case 'X': + vto[i].type = FORMAT_INT; + flags |= FLAGS_HEX|FLAGS_UPPER; + break; + case 'c': + vto[i].type = FORMAT_INT; + flags |= FLAGS_CHAR; + break; + case 'f': + vto[i].type = FORMAT_DOUBLE; + break; + case 'e': case 'E': + vto[i].type = FORMAT_DOUBLE; + flags |= FLAGS_FLOATE| (('E' == *fmt)?FLAGS_UPPER:0); + break; + case 'g': case 'G': + vto[i].type = FORMAT_DOUBLE; + flags |= FLAGS_FLOATG| (('G' == *fmt)?FLAGS_UPPER:0); + break; + default: + vto[i].type = FORMAT_UNKNOWN; + break; + } /* switch */ + + vto[i].flags = flags; + vto[i].width = width; + vto[i].precision = precision; + + if (flags & FLAGS_WIDTHPARAM) { + /* we have the width specified from a parameter, so we make that + parameter's info setup properly */ + vto[i].width = width - 1; + i = width - 1; + vto[i].type = FORMAT_WIDTH; + vto[i].flags = FLAGS_NEW; + vto[i].precision = vto[i].width = 0; /* can't use width or precision + of width! */ + } + if (flags & FLAGS_PRECPARAM) { + /* we have the precision specified from a parameter, so we make that + parameter's info setup properly */ + vto[i].precision = precision - 1; + i = precision - 1; + vto[i].type = FORMAT_WIDTH; + vto[i].flags = FLAGS_NEW; + vto[i].precision = vto[i].width = 0; /* can't use width or precision + of width! */ + } + *endpos++ = fmt + 1; /* end of this sequence */ + } + } + +#ifdef DPRINTF_DEBUG2 + dprintf_Pass1Report(vto, max_param); +#endif + + /* Read the arg list parameters into our data list */ + for (i=0; i<max_param; i++) { + if ((i + 1 < max_param) && (vto[i + 1].type == FORMAT_WIDTH)) + { + /* Width/precision arguments must be read before the main argument + * they are attached to + */ + vto[i + 1].data.num = va_arg(arglist, int); + } + + switch (vto[i].type) + { + case FORMAT_STRING: + vto[i].data.str = va_arg(arglist, char *); + break; + + case FORMAT_INTPTR: + case FORMAT_UNKNOWN: + case FORMAT_PTR: + vto[i].data.ptr = va_arg(arglist, void *); + break; + + case FORMAT_INT: +#if SIZEOF_LONG_LONG + if(vto[i].flags & FLAGS_LONGLONG) + vto[i].data.lnum = va_arg(arglist, long long); + else +#endif + if(vto[i].flags & FLAGS_LONG) + vto[i].data.num = va_arg(arglist, long); + else + vto[i].data.num = va_arg(arglist, int); + break; + + case FORMAT_DOUBLE: +#if SIZEOF_LONG_DOUBLE + if(vto[i].flags & FLAGS_LONG) + vto[i].data.ldnum = va_arg(arglist, long double); + else +#endif + vto[i].data.dnum = va_arg(arglist, double); + break; + + case FORMAT_WIDTH: + /* Argument has been read. Silently convert it into an integer + * for later use + */ + vto[i].type = FORMAT_INT; + break; + + default: + break; + } + } + + return max_param; + +} + +static int dprintf_formatf( + void *data, /* untouched by format(), just sent to the + stream() function in the first argument */ + int (*stream)(int, FILE *), /* function pointer called for each + output character */ + const char *format, /* %-formatted string */ + va_list ap_save) /* list of parameters */ +{ + /* Base-36 digits for numbers. */ + const char *digits = lower_digits; + + /* Pointer into the format string. */ + char *f; + + /* Number of characters written. */ + register size_t done = 0; + + long param; /* current parameter to read */ + long param_num=0; /* parameter counter */ + + va_stack_t vto[MAX_PARAMETERS]; + char *endpos[MAX_PARAMETERS]; + char **end; + + char work[BUFFSIZE]; + + va_stack_t *p; + + /* Do the actual %-code parsing */ + dprintf_Pass1((char *)format, vto, endpos, ap_save); + + end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1() + created for us */ + + f = (char *)format; + while (*f != '\0') { + /* Format spec modifiers. */ + char alt; + + /* Width of a field. */ + register long width; + /* Precision of a field. */ + long prec; + + /* Decimal integer is negative. */ + char is_neg; + + /* Base of a number to be written. */ + long base; + + /* Integral values to be written. */ +#if SIZEOF_LONG_LONG + unsigned long long num; +#else + unsigned long num; +#endif + long signed_num; + + if (*f != '%') { + /* This isn't a format spec, so write everything out until the next one + OR end of string is reached. */ + do { + OUTCHAR(*f); + } while(*++f && ('%' != *f)); + continue; + } + + ++f; + + /* Check for "%%". Note that although the ANSI standard lists + '%' as a conversion specifier, it says "The complete format + specification shall be `%%'," so we can avoid all the width + and precision processing. */ + if (*f == '%') { + ++f; + OUTCHAR('%'); + continue; + } + + /* If this is a positional parameter, the position must follow imediately + after the %, thus create a %<num>$ sequence */ + param=dprintf_DollarString(f, &f); + + if(!param) + param = param_num; + else + --param; + + param_num++; /* increase this always to allow "%2$s %1$s %s" and then the + third %s will pick the 3rd argument */ + + p = &vto[param]; + + /* pick up the specified width */ + if(p->flags & FLAGS_WIDTHPARAM) + width = vto[p->width].data.num; + else + width = p->width; + + /* pick up the specified precision */ + if(p->flags & FLAGS_PRECPARAM) + prec = vto[p->precision].data.num; + else if(p->flags & FLAGS_PREC) + prec = p->precision; + else + prec = -1; + + alt = p->flags & FLAGS_ALT; + + switch (p->type) { + case FORMAT_INT: + num = p->data.num; + if(p->flags & FLAGS_CHAR) { + /* Character. */ + if (!(p->flags & FLAGS_LEFT)) + while (--width > 0) + OUTCHAR(' '); + OUTCHAR((char) num); + if (p->flags & FLAGS_LEFT) + while (--width > 0) + OUTCHAR(' '); + break; + } + if(p->flags & FLAGS_UNSIGNED) { + /* Decimal unsigned integer. */ + base = 10; + goto unsigned_number; + } + if(p->flags & FLAGS_OCTAL) { + /* Octal unsigned integer. */ + base = 8; + goto unsigned_number; + } + if(p->flags & FLAGS_HEX) { + /* Hexadecimal unsigned integer. */ + + digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits; + base = 16; + goto unsigned_number; + } + + /* Decimal integer. */ + base = 10; + +#if SIZEOF_LONG_LONG + if(p->flags & FLAGS_LONGLONG) { + /* long long */ + is_neg = p->data.lnum < 0; + num = is_neg ? (- p->data.lnum) : p->data.lnum; + } + else +#endif + { + signed_num = (long) num; + + is_neg = signed_num < 0; + num = is_neg ? (- signed_num) : signed_num; + } + goto number; + + unsigned_number:; + /* Unsigned number of base BASE. */ + is_neg = 0; + + number:; + /* Number of base BASE. */ + { + char *workend = &work[sizeof(work) - 1]; + register char *w; + + /* Supply a default precision if none was given. */ + if (prec == -1) + prec = 1; + + /* Put the number in WORK. */ + w = workend; + while (num > 0) { + *w-- = digits[num % base]; + num /= base; + } + width -= workend - w; + prec -= workend - w; + + if (alt && base == 8 && prec <= 0) { + *w-- = '0'; + --width; + } + + if (prec > 0) { + width -= prec; + while (prec-- > 0) + *w-- = '0'; + } + + if (alt && base == 16) + width -= 2; + + if (is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE)) + --width; + + if (!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL)) + while (width-- > 0) + OUTCHAR(' '); + + if (is_neg) + OUTCHAR('-'); + else if (p->flags & FLAGS_SHOWSIGN) + OUTCHAR('+'); + else if (p->flags & FLAGS_SPACE) + OUTCHAR(' '); + + if (alt && base == 16) { + OUTCHAR('0'); + if(p->flags & FLAGS_UPPER) + OUTCHAR('X'); + else + OUTCHAR('x'); + } + + if (!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL)) + while (width-- > 0) + OUTCHAR('0'); + + /* Write the number. */ + while (++w <= workend) { + OUTCHAR(*w); + } + + if (p->flags & FLAGS_LEFT) + while (width-- > 0) + OUTCHAR(' '); + } + break; + + case FORMAT_STRING: + /* String. */ + { + static char null[] = "(nil)"; + char *str; + size_t len; + + str = (char *) p->data.str; + if ( str == NULL) { + /* Write null[] if there's space. */ + if (prec == -1 || prec >= (long) sizeof(null) - 1) { + str = null; + len = sizeof(null) - 1; + /* Disable quotes around (nil) */ + p->flags &= (~FLAGS_ALT); + } + else { + str = (char *)""; + len = 0; + } + } + else + len = strlen(str); + + if (prec != -1 && (size_t) prec < len) + len = prec; + width -= len; + + if (p->flags & FLAGS_ALT) + OUTCHAR('"'); + + if (!(p->flags&FLAGS_LEFT)) + while (width-- > 0) + OUTCHAR(' '); + + while (len-- > 0) + OUTCHAR(*str++); + if (p->flags&FLAGS_LEFT) + while (width-- > 0) + OUTCHAR(' '); + + if (p->flags & FLAGS_ALT) + OUTCHAR('"'); + } + break; + + case FORMAT_PTR: + /* Generic pointer. */ + { + void *ptr; + ptr = (void *) p->data.ptr; + if (ptr != NULL) { + /* If the pointer is not NULL, write it as a %#x spec. */ + base = 16; + digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits; + alt = 1; + num = (unsigned long) ptr; + is_neg = 0; + goto number; + } + else { + /* Write "(nil)" for a nil pointer. */ + static char strnil[] = "(nil)"; + register char *point; + + width -= sizeof(strnil) - 1; + if (p->flags & FLAGS_LEFT) + while (width-- > 0) + OUTCHAR(' '); + for (point = strnil; *point != '\0'; ++point) + OUTCHAR(*point); + if (! (p->flags & FLAGS_LEFT)) + while (width-- > 0) + OUTCHAR(' '); + } + } + break; + + case FORMAT_DOUBLE: + { + char formatbuf[32]="%"; + char *fptr; + + width = -1; + if (p->flags & FLAGS_WIDTH) + width = p->width; + else if (p->flags & FLAGS_WIDTHPARAM) + width = vto[p->width].data.num; + + prec = -1; + if (p->flags & FLAGS_PREC) + prec = p->precision; + else if (p->flags & FLAGS_PRECPARAM) + prec = vto[p->precision].data.num; + + if (p->flags & FLAGS_LEFT) + strcat(formatbuf, "-"); + if (p->flags & FLAGS_SHOWSIGN) + strcat(formatbuf, "+"); + if (p->flags & FLAGS_SPACE) + strcat(formatbuf, " "); + if (p->flags & FLAGS_ALT) + strcat(formatbuf, "#"); + + fptr=&formatbuf[strlen(formatbuf)]; + + if(width >= 0) { + /* RECURSIVE USAGE */ + fptr += curl_msprintf(fptr, "%d", width); + } + if(prec >= 0) { + /* RECURSIVE USAGE */ + fptr += curl_msprintf(fptr, ".%d", prec); + } + if (p->flags & FLAGS_LONG) + strcat(fptr, "l"); + + if (p->flags & FLAGS_FLOATE) + strcat(fptr, p->flags&FLAGS_UPPER?"E":"e"); + else if (p->flags & FLAGS_FLOATG) + strcat(fptr, (p->flags & FLAGS_UPPER) ? "G" : "g"); + else + strcat(fptr, "f"); + + /* NOTE NOTE NOTE!! Not all sprintf() implementations returns number + of output characters */ +#if SIZEOF_LONG_DOUBLE + if (p->flags & FLAGS_LONG) + /* This is for support of the 'long double' type */ + (sprintf)(work, formatbuf, p->data.ldnum); + else +#endif + (sprintf)(work, formatbuf, p->data.dnum); + + for(fptr=work; *fptr; fptr++) + OUTCHAR(*fptr); + } + break; + + case FORMAT_INTPTR: + /* Answer the count of characters written. */ +#if SIZEOF_LONG_LONG + if (p->flags & FLAGS_LONGLONG) + *(long long int *) p->data.ptr = done; + else +#endif + if (p->flags & FLAGS_LONG) + *(long int *) p->data.ptr = done; + else if (!(p->flags & FLAGS_SHORT)) + *(int *) p->data.ptr = done; + else + *(short int *) p->data.ptr = done; + break; + + default: + break; + } + f = *end++; /* goto end of %-code */ + + } + return done; +} + +/* fputc() look-alike */ +static int addbyter(int output, FILE *data) +{ + struct nsprintf *infop=(struct nsprintf *)data; + + if(infop->length < infop->max) { + /* only do this if we haven't reached max length yet */ + infop->buffer[0] = (char)output; /* store */ + infop->buffer++; /* increase pointer */ + infop->length++; /* we are now one byte larger */ + return output; /* fputc() returns like this on success */ + } + return -1; +} + +int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...) +{ + va_list ap_save; /* argument pointer */ + int retcode; + struct nsprintf info; + + info.buffer = buffer; + info.length = 0; + info.max = maxlength; + + va_start(ap_save, format); + retcode = dprintf_formatf(&info, addbyter, format, ap_save); + va_end(ap_save); + info.buffer[0] = 0; /* we terminate this with a zero byte */ + + /* we could even return things like */ + + return retcode; +} + +int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, va_list ap_save) +{ + int retcode; + struct nsprintf info; + + info.buffer = buffer; + info.length = 0; + info.max = maxlength; + + retcode = dprintf_formatf(&info, addbyter, format, ap_save); + info.buffer[0] = 0; /* we terminate this with a zero byte */ + return retcode; +} + + +/* fputc() look-alike */ +static int alloc_addbyter(int output, FILE *data) +{ + struct asprintf *infop=(struct asprintf *)data; + + if(!infop->buffer) { + infop->buffer=(char *)malloc(32); + if(!infop->buffer) + return -1; /* fail */ + infop->alloc = 32; + infop->len =0; + } + else if(infop->len+1 >= infop->alloc) { + char *newptr; + + newptr = (char *)realloc(infop->buffer, infop->alloc*2); + + if(!newptr) { + return -1; + } + infop->buffer = newptr; + infop->alloc *= 2; + } + + infop->buffer[ infop->len ] = output; + + infop->len++; + + return output; /* fputc() returns like this on success */ + +} + +char *curl_maprintf(const char *format, ...) +{ + va_list ap_save; /* argument pointer */ + int retcode; + struct asprintf info; + + info.buffer = NULL; + info.len = 0; + info.alloc = 0; + + va_start(ap_save, format); + retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save); + va_end(ap_save); + if(info.len) { + info.buffer[info.len] = 0; /* we terminate this with a zero byte */ + return info.buffer; + } + else + return NULL; +} + +char *curl_mvaprintf(const char *format, va_list ap_save) +{ + int retcode; + struct asprintf info; + + info.buffer = NULL; + info.len = 0; + info.alloc = 0; + + retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save); + info.buffer[info.len] = 0; /* we terminate this with a zero byte */ + if(info.len) { + info.buffer[info.len] = 0; /* we terminate this with a zero byte */ + return info.buffer; + } + else + return NULL; +} + +static int storebuffer(int output, FILE *data) +{ + char **buffer = (char **)data; + **buffer = (char)output; + (*buffer)++; + return output; /* act like fputc() ! */ +} + +int curl_msprintf(char *buffer, const char *format, ...) +{ + va_list ap_save; /* argument pointer */ + int retcode; + va_start(ap_save, format); + retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save); + va_end(ap_save); + *buffer=0; /* we terminate this with a zero byte */ + return retcode; +} + +#ifndef WIN32 /* not needed on win32 */ +extern int fputc(int, FILE *); +#endif + +int curl_mprintf(const char *format, ...) +{ + int retcode; + va_list ap_save; /* argument pointer */ + va_start(ap_save, format); + retcode = dprintf_formatf(stdout, fputc, format, ap_save); + va_end(ap_save); + return retcode; +} + +int curl_mfprintf(FILE *whereto, const char *format, ...) +{ + int retcode; + va_list ap_save; /* argument pointer */ + va_start(ap_save, format); + retcode = dprintf_formatf(whereto, fputc, format, ap_save); + va_end(ap_save); + return retcode; +} + +int curl_mvsprintf(char *buffer, const char *format, va_list ap_save) +{ + int retcode; + retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save); + *buffer=0; /* we terminate this with a zero byte */ + return retcode; +} + +int curl_mvprintf(const char *format, va_list ap_save) +{ + return dprintf_formatf(stdout, fputc, format, ap_save); +} + +int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save) +{ + return dprintf_formatf(whereto, fputc, format, ap_save); +} + +#ifdef DPRINTF_DEBUG +int main() +{ + char buffer[129]; + char *ptr; +#if SIZEOF_LONG_LONG>0 + long long hullo; + dprintf("%3$12s %1$s %2$qd %4$d\n", "daniel", hullo, "stenberg", 65); +#endif + + mprintf("%3d %5d\n", 10, 1998); + + ptr=maprintf("test this then baby %s%s%s%s%s%s %d %d %d loser baby get a hit in yer face now!", "", "pretty long string pretty long string pretty long string pretty long string pretty long string", "/", "/", "/", "pretty long string", 1998, 1999, 2001); + + puts(ptr); + + memset(ptr, 55, strlen(ptr)+1); + + free(ptr); + +#if 1 + mprintf(buffer, "%s %s %d", "daniel", "stenberg", 19988); + puts(buffer); + + mfprintf(stderr, "%s %#08x\n", "dummy", 65); + + printf("%s %#08x\n", "dummy", 65); + { + double tryout = 3.14156592; + mprintf(buffer, "%.2g %G %f %e %E", tryout, tryout, tryout, tryout, tryout); + puts(buffer); + printf("%.2g %G %f %e %E\n", tryout, tryout, tryout, tryout, tryout); + } +#endif + + return 0; +} + +#endif + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/multi.c b/Source/CTest/Curl/multi.c new file mode 100644 index 0000000..bfd3949 --- /dev/null +++ b/Source/CTest/Curl/multi.c @@ -0,0 +1,361 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" +#include <stdlib.h> +#include <string.h> +#include <curl/curl.h> + +#include "multi.h" /* will become <curl/multi.h> soon */ + +#include "urldata.h" +#include "transfer.h" +#include "url.h" + +struct Curl_message { + /* the 'CURLMsg' is the part that is visible to the external user */ + struct CURLMsg extmsg; + struct Curl_message *next; +}; + +typedef enum { + CURLM_STATE_INIT, + CURLM_STATE_CONNECT, + CURLM_STATE_DO, + CURLM_STATE_PERFORM, + CURLM_STATE_DONE, + CURLM_STATE_COMPLETED, + + CURLM_STATE_LAST /* not a true state, never use this */ +} CURLMstate; + +struct Curl_one_easy { + /* first, two fields for the linked list of these */ + struct Curl_one_easy *next; + struct Curl_one_easy *prev; + + struct SessionHandle *easy_handle; /* the easy handle for this unit */ + struct connectdata *easy_conn; /* the "unit's" connection */ + + CURLMstate state; /* the handle's state */ + CURLcode result; /* previous result */ +}; + + +#define CURL_MULTI_HANDLE 0x000bab1e + +#define GOOD_MULTI_HANDLE(x) ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE)) +#define GOOD_EASY_HANDLE(x) (x) + +/* This is the struct known as CURLM on the outside */ +struct Curl_multi { + /* First a simple identifier to easier detect if a user mix up + this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */ + long type; + + /* We have a linked list with easy handles */ + struct Curl_one_easy easy; + /* This is the amount of entries in the linked list above. */ + int num_easy; + + /* this is a linked list of posted messages */ + struct Curl_message *msgs; + /* amount of messages in the queue */ + int num_msgs; + /* Hostname cache */ + curl_hash *hostcache; +}; + + +CURLM *curl_multi_init(void) +{ + struct Curl_multi *multi; + + multi = (void *)malloc(sizeof(struct Curl_multi)); + + if(multi) { + memset(multi, 0, sizeof(struct Curl_multi)); + multi->type = CURL_MULTI_HANDLE; + } + + return (CURLM *) multi; +} + +CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *easy_handle) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Verify that we got a somewhat good easy handle too */ + if(!GOOD_EASY_HANDLE(easy_handle)) + return CURLM_BAD_EASY_HANDLE; + + /* Now, time to add an easy handle to the multi stack */ + easy = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy)); + if(!easy) + return CURLM_OUT_OF_MEMORY; + + /* clean it all first (just to be sure) */ + memset(easy, 0, sizeof(struct Curl_one_easy)); + + /* set the easy handle */ + easy->easy_handle = easy_handle; + easy->state = CURLM_STATE_INIT; + + /* We add this new entry first in the list. We make our 'next' point to the + previous next and our 'prev' point back to the 'first' struct */ + easy->next = multi->easy.next; + easy->prev = &multi->easy; + + /* make 'easy' the first node in the chain */ + multi->easy.next = easy; + + /* if there was a next node, make sure its 'prev' pointer links back to + the new node */ + if(easy->next) + easy->next->prev = easy; + + /* increase the node-counter */ + multi->num_easy++; + + return CURLM_CALL_MULTI_PERFORM; +} + +CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Verify that we got a somewhat good easy handle too */ + if(!GOOD_EASY_HANDLE(curl_handle)) + return CURLM_BAD_EASY_HANDLE; + + /* scan through the list and remove the 'curl_handle' */ + easy = multi->easy.next; + while(easy) { + if(easy->easy_handle == curl_handle) + break; + easy=easy->next; + } + if(easy) { + /* If the 'state' is not INIT or COMPLETED, we might need to do something + nice to put the easy_handle in a good known state when this returns. */ + + /* make the previous node point to our next */ + if(easy->prev) + easy->prev->next = easy->next; + /* make our next point to our previous node */ + if(easy->next) + easy->next->prev = easy->prev; + + /* NOTE NOTE NOTE + We do not touch the easy handle here! */ + free(easy); + + multi->num_easy--; /* one less to care about now */ + + return CURLM_OK; + } + else + return CURLM_BAD_EASY_HANDLE; /* twasn't found */ +} + +CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, fd_set *write_fd_set, + fd_set *exc_fd_set, int *max_fd) +{ + /* Scan through all the easy handles to get the file descriptors set. + Some easy handles may not have connected to the remote host yet, + and then we must make sure that is done. */ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + int this_max_fd=-1; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + *max_fd = -1; /* so far none! */ + + easy=multi->easy.next; + while(easy) { + switch(easy->state) { + default: + break; + case CURLM_STATE_PERFORM: + /* This should have a set of file descriptors for us to set. */ + /* after the transfer is done, go DONE */ + + Curl_single_fdset(easy->easy_conn, + read_fd_set, write_fd_set, + exc_fd_set, &this_max_fd); + + /* remember the maximum file descriptor */ + if(this_max_fd > *max_fd) + *max_fd = this_max_fd; + + break; + } + easy = easy->next; /* check next handle */ + } + + return CURLM_OK; +} + +CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + bool done; + CURLMcode result=CURLM_OK; + + *running_handles = 0; /* bump this once for every living handle */ + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + easy=multi->easy.next; + while(easy) { + switch(easy->state) { + case CURLM_STATE_INIT: + /* init this transfer. */ + easy->result=Curl_pretransfer(easy->easy_handle); + if(CURLE_OK == easy->result) { + /* after init, go CONNECT */ + easy->state = CURLM_STATE_CONNECT; + result = CURLM_CALL_MULTI_PERFORM; + } + break; + case CURLM_STATE_CONNECT: + if (Curl_global_host_cache_use(easy->easy_handle)) { + easy->easy_handle->hostcache = Curl_global_host_cache_get(); + } + else { + if (multi->hostcache == NULL) { + multi->hostcache = curl_hash_alloc(7, Curl_freeaddrinfo); + } + + easy->easy_handle->hostcache = multi->hostcache; + } + + /* Connect. We get a connection identifier filled in. */ + easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); + + /* after connect, go DO */ + if(CURLE_OK == easy->result) { + easy->state = CURLM_STATE_DO; + result = CURLM_CALL_MULTI_PERFORM; + } + break; + case CURLM_STATE_DO: + /* Do the fetch or put request */ + easy->result = Curl_do(&easy->easy_conn); + /* after do, go PERFORM */ + if(CURLE_OK == easy->result) { + if(CURLE_OK == Curl_readwrite_init(easy->easy_conn)) { + easy->state = CURLM_STATE_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; + } + } + break; + case CURLM_STATE_PERFORM: + /* read/write data if it is ready to do so */ + easy->result = Curl_readwrite(easy->easy_conn, &done); + /* hm, when we follow redirects, we may need to go back to the CONNECT + state */ + /* after the transfer is done, go DONE */ + if(TRUE == done) { + /* call this even if the readwrite function returned error */ + easy->result = Curl_posttransfer(easy->easy_handle); + easy->state = CURLM_STATE_DONE; + result = CURLM_CALL_MULTI_PERFORM; + } + break; + case CURLM_STATE_DONE: + /* post-transfer command */ + easy->result = Curl_done(easy->easy_conn); + /* after we have DONE what we're supposed to do, go COMPLETED */ + if(CURLE_OK == easy->result) + easy->state = CURLM_STATE_COMPLETED; + break; + case CURLM_STATE_COMPLETED: + /* this is a completed transfer, it is likely to still be connected */ + + /* This node should be delinked from the list now and we should post + an information message that we are complete. */ + break; + default: + return CURLM_INTERNAL_ERROR; + } + + if((CURLM_STATE_COMPLETED != easy->state) && + (CURLE_OK != easy->result)) { + /* + * If an error was returned, and we aren't in completed now, + * then we go to completed and consider this transfer aborted. + */ + easy->state = CURLM_STATE_COMPLETED; + } + else if(CURLM_STATE_COMPLETED != easy->state) + /* this one still lives! */ + (*running_handles)++; + + easy = easy->next; /* operate on next handle */ + } + return result; +} + +CURLMcode curl_multi_cleanup(CURLM *multi_handle) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + if(GOOD_MULTI_HANDLE(multi)) { + multi->type = 0; /* not good anymore */ + curl_hash_destroy(multi->hostcache); + /* remove all easy handles */ + + free(multi); + + return CURLM_OK; + } + else + return CURLM_BAD_HANDLE; +} + +CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue); + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/multi.h b/Source/CTest/Curl/multi.h new file mode 100644 index 0000000..d92f767 --- /dev/null +++ b/Source/CTest/Curl/multi.h @@ -0,0 +1,189 @@ +#ifndef __CURL_MULTI_H +#define __CURL_MULTI_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +/* + This is meant to be the "external" header file. Don't give away any + internals here! + + This document presents a mixture of ideas from at least: + - Daniel Stenberg + - Steve Dekorte + - Sterling Hughes + - Ben Greear + + ------------------------------------------- + GOALS + + o Enable a "pull" interface. The application that uses libcurl decides where + and when to ask libcurl to get/send data. + + o Enable multiple simultaneous transfers in the same thread without making it + complicated for the application. + + o Enable the application to select() on its own file descriptors and curl's + file descriptors simultaneous easily. + + Example sources using this interface is here: ../multi/ + +*/ +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#endif + +#include <curl/curl.h> + +typedef void CURLM; + +typedef enum { + CURLM_CALL_MULTI_PERFORM=-1, /* please call curl_multi_perform() soon */ + CURLM_OK, + CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ + CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ + CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */ + CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ + CURLM_LAST +} CURLMcode; + +typedef enum { + CURLMSG_NONE, /* first, not used */ + CURLMSG_DONE, /* This easy handle has completed. 'whatever' points to + the CURLcode of the transfer */ + CURLMSG_LAST /* last, not used */ +} CURLMSG; + +struct CURLMsg { + CURLMSG msg; /* what this message means */ + CURL *easy_handle; /* the handle it concerns */ + union { + void *whatever; /* message-specific data */ + CURLcode result; /* return code for transfer */ + } data; +}; +typedef struct CURLMsg CURLMsg; + +/* + * Name: curl_multi_init() + * + * Desc: inititalize multi-style curl usage + * Returns: a new CURLM handle to use in all 'curl_multi' functions. + */ +CURLM *curl_multi_init(void); + +/* + * Name: curl_multi_add_handle() + * + * Desc: add a standard curl handle to the multi stack + * Returns: CURLMcode type, general multi error code. + */ +CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * Returns: CURLMcode type, general multi error code. + */ +CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * Returns: CURLMcode type, general multi error code. + */ +CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); + + /* + * Name: curl_multi_perform() + * + * Desc: When the app thinks there's data available for curl it calls this + * function to read/write whatever there is right now. This returns + * as soon as the reads and writes are done. This function does not + * require that there actually is data available for reading or that + * data can be written, it can be called just in case. It returns + * the number of handles that still transfer data in the second + * argument's integer-pointer. + * + * Returns: CURLMcode type, general multi error code. *NOTE* that this only + * returns errors etc regarding the whole multi stack. There might + * still have occurred problems on invidual transfers even when this + * returns OK. + */ +CURLMcode curl_multi_perform(CURLM *multi_handle, + int *running_handles); + + /* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles will be if this function is called + * in the middle of a transfer. + * Returns: CURLMcode type, general multi error code. + */ +CURLMcode curl_multi_cleanup(CURLM *multi_handle); + +/* + * Name: curl_multi_info_read() + * + * Desc: Ask the multi handle if there's any messages/informationals from + * the individual transfers. Messages include informationals such as + * error code from the transfer or just the fact that a transfer is + * completed. More details on these should be written down as well. + * + * Repeated calls to this function will return a new struct each + * time, until a special "end of msgs" struct is returned as a signal + * that there is no more to get at this point. + * + * The data the returned pointer points to will not survive calling + * curl_multi_cleanup(). + * + * The 'CURLMsg' struct is meant to be very simple and only contain + * very basic informations. If more involved information is wanted, + * we will provide the particular "transfer handle" in that struct + * and that should/could/would be used in subsequent + * curl_easy_getinfo() calls (or similar). The point being that we + * must never expose complex structs to applications, as then we'll + * undoubtably get backwards compatibility problems in the future. + * + * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out + * of structs. It also writes the number of messages left in the + * queue (after this read) in the integer the second argument points + * to. + */ +CURLMsg *curl_multi_info_read(CURLM *multi_handle, + int *msgs_in_queue); + +#endif diff --git a/Source/CTest/Curl/netrc.c b/Source/CTest/Curl/netrc.c new file mode 100644 index 0000000..c2c8e65 --- /dev/null +++ b/Source/CTest/Curl/netrc.c @@ -0,0 +1,211 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif +#ifdef VMS +#include <unixlib.h> +#endif + +#include <curl/curl.h> + +#include "strequal.h" +#include "strtok.h" + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* Debug this single source file with: + 'make netrc' then run './netrc'! + + Oh, make sure you have a .netrc file too ;-) + */ + +/* Get user and password from .netrc when given a machine name */ + +enum { + NOTHING, + HOSTFOUND, /* the 'machine' keyword was found */ + HOSTCOMPLETE, /* the machine name following the keyword was found too */ + HOSTVALID, /* this is "our" machine! */ + + HOSTEND /* LAST enum */ +}; + +/* make sure we have room for at least this size: */ +#define LOGINSIZE 64 +#define PASSWORDSIZE 64 + +int Curl_parsenetrc(char *host, + char *login, + char *password) +{ + FILE *file; + char netrcbuffer[256]; + int retcode=1; + + char *home = NULL; + int state=NOTHING; + + char state_login=0; + char state_password=0; + +#define NETRC DOT_CHAR "netrc" + +#if defined(HAVE_GETPWUID) && defined(HAVE_GETEUID) + struct passwd *pw; + pw= getpwuid(geteuid()); + if (pw) { +#ifdef VMS + home = decc$translate_vms(pw->pw_dir); +#else + home = pw->pw_dir; +#endif + } +#else + void *pw=NULL; +#endif + + if(NULL == pw) { + home = curl_getenv("HOME"); /* portable environment reader */ + if(!home) { + return -1; + } + } + + if(strlen(home)>(sizeof(netrcbuffer)-strlen(NETRC))) { + if(NULL==pw) + free(home); + return -1; + } + + sprintf(netrcbuffer, "%s%s%s", home, DIR_CHAR, NETRC); + + file = fopen(netrcbuffer, "r"); + if(file) { + char *tok; + char *tok_buf; + while(fgets(netrcbuffer, sizeof(netrcbuffer), file)) { + tok=strtok_r(netrcbuffer, " \t\n", &tok_buf); + while(tok) { + switch(state) { + case NOTHING: + if(strequal("machine", tok)) { + /* the next tok is the machine name, this is in itself the + delimiter that starts the stuff entered for this machine, + after this we need to search for 'login' and + 'password'. */ + state=HOSTFOUND; + } + break; + case HOSTFOUND: + if(strequal(host, tok)) { + /* and yes, this is our host! */ + state=HOSTVALID; +#ifdef _NETRC_DEBUG + printf("HOST: %s\n", tok); +#endif + retcode=0; /* we did find our host */ + } + else + /* not our host */ + state=NOTHING; + break; + case HOSTVALID: + /* we are now parsing sub-keywords concerning "our" host */ + if(state_login) { + strncpy(login, tok, LOGINSIZE-1); +#ifdef _NETRC_DEBUG + printf("LOGIN: %s\n", login); +#endif + state_login=0; + } + else if(state_password) { + strncpy(password, tok, PASSWORDSIZE-1); +#ifdef _NETRC_DEBUG + printf("PASSWORD: %s\n", password); +#endif + state_password=0; + } + else if(strequal("login", tok)) + state_login=1; + else if(strequal("password", tok)) + state_password=1; + else if(strequal("machine", tok)) { + /* ok, there's machine here go => */ + state = HOSTFOUND; + } + break; + } /* switch (state) */ + tok = strtok_r(NULL, " \t\n", &tok_buf); + } /* while (tok) */ + } /* while fgets() */ + + fclose(file); + } + + if(NULL==pw) + free(home); + + return retcode; +} + +#ifdef _NETRC_DEBUG +int main(int argc, char **argv) +{ + char login[64]=""; + char password[64]=""; + + if(argc<2) + return -1; + + if(0 == ParseNetrc(argv[1], login, password)) { + printf("HOST: %s LOGIN: %s PASSWORD: %s\n", + argv[1], login, password); + } +} + +#endif + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/netrc.h b/Source/CTest/Curl/netrc.h new file mode 100644 index 0000000..de95390 --- /dev/null +++ b/Source/CTest/Curl/netrc.h @@ -0,0 +1,28 @@ +#ifndef __NETRC_H +#define __NETRC_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +int Curl_parsenetrc(char *host, + char *login, + char *password); +#endif diff --git a/Source/CTest/Curl/progress.c b/Source/CTest/Curl/progress.c new file mode 100644 index 0000000..318a6d8 --- /dev/null +++ b/Source/CTest/Curl/progress.c @@ -0,0 +1,384 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <string.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#if defined(__MINGW32__) +#include <winsock.h> +#endif +#include <time.h> +#endif + +/* 20000318 mgs + * later we use _scrsize to determine the screen width, this emx library + * function needs stdlib.h to be included */ +#if defined(__EMX__) +#include <stdlib.h> +#endif + +#include <curl/curl.h> +#include "urldata.h" +#include "sendf.h" + +#include "progress.h" + +static void time2str(char *r, int t) +{ + int h = (t/3600); + int m = (t-(h*3600))/60; + int s = (t-(h*3600)-(m*60)); + sprintf(r,"%2d:%02d:%02d",h,m,s); +} + +/* The point of this function would be to return a string of the input data, + but never longer than 5 columns. Add suffix k, M, G when suitable... */ +static char *max5data(double bytes, char *max5) +{ +#define ONE_KILOBYTE 1024 +#define ONE_MEGABYTE (1024*1024) + + if(bytes < 100000) { + sprintf(max5, "%5d", (int)bytes); + return max5; + } + if(bytes < (9999*ONE_KILOBYTE)) { + sprintf(max5, "%4dk", (int)bytes/ONE_KILOBYTE); + return max5; + } + if(bytes < (100*ONE_MEGABYTE)) { + /* 'XX.XM' is good as long as we're less than 100 megs */ + sprintf(max5, "%4.1fM", bytes/ONE_MEGABYTE); + return max5; + } + sprintf(max5, "%4dM", (int)bytes/ONE_MEGABYTE); + return max5; +} + +/* + + New proposed interface, 9th of February 2000: + + pgrsStartNow() - sets start time + pgrsSetDownloadSize(x) - known expected download size + pgrsSetUploadSize(x) - known expected upload size + pgrsSetDownloadCounter() - amount of data currently downloaded + pgrsSetUploadCounter() - amount of data currently uploaded + pgrsUpdate() - show progress + pgrsDone() - transfer complete + +*/ + +void Curl_pgrsDone(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + if(!(data->progress.flags & PGRS_HIDE)) { + data->progress.lastshow=0; + Curl_pgrsUpdate(conn); /* the final (forced) update */ + if(!data->progress.callback) + /* only output if we don't use progress callback */ + fprintf(data->set.err, "\n"); + } +} + +void Curl_pgrsTime(struct SessionHandle *data, timerid timer) +{ + switch(timer) { + default: + case TIMER_NONE: + /* mistake filter */ + break; + case TIMER_STARTSINGLE: + /* This is set at the start of a single fetch */ + data->progress.t_startsingle = Curl_tvnow(); + break; + + case TIMER_NAMELOOKUP: + data->progress.t_nslookup = + (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0; + break; + case TIMER_CONNECT: + data->progress.t_connect = + (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0; + break; + case TIMER_PRETRANSFER: + data->progress.t_pretransfer = + (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0; + break; + case TIMER_STARTTRANSFER: + data->progress.t_starttransfer = + (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0; + break; + case TIMER_POSTRANSFER: + /* this is the normal end-of-transfer thing */ + break; + } +} + +void Curl_pgrsStartNow(struct SessionHandle *data) +{ + data->progress.speeder_c = 0; /* reset the progress meter display */ + data->progress.start = Curl_tvnow(); +} + +void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, double size) +{ + data->progress.downloaded = size; +} + +void Curl_pgrsSetUploadCounter(struct SessionHandle *data, double size) +{ + data->progress.uploaded = size; +} + +void Curl_pgrsSetDownloadSize(struct SessionHandle *data, double size) +{ + if(size > 0) { + data->progress.size_dl = size; + data->progress.flags |= PGRS_DL_SIZE_KNOWN; + } +} + +void Curl_pgrsSetUploadSize(struct SessionHandle *data, double size) +{ + if(size > 0) { + data->progress.size_ul = size; + data->progress.flags |= PGRS_UL_SIZE_KNOWN; + } +} + +/* EXAMPLE OUTPUT to follow: + + % Total % Received % Xferd Average Speed Time Curr. + Dload Upload Total Current Left Speed +100 12345 100 12345 100 12345 12345 12345 12:12:12 12:12:12 12:12:12 12345 + + */ + +int Curl_pgrsUpdate(struct connectdata *conn) +{ + struct timeval now; + int result; + + char max5[6][10]; + double dlpercen=0; + double ulpercen=0; + double total_percen=0; + + double total_transfer; + double total_expected_transfer; + double timespent; + + struct SessionHandle *data = conn->data; + + int nowindex = data->progress.speeder_c% CURR_TIME; + int checkindex; + + int countindex; /* amount of seconds stored in the speeder array */ + + char time_left[10]; + char time_total[10]; + char time_current[10]; + + double ulestimate=0; + double dlestimate=0; + + double total_estimate; + + + if(data->progress.flags & PGRS_HIDE) + ; /* We do enter this function even if we don't wanna see anything, since + this is were lots of the calculations are being made that will be used + even when not displayed! */ + else if(!(data->progress.flags & PGRS_HEADERS_OUT)) { + if (!data->progress.callback) { + if(conn->resume_from) + fprintf(data->set.err, "** Resuming transfer from byte position %d\n", + conn->resume_from); + fprintf(data->set.err, + " %% Total %% Received %% Xferd Average Speed Time Curr.\n" + " Dload Upload Total Current Left Speed\n"); + } + data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */ + } + + now = Curl_tvnow(); /* what time is it */ + + /* The exact time spent so far (from the start) */ + timespent = (double)Curl_tvdiff (now, data->progress.start)/1000; + + data->progress.timespent = timespent; + + /* The average download speed this far */ + data->progress.dlspeed = + data->progress.downloaded/(timespent>0.01?timespent:1); + + /* The average upload speed this far */ + data->progress.ulspeed = + data->progress.uploaded/(timespent>0.01?timespent:1); + + if(data->progress.lastshow == Curl_tvlong(now)) + return 0; /* never update this more than once a second if the end isn't + reached */ + data->progress.lastshow = now.tv_sec; + + /* Let's do the "current speed" thing, which should use the fastest + of the dl/ul speeds. Store the fasted speed at entry 'nowindex'. */ + data->progress.speeder[ nowindex ] = + data->progress.downloaded>data->progress.uploaded? + data->progress.downloaded:data->progress.uploaded; + + /* remember the exact time for this moment */ + data->progress.speeder_time [ nowindex ] = now; + + /* advance our speeder_c counter, which is increased every time we get + here and we expect it to never wrap as 2^32 is a lot of seconds! */ + data->progress.speeder_c++; + + /* figure out how many index entries of data we have stored in our speeder + array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of + transfer. Imagine, after one second we have filled in two entries, + after two seconds we've filled in three entries etc. */ + countindex = ((data->progress.speeder_c>=CURR_TIME)? + CURR_TIME:data->progress.speeder_c) - 1; + + /* first of all, we don't do this if there's no counted seconds yet */ + if(countindex) { + long span_ms; + + /* Get the index position to compare with the 'nowindex' position. + Get the oldest entry possible. While we have less than CURR_TIME + entries, the first entry will remain the oldest. */ + checkindex = (data->progress.speeder_c>=CURR_TIME)? + data->progress.speeder_c%CURR_TIME:0; + + /* Figure out the exact time for the time span */ + span_ms = Curl_tvdiff(now, + data->progress.speeder_time[checkindex]); + if(0 == span_ms) + span_ms=1; /* at least one millisecond MUST have passed */ + + /* Calculate the average speed the last 'countindex' seconds */ + data->progress.current_speed = + (data->progress.speeder[nowindex]- + data->progress.speeder[checkindex])/((double)span_ms/1000); + } + else + /* the first second we use the main average */ + data->progress.current_speed = + (data->progress.ulspeed>data->progress.dlspeed)? + data->progress.ulspeed:data->progress.dlspeed; + + if(data->progress.flags & PGRS_HIDE) + return 0; + + else if(data->set.fprogress) { + /* There's a callback set, so we call that instead of writing + anything ourselves. This really is the way to go. */ + result= data->set.fprogress(data->set.progress_client, + data->progress.size_dl, + data->progress.downloaded, + data->progress.size_ul, + data->progress.uploaded); + if(result) + failf(data, "Callback aborted"); + return result; + } + + /* Figure out the estimated time of arrival for the upload */ + if((data->progress.flags & PGRS_UL_SIZE_KNOWN) && data->progress.ulspeed){ + ulestimate = data->progress.size_ul / data->progress.ulspeed; + ulpercen = (data->progress.uploaded / data->progress.size_ul)*100; + } + + /* ... and the download */ + if((data->progress.flags & PGRS_DL_SIZE_KNOWN) && data->progress.dlspeed) { + dlestimate = data->progress.size_dl / data->progress.dlspeed; + dlpercen = (data->progress.downloaded / data->progress.size_dl)*100; + } + + /* Now figure out which of them that is slower and use for the for + total estimate! */ + total_estimate = ulestimate>dlestimate?ulestimate:dlestimate; + + + /* If we have a total estimate, we can display that and the expected + time left */ + if(total_estimate) { + time2str(time_left, total_estimate-(int) data->progress.timespent); + time2str(time_total, total_estimate); + } + else { + /* otherwise we blank those times */ + strcpy(time_left, "--:--:--"); + strcpy(time_total, "--:--:--"); + } + /* The time spent so far is always known */ + time2str(time_current, data->progress.timespent); + + /* Get the total amount of data expected to get transfered */ + total_expected_transfer = + (data->progress.flags & PGRS_UL_SIZE_KNOWN? + data->progress.size_ul:data->progress.uploaded)+ + (data->progress.flags & PGRS_DL_SIZE_KNOWN? + data->progress.size_dl:data->progress.downloaded); + + /* We have transfered this much so far */ + total_transfer = data->progress.downloaded + data->progress.uploaded; + + /* Get the percentage of data transfered so far */ + if(total_expected_transfer) + total_percen=(double)(total_transfer/total_expected_transfer)*100; + + fprintf(data->set.err, + "\r%3d %s %3d %s %3d %s %s %s %s %s %s %s", + (int)total_percen, /* total % */ + max5data(total_expected_transfer, max5[2]), /* total size */ + (int)dlpercen, /* rcvd % */ + max5data(data->progress.downloaded, max5[0]), /* rcvd size */ + (int)ulpercen, /* xfer % */ + max5data(data->progress.uploaded, max5[1]), /* xfer size */ + + max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */ + max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */ + time_total, /* total time */ + time_current, /* current time */ + time_left, /* time left */ + max5data(data->progress.current_speed, max5[5]) /* current speed */ + ); + + /* we flush the output stream to make it appear as soon as possible */ + fflush(data->set.err); + + return 0; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/progress.h b/Source/CTest/Curl/progress.h new file mode 100644 index 0000000..2d45984 --- /dev/null +++ b/Source/CTest/Curl/progress.h @@ -0,0 +1,68 @@ +#ifndef __PROGRESS_H +#define __PROGRESS_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "timeval.h" + + +typedef enum { + TIMER_NONE, + TIMER_NAMELOOKUP, + TIMER_CONNECT, + TIMER_PRETRANSFER, + TIMER_STARTTRANSFER, + TIMER_POSTRANSFER, + TIMER_STARTSINGLE, + TIMER_LAST /* must be last */ +} timerid; + +void Curl_pgrsDone(struct connectdata *); +void Curl_pgrsStartNow(struct SessionHandle *data); +void Curl_pgrsSetDownloadSize(struct SessionHandle *data, double size); +void Curl_pgrsSetUploadSize(struct SessionHandle *data, double size); +void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, double size); +void Curl_pgrsSetUploadCounter(struct SessionHandle *data, double size); +int Curl_pgrsUpdate(struct connectdata *); +void Curl_pgrsTime(struct SessionHandle *data, timerid timer); + + +/* Don't show progress for sizes smaller than: */ +#define LEAST_SIZE_PROGRESS BUFSIZE + +#define PROGRESS_DOWNLOAD (1<<0) +#define PROGRESS_UPLOAD (1<<1) +#define PROGRESS_DOWN_AND_UP (PROGRESS_UPLOAD | PROGRESS_DOWNLOAD) + +#define PGRS_SHOW_DL (1<<0) +#define PGRS_SHOW_UL (1<<1) +#define PGRS_DONE_DL (1<<2) +#define PGRS_DONE_UL (1<<3) +#define PGRS_HIDE (1<<4) +#define PGRS_UL_SIZE_KNOWN (1<<5) +#define PGRS_DL_SIZE_KNOWN (1<<6) + +#define PGRS_HEADERS_OUT (1<<7) /* set when the headers have been written */ + + +#endif /* __PROGRESS_H */ diff --git a/Source/CTest/Curl/security.c b/Source/CTest/Curl/security.c new file mode 100644 index 0000000..9e2cc2e --- /dev/null +++ b/Source/CTest/Curl/security.c @@ -0,0 +1,562 @@ +/* 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" + +#ifdef KRB4 + +#define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */ +#include <curl/mprintf.h> + +#include "security.h" +#include <stdlib.h> +#include <string.h> +#include <netdb.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "base64.h" +#include "sendf.h" +#include "ftp.h" + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +static struct { + enum protection_level level; + const char *name; +} level_names[] = { + { prot_clear, "clear" }, + { prot_safe, "safe" }, + { prot_confidential, "confidential" }, + { prot_private, "private" } +}; + +static enum protection_level +name_to_level(const char *name) +{ + int i; + for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) + if(!strncasecmp(level_names[i].name, name, strlen(name))) + return level_names[i].level; + return (enum protection_level)-1; +} + +static struct Curl_sec_client_mech *mechs[] = { +#ifdef KRB5 + /* not supported */ +#endif +#ifdef KRB4 + &Curl_krb4_client_mech, +#endif + NULL +}; + +int +Curl_sec_getc(struct connectdata *conn, FILE *F) +{ + if(conn->sec_complete && conn->data_prot) { + char c; + if(Curl_sec_read(conn, fileno(F), &c, 1) <= 0) + return EOF; + return c; + } + else + return getc(F); +} + +static int +block_read(int fd, void *buf, size_t len) +{ + unsigned char *p = buf; + int b; + while(len) { + b = read(fd, p, len); + if (b == 0) + return 0; + else if (b < 0) + return -1; + len -= b; + p += b; + } + return p - (unsigned char*)buf; +} + +static int +block_write(int fd, void *buf, size_t len) +{ + unsigned char *p = buf; + int b; + while(len) { + b = write(fd, p, len); + if(b < 0) + return -1; + len -= b; + p += b; + } + return p - (unsigned char*)buf; +} + +static int +sec_get_data(struct connectdata *conn, + int fd, struct krb4buffer *buf) +{ + int len; + int b; + + b = block_read(fd, &len, sizeof(len)); + if (b == 0) + return 0; + else if (b < 0) + return -1; + len = ntohl(len); + buf->data = realloc(buf->data, len); + b = block_read(fd, buf->data, len); + if (b == 0) + return 0; + else if (b < 0) + return -1; + buf->size = (conn->mech->decode)(conn->app_data, buf->data, len, + conn->data_prot, conn); + buf->index = 0; + return 0; +} + +static size_t +buffer_read(struct krb4buffer *buf, void *data, size_t len) +{ + len = min(len, buf->size - buf->index); + memcpy(data, (char*)buf->data + buf->index, len); + buf->index += len; + return len; +} + +static size_t +buffer_write(struct krb4buffer *buf, void *data, size_t len) +{ + if(buf->index + len > buf->size) { + void *tmp; + if(buf->data == NULL) + tmp = malloc(1024); + else + tmp = realloc(buf->data, buf->index + len); + if(tmp == NULL) + return -1; + buf->data = tmp; + buf->size = buf->index + len; + } + memcpy((char*)buf->data + buf->index, data, len); + buf->index += len; + return len; +} + +int +Curl_sec_read(struct connectdata *conn, int fd, void *buffer, int length) +{ + size_t len; + int rx = 0; + + if(conn->sec_complete == 0 || conn->data_prot == 0) + return read(fd, buffer, length); + + if(conn->in_buffer.eof_flag){ + conn->in_buffer.eof_flag = 0; + return 0; + } + + len = buffer_read(&conn->in_buffer, buffer, length); + length -= len; + rx += len; + buffer = (char*)buffer + len; + + while(length) { + if(sec_get_data(conn, fd, &conn->in_buffer) < 0) + return -1; + if(conn->in_buffer.size == 0) { + if(rx) + conn->in_buffer.eof_flag = 1; + return rx; + } + len = buffer_read(&conn->in_buffer, buffer, length); + length -= len; + rx += len; + buffer = (char*)buffer + len; + } + return rx; +} + +static int +sec_send(struct connectdata *conn, int fd, char *from, int length) +{ + int bytes; + void *buf; + bytes = (conn->mech->encode)(conn->app_data, from, length, conn->data_prot, + &buf, conn); + bytes = htonl(bytes); + block_write(fd, &bytes, sizeof(bytes)); + block_write(fd, buf, ntohl(bytes)); + free(buf); + return length; +} + +int +Curl_sec_fflush_fd(struct connectdata *conn, int fd) +{ + if(conn->data_prot != prot_clear) { + if(conn->out_buffer.index > 0){ + Curl_sec_write(conn, fd, + conn->out_buffer.data, conn->out_buffer.index); + conn->out_buffer.index = 0; + } + sec_send(conn, fd, NULL, 0); + } + return 0; +} + +int +Curl_sec_write(struct connectdata *conn, int fd, char *buffer, int length) +{ + int len = conn->buffer_size; + int tx = 0; + + if(conn->data_prot == prot_clear) + return write(fd, buffer, length); + + len -= (conn->mech->overhead)(conn->app_data, conn->data_prot, len); + while(length){ + if(length < len) + len = length; + sec_send(conn, fd, buffer, len); + length -= len; + buffer += len; + tx += len; + } + return tx; +} + +int +Curl_sec_vfprintf2(struct connectdata *conn, FILE *f, const char *fmt, va_list ap) +{ + char *buf; + int ret; + if(conn->data_prot == prot_clear) + return vfprintf(f, fmt, ap); + else { + buf = aprintf(fmt, ap); + ret = buffer_write(&conn->out_buffer, buf, strlen(buf)); + free(buf); + return ret; + } +} + +int +Curl_sec_fprintf2(struct connectdata *conn, FILE *f, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = Curl_sec_vfprintf2(conn, f, fmt, ap); + va_end(ap); + return ret; +} + +int +Curl_sec_putc(struct connectdata *conn, int c, FILE *F) +{ + char ch = c; + if(conn->data_prot == prot_clear) + return putc(c, F); + + buffer_write(&conn->out_buffer, &ch, 1); + if(c == '\n' || conn->out_buffer.index >= 1024 /* XXX */) { + Curl_sec_write(conn, fileno(F), conn->out_buffer.data, conn->out_buffer.index); + conn->out_buffer.index = 0; + } + return c; +} + +int +Curl_sec_read_msg(struct connectdata *conn, char *s, int level) +{ + int len; + char *buf; + int code; + + buf = malloc(strlen(s)); + len = Curl_base64_decode(s + 4, buf); /* XXX */ + + len = (conn->mech->decode)(conn->app_data, buf, len, level, conn); + if(len < 0) + return -1; + + buf[len] = '\0'; + + if(buf[3] == '-') + code = 0; + else + sscanf(buf, "%d", &code); + if(buf[len-1] == '\n') + buf[len-1] = '\0'; + strcpy(s, buf); + free(buf); + return code; +} + +/* modified to return how many bytes written, or -1 on error ***/ +int +Curl_sec_vfprintf(struct connectdata *conn, FILE *f, const char *fmt, va_list ap) +{ + int ret = 0; + char *buf; + void *enc; + int len; + if(!conn->sec_complete) + return vfprintf(f, fmt, ap); + + buf = aprintf(fmt, ap); + len = (conn->mech->encode)(conn->app_data, buf, strlen(buf), + conn->command_prot, &enc, + conn); + free(buf); + if(len < 0) { + failf(conn->data, "Failed to encode command."); + return -1; + } + if(Curl_base64_encode(enc, len, &buf) < 0){ + failf(conn->data, "Out of memory base64-encoding."); + return -1; + } + if(conn->command_prot == prot_safe) + ret = fprintf(f, "MIC %s", buf); + else if(conn->command_prot == prot_private) + ret = fprintf(f, "ENC %s", buf); + else if(conn->command_prot == prot_confidential) + ret = fprintf(f, "CONF %s", buf); + + free(buf); + return ret; +} + +int +Curl_sec_fprintf(struct connectdata *conn, FILE *f, const char *fmt, ...) +{ + va_list ap; + int ret; + va_start(ap, fmt); + ret = Curl_sec_vfprintf(conn, f, fmt, ap); + va_end(ap); + return ret; +} + + +enum protection_level +Curl_set_command_prot(struct connectdata *conn, enum protection_level level) +{ + enum protection_level old = conn->command_prot; + conn->command_prot = level; + return old; +} + +static int +sec_prot_internal(struct connectdata *conn, int level) +{ + char *p; + unsigned int s = 1048576; + ssize_t nread; + + if(!conn->sec_complete){ + infof(conn->data, "No security data exchange has taken place.\n"); + return -1; + } + + if(level){ + if(Curl_ftpsendf(conn, "PBSZ %u", s)) + return -1; + + nread = Curl_GetFTPResponse(conn->data->state.buffer, conn, NULL); + if(nread < 0) + return -1; + + if(conn->data->state.buffer[0] != '2'){ + failf(conn->data, "Failed to set protection buffer size."); + return -1; + } + conn->buffer_size = s; + + p = strstr(conn->data->state.buffer, "PBSZ="); + if(p) + sscanf(p, "PBSZ=%u", &s); + if(s < conn->buffer_size) + conn->buffer_size = s; + } + + if(Curl_ftpsendf(conn, "PROT %c", level["CSEP"])) + return -1; + + nread = Curl_GetFTPResponse(conn->data->state.buffer, conn, NULL); + if(nread < 0) + return -1; + + if(conn->data->state.buffer[0] != '2'){ + failf(conn->data, "Failed to set protection level."); + return -1; + } + + conn->data_prot = (enum protection_level)level; + return 0; +} + +void +Curl_sec_set_protection_level(struct connectdata *conn) +{ + if(conn->sec_complete && conn->data_prot != conn->request_data_prot) + sec_prot_internal(conn, conn->request_data_prot); +} + + +int +Curl_sec_request_prot(struct connectdata *conn, const char *level) +{ + int l = name_to_level(level); + if(l == -1) + return -1; + conn->request_data_prot = (enum protection_level)l; + return 0; +} + +int +Curl_sec_login(struct connectdata *conn) +{ + int ret; + struct Curl_sec_client_mech **m; + ssize_t nread; + struct SessionHandle *data=conn->data; + int ftpcode; + + for(m = mechs; *m && (*m)->name; m++) { + void *tmp; + + tmp = realloc(conn->app_data, (*m)->size); + if (tmp == NULL) { + failf (data, "realloc %u failed", (*m)->size); + return -1; + } + conn->app_data = tmp; + + if((*m)->init && (*(*m)->init)(conn->app_data) != 0) { + infof(data, "Skipping %s...\n", (*m)->name); + continue; + } + infof(data, "Trying %s...\n", (*m)->name); + + if(Curl_ftpsendf(conn, "AUTH %s", (*m)->name)) + return -1; + + nread = Curl_GetFTPResponse(conn->data->state.buffer, conn, &ftpcode); + if(nread < 0) + return -1; + + if(conn->data->state.buffer[0] != '3'){ + switch(ftpcode) { + case 504: + infof(data, + "%s is not supported by the server.\n", (*m)->name); + break; + case 534: + infof(data, "%s rejected as security mechanism.\n", (*m)->name); + break; + default: + if(conn->data->state.buffer[0] == '5') { + infof(data, "The server doesn't support the FTP " + "security extensions.\n"); + return -1; + } + break; + } + continue; + } + + ret = (*(*m)->auth)(conn->app_data, conn); + + if(ret == AUTH_CONTINUE) + continue; + else if(ret != AUTH_OK){ + /* mechanism is supposed to output error string */ + return -1; + } + conn->mech = *m; + conn->sec_complete = 1; + conn->command_prot = prot_safe; + break; + } + + return *m == NULL; +} + +void +Curl_sec_end(struct connectdata *conn) +{ + if (conn->mech != NULL) { + if(conn->mech->end) + (conn->mech->end)(conn->app_data); + memset(conn->app_data, 0, conn->mech->size); + free(conn->app_data); + conn->app_data = NULL; + } + conn->sec_complete = 0; + conn->data_prot = (enum protection_level)0; + conn->mech=NULL; +} + +#endif /* KRB4 */ + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/security.h b/Source/CTest/Curl/security.h new file mode 100644 index 0000000..cd8d235 --- /dev/null +++ b/Source/CTest/Curl/security.h @@ -0,0 +1,72 @@ +#ifndef __SECURITY_H +#define __SECURITY_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* this is a re-write */ + +#include <stdarg.h> +#include "urldata.h" /* for struct connectdata * */ + +struct Curl_sec_client_mech { + const char *name; + size_t size; + int (*init)(void *); + int (*auth)(void *, struct connectdata *); + void (*end)(void *); + int (*check_prot)(void *, int); + int (*overhead)(void *, int, int); + int (*encode)(void *, void*, int, int, void**, struct connectdata *); + int (*decode)(void *, void*, int, int, struct connectdata *); +}; + + +#define AUTH_OK 0 +#define AUTH_CONTINUE 1 +#define AUTH_ERROR 2 + +extern struct Curl_sec_client_mech Curl_krb4_client_mech; + +int Curl_sec_fflush_fd(struct connectdata *conn, int fd); +int Curl_sec_fprintf (struct connectdata *, FILE *, const char *, ...); +int Curl_sec_getc (struct connectdata *conn, FILE *); +int Curl_sec_putc (struct connectdata *conn, int, FILE *); +int Curl_sec_read (struct connectdata *conn, int, void *, int); +int Curl_sec_read_msg (struct connectdata *conn, char *, int); + +int Curl_sec_vfprintf(struct connectdata *, FILE *, const char *, va_list); +int Curl_sec_fprintf2(struct connectdata *conn, FILE *f, const char *fmt, ...); +int Curl_sec_vfprintf2(struct connectdata *conn, FILE *, const char *, va_list); +int Curl_sec_write (struct connectdata *conn, int, char *, int); + +void Curl_sec_end (struct connectdata *); +int Curl_sec_login (struct connectdata *); +void Curl_sec_prot (int, char **); +int Curl_sec_request_prot (struct connectdata *conn, const char *level); +void Curl_sec_set_protection_level(struct connectdata *conn); +void Curl_sec_status (void); + +enum protection_level Curl_set_command_prot(struct connectdata *, + enum protection_level); + +#endif diff --git a/Source/CTest/Curl/sendf.c b/Source/CTest/Curl/sendf.c new file mode 100644 index 0000000..7f203ee --- /dev/null +++ b/Source/CTest/Curl/sendf.c @@ -0,0 +1,365 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2002, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> /* required for send() & recv() prototypes */ +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#endif + +#include <curl/curl.h> +#include "urldata.h" +#include "sendf.h" + +#define _MPRINTF_REPLACE /* use the internal *printf() functions */ +#include <curl/mprintf.h> + +#ifdef KRB4 +#include "security.h" +#endif +#include <string.h> +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* returns last node in linked list */ +static struct curl_slist *slist_get_last(struct curl_slist *list) +{ + struct curl_slist *item; + + /* if caller passed us a NULL, return now */ + if (!list) + return NULL; + + /* loop through to find the last item */ + item = list; + while (item->next) { + item = item->next; + } + return item; +} + +/* append a struct to the linked list. It always retunrs the address of the + * first record, so that you can sure this function as an initialization + * function as well as an append function. If you find this bothersome, + * then simply create a separate _init function and call it appropriately from + * within the proram. */ +struct curl_slist *curl_slist_append(struct curl_slist *list, + const char *data) +{ + struct curl_slist *last; + struct curl_slist *new_item; + + new_item = (struct curl_slist *) malloc(sizeof(struct curl_slist)); + if (new_item) { + new_item->next = NULL; + new_item->data = strdup(data); + } + else { + fprintf(stderr, "Cannot allocate memory for QUOTE list.\n"); + return NULL; + } + + if (list) { + last = slist_get_last(list); + last->next = new_item; + return list; + } + + /* if this is the first item, then new_item *is* the list */ + return new_item; +} + +/* be nice and clean up resources */ +void curl_slist_free_all(struct curl_slist *list) +{ + struct curl_slist *next; + struct curl_slist *item; + + if (!list) + return; + + item = list; + do { + next = item->next; + + if (item->data) { + free(item->data); + } + free(item); + item = next; + } while (next); +} + + +/* Curl_infof() is for info message along the way */ + +void Curl_infof(struct SessionHandle *data, const char *fmt, ...) +{ + va_list ap; + if(data->set.verbose) { + va_start(ap, fmt); + fputs("* ", data->set.err); + vfprintf(data->set.err, fmt, ap); + va_end(ap); + } +} + +/* Curl_failf() is for messages stating why we failed. + * The message SHALL NOT include any LF or CR. + */ + +void Curl_failf(struct SessionHandle *data, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if(data->set.errorbuffer && !data->state.errorbuf) { + vsnprintf(data->set.errorbuffer, CURL_ERROR_SIZE, fmt, ap); + data->state.errorbuf = TRUE; /* wrote error string */ + } + va_end(ap); +} + +/* Curl_sendf() sends formated data to the server */ +CURLcode Curl_sendf(int sockfd, struct connectdata *conn, + const char *fmt, ...) +{ + struct SessionHandle *data = conn->data; + ssize_t bytes_written; + CURLcode result; + char *s; + va_list ap; + va_start(ap, fmt); + s = vaprintf(fmt, ap); /* returns an allocated string */ + va_end(ap); + if(!s) + return 0; /* failure */ + if(data->set.verbose) + fprintf(data->set.err, "> %s", s); + + /* Write the buffer to the socket */ + result = Curl_write(conn, sockfd, s, strlen(s), &bytes_written); + + free(s); /* free the output string */ + + return result; +} + +/* + * Curl_write() is an internal write function that sends plain (binary) data + * to the server. Works with plain sockets, SSL or kerberos. + * + */ +CURLcode Curl_write(struct connectdata *conn, int sockfd, + void *mem, size_t len, + ssize_t *written) +{ + ssize_t bytes_written; + +#ifdef USE_SSLEAY + /* SSL_write() is said to return 'int' while write() and send() returns + 'size_t' */ + if (conn->ssl.use) { + int err; + int rc = SSL_write(conn->ssl.handle, mem, len); + + if(rc < 0) { + err = SSL_get_error(conn->ssl.handle, rc); + + switch(err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* this is basicly the EWOULDBLOCK equivalent */ + *written = 0; + return CURLE_OK; + } + /* a true error */ + failf(conn->data, "SSL_write() return error %d\n", err); + return CURLE_WRITE_ERROR; + } + bytes_written = rc; + } + else { +#endif +#ifdef KRB4 + if(conn->sec_complete) { + bytes_written = Curl_sec_write(conn, sockfd, mem, len); + } + else +#endif /* KRB4 */ + { + bytes_written = swrite(sockfd, mem, len); + } + if(-1 == bytes_written) { +#ifdef WIN32 + if(WSAEWOULDBLOCK == GetLastError()) +#else + if(EWOULDBLOCK == errno) +#endif + { + /* this is just a case of EWOULDBLOCK */ + *written=0; + return CURLE_OK; + } + } +#ifdef USE_SSLEAY + } +#endif + + *written = bytes_written; + return (-1 != bytes_written)?CURLE_OK:CURLE_WRITE_ERROR; +} + +/* client_write() sends data to the write callback(s) + + The bit pattern defines to what "streams" to write to. Body and/or header. + The defines are in sendf.h of course. + */ +CURLcode Curl_client_write(struct SessionHandle *data, + int type, + char *ptr, + size_t len) +{ + size_t wrote; + + if(0 == len) + len = strlen(ptr); + + if(type & CLIENTWRITE_BODY) { + wrote = data->set.fwrite(ptr, 1, len, data->set.out); + if(wrote != len) { + failf (data, "Failed writing body"); + return CURLE_WRITE_ERROR; + } + } + if((type & CLIENTWRITE_HEADER) && + (data->set.fwrite_header || data->set.writeheader) ) { + /* + * Write headers to the same callback or to the especially setup + * header callback function (added after version 7.7.1). + */ + curl_write_callback writeit= + data->set.fwrite_header?data->set.fwrite_header:data->set.fwrite; + + wrote = writeit(ptr, 1, len, data->set.writeheader); + if(wrote != len) { + failf (data, "Failed writing header"); + return CURLE_WRITE_ERROR; + } + } + + return CURLE_OK; +} + +/* + * Internal read-from-socket function. This is meant to deal with plain + * sockets, SSL sockets and kerberos sockets. + * + * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return + * a regular CURLcode value. + */ +int Curl_read(struct connectdata *conn, + int sockfd, + char *buf, + size_t buffersize, + ssize_t *n) +{ + ssize_t nread; + +#ifdef USE_SSLEAY + if (conn->ssl.use) { + bool loop=TRUE; + int err; + do { + nread = SSL_read(conn->ssl.handle, buf, buffersize); + + if(nread >= 0) + /* successful read */ + break; + + err = SSL_get_error(conn->ssl.handle, nread); + + switch(err) { + case SSL_ERROR_NONE: /* this is not an error */ + case SSL_ERROR_ZERO_RETURN: /* no more data */ + loop=0; /* get out of loop */ + break; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* if there's data pending, then we re-invoke SSL_read() */ + break; + } + } while(loop); + if(loop && SSL_pending(conn->ssl.handle)) + return -1; /* basicly EWOULDBLOCK */ + } + else { +#endif +#ifdef KRB4 + if(conn->sec_complete) + nread = Curl_sec_read(conn, sockfd, buf, buffersize); + else +#endif + nread = sread (sockfd, buf, buffersize); + + if(-1 == nread) { +#ifdef WIN32 + if(WSAEWOULDBLOCK == GetLastError()) +#else + if(EWOULDBLOCK == errno) +#endif + return -1; + } + +#ifdef USE_SSLEAY + } +#endif /* USE_SSLEAY */ + *n = nread; + return CURLE_OK; +} + + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/sendf.h b/Source/CTest/Curl/sendf.h new file mode 100644 index 0000000..6724810 --- /dev/null +++ b/Source/CTest/Curl/sendf.h @@ -0,0 +1,56 @@ +#ifndef __SENDF_H +#define __SENDF_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +CURLcode Curl_sendf(int fd, struct connectdata *, const char *fmt, ...); +void Curl_infof(struct SessionHandle *, const char *fmt, ...); +void Curl_failf(struct SessionHandle *, const char *fmt, ...); + +#define infof Curl_infof +#define failf Curl_failf + +struct send_buffer { + char *buffer; + size_t size_max; + size_t size_used; +}; +typedef struct send_buffer send_buffer; + +#define CLIENTWRITE_BODY 1 +#define CLIENTWRITE_HEADER 2 +#define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER) + +CURLcode Curl_client_write(struct SessionHandle *data, int type, char *ptr, + size_t len); + +/* internal read-function, does plain socket, SSL and krb4 */ +int Curl_read(struct connectdata *conn, int sockfd, + char *buf, size_t buffersize, + ssize_t *n); +/* internal write-function, does plain socket, SSL and krb4 */ +CURLcode Curl_write(struct connectdata *conn, int sockfd, + void *mem, size_t len, + ssize_t *written); + +#endif diff --git a/Source/CTest/Curl/setup.h b/Source/CTest/Curl/setup.h new file mode 100644 index 0000000..b18bae5 --- /dev/null +++ b/Source/CTest/Curl/setup.h @@ -0,0 +1,169 @@ +#ifndef __SETUP_H +#define __SETUP_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + + + +#if !defined(WIN32) && defined(_WIN32) +/* This _might_ be a good Borland fix. Please report whether this works or + not! */ +#define WIN32 +#endif + +#ifdef HAVE_CONFIG_H + +#ifdef VMS +#include "config-vms.h" +#else +#include "config.h" /* the configure script results */ +#endif + +#else +#ifdef WIN32 +/* hand-modified win32 config.h! */ +#include "config-win32.h" +#endif +#ifdef macintosh +/* hand-modified MacOS config.h! */ +#include "config-mac.h" +#endif + +#endif + +#ifndef __cplusplus /* (rabe) */ +typedef unsigned char bool; +#define typedef_bool +#endif /* (rabe) */ + +#ifdef NEED_REENTRANT +/* Solaris machines needs _REENTRANT set for a few function prototypes and + things to appear in the #include files. We need to #define it before all + #include files */ +#define _REENTRANT +#endif + + +#include <stdio.h> +#ifndef OS +#ifdef WIN32 +#define OS "win32" +#else +#define OS "unknown" +#endif +#endif + +#if defined(HAVE_X509_H) && defined(HAVE_SSL_H) && defined(HAVE_RSA_H) && \ +defined(HAVE_PEM_H) && defined(HAVE_ERR_H) && defined(HAVE_CRYPTO_H) && \ +defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) + /* the six important includes files all exist and so do both libs, + defined SSLeay usage */ +#define USE_SSLEAY 1 +#endif +#if defined(HAVE_OPENSSL_X509_H) && defined(HAVE_OPENSSL_SSL_H) && \ +defined(HAVE_OPENSSL_RSA_H) && defined(HAVE_OPENSSL_PEM_H) && \ +defined(HAVE_OPENSSL_ERR_H) && defined(HAVE_OPENSSL_CRYPTO_H) && \ +defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) + /* the six important includes files all exist and so do both libs, + defined SSLeay usage */ +#define USE_SSLEAY 1 +#define USE_OPENSSL 1 +#endif + +#ifndef STDC_HEADERS /* no standard C headers! */ +#ifdef VMS +#include "../include/curl/stdcheaders.h" +#else +#include <curl/stdcheaders.h> +#endif + +#else +#ifdef _AIX +#include <curl/stdcheaders.h> +#endif +#endif + +/* Below we define four functions. They should + 1. close a socket + 2. read from a socket + 3. write to a socket + + 4. set the SIGALRM signal timeout + 5. set dir/file naming defines + */ + +#ifdef WIN32 +#if !defined(__GNUC__) || defined(__MINGW32__) +#define sclose(x) closesocket(x) +#define sread(x,y,z) recv(x,y,z,0) +#define swrite(x,y,z) (size_t)send(x,y,z,0) +#undef HAVE_ALARM +#else + /* gcc-for-win is still good :) */ +#define sclose(x) close(x) +#define sread(x,y,z) recv(x,y,z,0) +#define swrite(x,y,z) send(x,y,z,0) +#define HAVE_ALARM +#endif + +#define PATH_CHAR ";" +#define DIR_CHAR "\\" +#define DOT_CHAR "_" + +#else +#define sclose(x) close(x) +#define sread(x,y,z) recv(x,y,z,0) +#define swrite(x,y,z) send(x,y,z,0) +#define HAVE_ALARM + +#define PATH_CHAR ":" +#define DIR_CHAR "/" +#define DOT_CHAR "." + +#ifdef HAVE_STRCASECMP +/* this is for "-ansi -Wall -pedantic" to stop complaining! */ +extern int (strcasecmp)(const char *s1, const char *s2); +extern int (strncasecmp)(const char *s1, const char *s2, size_t n); +#ifndef fileno /* sunos 4 have this as a macro! */ +int fileno( FILE *stream); +#endif +#endif + +#endif + +/* + * Curl_addrinfo MUST be used for name resolving information. + * Information regarding a single IP witin a Curl_addrinfo MUST be stored in + * a Curl_ipconnect struct. + */ +#ifdef ENABLE_IPV6 +typedef struct addrinfo Curl_addrinfo; +typedef struct addrinfo Curl_ipconnect; +#else +typedef struct hostent Curl_addrinfo; +typedef struct in_addr Curl_ipconnect; +#endif + + + +#endif /* __CONFIG_H */ diff --git a/Source/CTest/Curl/speedcheck.c b/Source/CTest/Curl/speedcheck.c new file mode 100644 index 0000000..dbd2e87 --- /dev/null +++ b/Source/CTest/Curl/speedcheck.c @@ -0,0 +1,78 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <stdio.h> +#include <string.h> +#if defined(__MINGW32__) +#include <winsock.h> +#endif + +#include <curl/curl.h> +#include "urldata.h" +#include "sendf.h" +#include "speedcheck.h" + +void Curl_speedinit(struct SessionHandle *data) +{ + memset(&data->state.keeps_speed, 0, sizeof(struct timeval)); +} + +CURLcode Curl_speedcheck(struct SessionHandle *data, + struct timeval now) +{ + if((data->progress.current_speed >= 0) && + data->set.low_speed_time && + (Curl_tvlong(data->state.keeps_speed) != 0) && + (data->progress.current_speed < data->set.low_speed_limit)) { + + /* We are now below the "low speed limit". If we are below it + for "low speed time" seconds we consider that enough reason + to abort the download. */ + + if( (Curl_tvdiff(now, data->state.keeps_speed)/1000) > + data->set.low_speed_time) { + /* we have been this slow for long enough, now die */ + failf(data, + "Operation too slow. " + "Less than %d bytes/sec transfered the last %d seconds", + data->set.low_speed_limit, + data->set.low_speed_time); + return CURLE_OPERATION_TIMEOUTED; + } + } + else { + /* we keep up the required speed all right */ + data->state.keeps_speed = now; + } + return CURLE_OK; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/speedcheck.h b/Source/CTest/Curl/speedcheck.h new file mode 100644 index 0000000..08eca0d --- /dev/null +++ b/Source/CTest/Curl/speedcheck.h @@ -0,0 +1,34 @@ +#ifndef __SPEEDCHECK_H +#define __SPEEDCHECK_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include "timeval.h" + +void Curl_speedinit(struct SessionHandle *data); +CURLcode Curl_speedcheck(struct SessionHandle *data, + struct timeval now); + +#endif diff --git a/Source/CTest/Curl/ssluse.c b/Source/CTest/Curl/ssluse.c new file mode 100644 index 0000000..74cdca1 --- /dev/null +++ b/Source/CTest/Curl/ssluse.c @@ -0,0 +1,956 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* + * The original SSL code for curl was written by + * Linas Vepstas <linas@linas.org> and Sampo Kellomaki <sampo@iki.fi> + */ + +#include "setup.h" + +#include <string.h> +#include <stdlib.h> + +#include "urldata.h" +#include "sendf.h" +#include "formdata.h" /* for the boundary function */ + +#ifdef USE_SSLEAY +#include <openssl/rand.h> + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x0090581fL +#define HAVE_SSL_GET1_SESSION 1 +#else +#undef HAVE_SSL_GET1_SESSION +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x00904100L +#define HAVE_USERDATA_IN_PWD_CALLBACK 1 +#else +#undef HAVE_USERDATA_IN_PWD_CALLBACK +#endif + +#ifndef HAVE_USERDATA_IN_PWD_CALLBACK +static char global_passwd[64]; +#endif + +static int passwd_callback(char *buf, int num, int verify +#if HAVE_USERDATA_IN_PWD_CALLBACK + /* This was introduced in 0.9.4, we can set this + using SSL_CTX_set_default_passwd_cb_userdata() + */ + , void *global_passwd +#endif + ) +{ + if(verify) + fprintf(stderr, "%s\n", buf); + else { + if(num > (int)strlen((char *)global_passwd)) { + strcpy(buf, global_passwd); + return strlen(buf); + } + } + return 0; +} + +static +bool seed_enough(int nread) +{ +#ifdef HAVE_RAND_STATUS + nread = 0; /* to prevent compiler warnings */ + + /* only available in OpenSSL 0.9.5a and later */ + if(RAND_status()) + return TRUE; +#else + if(nread > 500) + /* this is a very silly decision to make */ + return TRUE; +#endif + return FALSE; /* not enough */ +} + +static +int random_the_seed(struct SessionHandle *data) +{ + char *buf = data->state.buffer; /* point to the big buffer */ + int nread=0; + + /* Q: should we add support for a random file name as a libcurl option? + A: Yes, it is here */ + +#ifndef RANDOM_FILE + /* if RANDOM_FILE isn't defined, we only perform this if an option tells + us to! */ + if(data->set.ssl.random_file) +#define RANDOM_FILE "" /* doesn't matter won't be used */ +#endif + { + /* let the option override the define */ + nread += RAND_load_file((data->set.ssl.random_file? + data->set.ssl.random_file:RANDOM_FILE), + 16384); + if(seed_enough(nread)) + return nread; + } + +#if defined(HAVE_RAND_EGD) + /* only available in OpenSSL 0.9.5 and later */ + /* EGD_SOCKET is set at configure time or not at all */ +#ifndef EGD_SOCKET + /* If we don't have the define set, we only do this if the egd-option + is set */ + if(data->set.ssl.egdsocket) +#define EGD_SOCKET "" /* doesn't matter won't be used */ +#endif + { + /* If there's an option and a define, the option overrides the + define */ + int ret = RAND_egd(data->set.ssl.egdsocket?data->set.ssl.egdsocket:EGD_SOCKET); + if(-1 != ret) { + nread += ret; + if(seed_enough(nread)) + return nread; + } + } +#endif + + /* If we get here, it means we need to seed the PRNG using a "silly" + approach! */ +#ifdef HAVE_RAND_SCREEN + /* This one gets a random value by reading the currently shown screen */ + RAND_screen(); + nread = 100; /* just a value */ +#else + { + int len; + char *area = Curl_FormBoundary(); + if(!area) + return 3; /* out of memory */ + + len = strlen(area); + RAND_seed(area, len); + + free(area); /* now remove the random junk */ + } +#endif + + /* generates a default path for the random seed file */ + buf[0]=0; /* blank it first */ + RAND_file_name(buf, BUFSIZE); + if ( buf[0] ) { + /* we got a file name to try */ + nread += RAND_load_file(buf, 16384); + if(seed_enough(nread)) + return nread; + } + + infof(data, "libcurl is now using a weak random seed!\n"); + return nread; +} + +#ifndef SSL_FILETYPE_ENGINE +#define SSL_FILETYPE_ENGINE 42 +#endif +static int do_file_type(const char *type) +{ + if (!type || !type[0]) + return SSL_FILETYPE_PEM; + if (curl_strequal(type, "PEM")) + return SSL_FILETYPE_PEM; + if (curl_strequal(type, "DER")) + return SSL_FILETYPE_ASN1; + if (curl_strequal(type, "ENG")) + return SSL_FILETYPE_ENGINE; + return -1; +} + +static +int cert_stuff(struct connectdata *conn, + char *cert_file, + const char *cert_type, + char *key_file, + const char *key_type) +{ + struct SessionHandle *data = conn->data; + int file_type; + + if (cert_file != NULL) { + SSL *ssl; + X509 *x509; + + if(data->set.key_passwd) { +#ifndef HAVE_USERDATA_IN_PWD_CALLBACK + /* + * If password has been given, we store that in the global + * area (*shudder*) for a while: + */ + strcpy(global_passwd, data->set.key_passwd); +#else + /* + * We set the password in the callback userdata + */ + SSL_CTX_set_default_passwd_cb_userdata(conn->ssl.ctx, + data->set.key_passwd); +#endif + /* Set passwd callback: */ + SSL_CTX_set_default_passwd_cb(conn->ssl.ctx, passwd_callback); + } + +#if 0 + if (SSL_CTX_use_certificate_file(conn->ssl.ctx, + cert_file, + SSL_FILETYPE_PEM) != 1) { + failf(data, "unable to set certificate file (wrong password?)"); + return(0); + } + if (key_file == NULL) + key_file=cert_file; + + if (SSL_CTX_use_PrivateKey_file(conn->ssl.ctx, + key_file, + SSL_FILETYPE_PEM) != 1) { + failf(data, "unable to set public key file"); + return(0); + } +#else + /* The '#ifdef 0' section above was removed on 17-dec-2001 */ + + file_type = do_file_type(cert_type); + + switch(file_type) { + case SSL_FILETYPE_PEM: + case SSL_FILETYPE_ASN1: + if (SSL_CTX_use_certificate_file(conn->ssl.ctx, + cert_file, + file_type) != 1) { + failf(data, "unable to set certificate file (wrong password?)"); + return 0; + } + break; + case SSL_FILETYPE_ENGINE: + failf(data, "file type ENG for certificate not implemented"); + return 0; + + default: + failf(data, "not supported file type '%s' for certificate", cert_type); + return 0; + } + + file_type = do_file_type(key_type); + + switch(file_type) { + case SSL_FILETYPE_PEM: + if (key_file == NULL) + /* cert & key can only be in PEM case in the same file */ + key_file=cert_file; + case SSL_FILETYPE_ASN1: + if (SSL_CTX_use_PrivateKey_file(conn->ssl.ctx, + key_file, + file_type) != 1) { + failf(data, "unable to set private key file\n"); + return 0; + } + break; + case SSL_FILETYPE_ENGINE: +#ifdef HAVE_OPENSSL_ENGINE_H + { /* XXXX still needs some work */ + EVP_PKEY *priv_key = NULL; + if (conn && conn->data && conn->data->engine) { + if (!key_file || !key_file[0]) { + failf(data, "no key set to load from crypto engine\n"); + return 0; + } + priv_key = ENGINE_load_private_key(conn->data->engine,key_file, + data->set.key_passwd); + if (!priv_key) { + failf(data, "failed to load private key from crypto engine\n"); + return 0; + } + if (SSL_CTX_use_PrivateKey(conn->ssl.ctx, priv_key) != 1) { + failf(data, "unable to set private key\n"); + EVP_PKEY_free(priv_key); + return 0; + } + EVP_PKEY_free(priv_key); /* we don't need the handle any more... */ + } + else { + failf(data, "crypto engine not set, can't load private key\n"); + return 0; + } + } +#else + failf(data, "file type ENG for private key not supported\n"); + return 0; +#endif + break; + default: + failf(data, "not supported file type for private key\n"); + return 0; + } + +#endif + + ssl=SSL_new(conn->ssl.ctx); + x509=SSL_get_certificate(ssl); + + if (x509 != NULL) + EVP_PKEY_copy_parameters(X509_get_pubkey(x509), + SSL_get_privatekey(ssl)); + SSL_free(ssl); + + /* If we are using DSA, we can copy the parameters from + * the private key */ + + + /* Now we know that a key and cert have been set against + * the SSL context */ + if (!SSL_CTX_check_private_key(conn->ssl.ctx)) { + failf(data, "Private key does not match the certificate public key"); + return(0); + } +#ifndef HAVE_USERDATA_IN_PWD_CALLBACK + /* erase it now */ + memset(global_passwd, 0, sizeof(global_passwd)); +#endif + } + return(1); +} + +static +int cert_verify_callback(int ok, X509_STORE_CTX *ctx) +{ + X509 *err_cert; + char buf[256]; + + err_cert=X509_STORE_CTX_get_current_cert(ctx); + X509_NAME_oneline(X509_get_subject_name(err_cert),buf,256); + + return ok; +} + +#endif + +#ifdef USE_SSLEAY +/* "global" init done? */ +static int init_ssl=0; + +/* we have the "SSL is seeded" boolean global for the application to + prevent multiple time-consuming seedings in vain */ +static bool ssl_seeded = FALSE; +#endif + +/* Global init */ +void Curl_SSL_init(void) +{ +#ifdef USE_SSLEAY + /* make sure this is only done once */ + if(0 != init_ssl) + return; + + init_ssl++; /* never again */ + +#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES + ENGINE_load_builtin_engines(); +#endif + + /* Lets get nice error messages */ + SSL_load_error_strings(); + + /* Setup all the global SSL stuff */ + SSLeay_add_ssl_algorithms(); +#else + /* SSL disabled, do nothing */ +#endif +} + +/* Global cleanup */ +void Curl_SSL_cleanup(void) +{ +#ifdef USE_SSLEAY + if(init_ssl) { + /* only cleanup if we did a previous init */ + + /* Free the SSL error strings */ + ERR_free_strings(); + + /* EVP_cleanup() removes all ciphers and digests from the + table. */ + EVP_cleanup(); + +#ifdef HAVE_ENGINE_cleanup + ENGINE_cleanup(); +#endif + + init_ssl=0; /* not inited any more */ + } +#else + /* SSL disabled, do nothing */ +#endif +} + +#ifdef USE_SSLEAY + +/* + * This function is called when an SSL connection is closed. + */ +void Curl_SSL_Close(struct connectdata *conn) +{ + if (conn->ssl.use) { + /* + ERR_remove_state() frees the error queue associated with + thread pid. If pid == 0, the current thread will have its + error queue removed. + + Since error queue data structures are allocated + automatically for new threads, they must be freed when + threads are terminated in oder to avoid memory leaks. + */ + ERR_remove_state(0); + + if(conn->ssl.handle) { + (void)SSL_shutdown(conn->ssl.handle); + SSL_set_connect_state(conn->ssl.handle); + + SSL_free (conn->ssl.handle); + conn->ssl.handle = NULL; + } + if(conn->ssl.ctx) { + SSL_CTX_free (conn->ssl.ctx); + conn->ssl.ctx = NULL; + } + conn->ssl.use = FALSE; /* get back to ordinary socket usage */ + } +} + + +/* + * This sets up a session cache to the specified size. + */ +CURLcode Curl_SSL_InitSessions(struct SessionHandle *data, long amount) +{ + struct curl_ssl_session *session; + + if(data->state.session) + /* this is just a precaution to prevent multiple inits */ + return CURLE_OK; + + session = (struct curl_ssl_session *) + malloc(amount * sizeof(struct curl_ssl_session)); + if(!session) + return CURLE_OUT_OF_MEMORY; + + /* "blank out" the newly allocated memory */ + memset(session, 0, amount * sizeof(struct curl_ssl_session)); + + /* store the info in the SSL section */ + data->set.ssl.numsessions = amount; + data->state.session = session; + data->state.sessionage = 1; /* this is brand new */ + + return CURLE_OK; +} + +/* + * Check if there's a session ID for the given connection in the cache, + * and if there's one suitable, it is returned. + */ +static int Get_SSL_Session(struct connectdata *conn, + SSL_SESSION **ssl_sessionid) +{ + struct curl_ssl_session *check; + struct SessionHandle *data = conn->data; + long i; + + for(i=0; i< data->set.ssl.numsessions; i++) { + check = &data->state.session[i]; + if(!check->sessionid) + /* not session ID means blank entry */ + continue; + if(strequal(conn->name, check->name) && + (conn->remote_port == check->remote_port) ) { + /* yes, we have a session ID! */ + data->state.sessionage++; /* increase general age */ + check->age = data->state.sessionage; /* set this as used in this age */ + *ssl_sessionid = check->sessionid; + return FALSE; + } + } + *ssl_sessionid = (SSL_SESSION *)NULL; + return TRUE; +} + +/* + * Kill a single session ID entry in the cache. + */ +static int Kill_Single_Session(struct curl_ssl_session *session) +{ + if(session->sessionid) { + /* defensive check */ + + /* free the ID */ + SSL_SESSION_free(session->sessionid); + session->sessionid=NULL; + session->age = 0; /* fresh */ + free(session->name); + session->name = NULL; /* no name */ + + return 0; /* ok */ + } + else + return 1; +} + +/* + * This function is called when the 'data' struct is going away. Close + * down everything and free all resources! + */ +int Curl_SSL_Close_All(struct SessionHandle *data) +{ + int i; + + if(data->state.session) { + for(i=0; i< data->set.ssl.numsessions; i++) + /* the single-killer function handles empty table slots */ + Kill_Single_Session(&data->state.session[i]); + + /* free the cache data */ + free(data->state.session); + } +#ifdef HAVE_OPENSSL_ENGINE_H + if (data->engine) + { + ENGINE_free(data->engine); + data->engine = NULL; + } +#endif + return 0; +} + +/* + * Extract the session id and store it in the session cache. + */ +static int Store_SSL_Session(struct connectdata *conn) +{ + SSL_SESSION *ssl_sessionid; + int i; + struct SessionHandle *data=conn->data; /* the mother of all structs */ + struct curl_ssl_session *store = &data->state.session[0]; + int oldest_age=data->state.session[0].age; /* zero if unused */ + + /* ask OpenSSL, say please */ + +#ifdef HAVE_SSL_GET1_SESSION + ssl_sessionid = SSL_get1_session(conn->ssl.handle); + + /* SSL_get1_session() will increment the reference + count and the session will stay in memory until explicitly freed with + SSL_SESSION_free(3), regardless of its state. + This function was introduced in openssl 0.9.5a. */ +#else + ssl_sessionid = SSL_get_session(conn->ssl.handle); + + /* if SSL_get1_session() is unavailable, use SSL_get_session(). + This is an inferior option because the session can be flushed + at any time by openssl. It is included only so curl compiles + under versions of openssl < 0.9.5a. + + WARNING: How curl behaves if it's session is flushed is + untested. + */ +#endif + + /* Now we should add the session ID and the host name to the cache, (remove + the oldest if necessary) */ + + /* find an empty slot for us, or find the oldest */ + for(i=1; (i<data->set.ssl.numsessions) && + data->state.session[i].sessionid; i++) { + if(data->state.session[i].age < oldest_age) { + oldest_age = data->state.session[i].age; + store = &data->state.session[i]; + } + } + if(i == data->set.ssl.numsessions) + /* cache is full, we must "kill" the oldest entry! */ + Kill_Single_Session(store); + else + store = &data->state.session[i]; /* use this slot */ + + /* now init the session struct wisely */ + store->sessionid = ssl_sessionid; + store->age = data->state.sessionage; /* set current age */ + store->name = strdup(conn->name); /* clone host name */ + store->remote_port = conn->remote_port; /* port number */ + + return 0; +} + +static int Curl_ASN1_UTCTIME_output(struct connectdata *conn, + const char *prefix, + ASN1_UTCTIME *tm) +{ + char *asn1_string; + int gmt=FALSE; + int i; + int year=0,month=0,day=0,hour=0,minute=0,second=0; + struct SessionHandle *data = conn->data; + + if(!data->set.verbose) + return 0; + + i=tm->length; + asn1_string=(char *)tm->data; + + if (i < 10) + return 1; + if (asn1_string[i-1] == 'Z') + gmt=TRUE; + for (i=0; i<10; i++) + if ((asn1_string[i] > '9') || (asn1_string[i] < '0')) + return 2; + + year= (asn1_string[0]-'0')*10+(asn1_string[1]-'0'); + if (year < 50) + year+=100; + + month= (asn1_string[2]-'0')*10+(asn1_string[3]-'0'); + if ((month > 12) || (month < 1)) + return 3; + + day= (asn1_string[4]-'0')*10+(asn1_string[5]-'0'); + hour= (asn1_string[6]-'0')*10+(asn1_string[7]-'0'); + minute= (asn1_string[8]-'0')*10+(asn1_string[9]-'0'); + + if ( (asn1_string[10] >= '0') && (asn1_string[10] <= '9') && + (asn1_string[11] >= '0') && (asn1_string[11] <= '9')) + second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0'); + + infof(data, + "%s%04d-%02d-%02d %02d:%02d:%02d %s\n", + prefix, year+1900, month, day, hour, minute, second, (gmt?"GMT":"")); + + return 0; +} + +#endif + +/* ====================================================== */ +CURLcode +Curl_SSLConnect(struct connectdata *conn) +{ + CURLcode retcode = CURLE_OK; + +#ifdef USE_SSLEAY + struct SessionHandle *data = conn->data; + int err; + char * str; + SSL_METHOD *req_method; + SSL_SESSION *ssl_sessionid=NULL; + ASN1_TIME *certdate; + + /* mark this is being ssl enabled from here on out. */ + conn->ssl.use = TRUE; + + if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { + /* Make funny stuff to get random input */ + random_the_seed(data); + + ssl_seeded = TRUE; + } + + /* check to see if we've been told to use an explicit SSL/TLS version */ + switch(data->set.ssl.version) { + default: + case CURL_SSLVERSION_DEFAULT: + /* we try to figure out version */ + req_method = SSLv23_client_method(); + break; + case CURL_SSLVERSION_TLSv1: + req_method = TLSv1_client_method(); + break; + case CURL_SSLVERSION_SSLv2: + req_method = SSLv2_client_method(); + break; + case CURL_SSLVERSION_SSLv3: + req_method = SSLv3_client_method(); + break; + } + + conn->ssl.ctx = SSL_CTX_new(req_method); + + if(!conn->ssl.ctx) { + failf(data, "SSL: couldn't create a context!"); + return CURLE_OUT_OF_MEMORY; + } + + if(data->set.cert) { + if (!cert_stuff(conn, + data->set.cert, + data->set.cert_type, + data->set.key, + data->set.key_type)) { + /* failf() is already done in cert_stuff() */ + return CURLE_SSL_CONNECT_ERROR; + } + } + + if(data->set.ssl.cipher_list) { + if (!SSL_CTX_set_cipher_list(conn->ssl.ctx, + data->set.ssl.cipher_list)) { + failf(data, "failed setting cipher list"); + return CURLE_SSL_CONNECT_ERROR; + } + } + + if(data->set.ssl.verifypeer){ + SSL_CTX_set_verify(conn->ssl.ctx, + SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT| + SSL_VERIFY_CLIENT_ONCE, + cert_verify_callback); + if (!SSL_CTX_load_verify_locations(conn->ssl.ctx, + data->set.ssl.CAfile, + data->set.ssl.CApath)) { + failf(data,"error setting cerficate verify locations"); + return CURLE_SSL_CONNECT_ERROR; + } + } + else + SSL_CTX_set_verify(conn->ssl.ctx, SSL_VERIFY_NONE, cert_verify_callback); + + + /* Lets make an SSL structure */ + conn->ssl.handle = SSL_new (conn->ssl.ctx); + SSL_set_connect_state (conn->ssl.handle); + + conn->ssl.server_cert = 0x0; + + if(!conn->bits.reuse) { + /* We're not re-using a connection, check if there's a cached ID we + can/should use here! */ + if(!Get_SSL_Session(conn, &ssl_sessionid)) { + /* we got a session id, use it! */ + SSL_set_session(conn->ssl.handle, ssl_sessionid); + /* Informational message */ + infof (data, "SSL re-using session ID\n"); + } + } + + /* pass the raw socket into the SSL layers */ + SSL_set_fd(conn->ssl.handle, conn->firstsocket); + + do { + int what; + fd_set writefd; + fd_set readfd; + struct timeval interval; + long timeout_ms; + + err = SSL_connect(conn->ssl.handle); + + what = SSL_get_error(conn->ssl.handle, err); + + FD_ZERO(&writefd); + FD_ZERO(&readfd); + + if(SSL_ERROR_WANT_READ == what) + FD_SET(conn->firstsocket, &readfd); + else if(SSL_ERROR_WANT_WRITE == what) + FD_SET(conn->firstsocket, &writefd); + else + break; /* untreated error */ + + /* Find out if any timeout is set. If not, use 300 seconds. + Otherwise, figure out the most strict timeout of the two possible one + and then how much time that has elapsed to know how much time we + allow for the connect call */ + if(data->set.timeout || data->set.connecttimeout) { + double has_passed; + + /* Evaluate in milliseconds how much time that has passed */ + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + + /* get the most strict timeout of the ones converted to milliseconds */ + if(data->set.timeout && + (data->set.timeout>data->set.connecttimeout)) + timeout_ms = data->set.timeout*1000; + else + timeout_ms = data->set.connecttimeout*1000; + + /* subtract the passed time */ + timeout_ms -= (long)has_passed; + + if(timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEOUTED; + } + } + else + /* no particular time-out has been set */ + timeout_ms=300000; /* milliseconds, default to five minutes */ + + interval.tv_sec = timeout_ms/1000; + timeout_ms -= interval.tv_sec*1000; + + interval.tv_usec = timeout_ms*1000; + + what = select(conn->firstsocket+1, &readfd, &writefd, NULL, &interval); + if(what > 0) + /* reabable or writable, go loop yourself */ + continue; + else if(0 == what) { + /* timeout */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEOUTED; + } + else + break; /* get out of loop */ + } while(1); + + /* 1 is fine + 0 is "not successful but was shut down controlled" + <0 is "handshake was not successful, because a fatal error occurred" */ + if (err <= 0) { + err = ERR_get_error(); + failf(data, "SSL: %s", ERR_error_string(err, NULL)); + return CURLE_SSL_CONNECT_ERROR; + } + + /* Informational message */ + infof (data, "SSL connection using %s\n", + SSL_get_cipher(conn->ssl.handle)); + + if(!ssl_sessionid) { + /* Since this is not a cached session ID, then we want to stach this one + in the cache! */ + Store_SSL_Session(conn); + } + + + /* Get server's certificate (note: beware of dynamic allocation) - opt */ + /* major serious hack alert -- we should check certificates + * to authenticate the server; otherwise we risk man-in-the-middle + * attack + */ + + conn->ssl.server_cert = SSL_get_peer_certificate (conn->ssl.handle); + if(!conn->ssl.server_cert) { + failf(data, "SSL: couldn't get peer certificate!"); + return CURLE_SSL_PEER_CERTIFICATE; + } + infof (data, "Server certificate:\n"); + + str = X509_NAME_oneline (X509_get_subject_name (conn->ssl.server_cert), + NULL, 0); + if(!str) { + failf(data, "SSL: couldn't get X509-subject!"); + X509_free(conn->ssl.server_cert); + return CURLE_SSL_CONNECT_ERROR; + } + infof(data, "\t subject: %s\n", str); + CRYPTO_free(str); + + certdate = X509_get_notBefore(conn->ssl.server_cert); + Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate); + + certdate = X509_get_notAfter(conn->ssl.server_cert); + Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate); + + if (data->set.ssl.verifyhost) { + char peer_CN[257]; + if (X509_NAME_get_text_by_NID(X509_get_subject_name(conn->ssl.server_cert), + NID_commonName, + peer_CN, + sizeof(peer_CN)) < 0) { + failf(data, "SSL: unable to obtain common name from peer certificate"); + X509_free(conn->ssl.server_cert); + return CURLE_SSL_PEER_CERTIFICATE; + } + + if (!strequal(peer_CN, conn->hostname)) { + if (data->set.ssl.verifyhost > 1) { + failf(data, "SSL: certificate subject name '%s' does not match " + "target host name '%s'", + peer_CN, conn->hostname); + X509_free(conn->ssl.server_cert); + return CURLE_SSL_PEER_CERTIFICATE; + } + else + infof(data, + "\t common name: %s (does not match '%s')\n", + peer_CN, conn->hostname); + } + else + infof(data, "\t common name: %s (matched)\n", peer_CN); + } + + str = X509_NAME_oneline (X509_get_issuer_name (conn->ssl.server_cert), + NULL, 0); + if(!str) { + failf(data, "SSL: couldn't get X509-issuer name!"); + X509_free(conn->ssl.server_cert); + return CURLE_SSL_CONNECT_ERROR; + } + infof(data, "\t issuer: %s\n", str); + CRYPTO_free(str); + + /* We could do all sorts of certificate verification stuff here before + deallocating the certificate. */ + + if(data->set.ssl.verifypeer) { + data->set.ssl.certverifyresult=SSL_get_verify_result(conn->ssl.handle); + if (data->set.ssl.certverifyresult != X509_V_OK) { + failf(data, "SSL certificate verify result: %d", + data->set.ssl.certverifyresult); + retcode = CURLE_SSL_PEER_CERTIFICATE; + } + } + else + data->set.ssl.certverifyresult=0; + + X509_free(conn->ssl.server_cert); +#else /* USE_SSLEAY */ + /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */ + (void) conn; +#endif + return retcode; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/ssluse.h b/Source/CTest/Curl/ssluse.h new file mode 100644 index 0000000..e8115f0 --- /dev/null +++ b/Source/CTest/Curl/ssluse.h @@ -0,0 +1,38 @@ +#ifndef __SSLUSE_H +#define __SSLUSE_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +#include "urldata.h" +CURLcode Curl_SSLConnect(struct connectdata *conn); + +void Curl_SSL_init(void); /* Global SSL init */ +void Curl_SSL_cleanup(void); /* Global SSL cleanup */ + +/* init the SSL session ID cache */ +CURLcode Curl_SSL_InitSessions(struct SessionHandle *, long); +void Curl_SSL_Close(struct connectdata *conn); /* close a SSL connection */ + +/* tell the SSL stuff to close down all open information regarding + connections (and thus session ID caching etc) */ +int Curl_SSL_Close_All(struct SessionHandle *data); +#endif diff --git a/Source/CTest/Curl/strequal.c b/Source/CTest/Curl/strequal.c new file mode 100644 index 0000000..07bc16b --- /dev/null +++ b/Source/CTest/Curl/strequal.c @@ -0,0 +1,121 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <string.h> +#include <ctype.h> + +int curl_strequal(const char *first, const char *second) +{ +#if defined(HAVE_STRCASECMP) + return !(strcasecmp)(first, second); +#elif defined(HAVE_STRCMPI) + return !(strcmpi)(first, second); +#elif defined(HAVE_STRICMP) + return !(stricmp)(first, second); +#else + while (*first && *second) { + if (toupper(*first) != toupper(*second)) { + break; + } + first++; + second++; + } + return toupper(*first) == toupper(*second); +#endif +} + +int curl_strnequal(const char *first, const char *second, size_t max) +{ +#if defined(HAVE_STRCASECMP) + return !strncasecmp(first, second, max); +#elif defined(HAVE_STRCMPI) + return !strncmpi(first, second, max); +#elif defined(HAVE_STRICMP) + return !strnicmp(first, second, max); +#else + while (*first && *second && max) { + if (toupper(*first) != toupper(*second)) { + break; + } + max--; + first++; + second++; + } + if(0 == max) + return 1; /* they are equal this far */ + + return toupper(*first) == toupper(*second); +#endif +} + +#ifndef HAVE_STRLCAT +/* + * The strlcat() function appends the NUL-terminated string src to the end + * of dst. It will append at most size - strlen(dst) - 1 bytes, NUL-termi- + * nating the result. + * + * The strlcpy() and strlcat() functions return the total length of the + * string they tried to create. For strlcpy() that means the length of src. + * For strlcat() that means the initial length of dst plus the length of + * src. While this may seem somewhat confusing it was done to make trunca- + * tion detection simple. + * + * + */ +size_t Curl_strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} +#endif + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/strequal.h b/Source/CTest/Curl/strequal.h new file mode 100644 index 0000000..e376db9 --- /dev/null +++ b/Source/CTest/Curl/strequal.h @@ -0,0 +1,40 @@ +#ifndef __STREQUAL_H +#define __STREQUAL_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* + * These two actually are public functions. + */ +int curl_strequal(const char *first, const char *second); +int curl_strnequal(const char *first, const char *second, size_t max); + +#define strequal(a,b) curl_strequal(a,b) +#define strnequal(a,b,c) curl_strnequal(a,b,c) + +#ifndef HAVE_STRLCAT +#define strlcat(x,y,z) Curl_strlcat(x,y,z) +size_t Curl_strlcat(char *dst, const char *src, size_t siz); +#endif + +#endif diff --git a/Source/CTest/Curl/strtok.c b/Source/CTest/Curl/strtok.c new file mode 100644 index 0000000..e7724d9 --- /dev/null +++ b/Source/CTest/Curl/strtok.c @@ -0,0 +1,74 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#ifndef HAVE_STRTOK_R +#include <stddef.h> +#include <string.h> + +char * +Curl_strtok_r(char *ptr, const char *sep, char **end) +{ + if (!ptr) + /* we got NULL input so then we get our last position instead */ + ptr = *end; + + /* pass all letters that are including in the separator string */ + while (*ptr && strchr(sep, *ptr)) + ++ptr; + + if (*ptr) { + /* so this is where the next piece of string starts */ + char *start = ptr; + + /* set the end pointer to the first byte after the start */ + *end = start + 1; + + /* scan through the string to find where it ends, it ends on a + null byte or a character that exists in the separator string */ + while (**end && !strchr(sep, **end)) + ++*end; + + if (**end) { + /* the end is not a null byte */ + **end = '\0'; /* zero terminate it! */ + ++*end; /* advance the last pointer to beyond the null byte */ + } + + return start; /* return the position where the string starts */ + } + + /* we ended up on a null byte, there are no more strings to find! */ + return NULL; +} + +#endif /* this was only compiled if strtok_r wasn't present */ + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/strtok.h b/Source/CTest/Curl/strtok.h new file mode 100644 index 0000000..6e7e167 --- /dev/null +++ b/Source/CTest/Curl/strtok.h @@ -0,0 +1,38 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#ifndef _CURL_STRTOK_R_H +#define _CURL_STRTOK_R_H + +#include "setup.h" +#include <stddef.h> + +#ifndef HAVE_STRTOK_R +char *Curl_strtok_r(char *s, const char *delim, char **last); +#define strtok_r Curl_strtok_r +#else +#include <string.h> +#endif + +#endif + diff --git a/Source/CTest/Curl/telnet.c b/Source/CTest/Curl/telnet.c new file mode 100644 index 0000000..29c2110 --- /dev/null +++ b/Source/CTest/Curl/telnet.c @@ -0,0 +1,1213 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock2.h> +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <sys/time.h> +#include <sys/resource.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "transfer.h" +#include "sendf.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#define TELOPTS +#define TELCMDS + +#include "arpa_telnet.h" + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +#define SUBBUFSIZE 512 + +#define SB_CLEAR(x) x->subpointer = x->subbuffer; +#define SB_TERM(x) { x->subend = x->subpointer; SB_CLEAR(x); } +#define SB_ACCUM(x,c) if (x->subpointer < (x->subbuffer+sizeof x->subbuffer)) { \ + *x->subpointer++ = (c); \ + } + +#define SB_GET(x) ((*x->subpointer++)&0xff) +#define SB_PEEK(x) ((*x->subpointer)&0xff) +#define SB_EOF(x) (x->subpointer >= x->subend) +#define SB_LEN(x) (x->subend - x->subpointer) + +static +void telrcv(struct connectdata *, + unsigned char *inbuf, /* Data received from socket */ + int count); /* Number of bytes received */ + +static void printoption(struct SessionHandle *data, + const char *direction, + int cmd, int option); + +static void negotiate(struct connectdata *); +static void send_negotiation(struct connectdata *, int cmd, int option); +static void set_local_option(struct connectdata *, int cmd, int option); +static void set_remote_option(struct connectdata *, int cmd, int option); + +static void printsub(struct SessionHandle *data, + int direction, unsigned char *pointer, int length); +static void suboption(struct connectdata *); + +/* For negotiation compliant to RFC 1143 */ +#define NO 0 +#define YES 1 +#define WANTYES 2 +#define WANTNO 3 + +#define EMPTY 0 +#define OPPOSITE 1 + +/* + * Telnet receiver states for fsm + */ +typedef enum +{ + TS_DATA = 0, + TS_IAC, + TS_WILL, + TS_WONT, + TS_DO, + TS_DONT, + TS_CR, + TS_SB, /* sub-option collection */ + TS_SE /* looking for sub-option end */ +} TelnetReceive; + +struct TELNET { + int please_negotiate; + int already_negotiated; + int us[256]; + int usq[256]; + int us_preferred[256]; + int him[256]; + int himq[256]; + int him_preferred[256]; + char subopt_ttype[32]; /* Set with suboption TTYPE */ + char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */ + struct curl_slist *telnet_vars; /* Environment variables */ + + /* suboptions */ + char subbuffer[SUBBUFSIZE]; + char *subpointer, *subend; /* buffer for sub-options */ + + TelnetReceive telrcv_state; +}; + +static +CURLcode init_telnet(struct connectdata *conn) +{ + struct TELNET *tn; + + tn = (struct TELNET *)malloc(sizeof(struct TELNET)); + if(!tn) + return CURLE_OUT_OF_MEMORY; + + conn->proto.telnet = (void *)tn; /* make us known */ + + memset(tn, 0, sizeof(struct TELNET)); + + tn->telrcv_state = TS_DATA; + + /* Init suboptions */ + SB_CLEAR(tn); + + /* Set all options to NO */ +#if 0 + /* NO is zero => default fill pattern */ + memset(tn->us, NO, 256); + memset(tn->usq, NO, 256); + memset(tn->us_preferred, NO, 256); + memset(tn->him, NO, 256); + memset(tn->himq, NO, 256); + memset(tn->him_preferred, NO, 256); +#endif + /* Set the options we want by default */ + tn->us_preferred[TELOPT_BINARY] = YES; + tn->us_preferred[TELOPT_SGA] = YES; + tn->him_preferred[TELOPT_BINARY] = YES; + tn->him_preferred[TELOPT_SGA] = YES; + + return CURLE_OK; +} + +static void negotiate(struct connectdata *conn) +{ + int i; + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + + for(i = 0;i < NTELOPTS;i++) + { + if(tn->us_preferred[i] == YES) + set_local_option(conn, i, YES); + + if(tn->him_preferred[i] == YES) + set_remote_option(conn, i, YES); + } +} + +static void printoption(struct SessionHandle *data, + const char *direction, int cmd, int option) +{ + const char *fmt; + const char *opt; + + if (data->set.verbose) + { + if (cmd == IAC) + { + if (TELCMD_OK(option)) + Curl_infof(data, "%s IAC %s\n", direction, TELCMD(option)); + else + Curl_infof(data, "%s IAC %d\n", direction, option); + } + else + { + fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" : + (cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0; + if (fmt) + { + if (TELOPT_OK(option)) + opt = TELOPT(option); + else if (option == TELOPT_EXOPL) + opt = "EXOPL"; + else + opt = NULL; + + if(opt) + Curl_infof(data, "%s %s %s\n", direction, fmt, opt); + else + Curl_infof(data, "%s %s %d\n", direction, fmt, option); + } + else + Curl_infof(data, "%s %d %d\n", direction, cmd, option); + } + } +} + +static void send_negotiation(struct connectdata *conn, int cmd, int option) +{ + unsigned char buf[3]; + + buf[0] = IAC; + buf[1] = cmd; + buf[2] = option; + + swrite(conn->firstsocket, buf, 3); + + printoption(conn->data, "SENT", cmd, option); +} + +static +void set_remote_option(struct connectdata *conn, int option, int newstate) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + if(newstate == YES) + { + switch(tn->him[option]) + { + case NO: + tn->him[option] = WANTYES; + send_negotiation(conn, DO, option); + break; + + case YES: + /* Already enabled */ + break; + + case WANTNO: + switch(tn->himq[option]) + { + case EMPTY: + /* Already negotiating for YES, queue the request */ + tn->himq[option] = OPPOSITE; + break; + case OPPOSITE: + /* Error: already queued an enable request */ + break; + } + break; + + case WANTYES: + switch(tn->himq[option]) + { + case EMPTY: + /* Error: already negotiating for enable */ + break; + case OPPOSITE: + tn->himq[option] = EMPTY; + break; + } + break; + } + } + else /* NO */ + { + switch(tn->him[option]) + { + case NO: + /* Already disabled */ + break; + + case YES: + tn->him[option] = WANTNO; + send_negotiation(conn, DONT, option); + break; + + case WANTNO: + switch(tn->himq[option]) + { + case EMPTY: + /* Already negotiating for NO */ + break; + case OPPOSITE: + tn->himq[option] = EMPTY; + break; + } + break; + + case WANTYES: + switch(tn->himq[option]) + { + case EMPTY: + tn->himq[option] = OPPOSITE; + break; + case OPPOSITE: + break; + } + break; + } + } +} + +static +void rec_will(struct connectdata *conn, int option) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + switch(tn->him[option]) + { + case NO: + if(tn->him_preferred[option] == YES) + { + tn->him[option] = YES; + send_negotiation(conn, DO, option); + } + else + { + send_negotiation(conn, DONT, option); + } + break; + + case YES: + /* Already enabled */ + break; + + case WANTNO: + switch(tn->himq[option]) + { + case EMPTY: + /* Error: DONT answered by WILL */ + tn->him[option] = NO; + break; + case OPPOSITE: + /* Error: DONT answered by WILL */ + tn->him[option] = YES; + tn->himq[option] = EMPTY; + break; + } + break; + + case WANTYES: + switch(tn->himq[option]) + { + case EMPTY: + tn->him[option] = YES; + break; + case OPPOSITE: + tn->him[option] = WANTNO; + tn->himq[option] = EMPTY; + send_negotiation(conn, DONT, option); + break; + } + break; + } +} + +static +void rec_wont(struct connectdata *conn, int option) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + switch(tn->him[option]) + { + case NO: + /* Already disabled */ + break; + + case YES: + tn->him[option] = NO; + send_negotiation(conn, DONT, option); + break; + + case WANTNO: + switch(tn->himq[option]) + { + case EMPTY: + tn->him[option] = NO; + break; + + case OPPOSITE: + tn->him[option] = WANTYES; + tn->himq[option] = EMPTY; + send_negotiation(conn, DO, option); + break; + } + break; + + case WANTYES: + switch(tn->himq[option]) + { + case EMPTY: + tn->him[option] = NO; + break; + case OPPOSITE: + tn->him[option] = NO; + tn->himq[option] = EMPTY; + break; + } + break; + } +} + +void set_local_option(struct connectdata *conn, int option, int newstate) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + if(newstate == YES) + { + switch(tn->us[option]) + { + case NO: + tn->us[option] = WANTYES; + send_negotiation(conn, WILL, option); + break; + + case YES: + /* Already enabled */ + break; + + case WANTNO: + switch(tn->usq[option]) + { + case EMPTY: + /* Already negotiating for YES, queue the request */ + tn->usq[option] = OPPOSITE; + break; + case OPPOSITE: + /* Error: already queued an enable request */ + break; + } + break; + + case WANTYES: + switch(tn->usq[option]) + { + case EMPTY: + /* Error: already negotiating for enable */ + break; + case OPPOSITE: + tn->usq[option] = EMPTY; + break; + } + break; + } + } + else /* NO */ + { + switch(tn->us[option]) + { + case NO: + /* Already disabled */ + break; + + case YES: + tn->us[option] = WANTNO; + send_negotiation(conn, WONT, option); + break; + + case WANTNO: + switch(tn->usq[option]) + { + case EMPTY: + /* Already negotiating for NO */ + break; + case OPPOSITE: + tn->usq[option] = EMPTY; + break; + } + break; + + case WANTYES: + switch(tn->usq[option]) + { + case EMPTY: + tn->usq[option] = OPPOSITE; + break; + case OPPOSITE: + break; + } + break; + } + } +} + +static +void rec_do(struct connectdata *conn, int option) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + switch(tn->us[option]) + { + case NO: + if(tn->us_preferred[option] == YES) + { + tn->us[option] = YES; + send_negotiation(conn, WILL, option); + } + else + { + send_negotiation(conn, WONT, option); + } + break; + + case YES: + /* Already enabled */ + break; + + case WANTNO: + switch(tn->usq[option]) + { + case EMPTY: + /* Error: DONT answered by WILL */ + tn->us[option] = NO; + break; + case OPPOSITE: + /* Error: DONT answered by WILL */ + tn->us[option] = YES; + tn->usq[option] = EMPTY; + break; + } + break; + + case WANTYES: + switch(tn->usq[option]) + { + case EMPTY: + tn->us[option] = YES; + break; + case OPPOSITE: + tn->us[option] = WANTNO; + tn->himq[option] = EMPTY; + send_negotiation(conn, WONT, option); + break; + } + break; + } +} + +static +void rec_dont(struct connectdata *conn, int option) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + switch(tn->us[option]) + { + case NO: + /* Already disabled */ + break; + + case YES: + tn->us[option] = NO; + send_negotiation(conn, WONT, option); + break; + + case WANTNO: + switch(tn->usq[option]) + { + case EMPTY: + tn->us[option] = NO; + break; + + case OPPOSITE: + tn->us[option] = WANTYES; + tn->usq[option] = EMPTY; + send_negotiation(conn, WILL, option); + break; + } + break; + + case WANTYES: + switch(tn->usq[option]) + { + case EMPTY: + tn->us[option] = NO; + break; + case OPPOSITE: + tn->us[option] = NO; + tn->usq[option] = EMPTY; + break; + } + break; + } +} + + +static void printsub(struct SessionHandle *data, + int direction, /* '<' or '>' */ + unsigned char *pointer, /* where suboption data is */ + int length) /* length of suboption data */ +{ + int i = 0; + + if (data->set.verbose) + { + if (direction) + { + Curl_infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT"); + if (length >= 3) + { + int j; + + i = pointer[length-2]; + j = pointer[length-1]; + + if (i != IAC || j != SE) + { + Curl_infof(data, "(terminated by "); + if (TELOPT_OK(i)) + Curl_infof(data, "%s ", TELOPT(i)); + else if (TELCMD_OK(i)) + Curl_infof(data, "%s ", TELCMD(i)); + else + Curl_infof(data, "%d ", i); + if (TELOPT_OK(j)) + Curl_infof(data, "%s", TELOPT(j)); + else if (TELCMD_OK(j)) + Curl_infof(data, "%s", TELCMD(j)); + else + Curl_infof(data, "%d", j); + Curl_infof(data, ", not IAC SE!) "); + } + } + length -= 2; + } + if (length < 1) + { + Curl_infof(data, "(Empty suboption?)"); + return; + } + + if (TELOPT_OK(pointer[0])) { + switch(pointer[0]) { + case TELOPT_TTYPE: + case TELOPT_XDISPLOC: + case TELOPT_NEW_ENVIRON: + Curl_infof(data, "%s", TELOPT(pointer[0])); + break; + default: + Curl_infof(data, "%s (unsupported)", TELOPT(pointer[0])); + break; + } + } + else + Curl_infof(data, "%d (unknown)", pointer[i]); + + switch(pointer[1]) { + case TELQUAL_IS: + Curl_infof(data, " IS"); + break; + case TELQUAL_SEND: + Curl_infof(data, " SEND"); + break; + case TELQUAL_INFO: + Curl_infof(data, " INFO/REPLY"); + break; + case TELQUAL_NAME: + Curl_infof(data, " NAME"); + break; + } + + switch(pointer[0]) { + case TELOPT_TTYPE: + case TELOPT_XDISPLOC: + pointer[length] = 0; + Curl_infof(data, " \"%s\"", &pointer[2]); + break; + case TELOPT_NEW_ENVIRON: + if(pointer[1] == TELQUAL_IS) { + Curl_infof(data, " "); + for(i = 3;i < length;i++) { + switch(pointer[i]) { + case NEW_ENV_VAR: + Curl_infof(data, ", "); + break; + case NEW_ENV_VALUE: + Curl_infof(data, " = "); + break; + default: + Curl_infof(data, "%c", pointer[i]); + break; + } + } + } + break; + default: + for (i = 2; i < length; i++) + Curl_infof(data, " %.2x", pointer[i]); + break; + } + + if (direction) + { + Curl_infof(data, "\n"); + } + } +} + +static int check_telnet_options(struct connectdata *conn) +{ + struct curl_slist *head; + char option_keyword[128]; + char option_arg[256]; + char *buf; + struct SessionHandle *data = conn->data; + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + + /* Add the user name as an environment variable if it + was given on the command line */ + if(conn->bits.user_passwd) + { + char *buf = malloc(256); + sprintf(buf, "USER,%s", data->state.user); + tn->telnet_vars = curl_slist_append(tn->telnet_vars, buf); + + tn->us_preferred[TELOPT_NEW_ENVIRON] = YES; + } + + for(head = data->set.telnet_options; head; head=head->next) { + if(sscanf(head->data, "%127[^= ]%*[ =]%255s", + option_keyword, option_arg) == 2) { + + /* Terminal type */ + if(strequal(option_keyword, "TTYPE")) { + strncpy(tn->subopt_ttype, option_arg, 31); + tn->subopt_ttype[31] = 0; /* String termination */ + tn->us_preferred[TELOPT_TTYPE] = YES; + continue; + } + + /* Display variable */ + if(strequal(option_keyword, "XDISPLOC")) { + strncpy(tn->subopt_xdisploc, option_arg, 127); + tn->subopt_xdisploc[127] = 0; /* String termination */ + tn->us_preferred[TELOPT_XDISPLOC] = YES; + continue; + } + + /* Environment variable */ + if(strequal(option_keyword, "NEW_ENV")) { + buf = strdup(option_arg); + if(!buf) + return CURLE_OUT_OF_MEMORY; + tn->telnet_vars = curl_slist_append(tn->telnet_vars, buf); + tn->us_preferred[TELOPT_NEW_ENVIRON] = YES; + continue; + } + + failf(data, "Unknown telnet option %s", head->data); + return CURLE_UNKNOWN_TELNET_OPTION; + } else { + failf(data, "Syntax error in telnet option: %s", head->data); + return CURLE_TELNET_OPTION_SYNTAX; + } + } + + return CURLE_OK; +} + +/* + * suboption() + * + * Look at the sub-option buffer, and try to be helpful to the other + * side. + */ + +static void suboption(struct connectdata *conn) +{ + struct curl_slist *v; + unsigned char subchar; + unsigned char temp[2048]; + int len; + int tmplen; + char varname[128]; + char varval[128]; + struct SessionHandle *data = conn->data; + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + + printsub(data, '<', (unsigned char *)tn->subbuffer, SB_LEN(tn)+2); + switch (subchar = SB_GET(tn)) { + case TELOPT_TTYPE: + len = strlen(tn->subopt_ttype) + 4 + 2; + snprintf((char *)temp, sizeof(temp), + "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, + TELQUAL_IS, tn->subopt_ttype, IAC, SE); + swrite(conn->firstsocket, temp, len); + printsub(data, '>', &temp[2], len-2); + break; + case TELOPT_XDISPLOC: + len = strlen(tn->subopt_xdisploc) + 4 + 2; + snprintf((char *)temp, sizeof(temp), + "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC, + TELQUAL_IS, tn->subopt_xdisploc, IAC, SE); + swrite(conn->firstsocket, temp, len); + printsub(data, '>', &temp[2], len-2); + break; + case TELOPT_NEW_ENVIRON: + snprintf((char *)temp, sizeof(temp), + "%c%c%c%c", IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_IS); + len = 4; + + for(v = tn->telnet_vars;v;v = v->next) { + tmplen = (strlen(v->data) + 1); + /* Add the variable only if it fits */ + if(len + tmplen < (int)sizeof(temp)-6) { + sscanf(v->data, "%127[^,],%s", varname, varval); + snprintf((char *)&temp[len], sizeof(temp) - len, + "%c%s%c%s", NEW_ENV_VAR, varname, + NEW_ENV_VALUE, varval); + len += tmplen; + } + } + snprintf((char *)&temp[len], sizeof(temp) - len, + "%c%c", IAC, SE); + len += 2; + swrite(conn->firstsocket, temp, len); + printsub(data, '>', &temp[2], len-2); + break; + } + return; +} + +static +void telrcv(struct connectdata *conn, + unsigned char *inbuf, /* Data received from socket */ + int count) /* Number of bytes received */ +{ + unsigned char c; + int index = 0; + struct SessionHandle *data = conn->data; + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + + while(count--) + { + c = inbuf[index++]; + + switch (tn->telrcv_state) + { + case TS_CR: + tn->telrcv_state = TS_DATA; + if (c == '\0') + { + break; /* Ignore \0 after CR */ + } + + Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + continue; + + case TS_DATA: + if (c == IAC) + { + tn->telrcv_state = TS_IAC; + break; + } + else if(c == '\r') + { + tn->telrcv_state = TS_CR; + } + + Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + continue; + + case TS_IAC: + process_iac: + switch (c) + { + case WILL: + tn->telrcv_state = TS_WILL; + continue; + case WONT: + tn->telrcv_state = TS_WONT; + continue; + case DO: + tn->telrcv_state = TS_DO; + continue; + case DONT: + tn->telrcv_state = TS_DONT; + continue; + case SB: + SB_CLEAR(tn); + tn->telrcv_state = TS_SB; + continue; + case IAC: + Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + break; + case DM: + case NOP: + case GA: + default: + printoption(data, "RCVD", IAC, c); + break; + } + tn->telrcv_state = TS_DATA; + continue; + + case TS_WILL: + printoption(data, "RCVD", WILL, c); + tn->please_negotiate = 1; + rec_will(conn, c); + tn->telrcv_state = TS_DATA; + continue; + + case TS_WONT: + printoption(data, "RCVD", WONT, c); + tn->please_negotiate = 1; + rec_wont(conn, c); + tn->telrcv_state = TS_DATA; + continue; + + case TS_DO: + printoption(data, "RCVD", DO, c); + tn->please_negotiate = 1; + rec_do(conn, c); + tn->telrcv_state = TS_DATA; + continue; + + case TS_DONT: + printoption(data, "RCVD", DONT, c); + tn->please_negotiate = 1; + rec_dont(conn, c); + tn->telrcv_state = TS_DATA; + continue; + + case TS_SB: + if (c == IAC) + { + tn->telrcv_state = TS_SE; + } + else + { + SB_ACCUM(tn,c); + } + continue; + + case TS_SE: + if (c != SE) + { + if (c != IAC) + { + /* + * This is an error. We only expect to get + * "IAC IAC" or "IAC SE". Several things may + * have happend. An IAC was not doubled, the + * IAC SE was left off, or another option got + * inserted into the suboption are all possibilities. + * If we assume that the IAC was not doubled, + * and really the IAC SE was left off, we could + * get into an infinate loop here. So, instead, + * we terminate the suboption, and process the + * partial suboption if we can. + */ + SB_ACCUM(tn, (unsigned char)IAC); + SB_ACCUM(tn, c); + tn->subpointer -= 2; + SB_TERM(tn); + + printoption(data, "In SUBOPTION processing, RCVD", IAC, c); + suboption(conn); /* handle sub-option */ + tn->telrcv_state = TS_IAC; + goto process_iac; + } + SB_ACCUM(tn,c); + tn->telrcv_state = TS_SB; + } + else + { + SB_ACCUM(tn, (unsigned char)IAC); + SB_ACCUM(tn, (unsigned char)SE); + tn->subpointer -= 2; + SB_TERM(tn); + suboption(conn); /* handle sub-option */ + tn->telrcv_state = TS_DATA; + } + break; + } + } +} + +CURLcode Curl_telnet_done(struct connectdata *conn) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + curl_slist_free_all(tn->telnet_vars); + + free(conn->proto.telnet); + conn->proto.telnet = NULL; + + return CURLE_OK; +} + +CURLcode Curl_telnet(struct connectdata *conn) +{ + CURLcode code; + struct SessionHandle *data = conn->data; + int sockfd = conn->firstsocket; +#ifdef WIN32 + WSAEVENT event_handle; + WSANETWORKEVENTS events; + HANDLE stdin_handle; + HANDLE objs[2]; + DWORD waitret; +#else + fd_set readfd; + fd_set keepfd; +#endif + bool keepon = TRUE; + char *buf = data->state.buffer; + ssize_t nread; + struct TELNET *tn; + + code = init_telnet(conn); + if(code) + return code; + + tn = (struct TELNET *)conn->proto.telnet; + + code = check_telnet_options(conn); + if(code) + return code; + +#ifdef WIN32 + /* We want to wait for both stdin and the socket. Since + ** the select() function in winsock only works on sockets + ** we have to use the WaitForMultipleObjects() call. + */ + + /* First, create a sockets event object */ + event_handle = WSACreateEvent(); + + /* The get the Windows file handle for stdin */ + stdin_handle = GetStdHandle(STD_INPUT_HANDLE); + + /* Create the list of objects to wait for */ + objs[0] = stdin_handle; + objs[1] = event_handle; + + /* Tell winsock what events we want to listen to */ + if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) { + return 0; + } + + /* Keep on listening and act on events */ + while(keepon) { + waitret = WaitForMultipleObjects(2, objs, FALSE, INFINITE); + switch(waitret - WAIT_OBJECT_0) + { + case 0: + { + unsigned char outbuf[2]; + int out_count = 0; + ssize_t bytes_written; + char *buffer = buf; + + if(!ReadFile(stdin_handle, buf, 255, &nread, NULL)) { + keepon = FALSE; + break; + } + + while(nread--) { + outbuf[0] = *buffer++; + out_count = 1; + if(outbuf[0] == IAC) + outbuf[out_count++] = IAC; + + Curl_write(conn, conn->firstsocket, outbuf, + out_count, &bytes_written); + } + } + break; + + case 1: + if(WSAEnumNetworkEvents(sockfd, event_handle, &events) + != SOCKET_ERROR) + { + if(events.lNetworkEvents & FD_READ) + { + /* This reallu OUGHT to check its return code. */ + Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); + + telrcv(conn, (unsigned char *)buf, nread); + + fflush(stdout); + + /* Negotiate if the peer has started negotiating, + otherwise don't. We don't want to speak telnet with + non-telnet servers, like POP or SMTP. */ + if(tn->please_negotiate && !tn->already_negotiated) { + negotiate(conn); + tn->already_negotiated = 1; + } + } + + if(events.lNetworkEvents & FD_CLOSE) + { + keepon = FALSE; + } + } + break; + } + } +#else + FD_ZERO (&readfd); /* clear it */ + FD_SET (sockfd, &readfd); + FD_SET (0, &readfd); + + keepfd = readfd; + + while (keepon) { + readfd = keepfd; /* set this every lap in the loop */ + + switch (select (sockfd + 1, &readfd, NULL, NULL, NULL)) { + case -1: /* error, stop reading */ + keepon = FALSE; + continue; + case 0: /* timeout */ + break; + default: /* read! */ + if(FD_ISSET(0, &readfd)) { /* read from stdin */ + unsigned char outbuf[2]; + int out_count = 0; + ssize_t bytes_written; + char *buffer = buf; + + nread = read(0, buf, 255); + + while(nread--) { + outbuf[0] = *buffer++; + out_count = 1; + if(outbuf[0] == IAC) + outbuf[out_count++] = IAC; + + Curl_write(conn, conn->firstsocket, outbuf, + out_count, &bytes_written); + } + } + + if(FD_ISSET(sockfd, &readfd)) { + /* This OUGHT to check the return code... */ + Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); + + /* if we receive 0 or less here, the server closed the connection and + we bail out from this! */ + if (nread <= 0) { + keepon = FALSE; + break; + } + + telrcv(conn, (unsigned char *)buf, nread); + + /* Negotiate if the peer has started negotiating, + otherwise don't. We don't want to speak telnet with + non-telnet servers, like POP or SMTP. */ + if(tn->please_negotiate && !tn->already_negotiated) { + negotiate(conn); + tn->already_negotiated = 1; + } + } + } + } +#endif + /* mark this as "no further transfer wanted" */ + return Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/telnet.h b/Source/CTest/Curl/telnet.h new file mode 100644 index 0000000..e576d1d --- /dev/null +++ b/Source/CTest/Curl/telnet.h @@ -0,0 +1,29 @@ +#ifndef __TELNET_H +#define __TELNET_H + +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +CURLcode Curl_telnet(struct connectdata *conn); +CURLcode Curl_telnet_done(struct connectdata *conn); + +#endif diff --git a/Source/CTest/Curl/timeval.c b/Source/CTest/Curl/timeval.c new file mode 100644 index 0000000..cd44613 --- /dev/null +++ b/Source/CTest/Curl/timeval.c @@ -0,0 +1,89 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#ifdef WIN32 +#include <windows.h> +#endif +#include "timeval.h" + +#ifndef HAVE_GETTIMEOFDAY + +#ifdef WIN32 +int +gettimeofday (struct timeval *tp, void *nothing) +{ + SYSTEMTIME st; + time_t tt; + struct tm tmtm; + /* mktime converts local to UTC */ + GetLocalTime (&st); + tmtm.tm_sec = st.wSecond; + tmtm.tm_min = st.wMinute; + tmtm.tm_hour = st.wHour; + tmtm.tm_mday = st.wDay; + tmtm.tm_mon = st.wMonth - 1; + tmtm.tm_year = st.wYear - 1900; + tmtm.tm_isdst = -1; + tt = mktime (&tmtm); + tp->tv_sec = tt; + tp->tv_usec = st.wMilliseconds * 1000; + return 1; +} +#define HAVE_GETTIMEOFDAY +#endif +#endif + +struct timeval Curl_tvnow (void) +{ + struct timeval now; +#ifdef HAVE_GETTIMEOFDAY + gettimeofday (&now, NULL); +#else + now.tv_sec = (long) time(NULL); + now.tv_usec = 0; +#endif + return now; +} + +/* + * Make sure that the first argument is the more recent time, as otherwise + * we'll get a weird negative time-diff back... + */ +long Curl_tvdiff (struct timeval newer, struct timeval older) +{ + return (newer.tv_sec-older.tv_sec)*1000+ + (499+newer.tv_usec-older.tv_usec)/1000; +} + +long Curl_tvlong (struct timeval t1) +{ + return t1.tv_sec; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/timeval.h b/Source/CTest/Curl/timeval.h new file mode 100644 index 0000000..c8ce593 --- /dev/null +++ b/Source/CTest/Curl/timeval.h @@ -0,0 +1,52 @@ +#ifndef __TIMEVAL_H +#define __TIMEVAL_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <time.h> +#include <winsock.h> +#else +#include <sys/time.h> +#endif + + +#ifndef HAVE_GETTIMEOFDAY +#if !defined(_WINSOCKAPI_) && !defined(__MINGW32__) +struct timeval { + long tv_sec; + long tv_usec; +}; +#endif +#endif + +struct timeval Curl_tvnow (void); + +/* the diff is from now on returned in number of milliseconds! */ +long Curl_tvdiff (struct timeval t1, struct timeval t2); + +long Curl_tvlong (struct timeval t1); + +#endif diff --git a/Source/CTest/Curl/transfer.c b/Source/CTest/Curl/transfer.c new file mode 100644 index 0000000..e4c1599 --- /dev/null +++ b/Source/CTest/Curl/transfer.c @@ -0,0 +1,1357 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#include "strequal.h" + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <sys/time.h> +#include <sys/resource.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#ifndef HAVE_SELECT +#error "We can't compile without select() support!" +#endif +#ifndef HAVE_SOCKET +#error "We can't compile without socket() support!" +#endif + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include <curl/types.h> +#include "netrc.h" + +#include "hostip.h" +#include "transfer.h" +#include "sendf.h" +#include "speedcheck.h" +#include "getpass.h" +#include "progress.h" +#include "getdate.h" +#include "http.h" +#include "url.h" +#include "getinfo.h" +#include "ssluse.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +enum { + KEEP_NONE, + KEEP_READ, + KEEP_WRITE +}; + + +/* + * compareheader() + * + * Returns TRUE if 'headerline' contains the 'header' with given 'content'. + * Pass headers WITH the colon. + */ +static bool +compareheader(char *headerline, /* line to check */ + const char *header, /* header keyword _with_ colon */ + const char *content) /* content string to find */ +{ + /* RFC2616, section 4.2 says: "Each header field consists of a name followed + * by a colon (":") and the field value. Field names are case-insensitive. + * The field value MAY be preceded by any amount of LWS, though a single SP + * is preferred." */ + + size_t hlen = strlen(header); + size_t clen; + size_t len; + char *start; + char *end; + + if(!strnequal(headerline, header, hlen)) + return FALSE; /* doesn't start with header */ + + /* pass the header */ + start = &headerline[hlen]; + + /* pass all white spaces */ + while(*start && isspace((int)*start)) + start++; + + /* find the end of the header line */ + end = strchr(start, '\r'); /* lines end with CRLF */ + if(!end) { + /* in case there's a non-standard compliant line here */ + end = strchr(start, '\n'); + + if(!end) + /* hm, there's no line ending here, return false and bail out! */ + return FALSE; + } + + len = end-start; /* length of the content part of the input line */ + clen = strlen(content); /* length of the word to find */ + + /* find the content string in the rest of the line */ + for(;len>=clen;len--, start++) { + if(strnequal(start, content, clen)) + return TRUE; /* match! */ + } + + return FALSE; /* no match */ +} + +CURLcode Curl_readwrite(struct connectdata *conn, + bool *done) +{ + struct Curl_transfer_keeper *k = &conn->keep; + struct SessionHandle *data = conn->data; + int result; + ssize_t nread; /* number of bytes read */ + int didwhat=0; + + do { + if((k->keepon & KEEP_READ) && + FD_ISSET(conn->sockfd, &k->readfd)) { + + /* read! */ + result = Curl_read(conn, conn->sockfd, k->buf, + BUFSIZE -1, &nread); + + if(0>result) + break; /* get out of loop */ + if(result>0) + return result; + + if ((k->bytecount == 0) && (k->writebytecount == 0)) + Curl_pgrsTime(data, TIMER_STARTTRANSFER); + + didwhat |= KEEP_READ; + + /* NULL terminate, allowing string ops to be used */ + if (0 < nread) + k->buf[nread] = 0; + + /* if we receive 0 or less here, the server closed the connection and + we bail out from this! */ + else if (0 >= nread) { + k->keepon &= ~KEEP_READ; + FD_ZERO(&k->rkeepfd); + break; + } + + /* Default buffer to use when we write the buffer, it may be changed + in the flow below before the actual storing is done. */ + k->str = k->buf; + + /* Since this is a two-state thing, we check if we are parsing + headers at the moment or not. */ + if (k->header) { + /* we are in parse-the-header-mode */ + + /* header line within buffer loop */ + do { + int hbufp_index; + + /* str_start is start of line within buf */ + k->str_start = k->str; + + k->end_ptr = strchr (k->str_start, '\n'); + + if (!k->end_ptr) { + /* no more complete header lines within buffer */ + /* copy what is remaining into headerbuff */ + int str_length = (int)strlen(k->str); + + /* + * We enlarge the header buffer if it seems to be too + * smallish + */ + if (k->hbuflen + (int)str_length >= + data->state.headersize) { + char *newbuff; + long newsize=MAX((k->hbuflen+str_length)*3/2, + data->state.headersize*2); + hbufp_index = k->hbufp - data->state.headerbuff; + newbuff = (char *)realloc(data->state.headerbuff, newsize); + if(!newbuff) { + failf (data, "Failed to alloc memory for big header!"); + return CURLE_READ_ERROR; + } + data->state.headersize=newsize; + data->state.headerbuff = newbuff; + k->hbufp = data->state.headerbuff + hbufp_index; + } + strcpy (k->hbufp, k->str); + k->hbufp += strlen (k->str); + k->hbuflen += strlen (k->str); + break; /* read more and try again */ + } + + k->str = k->end_ptr + 1; /* move past new line */ + + /* + * We're about to copy a chunk of data to the end of the + * already received header. We make sure that the full string + * fit in the allocated header buffer, or else we enlarge + * it. + */ + if (k->hbuflen + (k->str - k->str_start) >= + data->state.headersize) { + char *newbuff; + long newsize=MAX((k->hbuflen+ + (k->str-k->str_start))*3/2, + data->state.headersize*2); + hbufp_index = k->hbufp - data->state.headerbuff; + newbuff = (char *)realloc(data->state.headerbuff, newsize); + if(!newbuff) { + failf (data, "Failed to alloc memory for big header!"); + return CURLE_READ_ERROR; + } + data->state.headersize= newsize; + data->state.headerbuff = newbuff; + k->hbufp = data->state.headerbuff + hbufp_index; + } + + /* copy to end of line */ + strncpy (k->hbufp, k->str_start, k->str - k->str_start); + k->hbufp += k->str - k->str_start; + k->hbuflen += k->str - k->str_start; + *k->hbufp = 0; + + k->p = data->state.headerbuff; + + /**** + * We now have a FULL header line that p points to + *****/ + + if (('\n' == *k->p) || ('\r' == *k->p)) { + /* Zero-length header line means end of headers! */ + + if ('\r' == *k->p) + k->p++; /* pass the \r byte */ + if ('\n' == *k->p) + k->p++; /* pass the \n byte */ + + if(100 == k->httpcode) { + /* + * we have made a HTTP PUT or POST and this is 1.1-lingo + * that tells us that the server is OK with this and ready + * to receive our stuff. + * However, we'll get more headers now so we must get + * back into the header-parsing state! + */ + k->header = TRUE; + k->headerline = 0; /* restart the header line counter */ + /* if we did wait for this do enable write now! */ + if (k->write_after_100_header) { + + k->write_after_100_header = FALSE; + FD_SET (conn->writesockfd, &k->writefd); /* write */ + k->keepon |= KEEP_WRITE; + k->wkeepfd = k->writefd; + } + } + else + k->header = FALSE; /* no more header to parse! */ + + if (417 == k->httpcode) { + /* + * we got: "417 Expectation Failed" this means: + * we have made a HTTP call and our Expect Header + * seems to cause a problem => abort the write operations + * (or prevent them from starting + */ + k->write_after_100_header = FALSE; + k->keepon &= ~KEEP_WRITE; + FD_ZERO(&k->wkeepfd); + } + + /* now, only output this if the header AND body are requested: + */ + k->writetype = CLIENTWRITE_HEADER; + if (data->set.http_include_header) + k->writetype |= CLIENTWRITE_BODY; + + result = Curl_client_write(data, k->writetype, + data->state.headerbuff, + k->p - data->state.headerbuff); + if(result) + return result; + + data->info.header_size += k->p - data->state.headerbuff; + conn->headerbytecount += k->p - data->state.headerbuff; + + if(!k->header) { + /* + * really end-of-headers. + * + * If we requested a "no body", this is a good time to get + * out and return home. + */ + bool stop_reading = FALSE; + + if(data->set.no_body) + stop_reading = TRUE; + else if(!conn->bits.close) { + /* If this is not the last request before a close, we must + set the maximum download size to the size of the + expected document or else, we won't know when to stop + reading! */ + if(-1 != conn->size) + conn->maxdownload = conn->size; + + /* If max download size is *zero* (nothing) we already + have nothing and can safely return ok now! */ + if(0 == conn->maxdownload) + stop_reading = TRUE; + + /* What to do if the size is *not* known? */ + } + + if(stop_reading) { + /* we make sure that this socket isn't read more now */ + k->keepon &= ~KEEP_READ; + FD_ZERO(&k->rkeepfd); + /* for a progress meter/info update before going away */ + Curl_pgrsUpdate(conn); + return CURLE_OK; + } + + break; /* exit header line loop */ + } + + /* We continue reading headers, so reset the line-based + header parsing variables hbufp && hbuflen */ + k->hbufp = data->state.headerbuff; + k->hbuflen = 0; + continue; + } + + /* + * Checks for special headers coming up. + */ + + if (!k->headerline++) { + /* This is the first header, it MUST be the error code line + or else we consiser this to be the body right away! */ + int httpversion_major; + int nc=sscanf (k->p, " HTTP/%d.%d %3d", + &httpversion_major, + &k->httpversion, + &k->httpcode); + if (nc==3) { + k->httpversion += 10 * httpversion_major; + } + else { + /* this is the real world, not a Nirvana + NCSA 1.5.x returns this crap when asked for HTTP/1.1 + */ + nc=sscanf (k->p, " HTTP %3d", &k->httpcode); + k->httpversion = 10; + } + + if (nc) { + data->info.httpcode = k->httpcode; + data->info.httpversion = k->httpversion; + + /* 404 -> URL not found! */ + if (data->set.http_fail_on_error && + (k->httpcode >= 400)) { + /* If we have been told to fail hard on HTTP-errors, + here is the check for that: */ + /* serious error, go home! */ + failf (data, "The requested file was not found"); + return CURLE_HTTP_NOT_FOUND; + } + + if(k->httpversion == 10) + /* Default action for HTTP/1.0 must be to close, unless + we get one of those fancy headers that tell us the + server keeps it open for us! */ + conn->bits.close = TRUE; + + switch(k->httpcode) { + case 204: + /* (quote from RFC2616, section 10.2.5): The server has + * fulfilled the request but does not need to return an + * entity-body ... The 204 response MUST NOT include a + * message-body, and thus is always terminated by the first + * empty line after the header fields. */ + /* FALLTHROUGH */ + case 304: + /* (quote from RFC2616, section 10.3.5): The 304 response MUST + * NOT contain a message-body, and thus is always terminated + * by the first empty line after the header fields. */ + conn->size=0; + break; + default: + /* nothing */ + break; + } + } + else { + k->header = FALSE; /* this is not a header line */ + break; + } + } + /* check for Content-Length: header lines to get size */ + if (strnequal("Content-Length:", k->p, 15) && + sscanf (k->p+15, " %ld", &k->contentlength)) { + conn->size = k->contentlength; + Curl_pgrsSetDownloadSize(data, k->contentlength); + } + /* check for Content-Type: header lines to get the mime-type */ + else if (strnequal("Content-Type:", k->p, 13)) { + char *start; + char *end; + int len; + + /* Find the first non-space letter */ + for(start=k->p+14; + *start && isspace((int)*start); + start++); + + /* count all non-space letters following */ + for(end=start, len=0; + *end && !isspace((int)*end); + end++, len++); + + /* allocate memory of a cloned copy */ + data->info.contenttype = malloc(len + 1); + if (NULL == data->info.contenttype) + return CURLE_OUT_OF_MEMORY; + + /* copy the content-type string */ + memcpy(data->info.contenttype, start, len); + data->info.contenttype[len] = 0; /* zero terminate */ + } + else if((k->httpversion == 10) && + conn->bits.httpproxy && + compareheader(k->p, "Proxy-Connection:", "keep-alive")) { + /* + * When a HTTP/1.0 reply comes when using a proxy, the + * 'Proxy-Connection: keep-alive' line tells us the + * connection will be kept alive for our pleasure. + * Default action for 1.0 is to close. + */ + conn->bits.close = FALSE; /* don't close when done */ + infof(data, "HTTP/1.0 proxy connection set to keep alive!\n"); + } + else if((k->httpversion == 10) && + compareheader(k->p, "Connection:", "keep-alive")) { + /* + * A HTTP/1.0 reply with the 'Connection: keep-alive' line + * tells us the connection will be kept alive for our + * pleasure. Default action for 1.0 is to close. + * + * [RFC2068, section 19.7.1] */ + conn->bits.close = FALSE; /* don't close when done */ + infof(data, "HTTP/1.0 connection set to keep alive!\n"); + } + else if (compareheader(k->p, "Connection:", "close")) { + /* + * [RFC 2616, section 8.1.2.1] + * "Connection: close" is HTTP/1.1 language and means that + * the connection will close when this request has been + * served. + */ + conn->bits.close = TRUE; /* close when done */ + } + else if (compareheader(k->p, "Transfer-Encoding:", "chunked")) { + /* + * [RFC 2616, section 3.6.1] A 'chunked' transfer encoding + * means that the server will send a series of "chunks". Each + * chunk starts with line with info (including size of the + * coming block) (terminated with CRLF), then a block of data + * with the previously mentioned size. There can be any amount + * of chunks, and a chunk-data set to zero signals the + * end-of-chunks. */ + conn->bits.chunk = TRUE; /* chunks coming our way */ + + /* init our chunky engine */ + Curl_httpchunk_init(conn); + } + else if (strnequal("Content-Range:", k->p, 14)) { + if (sscanf (k->p+14, " bytes %d-", &k->offset) || + sscanf (k->p+14, " bytes: %d-", &k->offset)) { + /* This second format was added August 1st 2000 by Igor + Khristophorov since Sun's webserver JavaWebServer/1.1.1 + obviously sends the header this way! :-( */ + if (conn->resume_from == k->offset) { + /* we asked for a resume and we got it */ + k->content_range = TRUE; + } + } + } + else if(data->cookies && + strnequal("Set-Cookie:", k->p, 11)) { + Curl_cookie_add(data->cookies, TRUE, k->p+12, conn->name); + } + else if(strnequal("Last-Modified:", k->p, + strlen("Last-Modified:")) && + (data->set.timecondition || data->set.get_filetime) ) { + time_t secs=time(NULL); + k->timeofdoc = curl_getdate(k->p+strlen("Last-Modified:"), + &secs); + if(data->set.get_filetime) + data->info.filetime = k->timeofdoc; + } + else if ((k->httpcode >= 300 && k->httpcode < 400) && + (data->set.http_follow_location) && + strnequal("Location:", k->p, 9)) { + /* this is the URL that the server advices us to get instead */ + char *ptr; + char *start=k->p; + char backup; + + start += 9; /* pass "Location:" */ + + /* Skip spaces and tabs. We do this to support multiple + white spaces after the "Location:" keyword. */ + while(*start && isspace((int)*start )) + start++; + ptr = start; /* start scanning here */ + + /* scan through the string to find the end */ + while(*ptr && !isspace((int)*ptr)) + ptr++; + backup = *ptr; /* store the ending letter */ + *ptr = '\0'; /* zero terminate */ + conn->newurl = strdup(start); /* clone string */ + *ptr = backup; /* restore ending letter */ + } + + /* + * End of header-checks. Write them to the client. + */ + + k->writetype = CLIENTWRITE_HEADER; + if (data->set.http_include_header) + k->writetype |= CLIENTWRITE_BODY; + + result = Curl_client_write(data, k->writetype, k->p, + k->hbuflen); + if(result) + return result; + + data->info.header_size += k->hbuflen; + conn->headerbytecount += k->hbuflen; + + /* reset hbufp pointer && hbuflen */ + k->hbufp = data->state.headerbuff; + k->hbuflen = 0; + } + while (*k->str); /* header line within buffer */ + + /* We might have reached the end of the header part here, but + there might be a non-header part left in the end of the read + buffer. */ + + if (!k->header) { + /* the next token and forward is not part of + the header! */ + + /* we subtract the remaining header size from the buffer */ + nread -= (k->str - k->buf); + } + + } /* end if header mode */ + + /* This is not an 'else if' since it may be a rest from the header + parsing, where the beginning of the buffer is headers and the end + is non-headers. */ + if (k->str && !k->header && (nread > 0)) { + + if(0 == k->bodywrites) { + /* These checks are only made the first time we are about to + write a piece of the body */ + if(conn->protocol&PROT_HTTP) { + /* HTTP-only checks */ + if (conn->newurl) { + /* abort after the headers if "follow Location" is set */ + infof (data, "Follow to new URL: %s\n", conn->newurl); + k->keepon &= ~KEEP_READ; + FD_ZERO(&k->rkeepfd); + return CURLE_OK; + } + else if (conn->resume_from && + !k->content_range && + (data->set.httpreq==HTTPREQ_GET)) { + /* we wanted to resume a download, although the server + doesn't seem to support this and we did this with a GET + (if it wasn't a GET we did a POST or PUT resume) */ + failf (data, "HTTP server doesn't seem to support " + "byte ranges. Cannot resume."); + return CURLE_HTTP_RANGE_ERROR; + } + else if(data->set.timecondition && !conn->range) { + /* A time condition has been set AND no ranges have been + requested. This seems to be what chapter 13.3.4 of + RFC 2616 defines to be the correct action for a + HTTP/1.1 client */ + if((k->timeofdoc > 0) && (data->set.timevalue > 0)) { + switch(data->set.timecondition) { + case TIMECOND_IFMODSINCE: + default: + if(k->timeofdoc < data->set.timevalue) { + infof(data, + "The requested document is not new enough\n"); + return CURLE_OK; + } + break; + case TIMECOND_IFUNMODSINCE: + if(k->timeofdoc > data->set.timevalue) { + infof(data, + "The requested document is not old enough\n"); + return CURLE_OK; + } + break; + } /* switch */ + } /* two valid time strings */ + } /* we have a time condition */ + + } /* this is HTTP */ + } /* this is the first time we write a body part */ + k->bodywrites++; + + if(conn->bits.chunk) { + /* + * Bless me father for I have sinned. Here comes a chunked + * transfer flying and we need to decode this properly. While + * the name says read, this function both reads and writes away + * the data. The returned 'nread' holds the number of actual + * data it wrote to the client. */ + CHUNKcode res = + Curl_httpchunk_read(conn, k->str, nread, &nread); + + if(CHUNKE_OK < res) { + failf(data, "Receeived problem in the chunky parser"); + return CURLE_READ_ERROR; + } + else if(CHUNKE_STOP == res) { + /* we're done reading chunks! */ + k->keepon &= ~KEEP_READ; /* read no more */ + FD_ZERO(&k->rkeepfd); + + /* There are now possibly N number of bytes at the end of the + str buffer that weren't written to the client, but we don't + care about them right now. */ + } + /* If it returned OK, we just keep going */ + } + + if((-1 != conn->maxdownload) && + (k->bytecount + nread >= conn->maxdownload)) { + nread = conn->maxdownload - k->bytecount; + if(nread < 0 ) /* this should be unusual */ + nread = 0; + + k->keepon &= ~KEEP_READ; /* we're done reading */ + FD_ZERO(&k->rkeepfd); + } + + k->bytecount += nread; + + Curl_pgrsSetDownloadCounter(data, (double)k->bytecount); + + if(!conn->bits.chunk && nread) { + /* If this is chunky transfer, it was already written */ + result = Curl_client_write(data, CLIENTWRITE_BODY, k->str, + nread); + if(result) + return result; + } + + } /* if (! header and data to read ) */ + } /* if( read from socket ) */ + + if((k->keepon & KEEP_WRITE) && + FD_ISSET(conn->writesockfd, &k->writefd)) { + /* write */ + + int i, si; + ssize_t bytes_written; + + if ((k->bytecount == 0) && (k->writebytecount == 0)) + Curl_pgrsTime(data, TIMER_STARTTRANSFER); + + didwhat |= KEEP_WRITE; + + /* only read more data if there's no upload data already + present in the upload buffer */ + if(0 == conn->upload_present) { + /* init the "upload from here" pointer */ + conn->upload_fromhere = k->uploadbuf; + + nread = data->set.fread(conn->upload_fromhere, 1, + BUFSIZE, data->set.in); + + /* the signed int typecase of nread of for systems that has + unsigned size_t */ + if (nread<=0) { + /* done */ + k->keepon &= ~KEEP_WRITE; /* we're done writing */ + FD_ZERO(&k->wkeepfd); + break; + } + + /* store number of bytes available for upload */ + conn->upload_present = nread; + + /* convert LF to CRLF if so asked */ + if (data->set.crlf) { + for(i = 0, si = 0; i < nread; i++, si++) { + if (k->buf[i] == 0x0a) { + data->state.scratch[si++] = 0x0d; + data->state.scratch[si] = 0x0a; + } + else { + data->state.scratch[si] = k->uploadbuf[i]; + } + } + nread = si; + k->buf = data->state.scratch; /* point to the new buffer */ + } + } + else { + /* We have a partial buffer left from a previous "round". Use + that instead of reading more data */ + } + + /* write to socket */ + result = Curl_write(conn, + conn->writesockfd, + conn->upload_fromhere, + conn->upload_present, + &bytes_written); + if(result) + return result; + else if(conn->upload_present != bytes_written) { + /* we only wrote a part of the buffer (if anything), deal with it! */ + + /* store the amount of bytes left in the buffer to write */ + conn->upload_present -= bytes_written; + + /* advance the pointer where to find the buffer when the next send + is to happen */ + conn->upload_fromhere += bytes_written; + } + else if(data->set.crlf) + k->buf = data->state.buffer; /* put it back on the buffer */ + else { + /* we've uploaded that buffer now */ + conn->upload_fromhere = k->uploadbuf; + conn->upload_present = 0; /* no more bytes left */ + } + + k->writebytecount += bytes_written; + Curl_pgrsSetUploadCounter(data, (double)k->writebytecount); + + } + + } while(0); /* just to break out from! */ + + if(didwhat) { + /* Update read/write counters */ + if(conn->bytecountp) + *conn->bytecountp = k->bytecount; /* read count */ + if(conn->writebytecountp) + *conn->writebytecountp = k->writebytecount; /* write count */ + } + else { + /* no read no write, this is a timeout? */ + if (k->write_after_100_header) { + /* This should allow some time for the header to arrive, but only a + very short time as otherwise it'll be too much wasted times too + often. */ + k->write_after_100_header = FALSE; + FD_SET (conn->writesockfd, &k->writefd); /* write socket */ + k->keepon |= KEEP_WRITE; + k->wkeepfd = k->writefd; + } + } + + k->now = Curl_tvnow(); + if(Curl_pgrsUpdate(conn)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck (data, k->now); + if (result) + return result; + + if (data->set.timeout && + ((Curl_tvdiff(k->now, k->start)/1000) >= data->set.timeout)) { + failf (data, "Operation timed out with %d out of %d bytes received", + k->bytecount, conn->size); + return CURLE_OPERATION_TIMEOUTED; + } + + if(!k->keepon) { + /* + * The transfer has been performed. Just make some general checks before + * returning. + */ + + if(!(data->set.no_body) && k->contentlength && + (k->bytecount != k->contentlength) && + !conn->newurl) { + failf(data, "transfer closed with %d bytes remaining to read", + k->contentlength-k->bytecount); + return CURLE_PARTIAL_FILE; + } + else if(conn->bits.chunk && conn->proto.http->chunk.datasize) { + failf(data, "transfer closed with at least %d bytes remaining", + conn->proto.http->chunk.datasize); + return CURLE_PARTIAL_FILE; + } + if(Curl_pgrsUpdate(conn)) + return CURLE_ABORTED_BY_CALLBACK; + } + + /* Now update the "done" boolean we return */ + *done = !k->keepon; + + return CURLE_OK; +} + +CURLcode Curl_readwrite_init(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + struct Curl_transfer_keeper *k = &conn->keep; + + memset(k, 0, sizeof(struct Curl_transfer_keeper)); + + k->start = Curl_tvnow(); /* start time */ + k->now = k->start; /* current time is now */ + k->header = TRUE; /* assume header */ + k->httpversion = -1; /* unknown at this point */ + k->conn = (struct connectdata *)conn; /* store the connection */ + + data = conn->data; /* there's the root struct */ + k->buf = data->state.buffer; + k->uploadbuf = data->state.uploadbuffer; + k->maxfd = (conn->sockfd>conn->writesockfd? + conn->sockfd:conn->writesockfd)+1; + k->hbufp = data->state.headerbuff; + + Curl_pgrsTime(data, TIMER_PRETRANSFER); + Curl_speedinit(data); + + if (!conn->getheader) { + k->header = FALSE; + if(conn->size > 0) + Curl_pgrsSetDownloadSize(data, conn->size); + } + /* we want header and/or body, if neither then don't do this! */ + if(conn->getheader || !data->set.no_body) { + + FD_ZERO (&k->readfd); /* clear it */ + if(conn->sockfd != -1) { + FD_SET (conn->sockfd, &k->readfd); /* read socket */ + k->keepon |= KEEP_READ; + } + + FD_ZERO (&k->writefd); /* clear it */ + if(conn->writesockfd != -1) { + if (data->set.expect100header) + /* wait with write until we either got 100-continue or a timeout */ + k->write_after_100_header = TRUE; + else { + FD_SET (conn->writesockfd, &k->writefd); /* write socket */ + k->keepon |= KEEP_WRITE; + } + } + + /* get these in backup variables to be able to restore them on each lap in + the select() loop */ + k->rkeepfd = k->readfd; + k->wkeepfd = k->writefd; + + } + + return CURLE_OK; +} + +void Curl_single_fdset(struct connectdata *conn, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd) +{ + *max_fd = -1; /* init */ + if(conn->keep.keepon & KEEP_READ) { + FD_SET(conn->sockfd, read_fd_set); + *max_fd = conn->sockfd; + } + if(conn->keep.keepon & KEEP_WRITE) { + FD_SET(conn->writesockfd, write_fd_set); + if(conn->writesockfd > *max_fd) + *max_fd = conn->writesockfd; + } + /* we don't use exceptions, only touch that one to prevent compiler + warnings! */ + *exc_fd_set = *exc_fd_set; +} + + +/* + * Transfer() + * + * This function is what performs the actual transfer. It is capable of + * doing both ways simultaneously. + * The transfer must already have been setup by a call to Curl_Transfer(). + * + * Note that headers are created in a preallocated buffer of a default size. + * That buffer can be enlarged on demand, but it is never shrinken again. + * + * Parts of this function was once written by the friendly Mark Butler + * <butlerm@xmission.com>. + */ + +static CURLcode +Transfer(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + CURLcode result; + struct Curl_transfer_keeper *k = &conn->keep; + bool done=FALSE; + + Curl_readwrite_init(conn); + + if((conn->sockfd == -1) && (conn->writesockfd == -1)) + /* nothing to read, nothing to write, we're already OK! */ + return CURLE_OK; + + /* we want header and/or body, if neither then don't do this! */ + if(!conn->getheader && data->set.no_body) + return CURLE_OK; + + while (!done) { + struct timeval interval; + k->readfd = k->rkeepfd; /* set these every lap in the loop */ + k->writefd = k->wkeepfd; + interval.tv_sec = 1; + interval.tv_usec = 0; + + switch (select (k->maxfd, &k->readfd, &k->writefd, NULL, + &interval)) { + case -1: /* select() error, stop reading */ +#ifdef EINTR + /* The EINTR is not serious, and it seems you might get this more + ofen when using the lib in a multi-threaded environment! */ + if(errno == EINTR) + ; + else +#endif + done = TRUE; /* no more read or write */ + continue; + case 0: /* timeout */ + result = Curl_readwrite(conn, &done); + break; + + default: /* readable descriptors */ + result = Curl_readwrite(conn, &done); + break; + } + if(result) + return result; + + /* "done" signals to us if the transfer(s) are ready */ + } + + return CURLE_OK; +} + +CURLcode Curl_pretransfer(struct SessionHandle *data) +{ + if(!data->change.url) + /* we can't do anything wihout URL */ + return CURLE_URL_MALFORMAT; + +#ifdef USE_SSLEAY + /* Init the SSL session ID cache here. We do it here since we want to + do it after the *_setopt() calls (that could change the size) but + before any transfer. */ + Curl_SSL_InitSessions(data, data->set.ssl.numsessions); +#endif + + data->set.followlocation=0; /* reset the location-follow counter */ + data->state.this_is_a_follow = FALSE; /* reset this */ + data->state.errorbuf = FALSE; /* no error has occurred */ + + /* Allow data->set.use_port to set which port to use. This needs to be + * disabled for example when we follow Location: headers to URLs using + * different ports! */ + data->state.allow_port = TRUE; + +#if defined(HAVE_SIGNAL) && defined(SIGPIPE) + /************************************************************* + * Tell signal handler to ignore SIGPIPE + *************************************************************/ + data->state.prev_signal = signal(SIGPIPE, SIG_IGN); +#endif + + Curl_initinfo(data); /* reset session-specific information "variables" */ + Curl_pgrsStartNow(data); + + return CURLE_OK; +} + +CURLcode Curl_posttransfer(struct SessionHandle *data) +{ +#if defined(HAVE_SIGNAL) && defined(SIGPIPE) + /* restore the signal handler for SIGPIPE before we get back */ + signal(SIGPIPE, data->state.prev_signal); +#endif + + return CURLE_OK; +} + +CURLcode Curl_perform(struct SessionHandle *data) +{ + CURLcode res; + CURLcode res2; + struct connectdata *conn=NULL; + char *newurl = NULL; /* possibly a new URL to follow to! */ + + res = Curl_pretransfer(data); + if(res) + return res; + + /* + * It is important that there is NO 'return' from this function any any + * other place than falling down the bottom! This is because we have cleanup + * stuff that must be done before we get back, and that is only performed + * after this do-while loop. + */ + + do { + Curl_pgrsTime(data, TIMER_STARTSINGLE); + res = Curl_connect(data, &conn); + if(res == CURLE_OK) { + res = Curl_do(&conn); + + if(res == CURLE_OK) { + CURLcode res2; /* just a local extra result container */ + + if(conn->protocol&PROT_FTPS) + /* FTPS, disable ssl while transfering data */ + conn->ssl.use = FALSE; + res = Transfer(conn); /* now fetch that URL please */ + if(conn->protocol&PROT_FTPS) + /* FTPS, enable ssl again after havving transferred data */ + conn->ssl.use = TRUE; + + if(res == CURLE_OK) + /* + * We must duplicate the new URL here as the connection data + * may be free()ed in the Curl_done() function. + */ + newurl = conn->newurl?strdup(conn->newurl):NULL; + else + /* The transfer phase returned error, we mark the connection to get + * closed to prevent being re-used. This is becasue we can't + * possibly know if the connection is in a good shape or not now. */ + conn->bits.close = TRUE; + + /* Always run Curl_done(), even if some of the previous calls + failed, but return the previous (original) error code */ + res2 = Curl_done(conn); + + if(CURLE_OK == res) + res = res2; + } + + /* + * Important: 'conn' cannot be used here, since it may have been closed + * in 'Curl_done' or other functions. + */ + + if((res == CURLE_OK) && newurl) { + /* Location: redirect + + This is assumed to happen for HTTP(S) only! + */ + char prot[16]; /* URL protocol string storage */ + char letter; /* used for a silly sscanf */ + + if (data->set.maxredirs && (data->set.followlocation >= data->set.maxredirs)) { + failf(data,"Maximum (%d) redirects followed", data->set.maxredirs); + res=CURLE_TOO_MANY_REDIRECTS; + break; + } + + /* mark the next request as a followed location: */ + data->state.this_is_a_follow = TRUE; + + data->set.followlocation++; /* count location-followers */ + + if(data->set.http_auto_referer) { + /* We are asked to automatically set the previous URL as the + referer when we get the next URL. We pick the ->url field, + which may or may not be 100% correct */ + + if(data->change.referer_alloc) + /* If we already have an allocated referer, free this first */ + free(data->change.referer); + + data->change.referer = strdup(data->change.url); + data->change.referer_alloc = TRUE; /* yes, free this later */ + } + + if(2 != sscanf(newurl, "%15[^?&/:]://%c", prot, &letter)) { + /*** + *DANG* this is an RFC 2068 violation. The URL is supposed + to be absolute and this doesn't seem to be that! + *** + Instead, we have to TRY to append this new path to the old URL + to the right of the host part. Oh crap, this is doomed to cause + problems in the future... + */ + char *protsep; + char *pathsep; + char *newest; + + /* we must make our own copy of the URL to play with, as it may + point to read-only data */ + char *url_clone=strdup(data->change.url); + + if(!url_clone) { + res = CURLE_OUT_OF_MEMORY; + break; /* skip out of this loop NOW */ + } + + /* protsep points to the start of the host name */ + protsep=strstr(url_clone, "//"); + if(!protsep) + protsep=url_clone; + else + protsep+=2; /* pass the slashes */ + + if('/' != newurl[0]) { + /* First we need to find out if there's a ?-letter in the URL, + and cut it and the right-side of that off */ + pathsep = strrchr(protsep, '?'); + if(pathsep) + *pathsep=0; + + /* we have a relative path to append to the last slash if + there's one available */ + pathsep = strrchr(protsep, '/'); + if(pathsep) + *pathsep=0; + } + else { + /* We got a new absolute path for this server, cut off from the + first slash */ + pathsep = strchr(protsep, '/'); + if(pathsep) + *pathsep=0; + } + + newest=(char *)malloc( strlen(url_clone) + + 1 + /* possible slash */ + strlen(newurl) + 1/* zero byte */); + + if(!newest) { + res = CURLE_OUT_OF_MEMORY; + break; /* go go go out from this loop */ + } + sprintf(newest, "%s%s%s", url_clone, ('/' == newurl[0])?"":"/", + newurl); + free(newurl); + free(url_clone); + newurl = newest; + } + else + /* This is an absolute URL, don't allow the custom port number */ + data->state.allow_port = FALSE; + + if(data->change.url_alloc) + free(data->change.url); + else + data->change.url_alloc = TRUE; /* the URL is allocated */ + + /* TBD: set the URL with curl_setopt() */ + data->change.url = newurl; + newurl = NULL; /* don't free! */ + + infof(data, "Follows Location: to new URL: '%s'\n", data->change.url); + + /* + * We get here when the HTTP code is 300-399. We need to perform + * differently based on exactly what return code there was. + * Discussed on the curl mailing list and posted about on the 26th + * of January 2001. + */ + switch(data->info.httpcode) { + case 300: /* Multiple Choices */ + case 301: /* Moved Permanently */ + case 306: /* Not used */ + case 307: /* Temporary Redirect */ + default: /* for all unknown ones */ + /* These are explicitly mention since I've checked RFC2616 and they + * seem to be OK to POST to. + */ + break; + case 302: /* Found */ + /* (From 10.3.3) + + Note: RFC 1945 and RFC 2068 specify that the client is not allowed + to change the method on the redirected request. However, most + existing user agent implementations treat 302 as if it were a 303 + response, performing a GET on the Location field-value regardless + of the original request method. The status codes 303 and 307 have + been added for servers that wish to make unambiguously clear which + kind of reaction is expected of the client. + + (From 10.3.4) + + Note: Many pre-HTTP/1.1 user agents do not understand the 303 + status. When interoperability with such clients is a concern, the + 302 status code may be used instead, since most user agents react + to a 302 response as described here for 303. + */ + case 303: /* See Other */ + /* Disable both types of POSTs, since doing a second POST when + * following isn't what anyone would want! */ + data->set.httpreq = HTTPREQ_GET; /* enforce GET request */ + infof(data, "Disables POST, goes with GET\n"); + break; + case 304: /* Not Modified */ + /* 304 means we did a conditional request and it was "Not modified". + * We shouldn't get any Location: header in this response! + */ + break; + case 305: /* Use Proxy */ + /* (quote from RFC2616, section 10.3.6): + * "The requested resource MUST be accessed through the proxy given + * by the Location field. The Location field gives the URI of the + * proxy. The recipient is expected to repeat this single request + * via the proxy. 305 responses MUST only be generated by origin + * servers." + */ + break; + } + continue; + } + } + break; /* it only reaches here when this shouldn't loop */ + + } while(1); /* loop if Location: */ + + if(newurl) + free(newurl); + + /* run post-transfer uncondionally, but don't clobber the return code if + we already have an error code recorder */ + res2 = Curl_posttransfer(data); + if(!res && res2) + res = res2; + + return res; +} + +CURLcode +Curl_Transfer(struct connectdata *c_conn, /* connection data */ + int sockfd, /* socket to read from or -1 */ + int size, /* -1 if unknown at this point */ + bool getheader, /* TRUE if header parsing is wanted */ + long *bytecountp, /* return number of bytes read or NULL */ + int writesockfd, /* socket to write to, it may very well be + the same we read from. -1 disables */ + long *writebytecountp /* return number of bytes written or + NULL */ + ) +{ + struct connectdata *conn = (struct connectdata *)c_conn; + if(!conn) + return CURLE_BAD_FUNCTION_ARGUMENT; + + /* now copy all input parameters */ + conn->sockfd = sockfd; + conn->size = size; + conn->getheader = getheader; + conn->bytecountp = bytecountp; + conn->writesockfd = writesockfd; + conn->writebytecountp = writebytecountp; + + return CURLE_OK; + +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/transfer.h b/Source/CTest/Curl/transfer.h new file mode 100644 index 0000000..ee6c93d --- /dev/null +++ b/Source/CTest/Curl/transfer.h @@ -0,0 +1,49 @@ +#ifndef __TRANSFER_H +#define __TRANSFER_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ +CURLcode Curl_perform(struct SessionHandle *data); + +CURLcode Curl_pretransfer(struct SessionHandle *data); +CURLcode Curl_posttransfer(struct SessionHandle *data); + +CURLcode Curl_readwrite(struct connectdata *conn, bool *done); +void Curl_single_fdset(struct connectdata *conn, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); +CURLcode Curl_readwrite_init(struct connectdata *conn); + +/* This sets up a forthcoming transfer */ +CURLcode +Curl_Transfer (struct connectdata *data, + int sockfd, /* socket to read from or -1 */ + int size, /* -1 if unknown at this point */ + bool getheader, /* TRUE if header parsing is wanted */ + long *bytecountp, /* return number of bytes read */ + int writesockfd, /* socket to write to, it may very well be + the same we read from. -1 disables */ + long *writebytecountp /* return number of bytes written */ +); +#endif diff --git a/Source/CTest/Curl/url.c b/Source/CTest/Curl/url.c new file mode 100644 index 0000000..d830fb6 --- /dev/null +++ b/Source/CTest/Curl/url.c @@ -0,0 +1,2379 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* -- WIN32 approved -- */ + +#include "setup.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <sys/time.h> +#include <sys/resource.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#ifdef VMS +#include <in.h> +#include <inet.h> +#endif + +#ifndef HAVE_SELECT +#error "We can't compile without select() support!" +#endif +#ifndef HAVE_SOCKET +#error "We can't compile without socket() support!" +#endif + +#endif + +#include "urldata.h" +#include "netrc.h" + +#include "formdata.h" +#include "base64.h" +#include "ssluse.h" +#include "hostip.h" +#include "if2ip.h" +#include "transfer.h" +#include "sendf.h" +#include "getpass.h" +#include "progress.h" +#include "cookie.h" +#include "strequal.h" +#include "escape.h" +#include "strtok.h" + +/* And now for the protocols */ +#include "ftp.h" +#include "dict.h" +#include "telnet.h" +#include "http.h" +#include "file.h" +#include "ldap.h" +#include "url.h" +#include "connect.h" + +#include <curl/types.h> + +#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) +#include "inet_ntoa_r.h" +#endif + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#ifdef KRB4 +#include "security.h" +#endif +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* Local static prototypes */ +static int ConnectionKillOne(struct SessionHandle *data); +static bool ConnectionExists(struct SessionHandle *data, + struct connectdata *needle, + struct connectdata **usethis); +static unsigned int ConnectionStore(struct SessionHandle *data, + struct connectdata *conn); + + +#if !defined(WIN32)||defined(__CYGWIN32__) +#ifndef RETSIGTYPE +#define RETSIGTYPE void +#endif +static +RETSIGTYPE alarmfunc(int signal) +{ + /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */ + (void)signal; + return; +} +#endif + + +/* + * This is the internal function curl_easy_cleanup() calls. This should + * cleanup and free all resources associated with this sessionhandle. + * + * NOTE: if we ever add something that attempts to write to a socket or + * similar here, we must ignore SIGPIPE first. It is currently only done + * when curl_easy_perform() is invoked. + */ + +CURLcode Curl_close(struct SessionHandle *data) +{ + /* Loop through all open connections and kill them one by one */ + while(-1 != ConnectionKillOne(data)); + +#ifdef USE_SSLEAY + /* Close down all open SSL info and sessions */ + Curl_SSL_Close_All(data); +#endif + + if(data->state.auth_host) + free(data->state.auth_host); + + if(data->change.proxy_alloc) + free(data->change.proxy); + + if(data->change.referer_alloc) + free(data->change.referer); + + if(data->change.url_alloc) + free(data->change.url); + + if(data->state.headerbuff) + free(data->state.headerbuff); + + if(data->set.cookiejar) + /* we have a "destination" for all the cookies to get dumped to */ + Curl_cookie_output(data->cookies, data->set.cookiejar); + + Curl_cookie_cleanup(data->cookies); + + /* free the connection cache */ + free(data->state.connects); + + if(data->info.contenttype) + free(data->info.contenttype); + + free(data); + return CURLE_OK; +} + +static +int my_getpass(void *clientp, const char *prompt, char* buffer, int buflen ) +{ + char *retbuf; + clientp=NULL; /* prevent compiler warning */ + + retbuf = getpass_r(prompt, buffer, buflen); + if(NULL == retbuf) + return 1; + else + return 0; /* success */ +} + + +CURLcode Curl_open(struct SessionHandle **curl) +{ + /* We don't yet support specifying the URL at this point */ + struct SessionHandle *data; + /* Very simple start-up: alloc the struct, init it with zeroes and return */ + data = (struct SessionHandle *)malloc(sizeof(struct SessionHandle)); + if(!data) + /* this is a very serious error */ + return CURLE_OUT_OF_MEMORY; + + memset(data, 0, sizeof(struct SessionHandle)); + + /* We do some initial setup here, all those fields that can't be just 0 */ + + data->state.headerbuff=(char*)malloc(HEADERSIZE); + if(!data->state.headerbuff) { + free(data); /* free the memory again */ + return CURLE_OUT_OF_MEMORY; + } + + data->state.headersize=HEADERSIZE; + + data->set.out = stdout; /* default output to stdout */ + data->set.in = stdin; /* default input from stdin */ + data->set.err = stderr; /* default stderr to stderr */ + + /* use fwrite as default function to store output */ + data->set.fwrite = (curl_write_callback)fwrite; + + /* use fread as default function to read input */ + data->set.fread = (curl_read_callback)fread; + + /* set the default passwd function */ + data->set.fpasswd = my_getpass; + + data->set.infilesize = -1; /* we don't know any size */ + + data->state.current_speed = -1; /* init to negative == impossible */ + + data->set.httpreq = HTTPREQ_GET; /* Default HTTP request */ + data->set.ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ + + data->set.dns_cache_timeout = 60; /* Timeout every 60 seconds by default */ + + /* make libcurl quiet by default: */ + data->set.hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */ + data->progress.flags |= PGRS_HIDE; + + /* Set the default size of the SSL session ID cache */ + data->set.ssl.numsessions = 5; + + /* create an array with connection data struct pointers */ + data->state.numconnects = 5; /* hard-coded right now */ + data->state.connects = (struct connectdata **) + malloc(sizeof(struct connectdata *) * data->state.numconnects); + + if(!data->state.connects) { + free(data); + return CURLE_OUT_OF_MEMORY; + } + + memset(data->state.connects, 0, + sizeof(struct connectdata *)*data->state.numconnects); + + *curl = data; + + return CURLE_OK; +} + +CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) +{ + va_list param; + char *cookiefile; + + va_start(param, option); + + switch(option) { + case CURLOPT_DNS_CACHE_TIMEOUT: + data->set.dns_cache_timeout = va_arg(param, int); + break; + case CURLOPT_DNS_USE_GLOBAL_CACHE: + { + int use_cache = va_arg(param, int); + if (use_cache) { + Curl_global_host_cache_init(); + } + + data->set.global_dns_cache = use_cache; + } + break; + case CURLOPT_SSL_CIPHER_LIST: + /* set a list of cipher we want to use in the SSL connection */ + data->set.ssl.cipher_list = va_arg(param, char *); + break; + + case CURLOPT_RANDOM_FILE: + /* + * This is the path name to a file that contains random data to seed + * the random SSL stuff with. The file is only used for reading. + */ + data->set.ssl.random_file = va_arg(param, char *); + break; + case CURLOPT_EGDSOCKET: + /* + * The Entropy Gathering Daemon socket pathname + */ + data->set.ssl.egdsocket = va_arg(param, char *); + break; + case CURLOPT_MAXCONNECTS: + /* + * Set the absolute number of maximum simultaneous alive connection that + * libcurl is allowed to have. + */ + { + long newconnects= va_arg(param, long); + struct connectdata **newptr; + + if(newconnects < data->state.numconnects) { + /* Since this number is *decreased* from the existing number, we must + close the possibly open connections that live on the indexes that + are being removed! */ + int i; + for(i=newconnects; i< data->state.numconnects; i++) + Curl_disconnect(data->state.connects[i]); + } + if(newconnects) { + newptr= (struct connectdata **) + realloc(data->state.connects, + sizeof(struct connectdata *) * newconnects); + if(!newptr) + /* we closed a few connections in vain, but so what? */ + return CURLE_OUT_OF_MEMORY; + data->state.connects = newptr; + data->state.numconnects = newconnects; + } + else { + /* zero makes NO cache at all */ + if(data->state.connects) + free(data->state.connects); + data->state.connects=NULL; + data->state.numconnects=0; + } + } + break; + case CURLOPT_FORBID_REUSE: + /* + * When this transfer is done, it must not be left to be reused by a + * subsequent transfer but shall be closed immediately. + */ + data->set.reuse_forbid = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_FRESH_CONNECT: + /* + * This transfer shall not use a previously cached connection but + * should be made with a fresh new connect! + */ + data->set.reuse_fresh = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_VERBOSE: + /* + * Verbose means infof() calls that give a lot of information about + * the connection and transfer procedures as well as internal choices. + */ + data->set.verbose = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_HEADER: + /* + * Set to include the header in the general data output stream. + */ + data->set.http_include_header = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_NOPROGRESS: + /* + * Shut off the internal supported progress meter + */ + data->set.hide_progress = va_arg(param, long)?TRUE:FALSE; + if(data->set.hide_progress) + data->progress.flags |= PGRS_HIDE; + else + data->progress.flags &= ~PGRS_HIDE; + break; + case CURLOPT_NOBODY: + /* + * Do not include the body part in the output data stream. + */ + data->set.no_body = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_FAILONERROR: + /* + * Don't output the >=300 error code HTML-page, but instead only + * return error. + */ + data->set.http_fail_on_error = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_UPLOAD: + /* + * We want to sent data to the remote host + */ + data->set.upload = va_arg(param, long)?TRUE:FALSE; + if(data->set.upload) + /* If this is HTTP, PUT is what's needed to "upload" */ + data->set.httpreq = HTTPREQ_PUT; + break; + case CURLOPT_FILETIME: + /* + * Try to get the file time of the remote document. The time will + * later (possibly) become available using curl_easy_getinfo(). + */ + data->set.get_filetime = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_FTPLISTONLY: + /* + * An FTP option that changes the command to one that asks for a list + * only, no file info details. + */ + data->set.ftp_list_only = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_FTPAPPEND: + /* + * We want to upload and append to an existing (FTP) file. + */ + data->set.ftp_append = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_NETRC: + /* + * Parse the $HOME/.netrc file + */ + data->set.use_netrc = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_FOLLOWLOCATION: + /* + * Follow Location: header hints on a HTTP-server. + */ + data->set.http_follow_location = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_HTTP_VERSION: + /* + * This sets a requested HTTP version to be used. The value is one of + * the listed enums in curl/curl.h. + */ + data->set.httpversion = va_arg(param, long); + break; + case CURLOPT_TRANSFERTEXT: + /* + * This option was previously named 'FTPASCII'. Renamed to work with + * more protocols than merely FTP. + * + * Transfer using ASCII (instead of BINARY). + */ + data->set.ftp_ascii = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_PUT: + /* + * Use the HTTP PUT request to transfer data if this is TRUE. If this is + * FALSE, don't set the httpreq. We can't know what to revert it to! + */ + if(va_arg(param, long)) + data->set.httpreq = HTTPREQ_PUT; + break; + case CURLOPT_TIMECONDITION: + /* + * Set HTTP time condition. This must be one of the defines in the + * curl/curl.h header file. + */ + data->set.timecondition = va_arg(param, long); + break; + case CURLOPT_TIMEVALUE: + /* + * This is the value to compare with the remote document with the + * method set with CURLOPT_TIMECONDITION + */ + data->set.timevalue = va_arg(param, long); + break; + case CURLOPT_SSLVERSION: + /* + * Set explicit SSL version to try to connect with, as some SSL + * implementations are lame. + */ + data->set.ssl.version = va_arg(param, long); + break; + + case CURLOPT_COOKIEFILE: + /* + * Set cookie file to read and parse. Can be used multiple times. + */ + cookiefile = (char *)va_arg(param, void *); + if(cookiefile) + data->cookies = Curl_cookie_init(cookiefile, data->cookies); + break; + + case CURLOPT_COOKIEJAR: + /* + * Set cookie file name to dump all cookies to when we're done. + */ + data->set.cookiejar = (char *)va_arg(param, void *); + + /* + * Activate the cookie parser. This may or may not already + * have been made. + */ + data->cookies = Curl_cookie_init(NULL, data->cookies); + break; + case CURLOPT_WRITEHEADER: + /* + * Custom pointer to pass the header write callback function + */ + data->set.writeheader = (void *)va_arg(param, void *); + break; + case CURLOPT_COOKIE: + /* + * Cookie string to send to the remote server in the request. + */ + data->set.cookie = va_arg(param, char *); + break; + case CURLOPT_ERRORBUFFER: + /* + * Error buffer provided by the caller to get the human readable + * error string in. + */ + data->set.errorbuffer = va_arg(param, char *); + break; + case CURLOPT_FILE: + /* + * FILE pointer to write to or include in the data write callback + */ + data->set.out = va_arg(param, FILE *); + break; + case CURLOPT_FTPPORT: + /* + * Use FTP PORT, this also specifies which IP address to use + */ + data->set.ftpport = va_arg(param, char *); + data->set.ftp_use_port = data->set.ftpport?1:0; + break; + + case CURLOPT_FTP_USE_EPSV: + data->set.ftp_use_epsv = va_arg(param, long)?TRUE:FALSE; + break; + + case CURLOPT_HTTPHEADER: + /* + * Set a list with HTTP headers to use (or replace internals with) + */ + data->set.headers = va_arg(param, struct curl_slist *); + break; + case CURLOPT_CUSTOMREQUEST: + /* + * Set a custom string to use as request + */ + data->set.customrequest = va_arg(param, char *); + + /* we don't set + data->set.httpreq = HTTPREQ_CUSTOM; + here, we continue as if we were using the already set type + and this just changes the actual request keyword */ + break; + case CURLOPT_HTTPPOST: + /* + * Set to make us do HTTP POST + */ + data->set.httppost = va_arg(param, struct HttpPost *); + if(data->set.httppost) + data->set.httpreq = HTTPREQ_POST_FORM; + break; + + case CURLOPT_HTTPGET: + /* + * Set to force us do HTTP GET + */ + if(va_arg(param, long)) { + data->set.httpreq = HTTPREQ_GET; + data->set.upload = FALSE; /* switch off upload */ + } + break; + + case CURLOPT_INFILE: + /* + * FILE pointer to read the file to be uploaded from. Or possibly + * used as argument to the read callback. + */ + data->set.in = va_arg(param, FILE *); + break; + case CURLOPT_INFILESIZE: + /* + * If known, this should inform curl about the file size of the + * to-be-uploaded file. + */ + data->set.infilesize = va_arg(param, long); + break; + case CURLOPT_LOW_SPEED_LIMIT: + /* + * The low speed limit that if transfers are below this for + * CURLOPT_LOW_SPEED_TIME, the transfer is aborted. + */ + data->set.low_speed_limit=va_arg(param, long); + break; + case CURLOPT_LOW_SPEED_TIME: + /* + * The low speed time that if transfers are below the set + * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted. + */ + data->set.low_speed_time=va_arg(param, long); + break; + case CURLOPT_URL: + /* + * The URL to fetch. + */ + if(data->change.url_alloc) { + /* the already set URL is allocated, free it first! */ + free(data->change.url); + data->change.url_alloc=FALSE; + } + data->set.set_url = va_arg(param, char *); + data->change.url = data->set.set_url; + break; + case CURLOPT_PORT: + /* + * The port number to use when getting the URL + */ + data->set.use_port = va_arg(param, long); + break; + case CURLOPT_POST: + /* Does this option serve a purpose anymore? */ + + if(va_arg(param, long)) + data->set.httpreq = HTTPREQ_POST; + break; + case CURLOPT_POSTFIELDS: + /* + * A string with POST data. Makes curl HTTP POST. + */ + data->set.postfields = va_arg(param, char *); + if(data->set.postfields) + data->set.httpreq = HTTPREQ_POST; + break; + case CURLOPT_POSTFIELDSIZE: + /* + * The size of the POSTFIELD data, if curl should now do a strlen + * to find out. Enables binary posts. + */ + data->set.postfieldsize = va_arg(param, long); + break; + case CURLOPT_REFERER: + /* + * String to set in the HTTP Referer: field. + */ + if(data->change.referer_alloc) { + free(data->change.referer); + data->change.referer_alloc = FALSE; + } + data->set.set_referer = va_arg(param, char *); + data->change.referer = data->set.set_referer; + break; + case CURLOPT_AUTOREFERER: + /* + * Switch on automatic referer that gets set if curl follows locations. + */ + data->set.http_auto_referer = va_arg(param, long)?1:0; + break; + case CURLOPT_PROXY: + /* + * Set proxy server:port to use as HTTP proxy + */ + if(data->change.proxy_alloc) { + /* + * The already set string is allocated, free that first + */ + data->change.proxy_alloc=FALSE;; + free(data->change.proxy); + } + data->set.set_proxy = va_arg(param, char *); + data->change.proxy = data->set.set_proxy; + break; + case CURLOPT_HTTPPROXYTUNNEL: + /* + * Tunnel operations through the proxy instead of normal proxy use + */ + data->set.tunnel_thru_httpproxy = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_PROXYPORT: + /* + * Explicitly set HTTP proxy port number. + */ + data->set.proxyport = va_arg(param, long); + break; + case CURLOPT_TIMEOUT: + /* + * The maximum time you allow curl to use for a single transfer + * operation. + */ + data->set.timeout = va_arg(param, long); + break; + case CURLOPT_CONNECTTIMEOUT: + /* + * The maximum time you allow curl to use to connect. + */ + data->set.connecttimeout = va_arg(param, long); + break; + case CURLOPT_MAXREDIRS: + /* + * The maximum amount of hops you allow curl to follow Location: + * headers. This should mostly be used to detect never-ending loops. + */ + data->set.maxredirs = va_arg(param, long); + break; + case CURLOPT_USERAGENT: + /* + * String to use in the HTTP User-Agent field + */ + data->set.useragent = va_arg(param, char *); + break; + case CURLOPT_USERPWD: + /* + * user:password to use in the operation + */ + data->set.userpwd = va_arg(param, char *); + break; + case CURLOPT_POSTQUOTE: + /* + * List of RAW FTP commands to use after a transfer + */ + data->set.postquote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_PREQUOTE: + /* + * List of RAW FTP commands to use prior to RETR (Wesley Laxton) + */ + data->set.prequote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_QUOTE: + /* + * List of RAW FTP commands to use before a transfer + */ + data->set.quote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_PROGRESSFUNCTION: + /* + * Progress callback function + */ + data->set.fprogress = va_arg(param, curl_progress_callback); + data->progress.callback = TRUE; /* no longer internal */ + break; + case CURLOPT_PROGRESSDATA: + /* + * Custom client data to pass to the progress callback + */ + data->set.progress_client = va_arg(param, void *); + break; + case CURLOPT_PASSWDFUNCTION: + /* + * Password prompt callback + */ + data->set.fpasswd = va_arg(param, curl_passwd_callback); + break; + case CURLOPT_PASSWDDATA: + /* + * Custom client data to pass to the password callback + */ + data->set.passwd_client = va_arg(param, void *); + break; + case CURLOPT_PROXYUSERPWD: + /* + * user:password needed to use the proxy + */ + data->set.proxyuserpwd = va_arg(param, char *); + break; + case CURLOPT_RANGE: + /* + * What range of the file you want to transfer + */ + data->set.set_range = va_arg(param, char *); + break; + case CURLOPT_RESUME_FROM: + /* + * Resume transfer at the give file position + */ + data->set.set_resume_from = va_arg(param, long); + break; + case CURLOPT_STDERR: + /* + * Set to a FILE * that should receive all error writes. This + * defaults to stderr for normal operations. + */ + data->set.err = va_arg(param, FILE *); + break; + case CURLOPT_HEADERFUNCTION: + /* + * Set header write callback + */ + data->set.fwrite_header = va_arg(param, curl_write_callback); + break; + case CURLOPT_WRITEFUNCTION: + /* + * Set data write callback + */ + data->set.fwrite = va_arg(param, curl_write_callback); + break; + case CURLOPT_READFUNCTION: + /* + * Read data callback + */ + data->set.fread = va_arg(param, curl_read_callback); + break; + case CURLOPT_SSLCERT: + /* + * String that holds file name of the SSL certificate to use + */ + data->set.cert = va_arg(param, char *); + break; + case CURLOPT_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use + */ + data->set.cert_type = va_arg(param, char *); + break; + case CURLOPT_SSLKEY: + /* + * String that holds file name of the SSL certificate to use + */ + data->set.key = va_arg(param, char *); + break; + case CURLOPT_SSLKEYTYPE: + /* + * String that holds file type of the SSL certificate to use + */ + data->set.key_type = va_arg(param, char *); + break; + case CURLOPT_SSLKEYPASSWD: + /* + * String that holds the SSL private key password. + */ + data->set.key_passwd = va_arg(param, char *); + break; + case CURLOPT_SSLENGINE: + /* + * String that holds the SSL crypto engine. + */ +#ifdef HAVE_OPENSSL_ENGINE_H + { + const char *cpTemp = va_arg(param, char *); + ENGINE *e; + if (cpTemp && cpTemp[0]) { + e = ENGINE_by_id(cpTemp); + if (e) { + if (data->engine) { + ENGINE_free(data->engine); + } + data->engine = e; + } + else { + failf(data, "SSL Engine '%s' not found", cpTemp); + return CURLE_SSL_ENGINE_NOTFOUND; + } + } + } +#else + return CURLE_SSL_ENGINE_NOTFOUND; +#endif + break; + case CURLOPT_SSLENGINE_DEFAULT: + /* + * flag to set engine as default. + */ +#ifdef HAVE_OPENSSL_ENGINE_H + if (data->engine) { + if (ENGINE_set_default(data->engine, ENGINE_METHOD_ALL) > 0) { +#ifdef DEBUG + fprintf(stderr,"set default crypto engine\n"); +#endif + } + else { +#ifdef DEBUG + failf(data, "set default crypto engine failed"); +#endif + return CURLE_SSL_ENGINE_SETFAILED; + } + } +#endif + break; + case CURLOPT_CRLF: + /* + * Kludgy option to enable CRLF convertions. Subject for removal. + */ + data->set.crlf = va_arg(param, long)?TRUE:FALSE; + break; + case CURLOPT_INTERFACE: + /* + * Set what interface to bind to when performing an operation and thus + * what from-IP your connection will use. + */ + data->set.device = va_arg(param, char *); + break; + case CURLOPT_KRB4LEVEL: + /* + * A string that defines the krb4 security level. + */ + data->set.krb4_level = va_arg(param, char *); + data->set.krb4=data->set.krb4_level?TRUE:FALSE; + break; + case CURLOPT_SSL_VERIFYPEER: + /* + * Enable peer SSL verifying. + */ + data->set.ssl.verifypeer = va_arg(param, long); + break; + case CURLOPT_SSL_VERIFYHOST: + /* + * Enable verification of the CN contained in the peer certificate + */ + data->set.ssl.verifyhost = va_arg(param, long); + break; + case CURLOPT_CAINFO: + /* + * Set CA info for SSL connection. Specify file name of the CA certificate + */ + data->set.ssl.CAfile = va_arg(param, char *); + data->set.ssl.CApath = NULL; /*This does not work on windows.*/ + break; + case CURLOPT_TELNETOPTIONS: + /* + * Set a linked list of telnet options + */ + data->set.telnet_options = va_arg(param, struct curl_slist *); + break; + default: + /* unknown tag and its companion, just ignore: */ + return CURLE_READ_ERROR; /* correct this */ + } + return CURLE_OK; +} + +CURLcode Curl_disconnect(struct connectdata *conn) +{ + if(!conn) + return CURLE_OK; /* this is closed and fine already */ + + /* + * The range string is usually freed in curl_done(), but we might + * get here *instead* if we fail prematurely. Thus we need to be able + * to free this resource here as well. + */ + if(conn->bits.rangestringalloc) { + free(conn->range); + conn->bits.rangestringalloc = FALSE; + } + + if(-1 != conn->connectindex) { + /* unlink ourselves! */ + infof(conn->data, "Closing connection #%d\n", conn->connectindex); + conn->data->state.connects[conn->connectindex] = NULL; + } + + if(conn->curl_disconnect) + /* This is set if protocol-specific cleanups should be made */ + conn->curl_disconnect(conn); + + if(conn->proto.generic) + free(conn->proto.generic); + + if(conn->newurl) + free(conn->newurl); + + if(conn->path) /* the URL path part */ + free(conn->path); + +#ifdef USE_SSLEAY + Curl_SSL_Close(conn); +#endif /* USE_SSLEAY */ + + /* close possibly still open sockets */ + if(-1 != conn->secondarysocket) + sclose(conn->secondarysocket); + if(-1 != conn->firstsocket) + sclose(conn->firstsocket); + + if(conn->allocptr.proxyuserpwd) + free(conn->allocptr.proxyuserpwd); + if(conn->allocptr.uagent) + free(conn->allocptr.uagent); + if(conn->allocptr.userpwd) + free(conn->allocptr.userpwd); + if(conn->allocptr.rangeline) + free(conn->allocptr.rangeline); + if(conn->allocptr.ref) + free(conn->allocptr.ref); + if(conn->allocptr.cookie) + free(conn->allocptr.cookie); + if(conn->allocptr.host) + free(conn->allocptr.host); + + if(conn->proxyhost) + free(conn->proxyhost); + + free(conn); /* free all the connection oriented data */ + + return CURLE_OK; +} + +/* + * This function should return TRUE if the socket is to be assumed to + * be dead. Most commonly this happens when the server has closed the + * connection due to inactivity. + */ +static bool SocketIsDead(int sock) +{ + int sval; + bool ret_val = TRUE; + fd_set check_set; + struct timeval to; + + FD_ZERO(&check_set); + FD_SET(sock,&check_set); + + to.tv_sec = 0; + to.tv_usec = 1; + + sval = select(sock + 1, &check_set, 0, 0, &to); + if(sval == 0) + /* timeout */ + ret_val = FALSE; + + return ret_val; +} + +/* + * Given one filled in connection struct (named needle), this function should + * detect if there already is one that have all the significant details + * exactly the same and thus should be used instead. + */ +static bool +ConnectionExists(struct SessionHandle *data, + struct connectdata *needle, + struct connectdata **usethis) +{ + long i; + struct connectdata *check; + + for(i=0; i< data->state.numconnects; i++) { + /* + * Note that if we use a HTTP proxy, we check connections to that + * proxy and not to the actual remote server. + */ + check = data->state.connects[i]; + if(!check) + /* NULL pointer means not filled-in entry */ + continue; + if(!needle->bits.httpproxy || needle->protocol&PROT_SSL) { + /* The requested connection does not use a HTTP proxy or it + uses SSL. */ + + if(!(needle->protocol&PROT_SSL) && check->bits.httpproxy) + /* we don't do SSL but the cached connection has a proxy, + then don't match this */ + continue; + + if(strequal(needle->protostr, check->protostr) && + strequal(needle->name, check->name) && + (needle->remote_port == check->remote_port) ) { + bool dead; + if(strequal(needle->protostr, "FTP")) { + /* This is FTP, verify that we're using the same name and + password as well */ + if(!strequal(needle->data->state.user, check->proto.ftp->user) || + !strequal(needle->data->state.passwd, check->proto.ftp->passwd)) { + /* one of them was different */ + continue; + } + } + dead = SocketIsDead(check->firstsocket); + if(dead) { + /* + * Even though the connection seems to have passed away, we could + * still make an effort to get the name information, as we intend to + * connect to the same host again. + * + * This is now subject to discussion. What do you think? + */ + infof(data, "Connection %d seems to be dead!\n", i); + Curl_disconnect(check); /* disconnect resources */ + data->state.connects[i]=NULL; /* nothing here */ + + /* There's no need to continue search, because we only store + one connection for each unique set of identifiers */ + return FALSE; + } + + *usethis = check; + return TRUE; /* yes, we found one to use! */ + + } + } + else { /* The requested needle connection is using a proxy, + is the checked one using the same? */ + if(check->bits.httpproxy && + strequal(needle->proxyhost, check->proxyhost) && + needle->port == check->port) { + /* This is the same proxy connection, use it! */ + *usethis = check; + return TRUE; + } + } + } + return FALSE; /* no matching connecting exists */ +} + +/* + * This function frees/closes a connection in the connection cache. This + * should take the previously set policy into account when deciding which + * of the connections to kill. + */ +static int +ConnectionKillOne(struct SessionHandle *data) +{ + long i; + struct connectdata *conn; + int highscore=-1; + int connindex=-1; + int score; + CURLcode result; + struct timeval now; + + now = Curl_tvnow(); + + for(i=0; i< data->state.numconnects; i++) { + conn = data->state.connects[i]; + + if(!conn) + continue; + + /* + * By using the set policy, we score each connection. + */ + switch(data->set.closepolicy) { + case CURLCLOSEPOLICY_LEAST_RECENTLY_USED: + default: + /* + * Set higher score for the age passed since the connection + * was used. + */ + score = Curl_tvdiff(now, conn->now); + break; + case CURLCLOSEPOLICY_OLDEST: + /* + * Set higher score for the age passed since the connection + * was created. + */ + score = Curl_tvdiff(now, conn->created); + break; + } + + if(score > highscore) { + highscore = score; + connindex = i; + } + } + if(connindex >= 0) { + + /* the winner gets the honour of being disconnected */ + result = Curl_disconnect(data->state.connects[connindex]); + + /* clean the array entry */ + data->state.connects[connindex] = NULL; + } + + return connindex; /* return the available index or -1 */ +} + +/* + * The given input connection struct pointer is to be stored. If the "cache" + * is already full, we must clean out the most suitable using the previously + * set policy. + * + * The given connection should be unique. That must've been checked prior to + * this call. + */ +static unsigned int +ConnectionStore(struct SessionHandle *data, + struct connectdata *conn) +{ + long i; + for(i=0; i< data->state.numconnects; i++) { + if(!data->state.connects[i]) + break; + } + if(i == data->state.numconnects) { + /* there was no room available, kill one */ + i = ConnectionKillOne(data); + infof(data, "Connection (#%d) was killed to make room\n", i); + } + + if(-1 != i) { + /* only do this if a true index was returned, if -1 was returned there + is no room in the cache for an unknown reason and we cannot store + this there. */ + data->state.connects[i] = conn; /* fill in this */ + conn->connectindex = i; /* make the child know where the pointer to this + particular data is stored */ + } + return i; +} + +static CURLcode ConnectPlease(struct connectdata *conn) +{ + CURLcode result; + Curl_ipconnect *addr; + + /************************************************************* + * Connect to server/proxy + *************************************************************/ + result= Curl_connecthost(conn, + conn->hostaddr, + conn->port, + &conn->firstsocket, + &addr); + if(CURLE_OK == result) { + /* All is cool, then we store the current information from the hostaddr + struct to the serv_addr, as it might be needed later. The address + returned from the function above is crucial here. */ +#ifdef ENABLE_IPV6 + conn->serv_addr = addr; +#else + memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr)); + memcpy((char *)&(conn->serv_addr.sin_addr), + (struct in_addr *)addr, sizeof(struct in_addr)); + conn->serv_addr.sin_family = conn->hostaddr->h_addrtype; + conn->serv_addr.sin_port = htons(conn->port); +#endif + } + + return result; +} + +static CURLcode CreateConnection(struct SessionHandle *data, + struct connectdata **in_connect) +{ + char *tmp; + char *buf; + CURLcode result=CURLE_OK; + char resumerange[40]=""; + struct connectdata *conn; + struct connectdata *conn_temp; + char endbracket; + int urllen; +#ifdef HAVE_INET_NTOA_R + char ntoa_buf[64]; +#endif +#ifdef HAVE_ALARM + unsigned int prev_alarm; +#endif + +#ifdef HAVE_SIGACTION + struct sigaction keep_sigact; /* store the old struct here */ + bool keep_copysig; /* did copy it? */ +#else +#ifdef HAVE_SIGNAL + void *keep_sigact; /* store the old handler here */ +#endif +#endif + + /************************************************************* + * Check input data + *************************************************************/ + + if(!data->change.url) + return CURLE_URL_MALFORMAT; + + /* First, split up the current URL in parts so that we can use the + parts for checking against the already present connections. In order + to not have to modify everything at once, we allocate a temporary + connection data struct and fill in for comparison purposes. */ + + conn = (struct connectdata *)malloc(sizeof(struct connectdata)); + if(!conn) { + *in_connect = NULL; /* clear the pointer */ + return CURLE_OUT_OF_MEMORY; + } + /* We must set the return variable as soon as possible, so that our + parent can cleanup any possible allocs we may have done before + any failure */ + *in_connect = conn; + + /* we have to init the struct */ + memset(conn, 0, sizeof(struct connectdata)); + + /* and we setup a few fields in case we end up actually using this struct */ + conn->data = data; /* remember our daddy */ + conn->firstsocket = -1; /* no file descriptor */ + conn->secondarysocket = -1; /* no file descriptor */ + conn->connectindex = -1; /* no index */ + conn->bits.httpproxy = data->change.proxy?TRUE:FALSE; /* proxy-or-not */ + conn->bits.use_range = data->set.set_range?TRUE:FALSE; /* range status */ + conn->range = data->set.set_range; /* clone the range setting */ + conn->resume_from = data->set.set_resume_from; /* inherite resume_from */ + + /* Default protocol-independent behavior doesn't support persistant + connections, so we set this to force-close. Protocols that support + this need to set this to FALSE in their "curl_do" functions. */ + conn->bits.close = TRUE; + + /* inherite initial knowledge from the data struct */ + conn->bits.user_passwd = data->set.userpwd?1:0; + conn->bits.proxy_user_passwd = data->set.proxyuserpwd?1:0; + + /* maxdownload must be -1 on init, as 0 is a valid value! */ + conn->maxdownload = -1; /* might have been used previously! */ + + /* Store creation time to help future close decision making */ + conn->created = Curl_tvnow(); + + + /*********************************************************** + * We need to allocate memory to store the path in. We get the size of the + * full URL to be sure, and we need to make it at least 256 bytes since + * other parts of the code will rely on this fact + ***********************************************************/ +#define LEAST_PATH_ALLOC 256 + urllen=strlen(data->change.url); + if(urllen < LEAST_PATH_ALLOC) + urllen=LEAST_PATH_ALLOC; + + conn->path=(char *)malloc(urllen); + if(NULL == conn->path) + return CURLE_OUT_OF_MEMORY; /* really bad error */ + + /************************************************************* + * Parse the URL. + * + * We need to parse the url even when using the proxy, because we will need + * the hostname and port in case we are trying to SSL connect through the + * proxy -- and we don't know if we will need to use SSL until we parse the + * url ... + ************************************************************/ + if((2 == sscanf(data->change.url, "%64[^:]://%[^\n]", + conn->protostr, + conn->path)) && strequal(conn->protostr, "file")) { + /* + * we deal with file://<host>/<path> differently since it supports no + * hostname other than "localhost" and "127.0.0.1", which is unique among + * the URL protocols specified in RFC 1738 + */ + if(conn->path[0] != '/') { + /* the URL included a host name, we ignore host names in file:// URLs + as the standards don't define what to do with them */ + char *ptr=strchr(conn->path, '/'); + if(ptr) { + /* there was a slash present + + RFC1738 (section 3.1, page 5) says: + + The rest of the locator consists of data specific to the scheme, + and is known as the "url-path". It supplies the details of how the + specified resource can be accessed. Note that the "/" between the + host (or port) and the url-path is NOT part of the url-path. + + As most agents use file://localhost/foo to get '/foo' although the + slash preceeding foo is a separator and not a slash for the path, + a URL as file://localhost//foo must be valid as well, to refer to + the same file with an absolute path. + */ + + if(ptr[1] && ('/' == ptr[1])) + /* if there was two slashes, we skip the first one as that is then + used truly as a separator */ + ptr++; + + strcpy(conn->path, ptr); + } + } + + strcpy(conn->protostr, "file"); /* store protocol string lowercase */ + } + else { + /* Set default host and default path */ + strcpy(conn->gname, "curl.haxx.se"); + strcpy(conn->path, "/"); + + if (2 > sscanf(data->change.url, + "%64[^\n:]://%512[^\n/]%[^\n]", + conn->protostr, conn->gname, conn->path)) { + + /* + * The URL was badly formatted, let's try the browser-style _without_ + * protocol specified like 'http://'. + */ + if((1 > sscanf(data->change.url, "%512[^\n/]%[^\n]", + conn->gname, conn->path)) ) { + /* + * We couldn't even get this format. + */ + failf(data, "<url> malformed"); + return CURLE_URL_MALFORMAT; + } + + /* + * Since there was no protocol part specified, we guess what protocol it + * is based on the first letters of the server name. + */ + + if(strnequal(conn->gname, "FTP", 3)) { + strcpy(conn->protostr, "ftp"); + } + else if(strnequal(conn->gname, "GOPHER", 6)) + strcpy(conn->protostr, "gopher"); +#ifdef USE_SSLEAY + else if(strnequal(conn->gname, "HTTPS", 5)) + strcpy(conn->protostr, "https"); + else if(strnequal(conn->gname, "FTPS", 4)) + strcpy(conn->protostr, "ftps"); +#endif /* USE_SSLEAY */ + else if(strnequal(conn->gname, "TELNET", 6)) + strcpy(conn->protostr, "telnet"); + else if (strnequal(conn->gname, "DICT", sizeof("DICT")-1)) + strcpy(conn->protostr, "DICT"); + else if (strnequal(conn->gname, "LDAP", sizeof("LDAP")-1)) + strcpy(conn->protostr, "LDAP"); + else { + strcpy(conn->protostr, "http"); + } + + conn->protocol |= PROT_MISSING; /* not given in URL */ + } + } + + buf = data->state.buffer; /* this is our buffer */ + + /************************************************************* + * Take care of user and password authentication stuff + *************************************************************/ + + if(conn->bits.user_passwd && !data->set.use_netrc) { + data->state.user[0] =0; + data->state.passwd[0]=0; + + if(*data->set.userpwd != ':') { + /* the name is given, get user+password */ + sscanf(data->set.userpwd, "%127[^:]:%127[^\n]", + data->state.user, data->state.passwd); + } + else + /* no name given, get the password only */ + sscanf(data->set.userpwd+1, "%127[^\n]", data->state.passwd); + + /* check for password, if no ask for one */ + if( !data->state.passwd[0] ) { + if(!data->set.fpasswd || + data->set.fpasswd(data->set.passwd_client, + "password:", data->state.passwd, + sizeof(data->state.passwd))) + return CURLE_BAD_PASSWORD_ENTERED; + } + } + + /************************************************************* + * Take care of proxy authentication stuff + *************************************************************/ + if(conn->bits.proxy_user_passwd) { + data->state.proxyuser[0] =0; + data->state.proxypasswd[0]=0; + + if(*data->set.proxyuserpwd != ':') { + /* the name is given, get user+password */ + sscanf(data->set.proxyuserpwd, "%127[^:]:%127[^\n]", + data->state.proxyuser, data->state.proxypasswd); + } + else + /* no name given, get the password only */ + sscanf(data->set.proxyuserpwd+1, "%127[^\n]", data->state.proxypasswd); + + /* check for password, if no ask for one */ + if( !data->state.proxypasswd[0] ) { + if(!data->set.fpasswd || + data->set.fpasswd( data->set.passwd_client, + "proxy password:", + data->state.proxypasswd, + sizeof(data->state.proxypasswd))) + return CURLE_BAD_PASSWORD_ENTERED; + } + + } + + /************************************************************* + * Set a few convenience pointers + *************************************************************/ + conn->name = conn->gname; + conn->ppath = conn->path; + conn->hostname = conn->name; + + + /************************************************************* + * Detect what (if any) proxy to use + *************************************************************/ + if(!data->change.proxy) { + /* If proxy was not specified, we check for default proxy environment + * variables, to enable i.e Lynx compliance: + * + * http_proxy=http://some.server.dom:port/ + * https_proxy=http://some.server.dom:port/ + * ftp_proxy=http://some.server.dom:port/ + * gopher_proxy=http://some.server.dom:port/ + * no_proxy=domain1.dom,host.domain2.dom + * (a comma-separated list of hosts which should + * not be proxied, or an asterisk to override + * all proxy variables) + * all_proxy=http://some.server.dom:port/ + * (seems to exist for the CERN www lib. Probably + * the first to check for.) + * + * For compatibility, the all-uppercase versions of these variables are + * checked if the lowercase versions don't exist. + */ + char *no_proxy=NULL; + char *no_proxy_tok_buf; + char *proxy=NULL; + char proxy_env[128]; + + no_proxy=curl_getenv("no_proxy"); + if(!no_proxy) + no_proxy=curl_getenv("NO_PROXY"); + + if(!no_proxy || !strequal("*", no_proxy)) { + /* NO_PROXY wasn't specified or it wasn't just an asterisk */ + char *nope; + + nope=no_proxy?strtok_r(no_proxy, ", ", &no_proxy_tok_buf):NULL; + while(nope) { + if(strlen(nope) <= strlen(conn->name)) { + char *checkn= + conn->name + strlen(conn->name) - strlen(nope); + if(strnequal(nope, checkn, strlen(nope))) { + /* no proxy for this host! */ + break; + } + } + nope=strtok_r(NULL, ", ", &no_proxy_tok_buf); + } + if(!nope) { + /* It was not listed as without proxy */ + char *protop = conn->protostr; + char *envp = proxy_env; + char *prox; + + /* Now, build <protocol>_proxy and check for such a one to use */ + while(*protop) + *envp++ = tolower(*protop++); + + /* append _proxy */ + strcpy(envp, "_proxy"); + + /* read the protocol proxy: */ + prox=curl_getenv(proxy_env); + + /* + * We don't try the uppercase version of HTTP_PROXY because of + * security reasons: + * + * When curl is used in a webserver application + * environment (cgi or php), this environment variable can + * be controlled by the web server user by setting the + * http header 'Proxy:' to some value. + * + * This can cause 'internal' http/ftp requests to be + * arbitrarily redirected by any external attacker. + */ + if(!prox && !strequal("http_proxy", proxy_env)) { + /* There was no lowercase variable, try the uppercase version: */ + for(envp = proxy_env; *envp; envp++) + *envp = toupper(*envp); + prox=curl_getenv(proxy_env); + } + + if(prox && *prox) { /* don't count "" strings */ + proxy = prox; /* use this */ + } + else { + proxy = curl_getenv("all_proxy"); /* default proxy to use */ + if(!proxy) + proxy=curl_getenv("ALL_PROXY"); + } + + if(proxy && *proxy) { + /* we have a proxy here to set */ + data->change.proxy = proxy; + data->change.proxy_alloc=TRUE; /* this needs to be freed later */ + conn->bits.httpproxy = TRUE; + } + } /* if (!nope) - it wasn't specified non-proxy */ + } /* NO_PROXY wasn't specified or '*' */ + if(no_proxy) + free(no_proxy); + } /* if not using proxy */ + + /************************************************************* + * No protocol part in URL was used, add it! + *************************************************************/ + if(conn->protocol&PROT_MISSING) { + /* We're guessing prefixes here and if we're told to use a proxy or if + we're gonna follow a Location: later or... then we need the protocol + part added so that we have a valid URL. */ + char *reurl; + + reurl = aprintf("%s://%s", conn->protostr, data->change.url); + + if(!reurl) + return CURLE_OUT_OF_MEMORY; + + data->change.url = reurl; + data->change.url_alloc = TRUE; /* free this later */ + conn->protocol &= ~PROT_MISSING; /* switch that one off again */ + } + + /************************************************************ + * RESUME on a HTTP page is a tricky business. First, let's just check that + * 'range' isn't used, then set the range parameter and leave the resume as + * it is to inform about this situation for later use. We will then + * "attempt" to resume, and if we're talking to a HTTP/1.1 (or later) + * server, we will get the document resumed. If we talk to a HTTP/1.0 + * server, we just fail since we can't rewind the file writing from within + * this function. + ***********************************************************/ + if(conn->resume_from) { + if(!conn->bits.use_range) { + /* if it already was in use, we just skip this */ + snprintf(resumerange, sizeof(resumerange), "%d-", conn->resume_from); + conn->range=strdup(resumerange); /* tell ourselves to fetch this range */ + conn->bits.rangestringalloc = TRUE; /* mark as allocated */ + conn->bits.use_range = 1; /* switch on range usage */ + } + } + + /************************************************************* + * Setup internals depending on protocol + *************************************************************/ + + if (strequal(conn->protostr, "HTTP")) { + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_HTTP; + conn->remote_port = PORT_HTTP; + conn->protocol |= PROT_HTTP; + conn->curl_do = Curl_http; + conn->curl_done = Curl_http_done; + conn->curl_connect = Curl_http_connect; + } + else if (strequal(conn->protostr, "HTTPS")) { +#ifdef USE_SSLEAY + + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_HTTPS; + conn->remote_port = PORT_HTTPS; + conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL; + + conn->curl_do = Curl_http; + conn->curl_done = Curl_http_done; + conn->curl_connect = Curl_http_connect; + +#else /* USE_SSLEAY */ + failf(data, LIBCURL_NAME + " was built with SSL disabled, https: not supported!"); + return CURLE_UNSUPPORTED_PROTOCOL; +#endif /* !USE_SSLEAY */ + } + else if (strequal(conn->protostr, "GOPHER")) { + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_GOPHER; + conn->remote_port = PORT_GOPHER; + /* Skip /<item-type>/ in path if present */ + if (isdigit((int)conn->path[1])) { + conn->ppath = strchr(&conn->path[1], '/'); + if (conn->ppath == NULL) + conn->ppath = conn->path; + } + conn->protocol |= PROT_GOPHER; + conn->curl_do = Curl_http; + conn->curl_done = Curl_http_done; + } + else if(strequal(conn->protostr, "FTP") || + strequal(conn->protostr, "FTPS")) { + char *type; + + if(strequal(conn->protostr, "FTPS")) { +#ifdef USE_SSLEAY + conn->protocol |= PROT_FTPS|PROT_SSL; +#else + failf(data, LIBCURL_NAME + " was built with SSL disabled, ftps: not supported!"); + return CURLE_UNSUPPORTED_PROTOCOL; +#endif /* !USE_SSLEAY */ + } + + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_FTP; + conn->remote_port = PORT_FTP; + conn->protocol |= PROT_FTP; + + if(data->change.proxy && + !data->set.tunnel_thru_httpproxy) { + /* Unless we have asked to tunnel ftp operations through the proxy, we + switch and use HTTP operations only */ + if(conn->protocol & PROT_FTPS) { + /* FTPS is a hacked protocol and does not work through your + ordinary http proxy! */ + failf(data, "ftps does not work through http proxy!"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + conn->curl_do = Curl_http; + conn->curl_done = Curl_http_done; + } + else { + conn->curl_do = Curl_ftp; + conn->curl_done = Curl_ftp_done; + conn->curl_connect = Curl_ftp_connect; + conn->curl_disconnect = Curl_ftp_disconnect; + } + + conn->ppath++; /* don't include the initial slash */ + + /* FTP URLs support an extension like ";type=<typecode>" that + * we'll try to get now! */ + type=strstr(conn->ppath, ";type="); + if(!type) { + type=strstr(conn->gname, ";type="); + } + if(type) { + char command; + *type=0; + command = toupper(type[6]); + switch(command) { + case 'A': /* ASCII mode */ + data->set.ftp_ascii = 1; + break; + case 'D': /* directory mode */ + data->set.ftp_list_only = 1; + break; + case 'I': /* binary mode */ + default: + /* switch off ASCII */ + data->set.ftp_ascii = 0; + break; + } + } + } + else if(strequal(conn->protostr, "TELNET")) { + /* telnet testing factory */ + conn->protocol |= PROT_TELNET; + + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port: PORT_TELNET; + conn->remote_port = PORT_TELNET; + conn->curl_do = Curl_telnet; + conn->curl_done = Curl_telnet_done; + } + else if (strequal(conn->protostr, "DICT")) { + conn->protocol |= PROT_DICT; + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_DICT; + conn->remote_port = PORT_DICT; + conn->curl_do = Curl_dict; + conn->curl_done = NULL; /* no DICT-specific done */ + } + else if (strequal(conn->protostr, "LDAP")) { + conn->protocol |= PROT_LDAP; + conn->port = (data->set.use_port && data->state.allow_port)? + data->set.use_port:PORT_LDAP; + conn->remote_port = PORT_LDAP; + conn->curl_do = Curl_ldap; + conn->curl_done = NULL; /* no LDAP-specific done */ + } + else if (strequal(conn->protostr, "FILE")) { + conn->protocol |= PROT_FILE; + + conn->curl_do = Curl_file; + /* no done() function */ + + /* anyway, this is supposed to be the connect function so we better + at least check that the file is present here! */ + result = Curl_file_connect(conn); + + /* Setup a "faked" transfer that'll do nothing */ + if(CURLE_OK == result) { + result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */ + -1, NULL); /* no upload */ + } + + return result; + } + else { + /* We fell through all checks and thus we don't support the specified + protocol */ + failf(data, "Unsupported protocol: %s", conn->protostr); + return CURLE_UNSUPPORTED_PROTOCOL; + } + + /************************************************************* + * .netrc scanning coming up + *************************************************************/ + if(data->set.use_netrc) { + if(Curl_parsenetrc(conn->hostname, + data->state.user, + data->state.passwd)) { + infof(data, "Couldn't find host %s in the .netrc file, using defaults", + conn->hostname); + } + else + conn->bits.user_passwd = 1; /* enable user+password */ + + /* weather we failed or not, we don't know which fields that were filled + in anyway */ + if(!data->state.user[0]) + strcpy(data->state.user, CURL_DEFAULT_USER); + if(!data->state.passwd[0]) + strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD); + } + else if(!(conn->bits.user_passwd) && + (conn->protocol & (PROT_FTP|PROT_HTTP)) ) { + /* This is a FTP or HTTP URL, and we haven't got the user+password in + * the extra parameter, we will now try to extract the possible + * user+password pair in a string like: + * ftp://user:password@ftp.my.site:8021/README */ + char *ptr=NULL; /* assign to remove possible warnings */ + if((ptr=strchr(conn->name, '@'))) { + /* there's a user+password given here, to the left of the @ */ + + data->state.user[0] =0; + data->state.passwd[0]=0; + + if(*conn->name != ':') { + /* the name is given, get user+password */ + sscanf(conn->name, "%127[^:@]:%127[^@]", + data->state.user, data->state.passwd); + } + else + /* no name given, get the password only */ + sscanf(conn->name+1, "%127[^@]", data->state.passwd); + + if(data->state.user[0]) { + char *newname=curl_unescape(data->state.user, 0); + if(strlen(newname) < sizeof(data->state.user)) { + strcpy(data->state.user, newname); + } + /* if the new name is longer than accepted, then just use + the unconverted name, it'll be wrong but what the heck */ + free(newname); + } + + /* check for password, if no ask for one */ + if( !data->state.passwd[0] ) { + if(!data->set.fpasswd || + data->set.fpasswd(data->set.passwd_client, + "password:", data->state.passwd, + sizeof(data->state.passwd))) + return CURLE_BAD_PASSWORD_ENTERED; + } + else { + /* we have a password found in the URL, decode it! */ + char *newpasswd=curl_unescape(data->state.passwd, 0); + if(strlen(newpasswd) < sizeof(data->state.passwd)) { + strcpy(data->state.passwd, newpasswd); + } + free(newpasswd); + } + + conn->name = ++ptr; + conn->bits.user_passwd=TRUE; /* enable user+password */ + } + else { + strcpy(data->state.user, CURL_DEFAULT_USER); + strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD); + } + } + + /************************************************************* + * Figure out the remote port number + * + * No matter if we use a proxy or not, we have to figure out the remote + * port number of various reasons. + * + * To be able to detect port number flawlessly, we must not confuse them + * IPv6-specified addresses in the [0::1] style. (RFC2732) + *************************************************************/ + + if((1 == sscanf(conn->name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) && + (']' == endbracket)) { + /* This is a (IPv6-style) specified IP-address. We support _any_ + IP within brackets to be really generic. */ + conn->name++; /* pass the starting bracket */ + + tmp = strchr(conn->name, ']'); + *tmp = 0; /* zero terminate */ + + tmp++; /* pass the ending bracket */ + if(':' != *tmp) + tmp = NULL; /* no port number available */ + } + else { + /* traditional IPv4-style port-extracting */ + tmp = strchr(conn->name, ':'); + } + + if (tmp) { + *tmp++ = '\0'; /* cut off the name there */ + conn->remote_port = atoi(tmp); + } + + if(data->change.proxy) { + /* If this is supposed to use a proxy, we need to figure out the proxy + host name name, so that we can re-use an existing connection + that may exist registered to the same proxy host. */ + + char *prox_portno; + char *endofprot; + + /* We need to make a duplicate of the proxy so that we can modify the + string safely. */ + char *proxydup=strdup(data->change.proxy); + + /* We use 'proxyptr' to point to the proxy name from now on... */ + char *proxyptr=proxydup; + + if(NULL == proxydup) { + failf(data, "memory shortage"); + return CURLE_OUT_OF_MEMORY; + } + + /* Daniel Dec 10, 1998: + We do the proxy host string parsing here. We want the host name and the + port name. Accept a protocol:// prefix, even though it should just be + ignored. */ + + /* 1. skip the protocol part if present */ + endofprot=strstr(proxyptr, "://"); + if(endofprot) { + proxyptr = endofprot+3; + } + + /* allow user to specify proxy.server.com:1080 if desired */ + prox_portno = strchr (proxyptr, ':'); + if (prox_portno) { + *prox_portno = 0x0; /* cut off number from host name */ + prox_portno ++; + /* now set the local port number */ + conn->port = atoi(prox_portno); + } + else if(data->set.proxyport) { + /* None given in the proxy string, then get the default one if it is + given */ + conn->port = data->set.proxyport; + } + + /* now, clone the cleaned proxy host name */ + conn->proxyhost = strdup(proxyptr); + + free(proxydup); /* free the duplicate pointer and not the modified */ + } + + /************************************************************* + * Check the current list of connections to see if we can + * re-use an already existing one or if we have to create a + * new one. + *************************************************************/ + + /* reuse_fresh is set TRUE if we are told to use a fresh connection + by force */ + if(!data->set.reuse_fresh && + ConnectionExists(data, conn, &conn_temp)) { + /* + * We already have a connection for this, we got the former connection + * in the conn_temp variable and thus we need to cleanup the one we + * just allocated before we can move along and use the previously + * existing one. + */ + struct connectdata *old_conn = conn; + char *path = old_conn->path; /* setup the current path pointer properly */ + char *ppath = old_conn->ppath; /* this is the modified path pointer */ + if(old_conn->proxyhost) + free(old_conn->proxyhost); + conn = conn_temp; /* use this connection from now on */ + + /* we need these pointers if we speak over a proxy */ + conn->hostname = conn->gname; + conn->name = &conn->gname[old_conn->name - old_conn->gname]; + + free(conn->path); /* free the previously allocated path pointer */ + + /* 'path' points to the allocated data, 'ppath' may have been advanced + to point somewhere within the 'path' area. */ + conn->path = path; + conn->ppath = ppath; + + /* re-use init */ + conn->bits.reuse = TRUE; /* yes, we're re-using here */ + conn->bits.chunk = FALSE; /* always assume not chunked unless told + otherwise */ + conn->maxdownload = -1; /* might have been used previously! */ + + free(old_conn); /* we don't need this anymore */ + + /* + * If we're doing a resumed transfer, we need to setup our stuff + * properly. + */ + conn->resume_from = data->set.set_resume_from; + if (conn->resume_from) { + snprintf(resumerange, sizeof(resumerange), "%d-", conn->resume_from); + if (conn->bits.rangestringalloc == TRUE) + free(conn->range); + + /* tell ourselves to fetch this range */ + conn->range = strdup(resumerange); + conn->bits.use_range = TRUE; /* enable range download */ + conn->bits.rangestringalloc = TRUE; /* mark range string allocated */ + } + else if (data->set.set_range) { + /* There is a range, but is not a resume, useful for random ftp access */ + conn->range = strdup(data->set.set_range); + conn->bits.rangestringalloc = TRUE; /* mark range string allocated */ + conn->bits.use_range = TRUE; /* enable range download */ + } + + *in_connect = conn; /* return this instead! */ + + infof(data, "Re-using existing connection! (#%d)\n", conn->connectindex); + } + else { + /* + * This is a brand new connection, so let's store it in the connection + * cache of ours! + */ + ConnectionStore(data, conn); + } + + /************************************************************* + * Set timeout if that is being used + *************************************************************/ + if(data->set.timeout || data->set.connecttimeout) { + /************************************************************* + * Set signal handler to catch SIGALRM + * Store the old value to be able to set it back later! + *************************************************************/ + +#ifdef HAVE_SIGACTION + struct sigaction sigact; + sigaction(SIGALRM, NULL, &sigact); + keep_sigact = sigact; + keep_copysig = TRUE; /* yes, we have a copy */ + sigact.sa_handler = alarmfunc; +#ifdef SA_RESTART + /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */ + sigact.sa_flags &= ~SA_RESTART; +#endif + /* now set the new struct */ + sigaction(SIGALRM, &sigact, NULL); +#else + /* no sigaction(), revert to the much lamer signal() */ +#ifdef HAVE_SIGNAL + keep_sigact = signal(SIGALRM, alarmfunc); +#endif +#endif + + /* We set the timeout on the name resolving phase first, separately from + * the download/upload part to allow a maximum time on everything. This is + * a signal-based timeout, why it won't work and shouldn't be used in + * multi-threaded environments. */ + +#ifdef HAVE_ALARM + /* alarm() makes a signal get sent when the timeout fires off, and that + will abort system calls */ + prev_alarm = alarm(data->set.connecttimeout? + data->set.connecttimeout: + data->set.timeout); + /* We can expect the conn->created time to be "now", as that was just + recently set in the beginning of this function and nothing slow + has been done since then until now. */ +#endif + } + + /************************************************************* + * Resolve the name of the server or proxy + *************************************************************/ + if(!data->change.proxy) { + /* If not connecting via a proxy, extract the port from the URL, if it is + * there, thus overriding any defaults that might have been set above. */ + conn->port = conn->remote_port; /* it is the same port */ + + /* Resolve target host right on */ + if(!conn->hostaddr) { + /* it might already be set if reusing a connection */ + conn->hostaddr = Curl_resolv(data, conn->name, conn->port, + &conn->hostent_buf); + } + if(!conn->hostaddr) { + failf(data, "Couldn't resolve host '%s'", conn->name); + result = CURLE_COULDNT_RESOLVE_HOST; + /* don't return yet, we need to clean up the timeout first */ + } + } + else if(!conn->hostaddr) { + /* This is a proxy that hasn't been resolved yet. It may be resolved + if we're reusing an existing connection. */ + + /* resolve proxy */ + /* it might already be set if reusing a connection */ + conn->hostaddr = Curl_resolv(data, conn->proxyhost, conn->port, + &conn->hostent_buf); + + if(!conn->hostaddr) { + failf(data, "Couldn't resolve proxy '%s'", conn->proxyhost); + result = CURLE_COULDNT_RESOLVE_PROXY; + /* don't return yet, we need to clean up the timeout first */ + } + } + Curl_pgrsTime(data, TIMER_NAMELOOKUP); +#ifdef HAVE_ALARM + if(data->set.timeout || data->set.connecttimeout) { +#ifdef HAVE_SIGACTION + if(keep_copysig) { + /* we got a struct as it looked before, now put that one back nice + and clean */ + sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */ + } +#else +#ifdef HAVE_SIGNAL + /* restore the previous SIGALRM handler */ + signal(SIGALRM, keep_sigact); +#endif +#endif + /* switch back the alarm() to either zero or to what it was before minus + the time we spent until now! */ + if(prev_alarm) { + /* there was an alarm() set before us, now put it back */ + long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created); + long alarm_set; + + /* the alarm period is counted in even number of seconds */ + alarm_set = prev_alarm - elapsed_ms/1000; + + if(alarm_set<=0) { + /* if it turned negative, we should fire off a SIGALRM here, but we + won't, and zero would be to switch it off so we never set it to + less than 1! */ + alarm(1); + result = CURLE_OPERATION_TIMEOUTED; + failf(data, "Previous alarm fired off!"); + } + else + alarm(alarm_set); + } + else + alarm(0); /* just shut it off */ + } +#endif + if(result) + return result; + + /************************************************************* + * Proxy authentication + *************************************************************/ + if(conn->bits.proxy_user_passwd) { + char *authorization; + snprintf(data->state.buffer, BUFSIZE, "%s:%s", + data->state.proxyuser, data->state.proxypasswd); + if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer), + &authorization) >= 0) { + if(conn->allocptr.proxyuserpwd) + free(conn->allocptr.proxyuserpwd); + conn->allocptr.proxyuserpwd = + aprintf("Proxy-authorization: Basic %s\015\012", authorization); + free(authorization); + } + } + + /************************************************************* + * Send user-agent to HTTP proxies even if the target protocol + * isn't HTTP. + *************************************************************/ + if((conn->protocol&PROT_HTTP) || data->change.proxy) { + if(data->set.useragent) { + if(conn->allocptr.uagent) + free(conn->allocptr.uagent); + conn->allocptr.uagent = + aprintf("User-Agent: %s\015\012", data->set.useragent); + } + } + + if(-1 == conn->firstsocket) { + /* Connect only if not already connected! */ + result = ConnectPlease(conn); + if(CURLE_OK != result) + return result; + + if(conn->curl_connect) { + /* is there a connect() procedure? */ + + /* set start time here for timeout purposes in the + * connect procedure, it is later set again for the + * progress meter purpose */ + conn->now = Curl_tvnow(); + + /* Call the protocol-specific connect function */ + result = conn->curl_connect(conn); + if(result != CURLE_OK) + return result; /* pass back errors */ + } + } + + Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected */ + + conn->now = Curl_tvnow(); /* time this *after* the connect is done */ + conn->bytecount = 0; + conn->headerbytecount = 0; + + /* Figure out the ip-number and display the first host name it shows: */ +#ifdef ENABLE_IPV6 + { + char hbuf[NI_MAXHOST]; +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST; +#endif + struct addrinfo *ai = conn->serv_addr; + + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, + niflags)) { + snprintf(hbuf, sizeof(hbuf), "?"); + } + if (ai->ai_canonname) { + infof(data, "Connected to %s (%s) port %d\n", ai->ai_canonname, hbuf, + conn->port); + } else { + infof(data, "Connected to %s port %d\n", hbuf, conn->port); + } + } +#else + { + struct in_addr in; + (void) memcpy(&in.s_addr, &conn->serv_addr.sin_addr, sizeof (in.s_addr)); + infof(data, "Connected to %s (%s)\n", conn->hostaddr->h_name, +#if defined(HAVE_INET_NTOA_R) + inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)) +#else + inet_ntoa(in) +#endif + ); + } +#endif + +#ifdef __EMX__ + /* 20000330 mgs + * the check is quite a hack... + * we're calling _fsetmode to fix the problem with fwrite converting newline + * characters (you get mangled text files, and corrupted binary files when + * you download to stdout and redirect it to a file). */ + + if ((data->set.out)->_handle == NULL) { + _fsetmode(stdout, "b"); + } +#endif + + return CURLE_OK; +} + +CURLcode Curl_connect(struct SessionHandle *data, + struct connectdata **in_connect) +{ + CURLcode code; + struct connectdata *conn; + + /* call the stuff that needs to be called */ + code = CreateConnection(data, in_connect); + + if(CURLE_OK != code) { + /* We're not allowed to return failure with memory left allocated + in the connectdata struct, free those here */ + conn = (struct connectdata *)*in_connect; + if(conn) { + Curl_disconnect(conn); /* close the connection */ + *in_connect = NULL; /* return a NULL */ + } + } + return code; +} + + +CURLcode Curl_done(struct connectdata *conn) +{ + struct SessionHandle *data=conn->data; + CURLcode result; + + /* cleanups done even if the connection is re-used */ + + if(conn->bits.rangestringalloc) { + free(conn->range); + conn->bits.rangestringalloc = FALSE; + } + + /* Cleanup possible redirect junk */ + if(conn->newurl) { + free(conn->newurl); + conn->newurl = NULL; + } + + /* this calls the protocol-specific function pointer previously set */ + if(conn->curl_done) + result = conn->curl_done(conn); + else + result = CURLE_OK; + + Curl_pgrsDone(conn); /* done with the operation */ + + /* if data->set.reuse_forbid is TRUE, it means the libcurl client has + forced us to close this no matter what we think. + + if conn->bits.close is TRUE, it means that the connection should be + closed in spite of all our efforts to be nice, due to protocol + restrictions in our or the server's end */ + if(data->set.reuse_forbid || + ((CURLE_OK == result) && conn->bits.close)) + result = Curl_disconnect(conn); /* close the connection */ + else + infof(data, "Connection #%d left intact\n", conn->connectindex); + + return result; +} + +CURLcode Curl_do(struct connectdata **connp) +{ + CURLcode result=CURLE_OK; + struct connectdata *conn = *connp; + struct SessionHandle *data=conn->data; + + if(conn->curl_do) { + /* generic protocol-specific function pointer set in curl_connect() */ + result = conn->curl_do(conn); + + /* This was formerly done in transfer.c, but we better do it here */ + + if((CURLE_WRITE_ERROR == result) && conn->bits.reuse) { + /* This was a re-use of a connection and we got a write error in the + * DO-phase. Then we DISCONNECT this connection and have another attempt + * to CONNECT and then DO again! The retry cannot possibly find another + * connection to re-use, since we only keep one possible connection for + * each. */ + + infof(data, "Re-used connection seems dead, get a new one\n"); + + conn->bits.close = TRUE; /* enforce close of this connetion */ + result = Curl_done(conn); /* we are so done with this */ + if(CURLE_OK == result) { + /* Now, redo the connect and get a new connection */ + result = Curl_connect(data, connp); + if(CURLE_OK == result) + /* ... finally back to actually retry the DO phase */ + result = conn->curl_do(*connp); + } + } + } + return result; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ diff --git a/Source/CTest/Curl/url.h b/Source/CTest/Curl/url.h new file mode 100644 index 0000000..de3c02e --- /dev/null +++ b/Source/CTest/Curl/url.h @@ -0,0 +1,38 @@ +#ifndef __URL_H +#define __URL_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* + * Prototypes for library-wide functions provided by url.c + */ + +CURLcode Curl_open(struct SessionHandle **curl); +CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...); +CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */ +CURLcode Curl_connect(struct SessionHandle *, struct connectdata **); +CURLcode Curl_do(struct connectdata **); +CURLcode Curl_done(struct connectdata *); +CURLcode Curl_disconnect(struct connectdata *); + +#endif diff --git a/Source/CTest/Curl/urldata.h b/Source/CTest/Curl/urldata.h new file mode 100644 index 0000000..de318f1 --- /dev/null +++ b/Source/CTest/Curl/urldata.h @@ -0,0 +1,687 @@ +#ifndef __URLDATA_H +#define __URLDATA_H +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +/* This file is for lib internal stuff */ + +#include "setup.h" +#include "hostip.h" +#include "hash.h" + +#define PORT_FTP 21 +#define PORT_TELNET 23 +#define PORT_GOPHER 70 +#define PORT_HTTP 80 +#define PORT_HTTPS 443 +#define PORT_DICT 2628 +#define PORT_LDAP 389 + +#define DICT_MATCH "/MATCH:" +#define DICT_MATCH2 "/M:" +#define DICT_MATCH3 "/FIND:" +#define DICT_DEFINE "/DEFINE:" +#define DICT_DEFINE2 "/D:" +#define DICT_DEFINE3 "/LOOKUP:" + +#define CURL_DEFAULT_USER "anonymous" +#define CURL_DEFAULT_PASSWORD "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" + +#include <curl/curl.h> + +#include "http_chunks.h" /* for the structs and enum stuff */ + +/* Download buffer size, keep it fairly big for speed reasons */ +#define BUFSIZE (1024*20) + +/* Initial size of the buffer to store headers in, it'll be enlarged in case + of need. */ +#define HEADERSIZE 256 + +/* Just a convenience macro to get the larger value out of two given */ +#ifndef MAX +#define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#ifdef KRB4 +/* Types needed for krb4-ftp connections */ +struct krb4buffer { + void *data; + size_t size; + size_t index; + int eof_flag; +}; +enum protection_level { + prot_clear, + prot_safe, + prot_confidential, + prot_private +}; +#endif + +#ifndef HAVE_OPENSSL_ENGINE_H +typedef void ENGINE; +#endif +/* struct for data related to SSL and SSL connections */ +struct ssl_connect_data { + bool use; /* use ssl encrypted communications TRUE/FALSE */ +#ifdef USE_SSLEAY + /* these ones requires specific SSL-types */ + SSL_CTX* ctx; + SSL* handle; + X509* server_cert; +#endif /* USE_SSLEAY */ +}; + +/* information about one single SSL session */ +struct curl_ssl_session { + char *name; /* host name for which this ID was used */ + void *sessionid; /* as returned from the SSL layer */ + long age; /* just a number, the higher the more recent */ + unsigned short remote_port; /* remote port to connect to */ +}; + +struct ssl_config_data { + long version; /* what version the client wants to use */ + long certverifyresult; /* result from the certificate verification */ + long verifypeer; /* set TRUE if this is desired */ + long verifyhost; /* 0: no verif, 1: check that CN exists, 2: CN must match hostname */ + char *CApath; /* DOES NOT WORK ON WINDOWS */ + char *CAfile; /* cerficate to verify peer against */ + char *random_file; /* path to file containing "random" data */ + char *egdsocket; /* path to file containing the EGD daemon socket */ + char *cipher_list; /* list of ciphers to use */ + + long numsessions; /* SSL session id cache size */ +}; + +/**************************************************************************** + * HTTP unique setup + ***************************************************************************/ +struct HTTP { + struct FormData *sendit; + int postsize; + const char *p_pragma; /* Pragma: string */ + const char *p_accept; /* Accept: string */ + long readbytecount; + long writebytecount; + + /* For FORM posting */ + struct Form form; + curl_read_callback storefread; + FILE *in; + + struct Curl_chunker chunk; +}; + +/**************************************************************************** + * FTP unique setup + ***************************************************************************/ +struct FTP { + long *bytecountp; + char *user; /* user name string */ + char *passwd; /* password string */ + char *urlpath; /* the originally given path part of the URL */ + char *dir; /* decoded directory */ + char *file; /* decoded file */ + + char *entrypath; /* the PWD reply when we logged on */ + + char *cache; /* data cache between getresponse()-calls */ + size_t cache_size; /* size of cache in bytes */ +}; + +/**************************************************************************** + * FILE unique setup + ***************************************************************************/ +struct FILE { + int fd; /* open file descriptor to read from! */ +}; + +/* + * Boolean values that concerns this connection. + */ +struct ConnectBits { + bool close; /* if set, we close the connection after this request */ + bool reuse; /* if set, this is a re-used connection */ + bool chunk; /* if set, this is a chunked transfer-encoding */ + bool httpproxy; /* if set, this transfer is done through a http proxy */ + bool user_passwd; /* do we use user+password for this connection? */ + bool proxy_user_passwd; /* user+password for the proxy? */ + + bool use_range; + bool rangestringalloc; /* the range string is malloc()'ed */ + + bool resume_done; /* nothing was transfered, resumed transfer already + complete */ +}; + +/* + * This struct is all the previously local variables from Curl_perform() moved + * to struct to allow the function to return and get re-invoked better without + * losing state. + */ + +struct Curl_transfer_keeper { + int bytecount; /* total number of bytes read */ + int writebytecount; /* number of bytes written */ + long contentlength; /* size of incoming data */ + struct timeval start; /* transfer started at this time */ + struct timeval now; /* current time */ + bool header; /* incoming data has HTTP header */ + int headerline; /* counts header lines to better track the + first one */ + char *hbufp; /* points at *end* of header line */ + int hbuflen; + char *str; /* within buf */ + char *str_start; /* within buf */ + char *end_ptr; /* within buf */ + char *p; /* within headerbuff */ + bool content_range; /* set TRUE if Content-Range: was found */ + int offset; /* possible resume offset read from the + Content-Range: header */ + int httpcode; /* error code from the 'HTTP/1.? XXX' line */ + int httpversion; /* the HTTP version*10 */ + bool write_after_100_header; /* should we enable the write after + we received a 100-continue/timeout + or directly */ + + /* for the low speed checks: */ + time_t timeofdoc; + long bodywrites; + int writetype; + + /* the highest fd we use + 1 */ + struct SessionHandle *data; + struct connectdata *conn; + char *buf; + char *uploadbuf; + int maxfd; + + /* the file descriptors to play with */ + fd_set readfd; + fd_set writefd; + fd_set rkeepfd; + fd_set wkeepfd; + int keepon; + +}; + + +/* + * The connectdata struct contains all fields and variables that should be + * unique for an entire connection. + */ +struct connectdata { + /**** Fields set when inited and not modified again */ + struct SessionHandle *data; /* link to the root CURL struct */ + int connectindex; /* what index in the connects index this particular + struct has */ + + long protocol; /* PROT_* flags concerning the protocol set */ +#define PROT_MISSING (1<<0) +#define PROT_GOPHER (1<<1) +#define PROT_HTTP (1<<2) +#define PROT_HTTPS (1<<3) +#define PROT_FTP (1<<4) +#define PROT_TELNET (1<<5) +#define PROT_DICT (1<<6) +#define PROT_LDAP (1<<7) +#define PROT_FILE (1<<8) +#define PROT_FTPS (1<<9) +#define PROT_SSL (1<<10) /* protocol requires SSL */ + + Curl_addrinfo *hostaddr; /* IP-protocol independent host info pointer list */ + char *hostent_buf; /* pointer to allocated memory for name info */ + +#ifdef ENABLE_IPV6 + struct addrinfo *serv_addr; /* the particular host we use */ +#else + struct sockaddr_in serv_addr; +#endif + char protostr[64]; /* store the protocol string in this buffer */ + char gname[513]; /* store the hostname in this buffer */ + char *name; /* host name pointer to fool around with */ + char *path; /* allocated buffer to store the URL's path part in */ + char *hostname; /* hostname to connect, as parsed from url */ + long port; /* which port to use locally */ + unsigned short remote_port; /* what remote port to connect to, + not the proxy port! */ + char *ppath; + long bytecount; + long headerbytecount; /* only count received headers */ + + char *range; /* range, if used. See README for detailed specification on + this syntax. */ + ssize_t resume_from; /* continue [ftp] transfer from here */ + + char *proxyhost; /* name of the http proxy host */ + + struct timeval now; /* "current" time */ + struct timeval created; /* creation time */ + int firstsocket; /* the main socket to use */ + int secondarysocket; /* for i.e ftp transfers */ + long maxdownload; /* in bytes, the maximum amount of data to fetch, 0 + means unlimited */ + + struct ssl_connect_data ssl; /* this is for ssl-stuff */ + + struct ConnectBits bits; /* various state-flags for this connection */ + + /* These two functions MUST be set by the curl_connect() function to be + be protocol dependent */ + CURLcode (*curl_do)(struct connectdata *connect); + CURLcode (*curl_done)(struct connectdata *connect); + + /* This function *MAY* be set to a protocol-dependent function that is run + * after the connect() and everything is done, as a step in the connection. + */ + CURLcode (*curl_connect)(struct connectdata *connect); + + /* This function *MAY* be set to a protocol-dependent function that is run + * by the curl_disconnect(), as a step in the disconnection. + */ + CURLcode (*curl_disconnect)(struct connectdata *connect); + + /* This function *MAY* be set to a protocol-dependent function that is run + * in the curl_close() function if protocol-specific cleanups are required. + */ + CURLcode (*curl_close)(struct connectdata *connect); + + /**** curl_get() phase fields */ + + /* READ stuff */ + int sockfd; /* socket to read from or -1 */ + int size; /* -1 if unknown at this point */ + bool getheader; /* TRUE if header parsing is wanted */ + long *bytecountp; /* return number of bytes read or NULL */ + + /* WRITE stuff */ + int writesockfd; /* socket to write to, it may very well be + the same we read from. -1 disables */ + long *writebytecountp; /* return number of bytes written or NULL */ + + /** Dynamicly allocated strings, may need to be freed before this **/ + /** struct is killed. **/ + struct dynamically_allocated_data { + char *proxyuserpwd; /* free later if not NULL! */ + char *uagent; /* free later if not NULL! */ + char *userpwd; /* free later if not NULL! */ + char *rangeline; /* free later if not NULL! */ + char *ref; /* free later if not NULL! */ + char *cookie; /* free later if not NULL! */ + char *host; /* free later if not NULL */ + } allocptr; + + char *newurl; /* This can only be set if a Location: was in the + document headers */ + +#ifdef KRB4 + enum protection_level command_prot; + enum protection_level data_prot; + enum protection_level request_data_prot; + + size_t buffer_size; + + struct krb4buffer in_buffer, out_buffer; + int sec_complete; + void *app_data; + + struct Curl_sec_client_mech *mech; + struct sockaddr_in local_addr; + +#endif + + /*************** Request - specific items ************/ + /* previously this was in the urldata struct */ + union { + struct HTTP *http; + struct HTTP *gopher; /* alias, just for the sake of being more readable */ + struct HTTP *https; /* alias, just for the sake of being more readable */ + struct FTP *ftp; + struct FILE *file; + void *telnet; /* private for telnet.c-eyes only */ +#if 0 /* no need for special ones for these: */ + struct LDAP *ldap; + struct DICT *dict; +#endif + void *generic; + } proto; + + /* This struct is inited when needed */ + struct Curl_transfer_keeper keep; + + /* 'upload_present' is used to keep a byte counter of how much data there is + still left in the buffer, aimed for upload. */ + int upload_present; + + /* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a + buffer, so the next read should read from where this pointer points to, + and the 'upload_present' contains the number of bytes available at this + position */ + char *upload_fromhere; +}; + +/* + * Struct to keep statistical and informational data. + */ +struct PureInfo { + int httpcode; + int httpversion; + long filetime; /* If requested, this is might get set. Set to -1 if + the time was unretrievable */ + long header_size; /* size of read header(s) in bytes */ + long request_size; /* the amount of bytes sent in the request(s) */ + + char *contenttype; /* the content type of the object */ +}; + + +struct Progress { + long lastshow; /* time() of the last displayed progress meter or NULL to + force redraw at next call */ + double size_dl; + double size_ul; + double downloaded; + double uploaded; + + double current_speed; /* uses the currently fastest transfer */ + + bool callback; /* set when progress callback is used */ + int width; /* screen width at download start */ + int flags; /* see progress.h */ + + double timespent; + + double dlspeed; + double ulspeed; + + double t_nslookup; + double t_connect; + double t_pretransfer; + double t_starttransfer; + + struct timeval start; + struct timeval t_startsingle; +#define CURR_TIME (5+1) /* 6 entries for 5 seconds */ + + double speeder[ CURR_TIME ]; + struct timeval speeder_time[ CURR_TIME ]; + int speeder_c; +}; + +typedef enum { + HTTPREQ_NONE, /* first in list */ + HTTPREQ_GET, + HTTPREQ_POST, + HTTPREQ_POST_FORM, /* we make a difference internally */ + HTTPREQ_PUT, + HTTPREQ_CUSTOM, + HTTPREQ_LAST /* last in list */ +} Curl_HttpReq; + +/* + * Values that are generated, temporary or calculated internally for a + * "session handle" must be defined within the 'struct urlstate'. This struct + * will be used within the SessionHandle struct. When the 'SessionHandle' + * struct is cloned, this data MUST NOT be copied. + * + * Remember that any "state" information goes globally for the curl handle. + * Session-data MUST be put in the connectdata struct and here. */ +#define MAX_CURL_USER_LENGTH 256 +#define MAX_CURL_PASSWORD_LENGTH 256 + +struct UrlState { + /* buffers to store authentication data in, as parsed from input options */ + char user[MAX_CURL_USER_LENGTH]; + char passwd[MAX_CURL_PASSWORD_LENGTH]; + char proxyuser[MAX_CURL_USER_LENGTH]; + char proxypasswd[MAX_CURL_PASSWORD_LENGTH]; + + struct timeval keeps_speed; /* for the progress meter really */ + + /* 'connects' will be an allocated array with pointers. If the pointer is + set, it holds an allocated connection. */ + struct connectdata **connects; + long numconnects; /* size of the 'connects' array */ + + char *headerbuff; /* allocated buffer to store headers in */ + int headersize; /* size of the allocation */ + + char buffer[BUFSIZE+1]; /* download buffer */ + char uploadbuffer[BUFSIZE+1]; /* upload buffer */ + double current_speed; /* the ProgressShow() funcion sets this */ + + bool this_is_a_follow; /* this is a followed Location: request */ + + char *auth_host; /* if set, this should be the host name that we will + sent authorization to, no else. Used to make Location: + following not keep sending user+password... This is + strdup() data. + */ + + struct curl_ssl_session *session; /* array of 'numsessions' size */ + long sessionage; /* number of the most recent session */ + + char scratch[BUFSIZE*2]; /* huge buffer when doing upload CRLF replacing */ + bool errorbuf; /* Set to TRUE if the error buffer is already filled in. + This must be set to FALSE every time _easy_perform() is + called. */ + +#ifdef HAVE_SIGNAL + /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */ + void (*prev_signal)(int sig); +#endif + bool allow_port; /* Is set.use_port allowed to take effect or not. This + is always set TRUE when curl_easy_perform() is called. */ +}; + + +/* + * This 'DynamicStatic' struct defines dynamic states that actually change + * values in the 'UserDefined' area, which MUST be taken into consideration + * if the UserDefined struct is cloned or similar. You can probably just + * copy these, but each one indicate a special action on other data. + */ + +struct DynamicStatic { + char *url; /* work URL, copied from UserDefined */ + bool url_alloc; /* URL string is malloc()'ed */ + char *proxy; /* work proxy, copied from UserDefined */ + bool proxy_alloc; /* http proxy string is malloc()'ed */ + char *referer; /* referer string */ + bool referer_alloc; /* referer sting is malloc()ed */ +}; + +/* + * This 'UserDefined' struct must only contain data that is set once to go + * for many (perhaps) independent connections. Values that are generated or + * calculated internally for the "session handle" MUST be defined within the + * 'struct urlstate' instead. The only exceptions MUST note the changes in + * the 'DynamicStatic' struct. + */ + +struct UserDefined { + FILE *err; /* the stderr writes goes here */ + char *errorbuffer; /* store failure messages in here */ + char *proxyuserpwd; /* Proxy <user:password>, if used */ + long proxyport; /* If non-zero, use this port number by default. If the + proxy string features a ":[port]" that one will override + this. */ + void *out; /* the fetched file goes here */ + void *in; /* the uploaded file is read from here */ + void *writeheader; /* write the header to this is non-NULL */ + char *set_url; /* what original URL to work on */ + char *set_proxy; /* proxy to use */ + long use_port; /* which port to use (when not using default) */ + char *userpwd; /* <user:password>, if used */ + char *set_range; /* range, if used. See README for detailed specification + on this syntax. */ + long followlocation; /* as in HTTP Location: */ + long maxredirs; /* maximum no. of http(s) redirects to follow */ + char *set_referer; /* custom string */ + bool free_referer; /* set TRUE if 'referer' points to a string we + allocated */ + char *useragent; /* User-Agent string */ + char *postfields; /* if POST, set the fields' values here */ + size_t postfieldsize; /* if POST, this might have a size to use instead of + strlen(), and then the data *may* be binary (contain + zero bytes) */ + char *ftpport; /* port to send with the FTP PORT command */ + char *device; /* network interface to use */ + curl_write_callback fwrite; /* function that stores the output */ + curl_write_callback fwrite_header; /* function that stores headers */ + curl_read_callback fread; /* function that reads the input */ + curl_progress_callback fprogress; /* function for progress information */ + void *progress_client; /* pointer to pass to the progress callback */ + curl_passwd_callback fpasswd; /* call for password */ + void *passwd_client; /* pass to the passwd callback */ + long timeout; /* in seconds, 0 means no timeout */ + long connecttimeout; /* in seconds, 0 means no timeout */ + long infilesize; /* size of file to upload, -1 means unknown */ + long low_speed_limit; /* bytes/second */ + long low_speed_time; /* number of seconds */ + int set_resume_from; /* continue [ftp] transfer from here */ + char *cookie; /* HTTP cookie string to send */ + struct curl_slist *headers; /* linked list of extra headers */ + struct HttpPost *httppost; /* linked list of POST data */ + char *cert; /* certificate */ + char *cert_type; /* format for certificate (default: PEM) */ + char *key; /* private key */ + char *key_type; /* format for private key (default: PEM) */ + char *key_passwd; /* plain text private key password */ + char *crypto_engine; /* name of the crypto engine to use */ + char *cookiejar; /* dump all cookies to this file */ + bool crlf; /* convert crlf on ftp upload(?) */ + struct curl_slist *quote; /* after connection is established */ + struct curl_slist *postquote; /* after the transfer */ + struct curl_slist *prequote; /* before the transfer, after type (Wesley Laxton)*/ + struct curl_slist *telnet_options; /* linked list of telnet options */ + curl_TimeCond timecondition; /* kind of time/date comparison */ + time_t timevalue; /* what time to compare with */ + curl_closepolicy closepolicy; /* connection cache close concept */ + Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */ + char *customrequest; /* HTTP/FTP request to use */ + long httpversion; /* when non-zero, a specific HTTP version requested to + be used in the library's request(s) */ + char *auth_host; /* if set, this is the allocated string to the host name + * to which to send the authorization data to, and no other + * host (which location-following otherwise could lead to) + */ + char *krb4_level; /* what security level */ + struct ssl_config_data ssl; /* user defined SSL stuff */ + + int dns_cache_timeout; /* DNS cache timeout */ + +/* Here follows boolean settings that define how to behave during + this session. They are STATIC, set by libcurl users or at least initially + and they don't change during operations. */ + + bool get_filetime; + bool tunnel_thru_httpproxy; + bool ftp_append; + bool ftp_ascii; + bool ftp_list_only; + bool ftp_use_port; + bool hide_progress; + bool http_fail_on_error; + bool http_follow_location; + bool include_header; +#define http_include_header include_header /* former name */ + + bool http_set_referer; + bool http_auto_referer; /* set "correct" referer when following location: */ + bool no_body; + bool set_port; + bool upload; + bool use_netrc; + bool verbose; + bool krb4; /* kerberos4 connection requested */ + bool reuse_forbid; /* forbidden to be reused, close after use */ + bool reuse_fresh; /* do not re-use an existing connection */ + bool expect100header; /* TRUE if we added Expect: 100-continue */ + bool ftp_use_epsv; /* if EPSV is to be attempted or not */ + + bool global_dns_cache; +}; + +/* + * In August 2001, this struct was redesigned and is since stricter than + * before. The 'connectdata' struct MUST have all the connection oriented + * stuff as we may now have several simultaneous connections and connection + * structs in memory. + * + * From now on, the 'SessionHandle' must only contain data that is set once to + * go for many (perhaps) independent connections. Values that are generated or + * calculated internally for the "session handle" must be defined within the + * 'struct urlstate' instead. */ + +struct SessionHandle { + curl_hash *hostcache; + struct UserDefined set; /* values set by the libcurl user */ + struct DynamicStatic change; /* possibly modified userdefined data */ + + struct CookieInfo *cookies; /* the cookies, read from files and servers */ + struct Progress progress; /* for all the progress meter data */ + struct UrlState state; /* struct for fields used for state info and + other dynamic purposes */ + struct PureInfo info; /* stats, reports and info data */ +#ifdef USE_SSLEAY + ENGINE* engine; +#endif /* USE_SSLEAY */ +}; + +#define LIBCURL_NAME "libcurl" + +#endif diff --git a/Source/CTest/Curl/version.c b/Source/CTest/Curl/version.c new file mode 100644 index 0000000..ecaec67 --- /dev/null +++ b/Source/CTest/Curl/version.c @@ -0,0 +1,121 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2002, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include <string.h> +#include <stdio.h> + +#include <curl/curl.h> +#include "urldata.h" + +char *curl_version(void) +{ + static char version[200]; + char *ptr; + strcpy(version, LIBCURL_NAME " " LIBCURL_VERSION ); + ptr=strchr(version, '\0'); + +#ifdef USE_SSLEAY + +#if (SSLEAY_VERSION_NUMBER >= 0x905000) + { + char sub[2]; + unsigned long ssleay_value; + sub[1]='\0'; + ssleay_value=SSLeay(); + if(ssleay_value < 0x906000) { + ssleay_value=SSLEAY_VERSION_NUMBER; + sub[0]='\0'; + } + else { + if(ssleay_value&0xff0) { + sub[0]=((ssleay_value>>4)&0xff) + 'a' -1; + } + else + sub[0]='\0'; + } + + sprintf(ptr, " (OpenSSL %lx.%lx.%lx%s)", + (ssleay_value>>28)&0xf, + (ssleay_value>>20)&0xff, + (ssleay_value>>12)&0xff, + sub); + } + +#else +#if (SSLEAY_VERSION_NUMBER >= 0x900000) + sprintf(ptr, " (SSL %lx.%lx.%lx)", + (SSLEAY_VERSION_NUMBER>>28)&0xff, + (SSLEAY_VERSION_NUMBER>>20)&0xff, + (SSLEAY_VERSION_NUMBER>>12)&0xf); +#else + { + char sub[2]; + sub[1]='\0'; + if(SSLEAY_VERSION_NUMBER&0x0f) { + sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1; + } + else + sub[0]='\0'; + + sprintf(ptr, " (SSL %x.%x.%x%s)", + (SSLEAY_VERSION_NUMBER>>12)&0xff, + (SSLEAY_VERSION_NUMBER>>8)&0xf, + (SSLEAY_VERSION_NUMBER>>4)&0xf, sub); + } +#endif +#endif + ptr=strchr(ptr, '\0'); +#endif + +#if defined(KRB4) || defined(ENABLE_IPV6) + strcat(ptr, " ("); + ptr+=2; +#ifdef KRB4 + sprintf(ptr, "krb4 "); + ptr += strlen(ptr); +#endif +#ifdef ENABLE_IPV6 + sprintf(ptr, "ipv6 "); + ptr += strlen(ptr); +#endif + sprintf(ptr, "enabled)"); + ptr += strlen(ptr); +#endif + +#ifdef USE_ZLIB + sprintf(ptr, " (zlib %s)", zlibVersion()); + ptr += strlen(ptr); +#endif + + return version; +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ |