diff options
Diffstat (limited to 'Utilities/cmcurl/easy.c')
-rw-r--r-- | Utilities/cmcurl/easy.c | 474 |
1 files changed, 393 insertions, 81 deletions
diff --git a/Utilities/cmcurl/easy.c b/Utilities/cmcurl/easy.c index fb116fd..f3c2273 100644 --- a/Utilities/cmcurl/easy.c +++ b/Utilities/cmcurl/easy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,14 +29,18 @@ #include <stdarg.h> #include <stdlib.h> #include <ctype.h> +#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> +#endif #include <errno.h> #include "strequal.h" -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef WIN32 #include <time.h> #include <io.h> #else @@ -44,7 +48,9 @@ #include <sys/socket.h> #endif #include <netinet/in.h> +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -71,20 +77,37 @@ #include "urldata.h" #include <curl/curl.h> #include "transfer.h" -#include "ssluse.h" +#include "sslgen.h" #include "url.h" #include "getinfo.h" #include "hostip.h" #include "share.h" -#include "curl_memory.h" +#include "strdup.h" +#include "memory.h" +#include "progress.h" +#include "easyif.h" +#include "sendf.h" /* for failf function prototype */ +#include <ca-bundle.h> #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> +#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) +#include <iconv.h> +/* set default codesets for iconv */ +#ifndef CURL_ICONV_CODESET_OF_NETWORK +#define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1" +#endif +#ifndef CURL_ICONV_CODESET_FOR_UTF8 +#define CURL_ICONV_CODESET_FOR_UTF8 "UTF-8" +#endif +#define ICONV_ERROR (size_t)-1 +#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */ + /* The last #include file should be: */ #include "memdebug.h" -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef USE_WINSOCK /* win32_cleanup() is for win32 socket cleanup functionality, the opposite of win32_init() */ static void win32_cleanup(void) @@ -100,12 +123,12 @@ static CURLcode win32_init(void) WSADATA wsaData; int err; -#ifdef ENABLE_IPV6 - wVersionRequested = MAKEWORD(2, 0); -#else - wVersionRequested = MAKEWORD(1, 1); +#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2) + Error IPV6_requires_winsock2 #endif + wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK); + err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) @@ -160,17 +183,31 @@ static void idna_init (void) #endif /* USE_LIBIDN */ /* true globals -- for curl_global_init() and curl_global_cleanup() */ -static unsigned int initialized = 0; -static long init_flags = 0; +static unsigned int initialized; +static long init_flags; + +/* + * strdup (and other memory functions) is redefined in complicated + * ways, but at this point it must be defined as the system-supplied strdup + * so the callback pointer is initialized correctly. + */ +#if defined(_WIN32_WCE) +#define system_strdup _strdup +#elif !defined(HAVE_STRDUP) +#define system_strdup curlx_strdup +#else +#define system_strdup strdup +#endif /* * If a memory-using function (like curl_getenv) is used before * curl_global_init() is called, we need to have these pointers set already. */ + curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; curl_free_callback Curl_cfree = (curl_free_callback)free; curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; -curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)strdup; +curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; /** @@ -179,18 +216,19 @@ curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; */ CURLcode curl_global_init(long flags) { - if (initialized) + if (initialized++) return CURLE_OK; /* Setup the default memory functions here (again) */ Curl_cmalloc = (curl_malloc_callback)malloc; Curl_cfree = (curl_free_callback)free; Curl_crealloc = (curl_realloc_callback)realloc; - Curl_cstrdup = (curl_strdup_callback)strdup; + Curl_cstrdup = (curl_strdup_callback)system_strdup; Curl_ccalloc = (curl_calloc_callback)calloc; if (flags & CURL_GLOBAL_SSL) - Curl_SSL_init(); + if (!Curl_ssl_init()) + return CURLE_FAILED_INIT; if (flags & CURL_GLOBAL_WIN32) if (win32_init() != CURLE_OK) @@ -205,7 +243,6 @@ CURLcode curl_global_init(long flags) idna_init(); #endif - initialized = 1; init_flags = flags; return CURLE_OK; @@ -219,7 +256,7 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, curl_free_callback f, curl_realloc_callback r, curl_strdup_callback s, curl_calloc_callback c) { - CURLcode code; + CURLcode code = CURLE_OK; /* Invalid input, return immediately */ if (!m || !f || !r || !s || !c) @@ -251,10 +288,13 @@ void curl_global_cleanup(void) if (!initialized) return; + if (--initialized) + return; + Curl_global_host_cache_dtor(); if (init_flags & CURL_GLOBAL_SSL) - Curl_SSL_cleanup(); + Curl_ssl_cleanup(); if (init_flags & CURL_GLOBAL_WIN32) win32_cleanup(); @@ -263,7 +303,6 @@ void curl_global_cleanup(void) amiga_cleanup(); #endif - initialized = 0; init_flags = 0; } @@ -296,14 +335,10 @@ CURL *curl_easy_init(void) * curl_easy_setopt() is the external interface for setting options on an * easy handle. */ -typedef int (*func_T)(void); + CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) { va_list arg; - func_T param_func; - long param_long; - void *param_obj; - curl_off_t param_offset; struct SessionHandle *data = curl; CURLcode ret; @@ -312,38 +347,95 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) va_start(arg, tag); - /* PORTING NOTE: - Object pointers can't necessarily be casted to function pointers and - therefore we need to know what type it is and read the correct type - at once. This should also correct problems with different sizes of - the types. - */ - - if(tag < CURLOPTTYPE_OBJECTPOINT) { - /* This is a LONG type */ - param_long = va_arg(arg, long); - ret = Curl_setopt(data, tag, param_long); - } - else if(tag < CURLOPTTYPE_FUNCTIONPOINT) { - /* This is a object pointer type */ - param_obj = va_arg(arg, void *); - ret = Curl_setopt(data, tag, param_obj); - } - else if(tag < CURLOPTTYPE_OFF_T) { - /* This is a function pointer type */ - param_func = va_arg(arg, func_T ); - ret = Curl_setopt(data, tag, param_func); - } - else { - /* This is a curl_off_t type */ - param_offset = va_arg(arg, curl_off_t); - ret = Curl_setopt(data, tag, param_offset); - } + ret = Curl_setopt(data, tag, arg); va_end(arg); return ret; } +#ifdef CURL_MULTIEASY +/*************************************************************************** + * This function is still only for testing purposes. It makes a great way + * to run the full test suite on the multi interface instead of the easy one. + *************************************************************************** + * + * The *new* curl_easy_perform() is the external interface that performs a + * transfer previously setup. + * + * Wrapper-function that: creates a multi handle, adds the easy handle to it, + * runs curl_multi_perform() until the transfer is done, then detaches the + * easy handle, destroys the multi handle and returns the easy handle's return + * code. This will make everything internally use and assume multi interface. + */ +CURLcode curl_easy_perform(CURL *easy) +{ + CURLM *multi; + CURLMcode mcode; + CURLcode code = CURLE_OK; + int still_running; + struct timeval timeout; + int rc; + CURLMsg *msg; + fd_set fdread; + fd_set fdwrite; + fd_set fdexcep; + int maxfd; + + if(!easy) + return CURLE_BAD_FUNCTION_ARGUMENT; + + multi = curl_multi_init(); + if(!multi) + return CURLE_OUT_OF_MEMORY; + + mcode = curl_multi_add_handle(multi, easy); + if(mcode) { + curl_multi_cleanup(multi); + return CURLE_FAILED_INIT; + } + + /* we start some action by calling perform right away */ + + do { + while(CURLM_CALL_MULTI_PERFORM == + curl_multi_perform(multi, &still_running)); + + if(!still_running) + break; + + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + + /* timeout once per second */ + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + /* get file descriptors from the transfers */ + curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd); + + rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); + + if(rc == -1) + /* select error */ + break; + + /* timeout or data to send/receive => loop! */ + } while(still_running); + + msg = curl_multi_info_read(multi, &rc); + if(msg) + code = msg->data.result; + + mcode = curl_multi_remove_handle(multi, easy); + /* what to do if it fails? */ + + mcode = curl_multi_cleanup(multi); + /* what to do if it fails? */ + + return code; +} +#else /* * curl_easy_perform() is the external interface that performs a transfer * previously setup. @@ -358,16 +450,18 @@ CURLcode curl_easy_perform(CURL *curl) if ( ! (data->share && data->share->hostcache) ) { if (Curl_global_host_cache_use(data) && - data->hostcache != Curl_global_host_cache_get()) { - if (data->hostcache) - Curl_hash_destroy(data->hostcache); - data->hostcache = Curl_global_host_cache_get(); + (data->dns.hostcachetype != HCACHE_GLOBAL)) { + if (data->dns.hostcachetype == HCACHE_PRIVATE) + Curl_hash_destroy(data->dns.hostcache); + data->dns.hostcache = Curl_global_host_cache_get(); + data->dns.hostcachetype = HCACHE_GLOBAL; } - if (!data->hostcache) { - data->hostcache = Curl_mk_dnscache(); + if (!data->dns.hostcache) { + data->dns.hostcachetype = HCACHE_PRIVATE; + data->dns.hostcache = Curl_mk_dnscache(); - if(!data->hostcache) + if(!data->dns.hostcache) /* While we possibly could survive and do good without a host cache, the fact that creating it failed indicates that things are truly screwed up and we should bail out! */ @@ -376,8 +470,16 @@ CURLcode curl_easy_perform(CURL *curl) } + if(!data->state.connc) { + /* oops, no connection cache, make one up */ + data->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, -1); + if(!data->state.connc) + return CURLE_OUT_OF_MEMORY; + } + return Curl_perform(data); } +#endif /* * curl_easy_cleanup() is the external interface to cleaning/freeing the given @@ -390,15 +492,26 @@ void curl_easy_cleanup(CURL *curl) if(!data) return; - if ( ! (data->share && data->share->hostcache) ) { - if ( !Curl_global_host_cache_use(data)) { - Curl_hash_destroy(data->hostcache); - } - } Curl_close(data); } /* + * Store a pointed to the multi handle within the easy handle's data struct. + */ +void Curl_easy_addmulti(struct SessionHandle *data, + void *multi) +{ + data->multi = multi; +} + +void Curl_easy_initHandleData(struct SessionHandle *data) +{ + memset(&data->reqdata, 0, sizeof(struct HandleData)); + + data->reqdata.maxdownload = -1; +} + +/* * curl_easy_getinfo() is an external interface that allows an app to retrieve * information from a performed transfer and similar. */ @@ -445,21 +558,21 @@ CURL *curl_easy_duphandle(CURL *incurl) /* 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) { + if(data->state.used_interface == Curl_if_multi) + outcurl->state.connc = data->state.connc; + else + outcurl->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, -1); + + if(!outcurl->state.connc) break; - } - memset(outcurl->state.connects, 0, - sizeof(struct connectdata *)*outcurl->state.numconnects); + outcurl->state.lastconnect = -1; outcurl->progress.flags = data->progress.flags; outcurl->progress.callback = data->progress.callback; -#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) if(data->cookies) { /* If cookies are enabled in the parent handle, we enable them in the clone as well! */ @@ -474,18 +587,14 @@ CURL *curl_easy_duphandle(CURL *incurl) #endif /* CURL_DISABLE_HTTP */ /* duplicate all values in 'change' */ + if(data->change.url) { outcurl->change.url = strdup(data->change.url); if(!outcurl->change.url) break; outcurl->change.url_alloc = TRUE; } - if(data->change.proxy) { - outcurl->change.proxy = strdup(data->change.proxy); - if(!outcurl->change.proxy) - break; - outcurl->change.proxy_alloc = TRUE; - } + if(data->change.referer) { outcurl->change.referer = strdup(data->change.referer); if(!outcurl->change.referer) @@ -499,18 +608,29 @@ CURL *curl_easy_duphandle(CURL *incurl) break; #endif +#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) + outcurl->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, + CURL_ICONV_CODESET_OF_NETWORK); + outcurl->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK, + CURL_ICONV_CODESET_OF_HOST); + outcurl->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, + CURL_ICONV_CODESET_FOR_UTF8); +#endif + + Curl_easy_initHandleData(outcurl); + + outcurl->magic = CURLEASY_MAGIC_NUMBER; + fail = FALSE; /* we reach this point and thus we are OK */ } while(0); if(fail) { if(outcurl) { - if(outcurl->state.connects) - free(outcurl->state.connects); + if(outcurl->state.connc->type == CONNCACHE_PRIVATE) + Curl_rm_connc(outcurl->state.connc); if(outcurl->state.headerbuff) free(outcurl->state.headerbuff); - if(outcurl->change.proxy) - free(outcurl->change.proxy); if(outcurl->change.url) free(outcurl->change.url); if(outcurl->change.referer) @@ -531,12 +651,21 @@ void curl_easy_reset(CURL *curl) { struct SessionHandle *data = (struct SessionHandle *)curl; + Curl_safefree(data->reqdata.pathbuffer); + data->reqdata.pathbuffer=NULL; + + Curl_safefree(data->reqdata.proto.generic); + data->reqdata.proto.generic=NULL; + /* zero out UserDefined data: */ memset(&data->set, 0, sizeof(struct UserDefined)); /* zero out Progress data: */ memset(&data->progress, 0, sizeof(struct Progress)); + /* init Handle data */ + Curl_easy_initHandleData(data); + /* The remainder of these calls have been taken from Curl_open() */ data->set.out = stdout; /* default output to stdout */ @@ -550,6 +679,7 @@ void curl_easy_reset(CURL *curl) data->set.fread = (curl_read_callback)fread; data->set.infilesize = -1; /* we don't know any size */ + data->set.postfieldsize = -1; data->state.current_speed = -1; /* init to negative == impossible */ @@ -561,6 +691,7 @@ void curl_easy_reset(CURL *curl) /* 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; @@ -580,4 +711,185 @@ void curl_easy_reset(CURL *curl) /* This is our prefered CA cert bundle since install time */ data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE; #endif + + data->set.ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth + type */ } + +#ifdef CURL_DOES_CONVERSIONS +/* + * Curl_convert_to_network() is an internal function + * for performing ASCII conversions on non-ASCII platforms. + */ +CURLcode Curl_convert_to_network(struct SessionHandle *data, + char *buffer, size_t length) +{ + CURLcode rc; + + if(data->set.convtonetwork) { + /* use translation callback */ + rc = data->set.convtonetwork(buffer, length); + if(rc != CURLE_OK) { + failf(data, + "CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %i: %s", + rc, curl_easy_strerror(rc)); + } + return(rc); + } else { +#ifdef HAVE_ICONV + /* do the translation ourselves */ + char *input_ptr, *output_ptr; + size_t in_bytes, out_bytes, rc; + + /* open an iconv conversion descriptor if necessary */ + if(data->outbound_cd == (iconv_t)-1) { + data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK, + CURL_ICONV_CODESET_OF_HOST); + if(data->outbound_cd == (iconv_t)-1) { + failf(data, + "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s", + CURL_ICONV_CODESET_OF_NETWORK, + CURL_ICONV_CODESET_OF_HOST, + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } + } + /* call iconv */ + input_ptr = output_ptr = buffer; + in_bytes = out_bytes = length; + rc = iconv(data->outbound_cd, (const char**)&input_ptr, &in_bytes, + &output_ptr, &out_bytes); + if ((rc == ICONV_ERROR) || (in_bytes != 0)) { + failf(data, + "The Curl_convert_to_network iconv call failed with errno %i: %s", + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } +#else + failf(data, "CURLOPT_CONV_TO_NETWORK_FUNCTION callback required"); + return CURLE_CONV_REQD; +#endif /* HAVE_ICONV */ + } + + return CURLE_OK; +} + +/* + * Curl_convert_from_network() is an internal function + * for performing ASCII conversions on non-ASCII platforms. + */ +CURLcode Curl_convert_from_network(struct SessionHandle *data, + char *buffer, size_t length) +{ + CURLcode rc; + + if(data->set.convfromnetwork) { + /* use translation callback */ + rc = data->set.convfromnetwork(buffer, length); + if(rc != CURLE_OK) { + failf(data, + "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %i: %s", + rc, curl_easy_strerror(rc)); + } + return(rc); + } else { +#ifdef HAVE_ICONV + /* do the translation ourselves */ + char *input_ptr, *output_ptr; + size_t in_bytes, out_bytes, rc; + + /* open an iconv conversion descriptor if necessary */ + if(data->inbound_cd == (iconv_t)-1) { + data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, + CURL_ICONV_CODESET_OF_NETWORK); + if(data->inbound_cd == (iconv_t)-1) { + failf(data, + "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s", + CURL_ICONV_CODESET_OF_HOST, + CURL_ICONV_CODESET_OF_NETWORK, + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } + } + /* call iconv */ + input_ptr = output_ptr = buffer; + in_bytes = out_bytes = length; + rc = iconv(data->inbound_cd, (const char **)&input_ptr, &in_bytes, + &output_ptr, &out_bytes); + if ((rc == ICONV_ERROR) || (in_bytes != 0)) { + failf(data, + "The Curl_convert_from_network iconv call failed with errno %i: %s", + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } +#else + failf(data, "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback required"); + return CURLE_CONV_REQD; +#endif /* HAVE_ICONV */ + } + + return CURLE_OK; +} + +/* + * Curl_convert_from_utf8() is an internal function + * for performing UTF-8 conversions on non-ASCII platforms. + */ +CURLcode Curl_convert_from_utf8(struct SessionHandle *data, + char *buffer, size_t length) +{ + CURLcode rc; + + if(data->set.convfromutf8) { + /* use translation callback */ + rc = data->set.convfromutf8(buffer, length); + if(rc != CURLE_OK) { + failf(data, + "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %i: %s", + rc, curl_easy_strerror(rc)); + } + return(rc); + } else { +#ifdef HAVE_ICONV + /* do the translation ourselves */ + char *input_ptr, *output_ptr; + size_t in_bytes, out_bytes, rc; + + /* open an iconv conversion descriptor if necessary */ + if(data->utf8_cd == (iconv_t)-1) { + data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, + CURL_ICONV_CODESET_FOR_UTF8); + if(data->utf8_cd == (iconv_t)-1) { + failf(data, + "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s", + CURL_ICONV_CODESET_OF_HOST, + CURL_ICONV_CODESET_FOR_UTF8, + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } + } + /* call iconv */ + input_ptr = output_ptr = buffer; + in_bytes = out_bytes = length; + rc = iconv(data->utf8_cd, (const char**)&input_ptr, &in_bytes, + &output_ptr, &out_bytes); + if ((rc == ICONV_ERROR) || (in_bytes != 0)) { + failf(data, + "The Curl_convert_from_utf8 iconv call failed with errno %i: %s", + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } + if (output_ptr < input_ptr) { + /* null terminate the now shorter output string */ + *output_ptr = 0x00; + } +#else + failf(data, "CURLOPT_CONV_FROM_UTF8_FUNCTION callback required"); + return CURLE_CONV_REQD; +#endif /* HAVE_ICONV */ + } + + return CURLE_OK; +} + +#endif /* CURL_DOES_CONVERSIONS */ |