summaryrefslogtreecommitdiffstats
path: root/Utilities/cmcurl/lib/easy.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/easy.c')
-rw-r--r--Utilities/cmcurl/lib/easy.c1265
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;
+}