Initial import

This commit is contained in:
Andy Cedilnik
2003-01-06 21:13:39 -05:00
parent 0f14e027b5
commit baeba76200
78 changed files with 24960 additions and 0 deletions
+309
View File
@@ -0,0 +1,309 @@
CMAKE_MINIMUM_REQUIRED(VERSION 1.5)
PROJECT(LIBCURL C)
SET(PACKAGE "curl")
SET(VERSION "7.9.5")
SET(OPERATING_SYSTEM ${CMAKE_SYSTEM_NAME})
SET(CMAKE_C_FLAGS "${CMAKE_ANSI_CFLAGS} ${CMAKE_C_FLAGS}")
SET(CMAKE_REQUIRED_FLAGS ${CMAKE_ANSI_CFLAGS})
INCLUDE (${CMAKE_ROOT}/Modules/CheckLibraryExists.cmake)
INCLUDE (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
INCLUDE (${CMAKE_ROOT}/Modules/CheckTypeSize.cmake)
INCLUDE (${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake)
INCLUDE (${CMAKE_ROOT}/Modules/CheckIncludeFiles.cmake)
SET(libCurl_SRCS
file.c
timeval.c
base64.c
hostip.c
progress.c
formdata.c
cookie.c
http.c
sendf.c
ftp.c
url.c
dict.c
if2ip.c
speedcheck.c
getdate.c
ldap.c
ssluse.c
version.c
getenv.c
escape.c
mprintf.c
telnet.c
getpass.c
netrc.c
getinfo.c
transfer.c
strequal.c
easy.c
security.c
krb4.c
memdebug.c
http_chunks.c
strtok.c
connect.c
llist.c
hash.c
multi.c
)
IF(WIN32)
INCLUDE(${LIBCURL_SOURCE_DIR}/WindowsCache.cmake)
ENDIF(WIN32)
SET(CURL_LIBS "")
CHECK_LIBRARY_EXISTS("nsl;${CURL_LIBS}" gethostname "" HAVE_LIBNSL)
IF(HAVE_LIBNSL)
SET(CURL_LIBS ${CURL_LIBS} nsl)
ENDIF(HAVE_LIBNSL)
CHECK_LIBRARY_EXISTS("dl;${CURL_LIBS}" dlopen "" HAVE_LIBDL)
IF(HAVE_LIBDL)
SET(HAVE_DLOPEN 1)
SET(CURL_LIBS ${CURL_LIBS} dl)
ENDIF(HAVE_LIBDL)
CHECK_LIBRARY_EXISTS("ucb;${CURL_LIBS}" gethostname "" HAVE_LIBUCB)
IF(HAVE_LIBUCB)
SET(CURL_LIBS ${CURL_LIBS} ucb)
ENDIF(HAVE_LIBUCB)
CHECK_LIBRARY_EXISTS("socket;${CURL_LIBS}" connect "" HAVE_LIBSOCKET)
IF(HAVE_LIBSOCKET)
SET(CURL_LIBS ${CURL_LIBS} socket)
ENDIF(HAVE_LIBSOCKET)
CHECK_LIBRARY_EXISTS("ws2_32;${CURL_LIBS}" printf "" HAVE_LIBWS2_32)
IF(HAVE_LIBWS2_32)
SET(CURL_LIBS ${CURL_LIBS} ws2_32)
ENDIF(HAVE_LIBWS2_32)
SET(CMAKE_REQUIRED_LIBRARIES ${CURL_LIBS})
CHECK_INCLUDE_FILE("sys/types.h" HAVE_SYS_TYPES_H)
CHECK_INCLUDE_FILE("inttypes.h" HAVE_INTTYPES_H)
CHECK_INCLUDE_FILE("alloca.h" HAVE_ALLOCA_H)
CHECK_INCLUDE_FILE("arpa/inet.h" HAVE_ARPA_INET_H)
CHECK_INCLUDE_FILE("dlfcn.h" HAVE_DLFCN_H)
CHECK_INCLUDE_FILE("fcntl.h" HAVE_FCNTL_H)
CHECK_INCLUDE_FILE("malloc.h" HAVE_MALLOC_H)
CHECK_INCLUDE_FILE("memory.h" HAVE_MEMORY_H)
CHECK_INCLUDE_FILE("netdb.h" HAVE_NETDB_H)
CHECK_INCLUDE_FILE("netinet/in.h" HAVE_NETINET_IN_H)
CHECK_INCLUDE_FILE("sys/socket.h" HAVE_SYS_SOCKET_H)
IF(HAVE_SYS_TYPES_H)
SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "sys/types.h")
ENDIF(HAVE_SYS_TYPES_H)
IF(HAVE_SYS_SOCKET_H)
SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "sys/socket.h")
ENDIF(HAVE_SYS_SOCKET_H)
CHECK_INCLUDE_FILES("${TEST_NETWORK_INCLUDES};net/if.h" HAVE_NET_IF_H)
IF(HAVE_NET_IF_H)
SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "net/if.h")
ENDIF(HAVE_NET_IF_H)
IF(HAVE_NETINET_IN_H)
SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "netinet/in.h")
ENDIF(HAVE_NETINET_IN_H)
CHECK_INCLUDE_FILES("${TEST_NETWORK_INCLUDES};netinet/if_ether.h" HAVE_NETINET_IF_ETHER_H)
CHECK_INCLUDE_FILE("netinet/in.h" HAVE_NETINET_IN_H)
CHECK_INCLUDE_FILE("pwd.h" HAVE_PWD_H)
CHECK_INCLUDE_FILE("sgtty.h" HAVE_SGTTY_H)
CHECK_INCLUDE_FILE("stdint.h" HAVE_STDINT_H)
CHECK_INCLUDE_FILE("stdlib.h" HAVE_STDLIB_H)
CHECK_INCLUDE_FILE("string.h" HAVE_STRING_H)
CHECK_INCLUDE_FILE("strings.h" HAVE_STRINGS_H)
CHECK_INCLUDE_FILE("sys/param.h" HAVE_SYS_PARAM_H)
CHECK_INCLUDE_FILE("sys/select.h" HAVE_SYS_SELECT_H)
CHECK_INCLUDE_FILE("sys/stat.h" HAVE_SYS_STAT_H)
CHECK_INCLUDE_FILE("sys/time.h" HAVE_SYS_TIME_H)
CHECK_INCLUDE_FILE("termios.h" HAVE_TERMIOS_H)
CHECK_INCLUDE_FILE("termio.h" HAVE_TERMIO_H)
CHECK_INCLUDE_FILE("io.h" HAVE_IO_H)
CHECK_INCLUDE_FILE("time.h" HAVE_TIME_H)
CHECK_INCLUDE_FILE("unistd.h" HAVE_UNISTD_H)
CHECK_INCLUDE_FILE("utime.h" HAVE_UTIME_H)
CHECK_INCLUDE_FILE("sys/utime.h" HAVE_SYS_UTIME_H)
CHECK_INCLUDE_FILE("winsock.h" HAVE_WINSOCK_H)
CHECK_INCLUDE_FILE("sockio.h" HAVE_SOCKIO_H)
CHECK_INCLUDE_FILE("sys/sockio.h" HAVE_SYS_SOCKIO_H)
CHECK_INCLUDE_FILE("x509.h" HAVE_X509_H)
CHECK_TYPE_SIZE(ssize_t SIZEOF_SSIZE_T)
CHECK_TYPE_SIZE("long double" SIZEOF_LONG_DOUBLE)
IF(NOT HAVE_SIZEOF_SSIZE_T)
SET(ssize_t int)
ENDIF(NOT HAVE_SIZEOF_SSIZE_T)
FIND_FILE(RANDOM_FILE urandom /dev)
CHECK_FUNCTION_EXISTS(_doprnt HAVE_DOPRNT)
CHECK_FUNCTION_EXISTS(vprintf HAVE_VPRINTF)
CHECK_FUNCTION_EXISTS(socket HAVE_SOCKET)
CHECK_FUNCTION_EXISTS(select HAVE_SELECT)
CHECK_FUNCTION_EXISTS(strdup HAVE_STRDUP)
CHECK_FUNCTION_EXISTS(strstr HAVE_STRSTR)
CHECK_FUNCTION_EXISTS(strtok_r HAVE_STRTOK_R)
CHECK_FUNCTION_EXISTS(strftime HAVE_STRFTIME)
CHECK_FUNCTION_EXISTS(uname HAVE_UNAME)
CHECK_FUNCTION_EXISTS(strcasecmp HAVE_STRCASECMP)
CHECK_FUNCTION_EXISTS(stricmp HAVE_STRICMP)
CHECK_FUNCTION_EXISTS(strcmpi HAVE_STRCMPI)
CHECK_FUNCTION_EXISTS(gethostname HAVE_GETHOSTNAME)
CHECK_FUNCTION_EXISTS(gethostbyaddr HAVE_GETHOSTBYADDR)
CHECK_FUNCTION_EXISTS(gettimeofday HAVE_GETTIMEOFDAY)
CHECK_FUNCTION_EXISTS(inet_addr HAVE_INET_ADDR)
CHECK_FUNCTION_EXISTS(inet_ntoa HAVE_INET_NTOA)
CHECK_FUNCTION_EXISTS(inet_ntoa_r HAVE_INET_NTOA_R)
CHECK_FUNCTION_EXISTS(tcsetattr HAVE_TCSETATTR)
CHECK_FUNCTION_EXISTS(tcgetattr HAVE_TCGETATTR)
CHECK_FUNCTION_EXISTS(perror HAVE_PERROR)
CHECK_FUNCTION_EXISTS(closesocket HAVE_CLOSESOCKET)
CHECK_FUNCTION_EXISTS(setvbuf HAVE_SETVBUF)
CHECK_FUNCTION_EXISTS(sigaction HAVE_SIGACTION)
CHECK_FUNCTION_EXISTS(signal HAVE_SIGNAL)
CHECK_FUNCTION_EXISTS(getpass_r HAVE_GETPASS_R)
CHECK_FUNCTION_EXISTS(strlcat HAVE_STRLCAT)
CHECK_FUNCTION_EXISTS(getpwuid HAVE_GETPWUID)
CHECK_FUNCTION_EXISTS(geteuid HAVE_GETEUID)
CHECK_FUNCTION_EXISTS(utime HAVE_UTIME)
CHECK_FUNCTION_EXISTS(RAND_status HAVE_RAND_STATUS)
CHECK_FUNCTION_EXISTS(RAND_screen HAVE_RAND_SCREEN)
CHECK_FUNCTION_EXISTS(RAND_egd HAVE_RAND_EGD)
CHECK_FUNCTION_EXISTS(gethostbyname HAVE_GETHOSTBYNAME_R)
CHECK_FUNCTION_EXISTS(gethostbyaddr HAVE_GETHOSTBYADDR_R)
CHECK_FUNCTION_EXISTS(gmtime HAVE_GMTIME_R)
CHECK_FUNCTION_EXISTS(localtime HAVE_LOCALTIME_R)
MACRO(CURL_INTERNAL_TEST CURL_TEST)
IF("${CURL_TEST}" MATCHES "^${CURL_TEST}$")
SET(MACRO_CHECK_FUNCTION_DEFINITIONS
"-D${CURL_TEST} ${CMAKE_REQUIRED_FLAGS}")
IF(CMAKE_REQUIRED_LIBRARIES)
SET(CURL_TEST_ADD_LIBRARIES
"-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}")
ENDIF(CMAKE_REQUIRED_LIBRARIES)
MESSAGE(STATUS "Performing Curl Test ${CURL_TEST}")
TRY_COMPILE(${CURL_TEST}
${CMAKE_BINARY_DIR}
${LIBCURL_SOURCE_DIR}/CMake/CurlTests.c
CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
"${CURL_TEST_ADD_LIBRARIES}"
OUTPUT_VARIABLE OUTPUT)
IF(${CURL_TEST})
SET(${CURL_TEST} 1 CACHE INTERNAL "Curl test ${FUNCTION}")
MESSAGE(STATUS "Performing Curl Test ${CURL_TEST} - Success")
ELSE(${CURL_TEST})
MESSAGE(STATUS "Performing Curl Test ${CURL_TEST} - Failed")
SET(${CURL_TEST} "" CACHE INTERNAL "Curl test ${FUNCTION}")
WRITE_FILE(${CMAKE_BINARY_DIR}/CMakeError.log
"Performing Curl Test ${CURL_TEST} failed with the following output:\n"
"${OUTPUT}\n" APPEND)
ENDIF(${CURL_TEST})
ENDIF("${CURL_TEST}" MATCHES "^${CURL_TEST}$")
ENDMACRO(CURL_INTERNAL_TEST)
FOREACH(CURL_TEST
TIME_WITH_SYS_TIME
HAVE_O_NONBLOCK
HAVE_GETHOSTBYADDR_R_5
HAVE_GETHOSTBYADDR_R_7
HAVE_GETHOSTBYADDR_R_8
HAVE_GETHOSTBYADDR_R_5_REENTRANT
HAVE_GETHOSTBYADDR_R_7_REENTRANT
HAVE_GETHOSTBYADDR_R_8_REENTRANT
HAVE_GETHOSTBYNAME_R_3
HAVE_GETHOSTBYNAME_R_5
HAVE_GETHOSTBYNAME_R_6
HAVE_GETHOSTBYNAME_R_3_REENTRANT
HAVE_GETHOSTBYNAME_R_5_REENTRANT
HAVE_GETHOSTBYNAME_R_6_REENTRANT
HAVE_SOCKLEN_T
HAVE_IN_ADDR_T
STDC_HEADERS
RETSIGTYPE_TEST
HAVE_INET_NTOA_R_DECL
HAVE_INET_NTOA_R_DECL_REENTRANT
)
CURL_INTERNAL_TEST(${CURL_TEST})
ENDFOREACH(CURL_TEST)
FOREACH(CURL_TEST
HAVE_GETHOSTBYADDR_R_5
HAVE_GETHOSTBYADDR_R_7
HAVE_GETHOSTBYADDR_R_8
HAVE_GETHOSTBYNAME_R_3
HAVE_GETHOSTBYNAME_R_5
HAVE_GETHOSTBYNAME_R_6
HAVE_INET_NTOA_R_DECL_REENTRANT)
IF(NOT ${CURL_TEST})
IF(${CURL_TEST}_REENTRANT)
SET(NEED_REENTRANT 1)
ENDIF(${CURL_TEST}_REENTRANT)
ENDIF(NOT ${CURL_TEST})
ENDFOREACH(CURL_TEST)
IF(NEED_REENTRANT)
FOREACH(CURL_TEST
HAVE_GETHOSTBYADDR_R_5
HAVE_GETHOSTBYADDR_R_7
HAVE_GETHOSTBYADDR_R_8
HAVE_GETHOSTBYNAME_R_3
HAVE_GETHOSTBYNAME_R_5
HAVE_GETHOSTBYNAME_R_6)
SET(${CURL_TEST} 0)
IF(${CURL_TEST}_REENTRANT)
SET(${CURL_TEST} 1)
ENDIF(${CURL_TEST}_REENTRANT)
ENDFOREACH(CURL_TEST)
ENDIF(NEED_REENTRANT)
IF(HAVE_INET_NTOA_R_DECL_REENTRANT)
SET(HAVE_INET_NTOA_R_DECL 1)
SET(NEED_REENTRANT 1)
ENDIF(HAVE_INET_NTOA_R_DECL_REENTRANT)
IF(NOT HAVE_SOCKLEN_T)
SET(socklen_t "int")
ENDIF(NOT HAVE_SOCKLEN_T)
IF(NOT HAVE_IN_ADDR_T)
SET(in_addr_t "unsigned long")
ENDIF(NOT HAVE_IN_ADDR_T)
IF(RETSIGTYPE_TEST)
SET(RETSIGTYPE void)
ELSE(RETSIGTYPE_TEST)
SET(RETSIGTYPE int)
ENDIF(RETSIGTYPE_TEST)
INCLUDE_DIRECTORIES(${LIBCURL_SOURCE_DIR})
INCLUDE_DIRECTORIES(${LIBCURL_BINARY_DIR})
ADD_DEFINITIONS(-DHAVE_CONFIG_H)
CONFIGURE_FILE(${LIBCURL_SOURCE_DIR}/config.h.in
${LIBCURL_BINARY_DIR}/config.h)
ADD_LIBRARY(Curl STATIC ${libCurl_SRCS})
TARGET_LINK_LIBRARIES(Curl ${CURL_LIBS})
OPTION(CURL_TESTING "Do libCurl testing" ON)
IF(CURL_TESTING)
SUBDIRS(Testing)
ENDIF(CURL_TESTING)
+106
View File
@@ -0,0 +1,106 @@
IF(WIN32)
SET(HAVE_GETHOSTNAME 1)
SET(HAVE_GETHOSTBYADDR 1)
SET(HAVE_GETSERVBYNAME 1)
SET(HAVE_INET_ADDR 1)
SET(HAVE_INET_NTOA 1)
SET(HAVE_SELECT 1)
SET(HAVE_SOCKET 1)
SET(HAVE_ARPA_INET 1)
SET(HAVE_NETDB_H 1)
SET(HAVE_SYS_SOCKET_H 0)
SET(HAVE_TERMIO_H 1)
SET(HAVE_TERMIOS_H 1)
SET(HAVE_ARPA_INET_H 1)
SET(HAVE_UTIME_H 0)
SET(HAVE_CLOSESOCKET 1)
SET(HAVE_RAND_SCREEN 1)
SET(HAVE_SIGNAL 0)
SET(OPERATING_SYSTEM "win32")
SET(HAVE_SYS_TYPES_H 1)
SET(HAVE_INTTYPES_H 0)
SET(HAVE_ALLOCA_H 0)
SET(HAVE_DLFCN_H 0)
SET(HAVE_FCNTL_H 1)
SET(HAVE_MALLOC_H 1)
SET(HAVE_MEMORY_H 1)
SET(HAVE_NETINET_IN_H 0)
SET(HAVE_NETINET_IF_ETHER_H 0)
SET(HAVE_NET_IF_H 0)
SET(HAVE_PWD_H 0)
SET(HAVE_SGTTY_H 0)
SET(HAVE_STDINT_H 0)
SET(HAVE_STDLIB_H 1)
SET(HAVE_STRING_H 1)
SET(HAVE_STRINGS_H 0)
SET(HAVE_SYS_PARAM_H 0)
SET(HAVE_SYS_SELECT_H 0)
SET(HAVE_SYS_SOCKIO_H 1)
SET(HAVE_SYS_STAT_H 1)
SET(HAVE_SYS_TIME_H 0)
SET(HAVE_IO_H 1)
SET(HAVE_TIME_H 1)
SET(HAVE_WINSOCK_H 1)
SET(HAVE_SOCK_H 0)
SET(HAVE_X509_H 0)
SET(SIZEOF_SSIZE_T 0)
SET(HAVE_DOPRNT 0)
SET(HAVE_VPRINTF 1)
SET(HAVE_STRDUP 1)
SET(HAVE_STRSTR 1)
SET(HAVE_STRTOK_R 0)
SET(HAVE_STRFTIME 1)
SET(HAVE_UNAME 0)
SET(HAVE_STRCASECMP 0)
SET(HAVE_STRICMP 1)
SET(HAVE_STRCMPI 0)
SET(HAVE_GETTIMEOFDAY 0)
SET(HAVE_TCGETATTR 0)
SET(HAVE_PERROR 1)
SET(HAVE_SETVBUF 1)
SET(HAVE_SIGACTION 0)
SET(HAVE_GETPASS_R 0)
SET(HAVE_STRLCAT 0)
SET(HAVE_GETPWUID 0)
SET(HAVE_GETEUID 0)
SET(HAVE_UTIME 0)
SET(HAVE_RAND_STATUS 0)
SET(HAVE_RAND_EGD 0)
SET(HAVE_DLOPEN 0)
SET(HAVE_CONNECT 0)
SET(HAVE_GETHOSTBYNAME 0)
SET(HAVE_LOCALTIME_R 0)
SET(HAVE_GMTIME_R 0)
SET(HAVE_LIBNSL 0)
SET(HAVE_LIBUCB 0)
SET(HAVE_LIBDL 0)
SET(HAVE_LIBSOCKET 0)
SET(HAVE_UNISTD_H 0)
SET(HAVE_SYS_UTIME_H 1)
SET(HAVE_SOCKIO_H 0)
SET(HAVE_INET_NTOA_R 0)
SET(HAVE_TCSETATTR 0)
SET(HAVE_GETHOSTBYNAME_R 0)
SET(HAVE_GETHOSTBYADDR_R 0)
SET(TIME_WITH_SYS_TIME 0)
SET(HAVE_O_NONBLOCK 0)
SET(HAVE_GETHOSTBYADDR_R_5 0)
SET(HAVE_GETHOSTBYADDR_R_7 0)
SET(HAVE_GETHOSTBYADDR_R_8 0)
SET(HAVE_GETHOSTBYADDR_R_5_REENTRANT 0)
SET(HAVE_GETHOSTBYADDR_R_7_REENTRANT 0)
SET(HAVE_GETHOSTBYADDR_R_8_REENTRANT 0)
SET(HAVE_GETHOSTBYNAME_R_3 0)
SET(HAVE_GETHOSTBYNAME_R_5 0)
SET(HAVE_GETHOSTBYNAME_R_6 0)
SET(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
SET(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
SET(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
SET(HAVE_SOCKLEN_T 0)
SET(HAVE_IN_ADDR_T 0)
SET(STDC_HEADERS 1)
SET(RETSIGTYPE_TEST 1)
ELSE(WIN32)
MESSAGE("This file should be included on Windows platform only")
ENDIF(WIN32)
+101
View File
@@ -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
+270
View File
@@ -0,0 +1,270 @@
/*****************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2001, Andrew Francis and Daniel Stenberg
*
* In order to be useful for every potential user, curl and libcurl are
* dual-licensed under the MPL and the MIT/X-derivate licenses.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the MPL or the MIT/X-derivate
* licenses. You may pick one of these licenses.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
*****************************************************************************/
/* Base64 encoding/decoding
*
* Test harnesses down the bottom - compile with -DTEST_ENCODE for
* a program that will read in raw data from stdin and write out
* a base64-encoded version to stdout, and the length returned by the
* encoding function to stderr. Compile with -DTEST_DECODE for a program that
* will go the other way.
*
* This code will break if int is smaller than 32 bits
*/
#include "setup.h"
#include <stdlib.h>
#include <string.h>
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
#include "base64.h"
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
static void decodeQuantum(unsigned char *dest, char *src)
{
unsigned int x = 0;
int i;
for(i = 0; i < 4; i++) {
if(src[i] >= 'A' && src[i] <= 'Z')
x = (x << 6) + (unsigned int)(src[i] - 'A' + 0);
else if(src[i] >= 'a' && src[i] <= 'z')
x = (x << 6) + (unsigned int)(src[i] - 'a' + 26);
else if(src[i] >= '0' && src[i] <= '9')
x = (x << 6) + (unsigned int)(src[i] - '0' + 52);
else if(src[i] == '+')
x = (x << 6) + 62;
else if(src[i] == '/')
x = (x << 6) + 63;
}
dest[2] = (unsigned char)(x & 255); x >>= 8;
dest[1] = (unsigned char)(x & 255); x >>= 8;
dest[0] = (unsigned char)(x & 255); x >>= 8;
}
/* base64Decode
* Given a base64 string at src, decode it into the memory pointed
* to by dest. If rawLength points to a valid address (ie not NULL),
* store the length of the decoded data to it.
*/
static void base64Decode(unsigned char *dest, char *src, int *rawLength)
{
int length = 0;
int equalsTerm = 0;
int i;
unsigned char lastQuantum[3];
while((src[length] != '=') && src[length])
length++;
while(src[length+equalsTerm] == '=')
equalsTerm++;
if(rawLength)
*rawLength = (length * 3 / 4) - equalsTerm;
for(i = 0; i < length/4 - 1; i++) {
decodeQuantum(dest, src);
dest += 3; src += 4;
}
decodeQuantum(lastQuantum, src);
for(i = 0; i < 3 - equalsTerm; i++) dest[i] = lastQuantum[i];
}
/* ---- Base64 Encoding --- */
static char table64[]=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/*
* Curl_base64_encode()
*
* Returns the length of the newly created base64 string. The third argument
* is a pointer to an allocated area holding the base64 data. If something
* went wrong, -1 is returned.
*
*/
int Curl_base64_encode(const void *inp, int insize, char **outptr)
{
unsigned char ibuf[3];
unsigned char obuf[4];
int i;
int inputparts;
char *output;
char *base64data;
char *indata = (char *)inp;
if(0 == insize)
insize = strlen(indata);
base64data = output = (char*)malloc(insize*4/3+4);
if(NULL == output)
return -1;
while(insize > 0) {
for (i = inputparts = 0; i < 3; i++) {
if(*indata) {
inputparts++;
ibuf[i] = *indata;
indata++;
insize--;
}
else
ibuf[i] = 0;
}
obuf [0] = (ibuf [0] & 0xFC) >> 2;
obuf [1] = ((ibuf [0] & 0x03) << 4) | ((ibuf [1] & 0xF0) >> 4);
obuf [2] = ((ibuf [1] & 0x0F) << 2) | ((ibuf [2] & 0xC0) >> 6);
obuf [3] = ibuf [2] & 0x3F;
switch(inputparts) {
case 1: /* only one byte read */
sprintf(output, "%c%c==",
table64[obuf[0]],
table64[obuf[1]]);
break;
case 2: /* two bytes read */
sprintf(output, "%c%c%c=",
table64[obuf[0]],
table64[obuf[1]],
table64[obuf[2]]);
break;
default:
sprintf(output, "%c%c%c%c",
table64[obuf[0]],
table64[obuf[1]],
table64[obuf[2]],
table64[obuf[3]] );
break;
}
output += 4;
}
*output=0;
*outptr = base64data; /* make it return the actual data memory */
return strlen(base64data); /* return the length of the new data */
}
/* ---- End of Base64 Encoding ---- */
int Curl_base64_decode(const char *str, void *data)
{
int ret;
base64Decode((unsigned char *)data, (char *)str, &ret);
return ret;
}
/************* TEST HARNESS STUFF ****************/
#ifdef TEST_ENCODE
/* encoding test harness. Read in standard input and write out the length
* returned by Curl_base64_encode, followed by the base64'd data itself
*/
#include <stdio.h>
#define TEST_NEED_SUCK
void *suck(int *);
int main(int argc, char **argv, char **envp) {
char *base64;
int base64Len;
unsigned char *data;
int dataLen;
data = (unsigned char *)suck(&dataLen);
base64Len = Curl_base64_encode(data, dataLen, &base64);
fprintf(stderr, "%d\n", base64Len);
fprintf(stdout, "%s", base64);
free(base64); free(data);
return 0;
}
#endif
#ifdef TEST_DECODE
/* decoding test harness. Read in a base64 string from stdin and write out the
* length returned by Curl_base64_decode, followed by the decoded data itself
*/
#include <stdio.h>
#define TEST_NEED_SUCK
void *suck(int *);
int main(int argc, char **argv, char **envp) {
char *base64;
int base64Len;
unsigned char *data;
int dataLen;
base64 = (char *)suck(&base64Len);
data = (unsigned char *)malloc(base64Len * 3/4 + 8);
dataLen = Curl_base64_decode(base64, data);
fprintf(stderr, "%d\n", dataLen);
fwrite(data,1,dataLen,stdout);
free(base64); free(data);
return 0;
}
#endif
#ifdef TEST_NEED_SUCK
/* this function 'sucks' in as much as possible from stdin */
void *suck(int *lenptr) {
int cursize = 8192;
unsigned char *buf = NULL;
int lastread;
int len = 0;
do {
cursize *= 2;
buf = (unsigned char *)realloc(buf, cursize);
memset(buf + len, 0, cursize - len);
lastread = fread(buf + len, 1, cursize - len, stdin);
len += lastread;
} while(!feof(stdin));
lenptr[0] = len;
return (void *)buf;
}
#endif
/*
* local variables:
* eval: (load-file "../curl-mode.el")
* end:
* vim600: fdm=marker
* vim: et sw=2 ts=2 sts=2 tw=78
*/
+27
View File
@@ -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
+409
View File
@@ -0,0 +1,409 @@
/* lib/config.h.in. Generated automatically from configure.in by autoheader. */
/* Name of this package! */
#define PACKAGE "${PACKAGE}"
/* Version number of this archive. */
#define VERSION "${VERSION}"
/* Define if you have the getpass function. */
#cmakedefine HAVE_GETPASS ${HAVE_GETPASS}
/* Define cpu-machine-OS */
#define OS "${OPERATING_SYSTEM}"
/* Define if you have the gethostbyaddr_r() function with 5 arguments */
#cmakedefine HAVE_GETHOSTBYADDR_R_5 ${HAVE_GETHOSTBYADDR_R_5}
/* Define if you have the gethostbyaddr_r() function with 7 arguments */
#cmakedefine HAVE_GETHOSTBYADDR_R_7 ${HAVE_GETHOSTBYADDR_R_7}
/* Define if you have the gethostbyaddr_r() function with 8 arguments */
#cmakedefine HAVE_GETHOSTBYADDR_R_8 ${HAVE_GETHOSTBYADDR_R_8}
/* Define if you have the gethostbyname_r() function with 3 arguments */
#cmakedefine HAVE_GETHOSTBYNAME_R_3 ${HAVE_GETHOSTBYNAME_R_3}
/* Define if you have the gethostbyname_r() function with 5 arguments */
#cmakedefine HAVE_GETHOSTBYNAME_R_5 ${HAVE_GETHOSTBYNAME_R_5}
/* Define if you have the gethostbyname_r() function with 6 arguments */
#cmakedefine HAVE_GETHOSTBYNAME_R_6 ${HAVE_GETHOSTBYNAME_R_6}
/* Define if you have the inet_ntoa_r function declared. */
#cmakedefine HAVE_INET_NTOA_R_DECL ${HAVE_INET_NTOA_R_DECL}
/* Define if you have the getservbyname function. */
#cmakedefine HAVE_GETSERVBYNAME ${HAVE_GETSERVBYNAME}
/* Define if you need the _REENTRANT define for some functions */
#cmakedefine NEED_REENTRANT ${NEED_REENTRANT}
/* Define if you have the Kerberos4 libraries (including -ldes) */
#cmakedefine KRB4 ${KRB4}
/* Define if you want to enable IPv6 support */
#cmakedefine ENABLE_IPV6 ${ENABLE_IPV6}
/* Define this to 'int' if ssize_t is not an available typedefed type */
#cmakedefine ssize_t ${ssize_t}
/* Define this to 'int' if socklen_t is not an available typedefed type */
#cmakedefine socklen_t ${socklen_t}
/* Define this as a suitable file to read random data from */
#cmakedefine RANDOM_FILE "${RANDOM_FILE}"
/* Define this to your Entropy Gathering Daemon socket pathname */
#cmakedefine EGD_SOCKET ${EGD_SOCKET}
/* Define if you have a working OpenSSL installation */
#cmakedefine OPENSSL_ENABLED ${OPENSSL_ENABLED}
/* Define the one correct non-blocking socket method below */
#cmakedefine HAVE_FIONBIO ${HAVE_FIONBIO}
#cmakedefine HAVE_IOCTLSOCKET ${HAVE_IOCTLSOCKET}
#cmakedefine HAVE_IOCTLSOCKET_CASE ${HAVE_IOCTLSOCKET_CASE}
#cmakedefine HAVE_O_NONBLOCK ${HAVE_O_NONBLOCK}
#cmakedefine HAVE_DISABLED_NONBLOCKING ${HAVE_DISABLED_NONBLOCKING}
/* Define this to 'int' if in_addr_t is not an available typedefed type */
#cmakedefine in_addr_t ${in_addr_t}
/* Set to explicitly specify we don't want to use thread-safe functions */
#cmakedefine DISABLED_THREADSAFE ${DISABLED_THREADSAFE}
/* Define if you want to enable IPv6 support */
#cmakedefine ENABLE_IPV6 ${ENABLE_IPV6}
/* Define if you have the <alloca.h> header file. */
#cmakedefine HAVE_ALLOCA_H ${HAVE_ALLOCA_H}
/* Define if you have the <arpa/inet.h> header file. */
#cmakedefine HAVE_ARPA_INET_H ${HAVE_ARPA_INET_H}
/* Define if you have the `closesocket' function. */
#cmakedefine HAVE_CLOSESOCKET ${HAVE_CLOSESOCKET}
/* Define if you have the <crypto.h> header file. */
#cmakedefine HAVE_CRYPTO_H ${HAVE_CRYPTO_H}
/* Define if you have the <des.h> header file. */
#cmakedefine HAVE_DES_H ${HAVE_DES_H}
/* Define if you have the <dlfcn.h> header file. */
#cmakedefine HAVE_DLFCN_H ${HAVE_DLFCN_H}
/* Define if you have the `dlopen' function. */
#cmakedefine HAVE_DLOPEN ${HAVE_DLOPEN}
/* Define if you have the <err.h> header file. */
#cmakedefine HAVE_ERR_H ${HAVE_ERR_H}
/* Define if you have the <fcntl.h> header file. */
#cmakedefine HAVE_FCNTL_H ${HAVE_FCNTL_H}
/* Define if getaddrinfo exists and works */
#cmakedefine HAVE_GETADDRINFO ${HAVE_GETADDRINFO}
/* Define if you have the `geteuid' function. */
#cmakedefine HAVE_GETEUID ${HAVE_GETEUID}
/* Define if you have the `gethostbyaddr' function. */
#cmakedefine HAVE_GETHOSTBYADDR ${HAVE_GETHOSTBYADDR}
/* Define if you have the `gethostbyaddr_r' function. */
#cmakedefine HAVE_GETHOSTBYADDR_R ${HAVE_GETHOSTBYADDR_R}
/* Define if you have the `gethostbyname_r' function. */
#cmakedefine HAVE_GETHOSTBYNAME_R ${HAVE_GETHOSTBYNAME_R}
/* Define if you have the `gethostname' function. */
#cmakedefine HAVE_GETHOSTNAME ${HAVE_GETHOSTNAME}
/* Define if you have the `getpass_r' function. */
#cmakedefine HAVE_GETPASS_R ${HAVE_GETPASS_R}
/* Define if you have the `getpwuid' function. */
#cmakedefine HAVE_GETPWUID ${HAVE_GETPWUID}
/* Define if you have the `gettimeofday' function. */
#cmakedefine HAVE_GETTIMEOFDAY ${HAVE_GETTIMEOFDAY}
/* Define if you have the `gmtime_r' function. */
#cmakedefine HAVE_GMTIME_R ${HAVE_GMTIME_R}
/* Define if you have the `inet_addr' function. */
#cmakedefine HAVE_INET_ADDR ${HAVE_INET_ADDR}
/* Define if you have the `inet_ntoa' function. */
#cmakedefine HAVE_INET_NTOA ${HAVE_INET_NTOA}
/* Define if you have the `inet_ntoa_r' function. */
#cmakedefine HAVE_INET_NTOA_R ${HAVE_INET_NTOA_R}
/* Define if you have the <inttypes.h> header file. */
#cmakedefine HAVE_INTTYPES_H ${HAVE_INTTYPES_H}
/* Define if you have the <io.h> header file. */
#cmakedefine HAVE_IO_H ${HAVE_IO_H}
/* Define if you have the `krb_get_our_ip_for_realm' function. */
#cmakedefine HAVE_KRB_GET_OUR_IP_FOR_REALM ${HAVE_KRB_GET_OUR_IP_FOR_REALM}
/* Define if you have the <krb.h> header file. */
#cmakedefine HAVE_KRB_H ${HAVE_KRB_H}
/* Define if you have the `crypto' library (-lcrypto). */
#cmakedefine HAVE_LIBCRYPTO ${HAVE_LIBCRYPTO}
/* Define if you have the `dl' library (-ldl). */
#cmakedefine HAVE_LIBDL ${HAVE_LIBDL}
/* Define if you have the `nsl' library (-lnsl). */
#cmakedefine HAVE_LIBNSL ${HAVE_LIBNSL}
/* Define if you have the `resolv' library (-lresolv). */
#cmakedefine HAVE_LIBRESOLV ${HAVE_LIBRESOLV}
/* Define if you have the `resolve' library (-lresolve). */
#cmakedefine HAVE_LIBRESOLVE ${HAVE_LIBRESOLVE}
/* Define if you have the `socket' library (-lsocket). */
#cmakedefine HAVE_LIBSOCKET ${HAVE_LIBSOCKET}
/* Define if you have the `ssl' library (-lssl). */
#cmakedefine HAVE_LIBSSL ${HAVE_LIBSSL}
/* Define if you have the `ucb' library (-lucb). */
#cmakedefine HAVE_LIBUCB ${HAVE_LIBUCB}
/* Define if you have the `localtime_r' function. */
#cmakedefine HAVE_LOCALTIME_R ${HAVE_LOCALTIME_R}
/* Define if you have the <malloc.h> header file. */
#cmakedefine HAVE_MALLOC_H ${HAVE_MALLOC_H}
/* Define if you have the <memory.h> header file. */
#cmakedefine HAVE_MEMORY_H ${HAVE_MEMORY_H}
/* Define if you have the <netdb.h> header file. */
#cmakedefine HAVE_NETDB_H ${HAVE_NETDB_H}
/* Define if you have the <netinet/if_ether.h> header file. */
#cmakedefine HAVE_NETINET_IF_ETHER_H ${HAVE_NETINET_IF_ETHER_H}
/* Define if you have the <netinet/in.h> header file. */
#cmakedefine HAVE_NETINET_IN_H ${HAVE_NETINET_IN_H}
/* Define if you have the <net/if.h> header file. */
#cmakedefine HAVE_NET_IF_H ${HAVE_NET_IF_H}
/* Define if you have the <openssl/crypto.h> header file. */
#cmakedefine HAVE_OPENSSL_CRYPTO_H ${HAVE_OPENSSL_CRYPTO_H}
/* Define if you have the <openssl/engine.h> header file. */
#cmakedefine HAVE_OPENSSL_ENGINE_H ${HAVE_OPENSSL_ENGINE_H}
/* Define if you have the <openssl/err.h> header file. */
#cmakedefine HAVE_OPENSSL_ERR_H ${HAVE_OPENSSL_ERR_H}
/* Define if you have the <openssl/pem.h> header file. */
#cmakedefine HAVE_OPENSSL_PEM_H ${HAVE_OPENSSL_PEM_H}
/* Define if you have the <openssl/rsa.h> header file. */
#cmakedefine HAVE_OPENSSL_RSA_H ${HAVE_OPENSSL_RSA_H}
/* Define if you have the <openssl/ssl.h> header file. */
#cmakedefine HAVE_OPENSSL_SSL_H ${HAVE_OPENSSL_SSL_H}
/* Define if you have the <openssl/x509.h> header file. */
#cmakedefine HAVE_OPENSSL_X509_H ${HAVE_OPENSSL_X509_H}
/* Define if you have the <pem.h> header file. */
#cmakedefine HAVE_PEM_H ${HAVE_PEM_H}
/* Define if you have the `perror' function. */
#cmakedefine HAVE_PERROR ${HAVE_PERROR}
/* Define if you have the <pwd.h> header file. */
#cmakedefine HAVE_PWD_H ${HAVE_PWD_H}
/* Define if you have the `RAND_egd' function. */
#cmakedefine HAVE_RAND_EGD ${HAVE_RAND_EGD}
/* Define if you have the `RAND_screen' function. */
#cmakedefine HAVE_RAND_SCREEN ${HAVE_RAND_SCREEN}
/* Define if you have the `RAND_status' function. */
#cmakedefine HAVE_RAND_STATUS ${HAVE_RAND_STATUS}
/* Define if you have the <rsa.h> header file. */
#cmakedefine HAVE_RSA_H ${HAVE_RSA_H}
/* Define if you have the `select' function. */
#cmakedefine HAVE_SELECT ${HAVE_SELECT}
/* Define if you have the `setvbuf' function. */
#cmakedefine HAVE_SETVBUF ${HAVE_SETVBUF}
/* Define if you have the <sgtty.h> header file. */
#cmakedefine HAVE_SGTTY_H ${HAVE_SGTTY_H}
/* Define if you have the `sigaction' function. */
#cmakedefine HAVE_SIGACTION ${HAVE_SIGACTION}
/* Define if you have the `signal' function. */
#cmakedefine HAVE_SIGNAL ${HAVE_SIGNAL}
/* Define if you have the `socket' function. */
#cmakedefine HAVE_SOCKET ${HAVE_SOCKET}
/* Define if you have the <ssl.h> header file. */
#cmakedefine HAVE_SSL_H ${HAVE_SSL_H}
/* Define if you have the <stdint.h> header file. */
#cmakedefine HAVE_STDINT_H ${HAVE_STDINT_H}
/* Define if you have the <stdlib.h> header file. */
#cmakedefine HAVE_STDLIB_H ${HAVE_STDLIB_H}
/* Define if you have the `strcasecmp' function. */
#cmakedefine HAVE_STRCASECMP ${HAVE_STRCASECMP}
/* Define if you have the `strcmpi' function. */
#cmakedefine HAVE_STRCMPI ${HAVE_STRCMPI}
/* Define if you have the `strdup' function. */
#cmakedefine HAVE_STRDUP ${HAVE_STRDUP}
/* Define if you have the `strftime' function. */
#cmakedefine HAVE_STRFTIME ${HAVE_STRFTIME}
/* Define if you have the `stricmp' function. */
#cmakedefine HAVE_STRICMP ${HAVE_STRICMP}
/* Define if you have the <strings.h> header file. */
#cmakedefine HAVE_STRINGS_H ${HAVE_STRINGS_H}
/* Define if you have the <string.h> header file. */
#cmakedefine HAVE_STRING_H ${HAVE_STRING_H}
/* Define if you have the `strlcat' function. */
#cmakedefine HAVE_STRLCAT ${HAVE_STRLCAT}
/* Define if you have the `strlcpy' function. */
#cmakedefine HAVE_STRLCPY ${HAVE_STRLCPY}
/* Define if you have the `strstr' function. */
#cmakedefine HAVE_STRSTR ${HAVE_STRSTR}
/* Define if you have the `strtok_r' function. */
#cmakedefine HAVE_STRTOK_R ${HAVE_STRTOK_R}
/* Define if you have the <sys/param.h> header file. */
#cmakedefine HAVE_SYS_PARAM_H ${HAVE_SYS_PARAM_H}
/* Define if you have the <sys/select.h> header file. */
#cmakedefine HAVE_SYS_SELECT_H ${HAVE_SYS_SELECT_H}
/* Define if you have the <sys/socket.h> header file. */
#cmakedefine HAVE_SYS_SOCKET_H ${HAVE_SYS_SOCKET_H}
/* Define if you have the <sys/sockio.h> header file. */
#cmakedefine HAVE_SYS_SOCKIO_H ${HAVE_SYS_SOCKIO_H}
/* Define if you have the <sys/stat.h> header file. */
#cmakedefine HAVE_SYS_STAT_H ${HAVE_SYS_STAT_H}
/* Define if you have the <sys/time.h> header file. */
#cmakedefine HAVE_SYS_TIME_H ${HAVE_SYS_TIME_H}
/* Define if you have the <sys/types.h> header file. */
#cmakedefine HAVE_SYS_TYPES_H ${HAVE_SYS_TYPES_H}
/* Define if you have the <sys/utime.h> header file. */
#cmakedefine HAVE_SYS_UTIME_H ${HAVE_SYS_UTIME_H}
/* Define if you have the `tcgetattr' function. */
#cmakedefine HAVE_TCGETATTR ${HAVE_TCGETATTR}
/* Define if you have the `tcsetattr' function. */
#cmakedefine HAVE_TCSETATTR ${HAVE_TCSETATTR}
/* Define if you have the <termios.h> header file. */
#cmakedefine HAVE_TERMIOS_H ${HAVE_TERMIOS_H}
/* Define if you have the <termio.h> header file. */
#cmakedefine HAVE_TERMIO_H ${HAVE_TERMIO_H}
/* Define if you have the <time.h> header file. */
#cmakedefine HAVE_TIME_H ${HAVE_TIME_H}
/* Define if you have the `uname' function. */
#cmakedefine HAVE_UNAME ${HAVE_UNAME}
/* Define if you have the <unistd.h> header file. */
#cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H}
/* Define if you have the `utime' function. */
#cmakedefine HAVE_UTIME ${HAVE_UTIME}
/* Define if you have the <utime.h> header file. */
#cmakedefine HAVE_UTIME_H ${HAVE_UTIME_H}
/* Define if you have the <winsock.h> header file. */
#cmakedefine HAVE_WINSOCK_H ${HAVE_WINSOCK_H}
/* Define if you have the <x509.h> header file. */
#cmakedefine HAVE_X509_H ${HAVE_X509_H}
/* Name of package */
#cmakedefine PACKAGE "${PACKAGE}"
/* Define as the return type of signal handlers (`int' or `void'). */
#cmakedefine RETSIGTYPE ${RETSIGTYPE}
/* Define if you have the ANSI C header files. */
#cmakedefine STDC_HEADERS ${STDC_HEADERS}
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#cmakedefine TIME_WITH_SYS_TIME ${TIME_WITH_SYS_TIME}
/* Version number of package */
#cmakedefine VERSION "${VERSION}"
/* Define if on AIX 3.
System headers sometimes define this.
We just want to avoid a redefinition error message. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
/* Define for large files, on AIX-style hosts. */
#cmakedefine _LARGE_FILES ${_LARGE_FILES}
/* Define to empty if `const' does not conform to ANSI C. */
#cmakedefine const ${const}
/* type to use in place of in_addr_t if not defined */
#cmakedefine in_addr_t ${in_addr_t}
/* Define to `unsigned' if <sys/types.h> does not define. */
#cmakedefine size_t ${size_t}
/* type to use in place of socklen_t if not defined */
#cmakedefine socklen_t ${socklen_t}
/* Define to `int' if <sys/types.h> does not define. */
#cmakedefine ssize_t ${ssize_t}
/* Define if you don't have vprintf but do have _doprnt. */
#cmakedefine HAVE_DOPRNT ${HAVE_DOPRNT}
/* Define if you have the vprintf function. */
#cmakedefine HAVE_VPRINTF ${HAVE_VPRINTF}
/* The number of bytes in a long double. */
#cmakedefine SIZEOF_LONG_DOUBLE ${SIZEOF_LONG_DOUBLE}
+581
View File
@@ -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;
}
+35
View File
@@ -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
+738
View File
@@ -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
*/
+84
View File
@@ -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
+241
View File
@@ -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
*/
+29
View File
@@ -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
+96
View File
@@ -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
*/
+340
View File
@@ -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
*/
+120
View File
@@ -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
*/
+32
View File
@@ -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
+206
View File
@@ -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
*/
+28
View File
@@ -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
+1501
View File
@@ -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
*/
+73
View File
@@ -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
+2138
View File
@@ -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
*/
+37
View File
@@ -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
+2149
View File
@@ -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) */
+37
View File
@@ -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));
+77
View File
@@ -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
*/
+161
View File
@@ -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
*/
+28
View File
@@ -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
+252
View File
@@ -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
*/
+35
View File
@@ -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
+285
View File
@@ -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
*/
+85
View File
@@ -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
+529
View File
@@ -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 *)&num;
sprintf(addr, "%u.%u.%u.%u", paddr[0], paddr[1], paddr[2], paddr[3]);
#endif
return (addr);
}
/* The original code to this function was once stolen from the Dancer source
code, written by Bjorn Reese, it has since been patched and modified
considerably. */
#ifndef INADDR_NONE
#define INADDR_NONE (in_addr_t) ~0
#endif
Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data,
char *hostname,
int port,
char **bufp)
{
struct hostent *h = NULL;
in_addr_t in;
int ret; /* this variable is unused on several platforms but used on some */
#define CURL_NAMELOOKUP_SIZE 9000
/* Allocate enough memory to hold the full name information structs and
* everything. OSF1 is known to require at least 8872 bytes. The buffer
* required for storing all possible aliases and IP numbers is according to
* Stevens' Unix Network Programming 2nd editor, p. 304: 8192 bytes! */
int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE);
if(!buf)
return NULL; /* major failure */
*bufp = (char *)buf;
port=0; /* unused in IPv4 code */
ret = 0; /* to prevent the compiler warning */
if ( (in=inet_addr(hostname)) != INADDR_NONE ) {
struct in_addr *addrentry;
h = (struct hostent*)buf;
h->h_addr_list = (char**)(buf + sizeof(*h));
addrentry = (struct in_addr*)(h->h_addr_list + 2);
addrentry->s_addr = in;
h->h_addr_list[0] = (char*)addrentry;
h->h_addr_list[1] = NULL;
h->h_addrtype = AF_INET;
h->h_length = sizeof(*addrentry);
h->h_name = *(h->h_addr_list) + h->h_length;
/* bad one h->h_name = (char*)(h->h_addr_list + h->h_length); */
MakeIP(ntohl(in),h->h_name, CURL_NAMELOOKUP_SIZE - (long)(h->h_name) + (long)buf);
}
#if defined(HAVE_GETHOSTBYNAME_R)
else {
int h_errnop;
/* Workaround for gethostbyname_r bug in qnx nto. It is also _required_
for some of these functions. */
memset(buf, 0, CURL_NAMELOOKUP_SIZE);
#ifdef HAVE_GETHOSTBYNAME_R_5
/* Solaris, IRIX and more */
if ((h = gethostbyname_r(hostname,
(struct hostent *)buf,
(char *)buf + sizeof(struct hostent),
CURL_NAMELOOKUP_SIZE - sizeof(struct hostent),
&h_errnop)) == NULL )
#endif
#ifdef HAVE_GETHOSTBYNAME_R_6
/* Linux */
if( gethostbyname_r(hostname,
(struct hostent *)buf,
(char *)buf + sizeof(struct hostent),
CURL_NAMELOOKUP_SIZE - sizeof(struct hostent),
&h, /* DIFFERENCE */
&h_errnop))
#endif
#ifdef HAVE_GETHOSTBYNAME_R_3
/* AIX, Digital Unix, HPUX 10, more? */
if(CURL_NAMELOOKUP_SIZE >=
(sizeof(struct hostent)+sizeof(struct hostent_data)))
/* August 22nd, 2000: Albert Chin-A-Young brought an updated version
* that should work! September 20: Richard Prescott worked on the buffer
* size dilemma. */
ret = gethostbyname_r(hostname,
(struct hostent *)buf,
(struct hostent_data *)(buf + sizeof(struct hostent)));
else
ret = -1; /* failure, too smallish buffer size */
/* result expected in h */
h = (struct hostent*)buf;
h_errnop= errno; /* we don't deal with this, but set it anyway */
if(ret)
#endif
{
infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
h = NULL; /* set return code to NULL */
free(buf);
*bufp=NULL;
}
#else
else {
if ((h = gethostbyname(hostname)) == NULL ) {
infof(data, "gethostbyname(2) failed for %s\n", hostname);
free(buf);
*bufp=NULL;
}
else
/* we make a copy of the hostent right now, right here, as the
static one we got a pointer to might get removed when we don't
want/expect that */
h = pack_hostent((char *)buf, h);
#endif
}
return (h);
}
#endif /* end of IPv4-specific code */
/*
* local variables:
* eval: (load-file "../curl-mode.el")
* end:
* vim600: fdm=marker
* vim: et sw=2 ts=2 sts=2 tw=78
*/
+60
View File
@@ -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
+985
View File
@@ -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
*/
+42
View File
@@ -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
+230
View File
@@ -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
*/
+87
View File
@@ -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
+134
View File
@@ -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
*/
+33
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
#ifndef __INET_NTOA_R_H
#define __INET_NTOA_R_H
/*
* My solaris 5.6 system running gcc 2.8.1 does *not* have this prototype
* in any system include file! Isn't that weird?
*/
char *inet_ntoa_r(const struct in_addr in, char *buffer, int buflen);
#endif
+401
View File
@@ -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
*/
+27
View File
@@ -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
+231
View File
@@ -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
*/
+29
View File
@@ -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 */
+168
View File
@@ -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;
}
+64
View File
@@ -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
+221
View File
@@ -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
*/
+61
View File
@@ -0,0 +1,61 @@
#ifdef MALLOCDEBUG
#include "setup.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <stdio.h>
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
extern FILE *logfile;
/* memory functions */
void *curl_domalloc(size_t size, int line, const char *source);
void *curl_dorealloc(void *ptr, size_t size, int line, const char *source);
void curl_dofree(void *ptr, int line, const char *source);
char *curl_dostrdup(const char *str, int line, const char *source);
void curl_memdebug(const char *logname);
/* file descriptor manipulators */
int curl_socket(int domain, int type, int protocol, int, const char *);
int curl_sclose(int sockfd, int, const char *source);
int curl_accept(int s, struct sockaddr *addr, socklen_t *addrlen,
int line, const char *source);
/* FILE functions */
FILE *curl_fopen(const char *file, const char *mode, int line,
const char *source);
int curl_fclose(FILE *file, int line, const char *source);
/* Set this symbol on the command-line, recompile all lib-sources */
#define strdup(ptr) curl_dostrdup(ptr, __LINE__, __FILE__)
#define malloc(size) curl_domalloc(size, __LINE__, __FILE__)
#define realloc(ptr,size) curl_dorealloc(ptr, size, __LINE__, __FILE__)
#define free(ptr) curl_dofree(ptr, __LINE__, __FILE__)
#define socket(domain,type,protocol)\
curl_socket(domain,type,protocol,__LINE__,__FILE__)
#define accept(sock,addr,len)\
curl_accept(sock,addr,len,__LINE__,__FILE__)
#define getaddrinfo(host,serv,hint,res) \
curl_getaddrinfo(host,serv,hint,res,__LINE__,__FILE__)
#define freeaddrinfo(data) \
curl_freeaddrinfo(data,__LINE__,__FILE__)
/* sclose is probably already defined, redefine it! */
#undef sclose
#define sclose(sockfd) curl_sclose(sockfd,__LINE__,__FILE__)
#undef fopen
#define fopen(file,mode) curl_fopen(file,mode,__LINE__,__FILE__)
#define fclose(file) curl_fclose(file,__LINE__,__FILE__)
#endif
+1181
View File
@@ -0,0 +1,1181 @@
/****************************************************************************
*
* $Id$
*
*************************************************************************
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
*
* Purpose:
* A merge of Bjorn Reese's format() function and Daniel's dsprintf()
* 1.0. A full blooded printf() clone with full support for <num>$
* everywhere (parameters, widths and precisions) including variabled
* sized parameters (like doubles, long longs, long doubles and even
* void * in 64-bit architectures).
*
* Current restrictions:
* - Max 128 parameters
* - No 'long double' support.
*
* If you ever want truly portable and good *printf() clones, the project that
* took on from here is named 'Trio' and you find more details on the trio web
* page at http://daniel.haxx.se/trio/
*/
#include "setup.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#ifndef SIZEOF_LONG_LONG
/* prevents warnings on picky compilers */
#define SIZEOF_LONG_LONG 0
#endif
#ifndef SIZEOF_LONG_DOUBLE
#define SIZEOF_LONG_DOUBLE 0
#endif
/* The last #include file should be: */
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
#define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
#define MAX_PARAMETERS 128 /* lame static limit */
#undef TRUE
#undef FALSE
#undef BOOL
#ifdef __cplusplus
# define TRUE true
# define FALSE false
# define BOOL bool
#else
# define TRUE ((char)(1 == 1))
# define FALSE ((char)(0 == 1))
# define BOOL char
#endif
/* Lower-case digits. */
static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
/* Upper-case digits. */
static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
#define OUTCHAR(x) done+=(stream(x, (FILE *)data)==-1?0:1)
/* Data type to read from the arglist */
typedef enum {
FORMAT_UNKNOWN = 0,
FORMAT_STRING,
FORMAT_PTR,
FORMAT_INT,
FORMAT_INTPTR,
FORMAT_LONG,
FORMAT_LONGLONG,
FORMAT_DOUBLE,
FORMAT_LONGDOUBLE,
FORMAT_WIDTH /* For internal use */
} FormatType;
/* convertion and display flags */
enum {
FLAGS_NEW = 0,
FLAGS_SPACE = 1<<0,
FLAGS_SHOWSIGN = 1<<1,
FLAGS_LEFT = 1<<2,
FLAGS_ALT = 1<<3,
FLAGS_SHORT = 1<<4,
FLAGS_LONG = 1<<5,
FLAGS_LONGLONG = 1<<6,
FLAGS_LONGDOUBLE = 1<<7,
FLAGS_PAD_NIL = 1<<8,
FLAGS_UNSIGNED = 1<<9,
FLAGS_OCTAL = 1<<10,
FLAGS_HEX = 1<<11,
FLAGS_UPPER = 1<<12,
FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */
FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
FLAGS_PREC = 1<<15, /* precision was specified */
FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */
FLAGS_CHAR = 1<<17, /* %c story */
FLAGS_FLOATE = 1<<18, /* %e or %E */
FLAGS_FLOATG = 1<<19 /* %g or %G */
};
typedef struct {
FormatType type;
int flags;
int width; /* width OR width parameter number */
int precision; /* precision OR precision parameter number */
union {
char *str;
void *ptr;
long num;
#if SIZEOF_LONG_LONG /* if this is non-zero */
long long lnum;
#endif
double dnum;
#if SIZEOF_LONG_DOUBLE
long double ldnum;
#endif
} data;
} va_stack_t;
struct nsprintf {
char *buffer;
size_t length;
size_t max;
};
struct asprintf {
char *buffer; /* allocated buffer */
size_t len; /* length of string */
size_t alloc; /* length of alloc */
};
int curl_msprintf(char *buffer, const char *format, ...);
static int dprintf_DollarString(char *input, char **end)
{
int number=0;
while(isdigit((int)*input)) {
number *= 10;
number += *input-'0';
input++;
}
if(number && ('$'==*input++)) {
*end = input;
return number;
}
return 0;
}
static BOOL dprintf_IsQualifierNoDollar(char c)
{
switch (c) {
case '-': case '+': case ' ': case '#': case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'h': case 'l': case 'L': case 'Z': case 'q':
return TRUE;
default:
return FALSE;
}
}
#ifdef DPRINTF_DEBUG2
int dprintf_Pass1Report(va_stack_t *vto, int max)
{
int i;
char buffer[128];
int bit;
int flags;
for(i=0; i<max; i++) {
char *type;
switch(vto[i].type) {
case FORMAT_UNKNOWN:
type = "unknown";
break;
case FORMAT_STRING:
type ="string";
break;
case FORMAT_PTR:
type ="pointer";
break;
case FORMAT_INT:
type = "int";
break;
case FORMAT_LONG:
type = "long";
break;
case FORMAT_LONGLONG:
type = "long long";
break;
case FORMAT_DOUBLE:
type = "double";
break;
case FORMAT_LONGDOUBLE:
type = "long double";
break;
}
buffer[0]=0;
for(bit=0; bit<31; bit++) {
flags = vto[i].flags & (1<<bit);
if(flags & FLAGS_SPACE)
strcat(buffer, "space ");
else if(flags & FLAGS_SHOWSIGN)
strcat(buffer, "plus ");
else if(flags & FLAGS_LEFT)
strcat(buffer, "left ");
else if(flags & FLAGS_ALT)
strcat(buffer, "alt ");
else if(flags & FLAGS_SHORT)
strcat(buffer, "short ");
else if(flags & FLAGS_LONG)
strcat(buffer, "long ");
else if(flags & FLAGS_LONGLONG)
strcat(buffer, "longlong ");
else if(flags & FLAGS_LONGDOUBLE)
strcat(buffer, "longdouble ");
else if(flags & FLAGS_PAD_NIL)
strcat(buffer, "padnil ");
else if(flags & FLAGS_UNSIGNED)
strcat(buffer, "unsigned ");
else if(flags & FLAGS_OCTAL)
strcat(buffer, "octal ");
else if(flags & FLAGS_HEX)
strcat(buffer, "hex ");
else if(flags & FLAGS_UPPER)
strcat(buffer, "upper ");
else if(flags & FLAGS_WIDTH)
strcat(buffer, "width ");
else if(flags & FLAGS_WIDTHPARAM)
strcat(buffer, "widthparam ");
else if(flags & FLAGS_PREC)
strcat(buffer, "precision ");
else if(flags & FLAGS_PRECPARAM)
strcat(buffer, "precparam ");
else if(flags & FLAGS_CHAR)
strcat(buffer, "char ");
else if(flags & FLAGS_FLOATE)
strcat(buffer, "floate ");
else if(flags & FLAGS_FLOATG)
strcat(buffer, "floatg ");
}
printf("REPORT: %d. %s [%s]\n", i, type, buffer);
}
}
#endif
/******************************************************************
*
* Pass 1:
* Create an index with the type of each parameter entry and its
* value (may vary in size)
*
******************************************************************/
static int dprintf_Pass1(char *format, va_stack_t *vto, char **endpos, va_list arglist)
{
char *fmt = format;
int param_num = 0;
int this_param;
int width;
int precision;
int flags;
int max_param=0;
int i;
while (*fmt) {
if (*fmt++ == '%') {
if (*fmt == '%') {
fmt++;
continue; /* while */
}
flags = FLAGS_NEW;
/* Handle the positional case (N$) */
param_num++;
this_param = dprintf_DollarString(fmt, &fmt);
if (0 == this_param)
/* we got no positional, get the next counter */
this_param = param_num;
if (this_param > max_param)
max_param = this_param;
/*
* The parameter with number 'i' should be used. Next, we need
* to get SIZE and TYPE of the parameter. Add the information
* to our array.
*/
width = 0;
precision = 0;
/* Handle the flags */
while (dprintf_IsQualifierNoDollar(*fmt)) {
switch (*fmt++) {
case ' ':
flags |= FLAGS_SPACE;
break;
case '+':
flags |= FLAGS_SHOWSIGN;
break;
case '-':
flags |= FLAGS_LEFT;
flags &= ~FLAGS_PAD_NIL;
break;
case '#':
flags |= FLAGS_ALT;
break;
case '.':
flags |= FLAGS_PREC;
if ('*' == *fmt) {
/* The precision is picked from a specified parameter */
flags |= FLAGS_PRECPARAM;
fmt++;
param_num++;
i = dprintf_DollarString(fmt, &fmt);
if (i)
precision = i;
else
precision = param_num;
if (precision > max_param)
max_param = precision;
}
else {
flags |= FLAGS_PREC;
precision = strtol(fmt, &fmt, 10);
}
break;
case 'h':
flags |= FLAGS_SHORT;
break;
case 'l':
if (flags & FLAGS_LONG)
flags |= FLAGS_LONGLONG;
else
flags |= FLAGS_LONG;
break;
case 'L':
flags |= FLAGS_LONGDOUBLE;
break;
case 'q':
flags |= FLAGS_LONGLONG;
break;
case 'Z':
if (sizeof(size_t) > sizeof(unsigned long int))
flags |= FLAGS_LONGLONG;
if (sizeof(size_t) > sizeof(unsigned int))
flags |= FLAGS_LONG;
break;
case '0':
if (!(flags & FLAGS_LEFT))
flags |= FLAGS_PAD_NIL;
/* FALLTHROUGH */
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
flags |= FLAGS_WIDTH;
width = strtol(fmt-1, &fmt, 10);
break;
case '*': /* Special case */
flags |= FLAGS_WIDTHPARAM;
param_num++;
i = dprintf_DollarString(fmt, &fmt);
if(i)
width = i;
else
width = param_num;
if(width > max_param)
max_param=width;
break;
default:
break;
}
} /* switch */
/* Handle the specifier */
i = this_param - 1;
switch (*fmt) {
case 'S':
flags |= FLAGS_ALT;
/* FALLTHROUGH */
case 's':
vto[i].type = FORMAT_STRING;
break;
case 'n':
vto[i].type = FORMAT_INTPTR;
break;
case 'p':
vto[i].type = FORMAT_PTR;
break;
case 'd': case 'i':
vto[i].type = FORMAT_INT;
break;
case 'u':
vto[i].type = FORMAT_INT;
flags |= FLAGS_UNSIGNED;
break;
case 'o':
vto[i].type = FORMAT_INT;
flags |= FLAGS_OCTAL;
break;
case 'x':
vto[i].type = FORMAT_INT;
flags |= FLAGS_HEX;
break;
case 'X':
vto[i].type = FORMAT_INT;
flags |= FLAGS_HEX|FLAGS_UPPER;
break;
case 'c':
vto[i].type = FORMAT_INT;
flags |= FLAGS_CHAR;
break;
case 'f':
vto[i].type = FORMAT_DOUBLE;
break;
case 'e': case 'E':
vto[i].type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATE| (('E' == *fmt)?FLAGS_UPPER:0);
break;
case 'g': case 'G':
vto[i].type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATG| (('G' == *fmt)?FLAGS_UPPER:0);
break;
default:
vto[i].type = FORMAT_UNKNOWN;
break;
} /* switch */
vto[i].flags = flags;
vto[i].width = width;
vto[i].precision = precision;
if (flags & FLAGS_WIDTHPARAM) {
/* we have the width specified from a parameter, so we make that
parameter's info setup properly */
vto[i].width = width - 1;
i = width - 1;
vto[i].type = FORMAT_WIDTH;
vto[i].flags = FLAGS_NEW;
vto[i].precision = vto[i].width = 0; /* can't use width or precision
of width! */
}
if (flags & FLAGS_PRECPARAM) {
/* we have the precision specified from a parameter, so we make that
parameter's info setup properly */
vto[i].precision = precision - 1;
i = precision - 1;
vto[i].type = FORMAT_WIDTH;
vto[i].flags = FLAGS_NEW;
vto[i].precision = vto[i].width = 0; /* can't use width or precision
of width! */
}
*endpos++ = fmt + 1; /* end of this sequence */
}
}
#ifdef DPRINTF_DEBUG2
dprintf_Pass1Report(vto, max_param);
#endif
/* Read the arg list parameters into our data list */
for (i=0; i<max_param; i++) {
if ((i + 1 < max_param) && (vto[i + 1].type == FORMAT_WIDTH))
{
/* Width/precision arguments must be read before the main argument
* they are attached to
*/
vto[i + 1].data.num = va_arg(arglist, int);
}
switch (vto[i].type)
{
case FORMAT_STRING:
vto[i].data.str = va_arg(arglist, char *);
break;
case FORMAT_INTPTR:
case FORMAT_UNKNOWN:
case FORMAT_PTR:
vto[i].data.ptr = va_arg(arglist, void *);
break;
case FORMAT_INT:
#if SIZEOF_LONG_LONG
if(vto[i].flags & FLAGS_LONGLONG)
vto[i].data.lnum = va_arg(arglist, long long);
else
#endif
if(vto[i].flags & FLAGS_LONG)
vto[i].data.num = va_arg(arglist, long);
else
vto[i].data.num = va_arg(arglist, int);
break;
case FORMAT_DOUBLE:
#if SIZEOF_LONG_DOUBLE
if(vto[i].flags & FLAGS_LONG)
vto[i].data.ldnum = va_arg(arglist, long double);
else
#endif
vto[i].data.dnum = va_arg(arglist, double);
break;
case FORMAT_WIDTH:
/* Argument has been read. Silently convert it into an integer
* for later use
*/
vto[i].type = FORMAT_INT;
break;
default:
break;
}
}
return max_param;
}
static int dprintf_formatf(
void *data, /* untouched by format(), just sent to the
stream() function in the first argument */
int (*stream)(int, FILE *), /* function pointer called for each
output character */
const char *format, /* %-formatted string */
va_list ap_save) /* list of parameters */
{
/* Base-36 digits for numbers. */
const char *digits = lower_digits;
/* Pointer into the format string. */
char *f;
/* Number of characters written. */
register size_t done = 0;
long param; /* current parameter to read */
long param_num=0; /* parameter counter */
va_stack_t vto[MAX_PARAMETERS];
char *endpos[MAX_PARAMETERS];
char **end;
char work[BUFFSIZE];
va_stack_t *p;
/* Do the actual %-code parsing */
dprintf_Pass1((char *)format, vto, endpos, ap_save);
end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
created for us */
f = (char *)format;
while (*f != '\0') {
/* Format spec modifiers. */
char alt;
/* Width of a field. */
register long width;
/* Precision of a field. */
long prec;
/* Decimal integer is negative. */
char is_neg;
/* Base of a number to be written. */
long base;
/* Integral values to be written. */
#if SIZEOF_LONG_LONG
unsigned long long num;
#else
unsigned long num;
#endif
long signed_num;
if (*f != '%') {
/* This isn't a format spec, so write everything out until the next one
OR end of string is reached. */
do {
OUTCHAR(*f);
} while(*++f && ('%' != *f));
continue;
}
++f;
/* Check for "%%". Note that although the ANSI standard lists
'%' as a conversion specifier, it says "The complete format
specification shall be `%%'," so we can avoid all the width
and precision processing. */
if (*f == '%') {
++f;
OUTCHAR('%');
continue;
}
/* If this is a positional parameter, the position must follow imediately
after the %, thus create a %<num>$ sequence */
param=dprintf_DollarString(f, &f);
if(!param)
param = param_num;
else
--param;
param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
third %s will pick the 3rd argument */
p = &vto[param];
/* pick up the specified width */
if(p->flags & FLAGS_WIDTHPARAM)
width = vto[p->width].data.num;
else
width = p->width;
/* pick up the specified precision */
if(p->flags & FLAGS_PRECPARAM)
prec = vto[p->precision].data.num;
else if(p->flags & FLAGS_PREC)
prec = p->precision;
else
prec = -1;
alt = p->flags & FLAGS_ALT;
switch (p->type) {
case FORMAT_INT:
num = p->data.num;
if(p->flags & FLAGS_CHAR) {
/* Character. */
if (!(p->flags & FLAGS_LEFT))
while (--width > 0)
OUTCHAR(' ');
OUTCHAR((char) num);
if (p->flags & FLAGS_LEFT)
while (--width > 0)
OUTCHAR(' ');
break;
}
if(p->flags & FLAGS_UNSIGNED) {
/* Decimal unsigned integer. */
base = 10;
goto unsigned_number;
}
if(p->flags & FLAGS_OCTAL) {
/* Octal unsigned integer. */
base = 8;
goto unsigned_number;
}
if(p->flags & FLAGS_HEX) {
/* Hexadecimal unsigned integer. */
digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
base = 16;
goto unsigned_number;
}
/* Decimal integer. */
base = 10;
#if SIZEOF_LONG_LONG
if(p->flags & FLAGS_LONGLONG) {
/* long long */
is_neg = p->data.lnum < 0;
num = is_neg ? (- p->data.lnum) : p->data.lnum;
}
else
#endif
{
signed_num = (long) num;
is_neg = signed_num < 0;
num = is_neg ? (- signed_num) : signed_num;
}
goto number;
unsigned_number:;
/* Unsigned number of base BASE. */
is_neg = 0;
number:;
/* Number of base BASE. */
{
char *workend = &work[sizeof(work) - 1];
register char *w;
/* Supply a default precision if none was given. */
if (prec == -1)
prec = 1;
/* Put the number in WORK. */
w = workend;
while (num > 0) {
*w-- = digits[num % base];
num /= base;
}
width -= workend - w;
prec -= workend - w;
if (alt && base == 8 && prec <= 0) {
*w-- = '0';
--width;
}
if (prec > 0) {
width -= prec;
while (prec-- > 0)
*w-- = '0';
}
if (alt && base == 16)
width -= 2;
if (is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
--width;
if (!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
while (width-- > 0)
OUTCHAR(' ');
if (is_neg)
OUTCHAR('-');
else if (p->flags & FLAGS_SHOWSIGN)
OUTCHAR('+');
else if (p->flags & FLAGS_SPACE)
OUTCHAR(' ');
if (alt && base == 16) {
OUTCHAR('0');
if(p->flags & FLAGS_UPPER)
OUTCHAR('X');
else
OUTCHAR('x');
}
if (!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
while (width-- > 0)
OUTCHAR('0');
/* Write the number. */
while (++w <= workend) {
OUTCHAR(*w);
}
if (p->flags & FLAGS_LEFT)
while (width-- > 0)
OUTCHAR(' ');
}
break;
case FORMAT_STRING:
/* String. */
{
static char null[] = "(nil)";
char *str;
size_t len;
str = (char *) p->data.str;
if ( str == NULL) {
/* Write null[] if there's space. */
if (prec == -1 || prec >= (long) sizeof(null) - 1) {
str = null;
len = sizeof(null) - 1;
/* Disable quotes around (nil) */
p->flags &= (~FLAGS_ALT);
}
else {
str = (char *)"";
len = 0;
}
}
else
len = strlen(str);
if (prec != -1 && (size_t) prec < len)
len = prec;
width -= len;
if (p->flags & FLAGS_ALT)
OUTCHAR('"');
if (!(p->flags&FLAGS_LEFT))
while (width-- > 0)
OUTCHAR(' ');
while (len-- > 0)
OUTCHAR(*str++);
if (p->flags&FLAGS_LEFT)
while (width-- > 0)
OUTCHAR(' ');
if (p->flags & FLAGS_ALT)
OUTCHAR('"');
}
break;
case FORMAT_PTR:
/* Generic pointer. */
{
void *ptr;
ptr = (void *) p->data.ptr;
if (ptr != NULL) {
/* If the pointer is not NULL, write it as a %#x spec. */
base = 16;
digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
alt = 1;
num = (unsigned long) ptr;
is_neg = 0;
goto number;
}
else {
/* Write "(nil)" for a nil pointer. */
static char strnil[] = "(nil)";
register char *point;
width -= sizeof(strnil) - 1;
if (p->flags & FLAGS_LEFT)
while (width-- > 0)
OUTCHAR(' ');
for (point = strnil; *point != '\0'; ++point)
OUTCHAR(*point);
if (! (p->flags & FLAGS_LEFT))
while (width-- > 0)
OUTCHAR(' ');
}
}
break;
case FORMAT_DOUBLE:
{
char formatbuf[32]="%";
char *fptr;
width = -1;
if (p->flags & FLAGS_WIDTH)
width = p->width;
else if (p->flags & FLAGS_WIDTHPARAM)
width = vto[p->width].data.num;
prec = -1;
if (p->flags & FLAGS_PREC)
prec = p->precision;
else if (p->flags & FLAGS_PRECPARAM)
prec = vto[p->precision].data.num;
if (p->flags & FLAGS_LEFT)
strcat(formatbuf, "-");
if (p->flags & FLAGS_SHOWSIGN)
strcat(formatbuf, "+");
if (p->flags & FLAGS_SPACE)
strcat(formatbuf, " ");
if (p->flags & FLAGS_ALT)
strcat(formatbuf, "#");
fptr=&formatbuf[strlen(formatbuf)];
if(width >= 0) {
/* RECURSIVE USAGE */
fptr += curl_msprintf(fptr, "%d", width);
}
if(prec >= 0) {
/* RECURSIVE USAGE */
fptr += curl_msprintf(fptr, ".%d", prec);
}
if (p->flags & FLAGS_LONG)
strcat(fptr, "l");
if (p->flags & FLAGS_FLOATE)
strcat(fptr, p->flags&FLAGS_UPPER?"E":"e");
else if (p->flags & FLAGS_FLOATG)
strcat(fptr, (p->flags & FLAGS_UPPER) ? "G" : "g");
else
strcat(fptr, "f");
/* NOTE NOTE NOTE!! Not all sprintf() implementations returns number
of output characters */
#if SIZEOF_LONG_DOUBLE
if (p->flags & FLAGS_LONG)
/* This is for support of the 'long double' type */
(sprintf)(work, formatbuf, p->data.ldnum);
else
#endif
(sprintf)(work, formatbuf, p->data.dnum);
for(fptr=work; *fptr; fptr++)
OUTCHAR(*fptr);
}
break;
case FORMAT_INTPTR:
/* Answer the count of characters written. */
#if SIZEOF_LONG_LONG
if (p->flags & FLAGS_LONGLONG)
*(long long int *) p->data.ptr = done;
else
#endif
if (p->flags & FLAGS_LONG)
*(long int *) p->data.ptr = done;
else if (!(p->flags & FLAGS_SHORT))
*(int *) p->data.ptr = done;
else
*(short int *) p->data.ptr = done;
break;
default:
break;
}
f = *end++; /* goto end of %-code */
}
return done;
}
/* fputc() look-alike */
static int addbyter(int output, FILE *data)
{
struct nsprintf *infop=(struct nsprintf *)data;
if(infop->length < infop->max) {
/* only do this if we haven't reached max length yet */
infop->buffer[0] = (char)output; /* store */
infop->buffer++; /* increase pointer */
infop->length++; /* we are now one byte larger */
return output; /* fputc() returns like this on success */
}
return -1;
}
int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
{
va_list ap_save; /* argument pointer */
int retcode;
struct nsprintf info;
info.buffer = buffer;
info.length = 0;
info.max = maxlength;
va_start(ap_save, format);
retcode = dprintf_formatf(&info, addbyter, format, ap_save);
va_end(ap_save);
info.buffer[0] = 0; /* we terminate this with a zero byte */
/* we could even return things like */
return retcode;
}
int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, va_list ap_save)
{
int retcode;
struct nsprintf info;
info.buffer = buffer;
info.length = 0;
info.max = maxlength;
retcode = dprintf_formatf(&info, addbyter, format, ap_save);
info.buffer[0] = 0; /* we terminate this with a zero byte */
return retcode;
}
/* fputc() look-alike */
static int alloc_addbyter(int output, FILE *data)
{
struct asprintf *infop=(struct asprintf *)data;
if(!infop->buffer) {
infop->buffer=(char *)malloc(32);
if(!infop->buffer)
return -1; /* fail */
infop->alloc = 32;
infop->len =0;
}
else if(infop->len+1 >= infop->alloc) {
char *newptr;
newptr = (char *)realloc(infop->buffer, infop->alloc*2);
if(!newptr) {
return -1;
}
infop->buffer = newptr;
infop->alloc *= 2;
}
infop->buffer[ infop->len ] = output;
infop->len++;
return output; /* fputc() returns like this on success */
}
char *curl_maprintf(const char *format, ...)
{
va_list ap_save; /* argument pointer */
int retcode;
struct asprintf info;
info.buffer = NULL;
info.len = 0;
info.alloc = 0;
va_start(ap_save, format);
retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
va_end(ap_save);
if(info.len) {
info.buffer[info.len] = 0; /* we terminate this with a zero byte */
return info.buffer;
}
else
return NULL;
}
char *curl_mvaprintf(const char *format, va_list ap_save)
{
int retcode;
struct asprintf info;
info.buffer = NULL;
info.len = 0;
info.alloc = 0;
retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
info.buffer[info.len] = 0; /* we terminate this with a zero byte */
if(info.len) {
info.buffer[info.len] = 0; /* we terminate this with a zero byte */
return info.buffer;
}
else
return NULL;
}
static int storebuffer(int output, FILE *data)
{
char **buffer = (char **)data;
**buffer = (char)output;
(*buffer)++;
return output; /* act like fputc() ! */
}
int curl_msprintf(char *buffer, const char *format, ...)
{
va_list ap_save; /* argument pointer */
int retcode;
va_start(ap_save, format);
retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
va_end(ap_save);
*buffer=0; /* we terminate this with a zero byte */
return retcode;
}
#ifndef WIN32 /* not needed on win32 */
extern int fputc(int, FILE *);
#endif
int curl_mprintf(const char *format, ...)
{
int retcode;
va_list ap_save; /* argument pointer */
va_start(ap_save, format);
retcode = dprintf_formatf(stdout, fputc, format, ap_save);
va_end(ap_save);
return retcode;
}
int curl_mfprintf(FILE *whereto, const char *format, ...)
{
int retcode;
va_list ap_save; /* argument pointer */
va_start(ap_save, format);
retcode = dprintf_formatf(whereto, fputc, format, ap_save);
va_end(ap_save);
return retcode;
}
int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
{
int retcode;
retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
*buffer=0; /* we terminate this with a zero byte */
return retcode;
}
int curl_mvprintf(const char *format, va_list ap_save)
{
return dprintf_formatf(stdout, fputc, format, ap_save);
}
int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
{
return dprintf_formatf(whereto, fputc, format, ap_save);
}
#ifdef DPRINTF_DEBUG
int main()
{
char buffer[129];
char *ptr;
#if SIZEOF_LONG_LONG>0
long long hullo;
dprintf("%3$12s %1$s %2$qd %4$d\n", "daniel", hullo, "stenberg", 65);
#endif
mprintf("%3d %5d\n", 10, 1998);
ptr=maprintf("test this then baby %s%s%s%s%s%s %d %d %d loser baby get a hit in yer face now!", "", "pretty long string pretty long string pretty long string pretty long string pretty long string", "/", "/", "/", "pretty long string", 1998, 1999, 2001);
puts(ptr);
memset(ptr, 55, strlen(ptr)+1);
free(ptr);
#if 1
mprintf(buffer, "%s %s %d", "daniel", "stenberg", 19988);
puts(buffer);
mfprintf(stderr, "%s %#08x\n", "dummy", 65);
printf("%s %#08x\n", "dummy", 65);
{
double tryout = 3.14156592;
mprintf(buffer, "%.2g %G %f %e %E", tryout, tryout, tryout, tryout, tryout);
puts(buffer);
printf("%.2g %G %f %e %E\n", tryout, tryout, tryout, tryout, tryout);
}
#endif
return 0;
}
#endif
/*
* local variables:
* eval: (load-file "../curl-mode.el")
* end:
* vim600: fdm=marker
* vim: et sw=2 ts=2 sts=2 tw=78
*/
+361
View File
@@ -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
*/
+189
View File
@@ -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
+211
View File
@@ -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
*/
+28
View File
@@ -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
+384
View File
@@ -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
*/
+68
View File
@@ -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 */
+562
View File
@@ -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
*/
+72
View File
@@ -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
+365
View File
@@ -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
*/
+56
View File
@@ -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
+169
View File
@@ -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 */
+78
View File
@@ -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
*/
+34
View File
@@ -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
+956
View File
@@ -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
*/
+38
View File
@@ -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
+121
View File
@@ -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
*/
+40
View File
@@ -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
+74
View File
@@ -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
*/
+38
View File
@@ -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
+1213
View File
@@ -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
*/
+29
View File
@@ -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
+89
View File
@@ -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
*/
+52
View File
@@ -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
+1357
View File
@@ -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
*/
+49
View File
@@ -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
+2379
View File
@@ -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
*/
+38
View File
@@ -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
+687
View File
@@ -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
+121
View File
@@ -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
*/