diff options
Diffstat (limited to 'Utilities/cmcurl/lib/easy.c')
-rw-r--r-- | Utilities/cmcurl/lib/easy.c | 1265 |
1 files changed, 776 insertions, 489 deletions
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index 209d1c3..160712e 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, 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 @@ -18,110 +18,89 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * - * $Id$ ***************************************************************************/ -#include "setup.h" +#include "curl_setup.h" -/* -- WIN32 approved -- */ -#include <stdio.h> -#include <string.h> -#include <stdarg.h> -#include <stdlib.h> -#include <ctype.h> -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif - -#include <errno.h> - -#include "strequal.h" +/* + * See comment in curl_memory.h for the explanation of this sanity check. + */ -#ifdef WIN32 -#include <time.h> -#include <io.h> -#else -#ifdef HAVE_SYS_SOCKET_H -#include <sys/socket.h> +#ifdef CURLX_NO_MEMORY_CALLBACKS +#error "libcurl shall not ever be built with CURLX_NO_MEMORY_CALLBACKS defined" #endif + +#ifdef HAVE_NETINET_IN_H #include <netinet/in.h> -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif -#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 +#ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> -#include <signal.h> +#endif #ifdef HAVE_SYS_PARAM_H #include <sys/param.h> #endif -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif - -#endif /* WIN32 ... */ - +#include "strequal.h" #include "urldata.h" #include <curl/curl.h> #include "transfer.h" -#include "sslgen.h" +#include "vtls/vtls.h" #include "url.h" #include "getinfo.h" #include "hostip.h" #include "share.h" #include "strdup.h" -#include "memory.h" +#include "curl_memory.h" #include "progress.h" #include "easyif.h" +#include "select.h" #include "sendf.h" /* for failf function prototype */ -#include <ca-bundle.h> +#include "curl_ntlm.h" +#include "connect.h" /* for Curl_getconnectinfo */ +#include "slist.h" +#include "amigaos.h" +#include "non-ascii.h" +#include "warnless.h" +#include "conncache.h" +#include "multiif.h" +#include "sigpipe.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" -#ifdef USE_WINSOCK /* win32_cleanup() is for win32 socket cleanup functionality, the opposite of win32_init() */ static void win32_cleanup(void) { +#ifdef USE_WINSOCK WSACleanup(); +#endif +#ifdef USE_WINDOWS_SSPI + Curl_sspi_global_cleanup(); +#endif } /* win32_init() performs win32 socket initialization to properly setup the stack to allow networking */ static CURLcode win32_init(void) { +#ifdef USE_WINSOCK WORD wVersionRequested; WSADATA wsaData; - int err; + int res; #if defined(ENABLE_IPV6) && (USE_WINSOCK < 2) Error IPV6_requires_winsock2 @@ -129,9 +108,9 @@ static CURLcode win32_init(void) wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK); - err = WSAStartup(wVersionRequested, &wsaData); + res = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) + if(res != 0) /* Tell the user that we couldn't find a useable */ /* winsock.dll. */ return CURLE_FAILED_INIT; @@ -142,8 +121,8 @@ static CURLcode win32_init(void) /* wVersionRequested in wVersion. wHighVersion contains the */ /* highest supported version. */ - if ( LOBYTE( wsaData.wVersion ) != LOBYTE(wVersionRequested) || - HIBYTE( wsaData.wVersion ) != HIBYTE(wVersionRequested) ) { + if(LOBYTE( wsaData.wVersion ) != LOBYTE(wVersionRequested) || + HIBYTE( wsaData.wVersion ) != HIBYTE(wVersionRequested) ) { /* Tell the user that we couldn't find a useable */ /* winsock.dll. */ @@ -151,15 +130,21 @@ static CURLcode win32_init(void) return CURLE_FAILED_INIT; } /* The Windows Sockets DLL is acceptable. Proceed. */ - return CURLE_OK; -} +#elif defined(USE_LWIPSOCK) + lwip_init(); +#endif -#else -/* These functions exist merely to prevent compiler warnings */ -static CURLcode win32_init(void) { return CURLE_OK; } -static void win32_cleanup(void) { } +#ifdef USE_WINDOWS_SSPI + { + CURLcode err = Curl_sspi_global_init(); + if(err != CURLE_OK) + return err; + } #endif + return CURLE_OK; +} + #ifdef USE_LIBIDN /* * Initialise use of IDNA library. @@ -172,7 +157,7 @@ static void idna_init (void) char buf[60]; UINT cp = GetACP(); - if (!getenv("CHARSET") && cp > 0) { + if(!getenv("CHARSET") && cp > 0) { snprintf(buf, sizeof(buf), "CHARSET=cp%u", cp); putenv(buf); } @@ -199,16 +184,38 @@ static long init_flags; #define system_strdup strdup #endif +#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) +# pragma warning(disable:4232) /* MSVC extension, dllimport identity */ +#endif + +#ifndef __SYMBIAN32__ /* * 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)system_strdup; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; +#if defined(WIN32) && defined(UNICODE) +curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; +#endif +#else +/* + * Symbian OS doesn't support initialization to code in writeable static data. + * Initialization will occur in the curl_global_init() call. + */ +curl_malloc_callback Curl_cmalloc; +curl_free_callback Curl_cfree; +curl_realloc_callback Curl_crealloc; +curl_strdup_callback Curl_cstrdup; +curl_calloc_callback Curl_ccalloc; +#endif + +#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) +# pragma warning(default:4232) /* MSVC extension, dllimport identity */ +#endif /** * curl_global_init() globally initializes cURL given a bitwise set of the @@ -216,7 +223,7 @@ 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) */ @@ -225,24 +232,54 @@ CURLcode curl_global_init(long flags) Curl_crealloc = (curl_realloc_callback)realloc; Curl_cstrdup = (curl_strdup_callback)system_strdup; Curl_ccalloc = (curl_calloc_callback)calloc; +#if defined(WIN32) && defined(UNICODE) + Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; +#endif - if (flags & CURL_GLOBAL_SSL) - if (!Curl_ssl_init()) + if(flags & CURL_GLOBAL_SSL) + if(!Curl_ssl_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); return CURLE_FAILED_INIT; + } - if (flags & CURL_GLOBAL_WIN32) - if (win32_init() != CURLE_OK) + if(flags & CURL_GLOBAL_WIN32) + if(win32_init() != CURLE_OK) { + DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); return CURLE_FAILED_INIT; + } -#ifdef _AMIGASF - if(!amiga_init()) +#ifdef __AMIGA__ + if(!Curl_amiga_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n")); return CURLE_FAILED_INIT; + } +#endif + +#ifdef NETWARE + if(netware_init()) { + DEBUGF(fprintf(stderr, "Warning: LONG namespace not available\n")); + } #endif #ifdef USE_LIBIDN idna_init(); #endif + if(Curl_resolver_global_init() != CURLE_OK) { + DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); + return CURLE_FAILED_INIT; + } + +#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_INIT) + if(libssh2_init(0)) { + DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n")); + return CURLE_FAILED_INIT; + } +#endif + + if(flags & CURL_GLOBAL_ACK_EINTR) + Curl_ack_eintr = 1; + init_flags = flags; return CURLE_OK; @@ -259,16 +296,20 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, CURLcode code = CURLE_OK; /* Invalid input, return immediately */ - if (!m || !f || !r || !s || !c) + if(!m || !f || !r || !s || !c) return CURLE_FAILED_INIT; - /* Already initialized, don't do it again */ - if ( initialized ) + if(initialized) { + /* Already initialized, don't do it again, but bump the variable anyway to + work like curl_global_init() and require the same amount of cleanup + calls. */ + initialized++; return CURLE_OK; + } /* Call the actual init function first */ code = curl_global_init(flags); - if (code == CURLE_OK) { + if(code == CURLE_OK) { Curl_cmalloc = m; Curl_cfree = f; Curl_cstrdup = s; @@ -285,22 +326,26 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, */ void curl_global_cleanup(void) { - if (!initialized) + if(!initialized) return; - if (--initialized) + if(--initialized) return; Curl_global_host_cache_dtor(); - if (init_flags & CURL_GLOBAL_SSL) + if(init_flags & CURL_GLOBAL_SSL) Curl_ssl_cleanup(); - if (init_flags & CURL_GLOBAL_WIN32) + Curl_resolver_global_cleanup(); + + if(init_flags & CURL_GLOBAL_WIN32) win32_cleanup(); -#ifdef _AMIGASF - amiga_cleanup(); + Curl_amiga_cleanup(); + +#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_EXIT) + (void)libssh2_exit(); #endif init_flags = 0; @@ -316,17 +361,21 @@ CURL *curl_easy_init(void) struct SessionHandle *data; /* Make sure we inited the global SSL stuff */ - if (!initialized) { + if(!initialized) { res = curl_global_init(CURL_GLOBAL_DEFAULT); - if(res) + if(res) { /* something in the global init failed, return nothing */ + DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n")); return NULL; + } } /* We use curl_open() with undefined URL so far */ res = Curl_open(&data); - if(res != CURLE_OK) + if(res != CURLE_OK) { + DEBUGF(fprintf(stderr, "Error: Curl_open failed\n")); return NULL; + } return data; } @@ -336,6 +385,7 @@ CURL *curl_easy_init(void) * easy handle. */ +#undef curl_easy_setopt CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) { va_list arg; @@ -353,178 +403,467 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) 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. - *************************************************************************** +#ifdef CURLDEBUG + +struct socketmonitor { + struct socketmonitor *next; /* the next node in the list or NULL */ + struct pollfd socket; /* socket info of what to monitor */ +}; + +struct events { + long ms; /* timeout, run the timeout function when reached */ + bool msbump; /* set TRUE when timeout is set by callback */ + int num_sockets; /* number of nodes in the monitor list */ + struct socketmonitor *list; /* list of sockets to monitor */ + int running_handles; /* store the returned number */ +}; + +/* events_timer * - * The *new* curl_easy_perform() is the external interface that performs a - * transfer previously setup. + * Callback that gets called with a new value when the timeout should be + * updated. + */ + +static int events_timer(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp) /* private callback pointer */ +{ + struct events *ev = userp; + (void)multi; + if(timeout_ms == -1) + /* timeout removed */ + timeout_ms = 0; + else if(timeout_ms == 0) + /* timeout is already reached! */ + timeout_ms = 1; /* trigger asap */ + + ev->ms = timeout_ms; + ev->msbump = TRUE; + return 0; +} + + +/* poll2cselect * - * 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. + * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones */ -CURLcode curl_easy_perform(CURL *easy) +static int poll2cselect(int pollmask) { - 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; + int omask=0; + if(pollmask & POLLIN) + omask |= CURL_CSELECT_IN; + if(pollmask & POLLOUT) + omask |= CURL_CSELECT_OUT; + if(pollmask & POLLERR) + omask |= CURL_CSELECT_ERR; + return omask; +} - 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; +/* socketcb2poll + * + * convert from libcurl' CURL_POLL_* bit definitions to poll()'s + */ +static short socketcb2poll(int pollmask) +{ + short omask=0; + if(pollmask & CURL_POLL_IN) + omask |= POLLIN; + if(pollmask & CURL_POLL_OUT) + omask |= POLLOUT; + return omask; +} + +/* events_socket + * + * Callback that gets called with information about socket activity to + * monitor. + */ +static int events_socket(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp) /* private socket + pointer */ +{ + struct events *ev = userp; + struct socketmonitor *m; + struct socketmonitor *prev=NULL; + (void)socketp; + + m = ev->list; + while(m) { + if(m->socket.fd == s) { + + if(what == CURL_POLL_REMOVE) { + struct socketmonitor *nxt = m->next; + /* remove this node from the list of monitored sockets */ + if(prev) + prev->next = nxt; + else + ev->list = nxt; + free(m); + m = nxt; + infof(easy, "socket cb: socket %d REMOVED\n", s); + } + else { + /* The socket 's' is already being monitored, update the activity + mask. Convert from libcurl bitmask to the poll one. */ + m->socket.events = socketcb2poll(what); + infof(easy, "socket cb: socket %d UPDATED as %s%s\n", s, + what&CURL_POLL_IN?"IN":"", + what&CURL_POLL_OUT?"OUT":""); + } + break; + } + prev = m; + m = m->next; /* move to next node */ + } + if(!m) { + if(what == CURL_POLL_REMOVE) { + /* this happens a bit too often, libcurl fix perhaps? */ + /* fprintf(stderr, + "%s: socket %d asked to be REMOVED but not present!\n", + __func__, s); */ + } + else { + m = malloc(sizeof(struct socketmonitor)); + m->next = ev->list; + m->socket.fd = s; + m->socket.events = socketcb2poll(what); + m->socket.revents = 0; + ev->list = m; + infof(easy, "socket cb: socket %d ADDED as %s%s\n", s, + what&CURL_POLL_IN?"IN":"", + what&CURL_POLL_OUT?"OUT":""); + } } - /* we start some action by calling perform right away */ + return 0; +} - do { - while(CURLM_CALL_MULTI_PERFORM == - curl_multi_perform(multi, &still_running)); - if(!still_running) - break; +/* + * events_setup() + * + * Do the multi handle setups that only event-based transfers need. + */ +static void events_setup(CURLM *multi, struct events *ev) +{ + /* timer callback */ + curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, events_timer); + curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ev); - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - FD_ZERO(&fdexcep); + /* socket callback */ + curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, events_socket); + curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev); +} - /* 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); +/* wait_or_timeout() + * + * waits for activity on any of the given sockets, or the timeout to trigger. + */ - rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); +static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) +{ + bool done = FALSE; + CURLMcode mcode; + CURLcode rc = CURLE_OK; + + while(!done) { + CURLMsg *msg; + struct socketmonitor *m; + struct pollfd *f; + struct pollfd fds[4]; + int numfds=0; + int pollrc; + int i; + struct timeval before; + struct timeval after; + + /* populate the fds[] array */ + for(m = ev->list, f=&fds[0]; m; m = m->next) { + f->fd = m->socket.fd; + f->events = m->socket.events; + f->revents = 0; + /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */ + f++; + numfds++; + } - if(rc == -1) - /* select error */ - break; + /* get the time stamp to use to figure out how long poll takes */ + before = curlx_tvnow(); - /* timeout or data to send/receive => loop! */ - } while(still_running); + /* wait for activity or timeout */ + pollrc = Curl_poll(fds, numfds, (int)ev->ms); - msg = curl_multi_info_read(multi, &rc); - if(msg) - code = msg->data.result; + after = curlx_tvnow(); - mcode = curl_multi_remove_handle(multi, easy); - /* what to do if it fails? */ + ev->msbump = FALSE; /* reset here */ - mcode = curl_multi_cleanup(multi); - /* what to do if it fails? */ + if(0 == pollrc) { + /* timeout! */ + ev->ms = 0; + /* fprintf(stderr, "call curl_multi_socket_action( TIMEOUT )\n"); */ + mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, + &ev->running_handles); + } + else if(pollrc > 0) { + /* loop over the monitored sockets to see which ones had activity */ + for(i = 0; i< numfds; i++) { + if(fds[i].revents) { + /* socket activity, tell libcurl */ + int act = poll2cselect(fds[i].revents); /* convert */ + infof(multi->easyp, "call curl_multi_socket_action( socket %d )\n", + fds[i].fd); + mcode = curl_multi_socket_action(multi, fds[i].fd, act, + &ev->running_handles); + } + } - return code; + if(!ev->msbump) + /* If nothing updated the timeout, we decrease it by the spent time. + * If it was updated, it has the new timeout time stored already. + */ + ev->ms += curlx_tvdiff(after, before); + + } + if(mcode) + return CURLE_URL_MALFORMAT; /* TODO: return a proper error! */ + + /* we don't really care about the "msgs_in_queue" value returned in the + second argument */ + msg = curl_multi_info_read(multi, &pollrc); + if(msg) { + rc = msg->data.result; + done = TRUE; + } + } + + return rc; } -#else -/* - * curl_easy_perform() is the external interface that performs a transfer - * previously setup. + + +/* easy_events() + * + * Runs a transfer in a blocking manner using the events-based API */ -CURLcode curl_easy_perform(CURL *curl) +static CURLcode easy_events(CURLM *multi) { - struct SessionHandle *data = (struct SessionHandle *)curl; + struct events evs= {2, FALSE, 0, NULL, 0}; - if(!data) - return CURLE_BAD_FUNCTION_ARGUMENT; + /* if running event-based, do some further multi inits */ + events_setup(multi, &evs); - if ( ! (data->share && data->share->hostcache) ) { + return wait_or_timeout(multi, &evs); +} +#else /* CURLDEBUG */ +/* when not built with debug, this function doesn't exist */ +#define easy_events(x) CURLE_NOT_BUILT_IN +#endif - if (Curl_global_host_cache_use(data) && - (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; - } +static CURLcode easy_transfer(CURLM *multi) +{ + bool done = FALSE; + CURLMcode mcode = CURLM_OK; + CURLcode code = CURLE_OK; + struct timeval before; + int without_fds = 0; /* count number of consecutive returns from + curl_multi_wait() without any filedescriptors */ + + while(!done && !mcode) { + int still_running = 0; + int ret; - if (!data->dns.hostcache) { - data->dns.hostcachetype = HCACHE_PRIVATE; - data->dns.hostcache = Curl_mk_dnscache(); + before = curlx_tvnow(); + mcode = curl_multi_wait(multi, NULL, 0, 1000, &ret); - 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! */ - return CURLE_OUT_OF_MEMORY; + if(mcode == CURLM_OK) { + if(ret == -1) { + /* poll() failed not on EINTR, indicate a network problem */ + code = CURLE_RECV_ERROR; + break; + } + else if(ret == 0) { + struct timeval after = curlx_tvnow(); + /* If it returns without any filedescriptor instantly, we need to + avoid busy-looping during periods where it has nothing particular + to wait for */ + if(curlx_tvdiff(after, before) <= 10) { + without_fds++; + if(without_fds > 2) { + int sleep_ms = without_fds < 10 ? (1 << (without_fds-1)): 1000; + Curl_wait_ms(sleep_ms); + } + } + else + /* it wasn't "instant", restart counter */ + without_fds = 0; + } + else + /* got file descriptor, restart counter */ + without_fds = 0; + + mcode = curl_multi_perform(multi, &still_running); } + /* only read 'still_running' if curl_multi_perform() return OK */ + if((mcode == CURLM_OK) && !still_running) { + int rc; + CURLMsg *msg = curl_multi_info_read(multi, &rc); + if(msg) { + code = msg->data.result; + done = TRUE; + } + } } - 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; + /* Make sure to return some kind of error if there was a multi problem */ + if(mcode) { + return (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY : + /* The other multi errors should never happen, so return + something suitably generic */ + CURLE_BAD_FUNCTION_ARGUMENT; } - return Curl_perform(data); + return code; } -#endif + /* - * curl_easy_cleanup() is the external interface to cleaning/freeing the given - * easy handle. + * easy_perform() is the external interface that performs a blocking + * transfer as previously setup. + * + * CONCEPT: This function 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. + * + * REALITY: it can't just create and destroy the multi handle that easily. It + * needs to keep it around since if this easy handle is used again by this + * function, the same multi handle must be re-used so that the same pools and + * caches can be used. + * + * DEBUG: if 'events' is set TRUE, this function will use a replacement engine + * instead of curl_multi_perform() and use curl_multi_socket_action(). */ -void curl_easy_cleanup(CURL *curl) +static CURLcode easy_perform(struct SessionHandle *data, bool events) { - struct SessionHandle *data = (struct SessionHandle *)curl; + CURLM *multi; + CURLMcode mcode; + CURLcode code = CURLE_OK; + SIGPIPE_VARIABLE(pipe_st); if(!data) - return; + return CURLE_BAD_FUNCTION_ARGUMENT; - Curl_close(data); + if(data->multi) { + failf(data, "easy handle already used in multi handle"); + return CURLE_FAILED_INIT; + } + + if(data->multi_easy) + multi = data->multi_easy; + else { + /* this multi handle will only ever have a single easy handled attached + to it, so make it use minimal hashes */ + multi = Curl_multi_handle(1, 3); + if(!multi) + return CURLE_OUT_OF_MEMORY; + data->multi_easy = multi; + } + + /* Copy the MAXCONNECTS option to the multi handle */ + curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); + + mcode = curl_multi_add_handle(multi, data); + if(mcode) { + curl_multi_cleanup(multi); + if(mcode == CURLM_OUT_OF_MEMORY) + return CURLE_OUT_OF_MEMORY; + else + return CURLE_FAILED_INIT; + } + + sigpipe_ignore(data, &pipe_st); + + /* assign this after curl_multi_add_handle() since that function checks for + it and rejects this handle otherwise */ + data->multi = multi; + + /* run the transfer */ + code = events ? easy_events(multi) : easy_transfer(multi); + + /* ignoring the return code isn't nice, but atm we can't really handle + a failure here, room for future improvement! */ + (void)curl_multi_remove_handle(multi, data); + + sigpipe_restore(&pipe_st); + + /* The multi handle is kept alive, owned by the easy handle */ + return code; } + /* - * Store a pointed to the multi handle within the easy handle's data struct. + * curl_easy_perform() is the external interface that performs a blocking + * transfer as previously setup. */ -void Curl_easy_addmulti(struct SessionHandle *data, - void *multi) +CURLcode curl_easy_perform(CURL *easy) { - data->multi = multi; + return easy_perform(easy, FALSE); } -void Curl_easy_initHandleData(struct SessionHandle *data) +#ifdef CURLDEBUG +/* + * curl_easy_perform_ev() is the external interface that performs a blocking + * transfer using the event-based API internally. + */ +CURLcode curl_easy_perform_ev(CURL *easy) { - memset(&data->reqdata, 0, sizeof(struct HandleData)); + return easy_perform(easy, TRUE); +} - data->reqdata.maxdownload = -1; +#endif + +/* + * curl_easy_cleanup() is the external interface to cleaning/freeing the given + * easy handle. + */ +void curl_easy_cleanup(CURL *curl) +{ + struct SessionHandle *data = (struct SessionHandle *)curl; + SIGPIPE_VARIABLE(pipe_st); + + if(!data) + return; + + sigpipe_ignore(data, &pipe_st); + Curl_close(data); + sigpipe_restore(&pipe_st); } /* * curl_easy_getinfo() is an external interface that allows an app to retrieve * information from a performed transfer and similar. */ +#undef curl_easy_getinfo CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) { va_list arg; void *paramp; + CURLcode ret; struct SessionHandle *data = (struct SessionHandle *)curl; va_start(arg, info); paramp = va_arg(arg, void *); - return Curl_getinfo(data, info, paramp); + ret = Curl_getinfo(data, info, paramp); + + va_end(arg); + return ret; } /* @@ -534,113 +873,93 @@ CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) */ CURL *curl_easy_duphandle(CURL *incurl) { - bool fail = TRUE; struct SessionHandle *data=(struct SessionHandle *)incurl; - struct SessionHandle *outcurl = (struct SessionHandle *) - calloc(sizeof(struct SessionHandle), 1); - + struct SessionHandle *outcurl = calloc(1, sizeof(struct SessionHandle)); if(NULL == outcurl) - return NULL; /* failure */ - - do { + goto fail; - /* - * We setup a few buffers we need. We should probably make them - * get setup on-demand in the code, as that would probably decrease - * the likeliness of us forgetting to init a buffer here in the future. - */ - outcurl->state.headerbuff=(char*)malloc(HEADERSIZE); - if(!outcurl->state.headerbuff) { - break; - } - outcurl->state.headersize=HEADERSIZE; - - /* copy all userdefined values */ - outcurl->set = data->set; - - 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; - - outcurl->state.lastconnect = -1; - - outcurl->progress.flags = data->progress.flags; - outcurl->progress.callback = data->progress.callback; - -#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! */ - outcurl->cookies = Curl_cookie_init(data, - data->cookies->filename, - outcurl->cookies, - data->set.cookiesession); - if(!outcurl->cookies) { - break; - } - } -#endif /* CURL_DISABLE_HTTP */ + /* + * 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 = malloc(HEADERSIZE); + if(!outcurl->state.headerbuff) + goto fail; + outcurl->state.headersize = HEADERSIZE; + + /* copy all userdefined values */ + if(Curl_dupset(outcurl, data) != CURLE_OK) + goto fail; + + /* the connection cache is setup on demand */ + outcurl->state.conn_cache = NULL; + + outcurl->state.lastconnect = NULL; + + 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, + data->cookies->filename, + outcurl->cookies, + data->set.cookiesession); + if(!outcurl->cookies) + goto fail; + } - /* duplicate all values in 'change' */ + /* duplicate all values in 'change' */ + if(data->change.cookielist) { + outcurl->change.cookielist = + Curl_slist_duplicate(data->change.cookielist); + if(!outcurl->change.cookielist) + goto fail; + } - if(data->change.url) { - outcurl->change.url = strdup(data->change.url); - if(!outcurl->change.url) - break; - outcurl->change.url_alloc = TRUE; - } + if(data->change.url) { + outcurl->change.url = strdup(data->change.url); + if(!outcurl->change.url) + goto fail; + outcurl->change.url_alloc = TRUE; + } - if(data->change.referer) { - outcurl->change.referer = strdup(data->change.referer); - if(!outcurl->change.referer) - break; - outcurl->change.referer_alloc = TRUE; - } + if(data->change.referer) { + outcurl->change.referer = strdup(data->change.referer); + if(!outcurl->change.referer) + goto fail; + outcurl->change.referer_alloc = TRUE; + } -#ifdef USE_ARES - /* If we use ares, we setup a new ares channel for the new handle */ - if(ARES_SUCCESS != ares_init(&outcurl->state.areschannel)) - break; -#endif + /* Clone the resolver handle, if present, for the new handle */ + if(Curl_resolver_duphandle(&outcurl->state.resolver, + data->state.resolver) != CURLE_OK) + goto fail; -#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_convert_setup(outcurl); - Curl_easy_initHandleData(outcurl); + outcurl->magic = CURLEASY_MAGIC_NUMBER; - outcurl->magic = CURLEASY_MAGIC_NUMBER; + /* we reach this point and thus we are OK */ - fail = FALSE; /* we reach this point and thus we are OK */ + return outcurl; - } while(0); + fail: - if(fail) { - if(outcurl) { - if(outcurl->state.connc->type == CONNCACHE_PRIVATE) - Curl_rm_connc(outcurl->state.connc); - if(outcurl->state.headerbuff) - free(outcurl->state.headerbuff); - if(outcurl->change.url) - free(outcurl->change.url); - if(outcurl->change.referer) - free(outcurl->change.referer); - free(outcurl); /* free the memory again */ - outcurl = NULL; - } + if(outcurl) { + curl_slist_free_all(outcurl->change.cookielist); + outcurl->change.cookielist = NULL; + Curl_safefree(outcurl->state.headerbuff); + Curl_safefree(outcurl->change.url); + Curl_safefree(outcurl->change.referer); + Curl_freeset(outcurl); + free(outcurl); } - return outcurl; + return NULL; } /* @@ -651,245 +970,213 @@ void curl_easy_reset(CURL *curl) { struct SessionHandle *data = (struct SessionHandle *)curl; - Curl_safefree(data->reqdata.pathbuffer); - data->reqdata.pathbuffer=NULL; + Curl_safefree(data->state.pathbuffer); + + data->state.path = NULL; - Curl_safefree(data->reqdata.proto.generic); - data->reqdata.proto.generic=NULL; + Curl_free_request_state(data); /* zero out UserDefined data: */ + Curl_freeset(data); memset(&data->set, 0, sizeof(struct UserDefined)); + (void)Curl_init_userdefined(&data->set); /* 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 */ - 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; + data->progress.flags |= PGRS_HIDE; + data->state.current_speed = -1; /* init to negative == impossible */ +} - /* use fread as default function to read input */ - data->set.fread = (curl_read_callback)fread; +/* + * curl_easy_pause() allows an application to pause or unpause a specific + * transfer and direction. This function sets the full new state for the + * current connection this easy handle operates on. + * + * NOTE: if you have the receiving paused and you call this function to remove + * the pausing, you may get your write callback called at this point. + * + * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h + */ +CURLcode curl_easy_pause(CURL *curl, int action) +{ + struct SessionHandle *data = (struct SessionHandle *)curl; + struct SingleRequest *k = &data->req; + CURLcode result = CURLE_OK; + + /* first switch off both pause bits */ + int newstate = k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE); + + /* set the new desired pause bits */ + newstate |= ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) | + ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0); + + /* put it back in the keepon */ + k->keepon = newstate; + + if(!(newstate & KEEP_RECV_PAUSE) && data->state.tempwrite) { + /* we have a buffer for sending that we now seem to be able to deliver + since the receive pausing is lifted! */ + + /* get the pointer, type and length in local copies since the function may + return PAUSE again and then we'll get a new copy allocted and stored in + the tempwrite variables */ + char *tempwrite = data->state.tempwrite; + char *freewrite = tempwrite; /* store this pointer to free it later */ + size_t tempsize = data->state.tempwritesize; + int temptype = data->state.tempwritetype; + size_t chunklen; + + /* clear tempwrite here just to make sure it gets cleared if there's no + further use of it, and make sure we don't clear it after the function + invoke as it may have been set to a new value by then */ + data->state.tempwrite = NULL; + + /* since the write callback API is define to never exceed + CURL_MAX_WRITE_SIZE bytes in a single call, and since we may in fact + have more data than that in our buffer here, we must loop sending the + data in multiple calls until there's no data left or we get another + pause returned. + + A tricky part is that the function we call will "buffer" the data + itself when it pauses on a particular buffer, so we may need to do some + extra trickery if we get a pause return here. + */ + do { + chunklen = (tempsize > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:tempsize; + + result = Curl_client_write(data->easy_conn, + temptype, tempwrite, chunklen); + if(result) + /* failures abort the loop at once */ + break; - data->set.infilesize = -1; /* we don't know any size */ - data->set.postfieldsize = -1; + if(data->state.tempwrite && (tempsize - chunklen)) { + /* Ouch, the reading is again paused and the block we send is now + "cached". If this is the final chunk we can leave it like this, but + if we have more chunks that are cached after this, we need to free + the newly cached one and put back a version that is truly the entire + contents that is saved for later + */ + char *newptr; + + /* note that tempsize is still the size as before the callback was + used, and thus the whole piece of data to keep */ + newptr = realloc(data->state.tempwrite, tempsize); + + if(!newptr) { + free(data->state.tempwrite); /* free old area */ + data->state.tempwrite = NULL; + result = CURLE_OUT_OF_MEMORY; + /* tempwrite will be freed further down */ + break; + } + data->state.tempwrite = newptr; /* store new pointer */ + memcpy(newptr, tempwrite, tempsize); + data->state.tempwritesize = tempsize; /* store new size */ + /* tempwrite will be freed further down */ + break; /* go back to pausing until further notice */ + } + else { + tempsize -= chunklen; /* left after the call above */ + tempwrite += chunklen; /* advance the pointer */ + } - data->state.current_speed = -1; /* init to negative == impossible */ + } while((result == CURLE_OK) && tempsize); - data->set.httpreq = HTTPREQ_GET; /* Default HTTP request */ - data->set.ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ - data->set.ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */ + free(freewrite); /* this is unconditionally no longer used */ + } - data->set.dns_cache_timeout = 60; /* Timeout every 60 seconds by default */ + /* if there's no error and we're not pausing both directions, we want + to have this handle checked soon */ + if(!result && + ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != + (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) ) + Curl_expire(data, 1); /* get this handle going again */ - /* make libcurl quiet by default: */ - data->set.hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */ - data->progress.flags |= PGRS_HIDE; + return result; +} - /* Set the default size of the SSL session ID cache */ - data->set.ssl.numsessions = 5; - data->set.proxyport = 1080; - data->set.proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ - data->set.httpauth = CURLAUTH_BASIC; /* defaults to basic */ - data->set.proxyauth = CURLAUTH_BASIC; /* defaults to basic */ +static CURLcode easy_connection(struct SessionHandle *data, + curl_socket_t *sfd, + struct connectdata **connp) +{ + if(data == NULL) + return CURLE_BAD_FUNCTION_ARGUMENT; - /* - * libcurl 7.10 introduced SSL verification *by default*! This needs to be - * switched off unless wanted. - */ - data->set.ssl.verifypeer = TRUE; - data->set.ssl.verifyhost = 2; -#ifdef CURL_CA_BUNDLE - /* This is our preferred CA cert bundle since install time */ - data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE; -#endif + /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */ + if(!data->set.connect_only) { + failf(data, "CONNECT_ONLY is required!"); + return CURLE_UNSUPPORTED_PROTOCOL; + } - data->set.ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth - type */ -} + *sfd = Curl_getconnectinfo(data, connp); -#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 */ + if(*sfd == CURL_SOCKET_BAD) { + failf(data, "Failed to get recent socket"); + return CURLE_UNSUPPORTED_PROTOCOL; } return CURLE_OK; } /* - * Curl_convert_from_network() is an internal function - * for performing ASCII conversions on non-ASCII platforms. + * Receives data from the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + * Returns CURLE_OK on success, error code on error. */ -CURLcode Curl_convert_from_network(struct SessionHandle *data, - char *buffer, size_t length) +CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) { - 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 */ - } + curl_socket_t sfd; + CURLcode ret; + ssize_t n1; + struct connectdata *c; + struct SessionHandle *data = (struct SessionHandle *)curl; + + ret = easy_connection(data, &sfd, &c); + if(ret) + return ret; + + *n = 0; + ret = Curl_read(c, sfd, buffer, buflen, &n1); + + if(ret != CURLE_OK) + return ret; + + *n = (size_t)n1; return CURLE_OK; } /* - * Curl_convert_from_utf8() is an internal function - * for performing UTF-8 conversions on non-ASCII platforms. + * Sends data over the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ -CURLcode Curl_convert_from_utf8(struct SessionHandle *data, - char *buffer, size_t length) +CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, + size_t *n) { - 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 */ - } + curl_socket_t sfd; + CURLcode ret; + ssize_t n1; + struct connectdata *c = NULL; + struct SessionHandle *data = (struct SessionHandle *)curl; - return CURLE_OK; -} + ret = easy_connection(data, &sfd, &c); + if(ret) + return ret; + + *n = 0; + ret = Curl_write(c, sfd, buffer, buflen, &n1); + + if(n1 == -1) + return CURLE_SEND_ERROR; -#endif /* CURL_DOES_CONVERSIONS */ + /* detect EAGAIN */ + if((CURLE_OK == ret) && (0 == n1)) + return CURLE_AGAIN; + + *n = (size_t)n1; + + return ret; +} |