mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-27 04:09:36 +03:00
Initial import
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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) */
|
||||
@@ -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));
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
Reference in New Issue
Block a user