#include "curl_setup.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>
#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>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#include "strequal.h"
#include "urldata.h"
#include <curl/curl.h>
#include "transfer.h"
#include "sslgen.h"
#include "url.h"
#include "getinfo.h"
#include "hostip.h"
#include "share.h"
#include "strdup.h"
#include "curl_memory.h"
#include "progress.h"
#include "easyif.h"
#include "select.h"
#include "sendf.h"
#include "curl_ntlm.h"
#include "connect.h"
#include "slist.h"
#include "amigaos.h"
#include "curl_rand.h"
#include "non-ascii.h"
#include "warnless.h"
#include "conncache.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#include "memdebug.h"
static void win32_cleanup(void)
{
#ifdef USE_WINSOCK
WSACleanup();
#endif
#ifdef USE_WINDOWS_SSPI
Curl_sspi_global_cleanup();
#endif
}
static CURLcode win32_init(void)
{
#ifdef USE_WINSOCK
WORD wVersionRequested;
WSADATA wsaData;
int res;
#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
Error IPV6_requires_winsock2
#endif
wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
res = WSAStartup(wVersionRequested, &wsaData);
if(res != 0)
return CURLE_FAILED_INIT;
if(LOBYTE( wsaData.wVersion ) != LOBYTE(wVersionRequested) ||
HIBYTE( wsaData.wVersion ) != HIBYTE(wVersionRequested) ) {
WSACleanup();
return CURLE_FAILED_INIT;
}
#elif defined(USE_LWIPSOCK)
lwip_init();
#endif
#ifdef USE_WINDOWS_SSPI
{
CURLcode err = Curl_sspi_global_init();
if(err != CURLE_OK)
return err;
}
#endif
return CURLE_OK;
}
#ifdef USE_LIBIDN
static void idna_init (void)
{
#ifdef WIN32
char buf[60];
UINT cp = GetACP();
if(!getenv("CHARSET") && cp > 0) {
snprintf(buf, sizeof(buf), "CHARSET=cp%u", cp);
putenv(buf);
}
#else
#endif
}
#endif
static unsigned int initialized;
static long init_flags;
#if defined(_WIN32_WCE)
#define system_strdup _strdup
#elif !defined(HAVE_STRDUP)
#define system_strdup curlx_strdup
#else
#define system_strdup strdup
#endif
#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
# pragma warning(disable:4232)
#endif
#ifndef __SYMBIAN32__
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;
#ifdef WIN32
curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)wcsdup;
#endif
#else
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)
#endif
CURLcode curl_global_init(long flags)
{
if(initialized++)
return CURLE_OK;
Curl_cmalloc = (curl_malloc_callback)malloc;
Curl_cfree = (curl_free_callback)free;
Curl_crealloc = (curl_realloc_callback)realloc;
Curl_cstrdup = (curl_strdup_callback)system_strdup;
Curl_ccalloc = (curl_calloc_callback)calloc;
#ifdef WIN32
Curl_cwcsdup = (curl_wcsdup_callback)wcsdup;
#endif
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) {
DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
return CURLE_FAILED_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;
Curl_srand();
return CURLE_OK;
}
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 = CURLE_OK;
if(!m || !f || !r || !s || !c)
return CURLE_FAILED_INIT;
if(initialized)
return CURLE_OK;
code = curl_global_init(flags);
if(code == CURLE_OK) {
Curl_cmalloc = m;
Curl_cfree = f;
Curl_cstrdup = s;
Curl_crealloc = r;
Curl_ccalloc = c;
}
return code;
}
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_resolver_global_cleanup();
if(init_flags & CURL_GLOBAL_WIN32)
win32_cleanup();
Curl_amiga_cleanup();
#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_EXIT)
(void)libssh2_exit();
#endif
init_flags = 0;
}
CURL *curl_easy_init(void)
{
CURLcode res;
struct SessionHandle *data;
if(!initialized) {
res = curl_global_init(CURL_GLOBAL_DEFAULT);
if(res) {
DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n"));
return NULL;
}
}
res = Curl_open(&data);
if(res != CURLE_OK) {
DEBUGF(fprintf(stderr, "Error: Curl_open failed\n"));
return NULL;
}
return data;
}
#undef curl_easy_setopt
CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...)
{
va_list arg;
struct SessionHandle *data = curl;
CURLcode ret;
if(!curl)
return CURLE_BAD_FUNCTION_ARGUMENT;
va_start(arg, tag);
ret = Curl_setopt(data, tag, arg);
va_end(arg);
return ret;
}
CURLcode curl_easy_perform(CURL *easy)
{
CURLM *multi;
CURLMcode mcode;
CURLcode code = CURLE_OK;
CURLMsg *msg;
bool done = FALSE;
int rc;
struct SessionHandle *data = easy;
if(!easy)
return CURLE_BAD_FUNCTION_ARGUMENT;
if(data->multi) {
failf(data, "easy handled already used in multi handle");
return CURLE_FAILED_INIT;
}
if(data->multi_easy)
multi = data->multi_easy;
else {
multi = curl_multi_init();
if(!multi)
return CURLE_OUT_OF_MEMORY;
data->multi_easy = multi;
}
curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects);
mcode = curl_multi_add_handle(multi, easy);
if(mcode) {
curl_multi_cleanup(multi);
if(mcode == CURLM_OUT_OF_MEMORY)
return CURLE_OUT_OF_MEMORY;
else
return CURLE_FAILED_INIT;
}
data->multi = multi;
while(!done && !mcode) {
int still_running;
int ret;
mcode = curl_multi_wait(multi, NULL, 0, 1000, &ret);
if(mcode == CURLM_OK) {
if(ret == -1) {
code = CURLE_RECV_ERROR;
break;
}
mcode = curl_multi_perform(multi, &still_running);
}
if((mcode == CURLM_OK) && !still_running) {
msg = curl_multi_info_read(multi, &rc);
if(msg) {
code = msg->data.result;
done = TRUE;
}
}
}
(void)curl_multi_remove_handle(multi, easy);
return code;
}
void curl_easy_cleanup(CURL *curl)
{
struct SessionHandle *data = (struct SessionHandle *)curl;
if(!data)
return;
Curl_close(data);
}
void Curl_easy_addmulti(struct SessionHandle *data,
void *multi)
{
data->multi = multi;
}
void Curl_easy_initHandleData(struct SessionHandle *data)
{
memset(&data->req, 0, sizeof(struct SingleRequest));
data->req.maxdownload = -1;
}
#undef curl_easy_getinfo
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...)
{
va_list arg;
void *paramp;
struct SessionHandle *data = (struct SessionHandle *)curl;
va_start(arg, info);
paramp = va_arg(arg, void *);
return Curl_getinfo(data, info, paramp);
}
CURL *curl_easy_duphandle(CURL *incurl)
{
struct SessionHandle *data=(struct SessionHandle *)incurl;
struct SessionHandle *outcurl = calloc(1, sizeof(struct SessionHandle));
if(NULL == outcurl)
goto fail;
outcurl->state.headerbuff = malloc(HEADERSIZE);
if(!outcurl->state.headerbuff)
goto fail;
outcurl->state.headersize = HEADERSIZE;
if(Curl_dupset(outcurl, data) != CURLE_OK)
goto fail;
outcurl->state.conn_cache = NULL;
outcurl->state.lastconnect = NULL;
outcurl->progress.flags = data->progress.flags;
outcurl->progress.callback = data->progress.callback;
if(data->cookies) {
outcurl->cookies = Curl_cookie_init(data,
data->cookies->filename,
outcurl->cookies,
data->set.cookiesession);
if(!outcurl->cookies)
goto fail;
}
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)
goto fail;
outcurl->change.url_alloc = TRUE;
}
if(data->change.referer) {
outcurl->change.referer = strdup(data->change.referer);
if(!outcurl->change.referer)
goto fail;
outcurl->change.referer_alloc = TRUE;
}
if(Curl_resolver_duphandle(&outcurl->state.resolver,
data->state.resolver) != CURLE_OK)
goto fail;
Curl_convert_setup(outcurl);
Curl_easy_initHandleData(outcurl);
outcurl->magic = CURLEASY_MAGIC_NUMBER;
return outcurl;
fail:
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 NULL;
}
void curl_easy_reset(CURL *curl)
{
struct SessionHandle *data = (struct SessionHandle *)curl;
Curl_safefree(data->state.pathbuffer);
data->state.path = NULL;
Curl_safefree(data->state.proto.generic);
Curl_freeset(data);
memset(&data->set, 0, sizeof(struct UserDefined));
(void)Curl_init_userdefined(&data->set);
memset(&data->progress, 0, sizeof(struct Progress));
Curl_easy_initHandleData(data);
data->progress.flags |= PGRS_HIDE;
data->state.current_speed = -1;
}
CURLcode curl_easy_pause(CURL *curl, int action)
{
struct SessionHandle *data = (struct SessionHandle *)curl;
struct SingleRequest *k = &data->req;
CURLcode result = CURLE_OK;
int newstate = k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE);
newstate |= ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) |
((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0);
k->keepon = newstate;
if(!(newstate & KEEP_RECV_PAUSE) && data->state.tempwrite) {
char *tempwrite = data->state.tempwrite;
char *freewrite = tempwrite;
size_t tempsize = data->state.tempwritesize;
int temptype = data->state.tempwritetype;
size_t chunklen;
data->state.tempwrite = NULL;
do {
chunklen = (tempsize > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:tempsize;
result = Curl_client_write(data->state.current_conn,
temptype, tempwrite, chunklen);
if(result)
break;
if(data->state.tempwrite && (tempsize - chunklen)) {
char *newptr;
newptr = realloc(data->state.tempwrite, tempsize);
if(!newptr) {
free(data->state.tempwrite);
data->state.tempwrite = NULL;
result = CURLE_OUT_OF_MEMORY;
break;
}
data->state.tempwrite = newptr;
memcpy(newptr, tempwrite, tempsize);
data->state.tempwritesize = tempsize;
break;
}
else {
tempsize -= chunklen;
tempwrite += chunklen;
}
} while((result == CURLE_OK) && tempsize);
free(freewrite);
}
return result;
}
static CURLcode easy_connection(struct SessionHandle *data,
curl_socket_t *sfd,
struct connectdata **connp)
{
if(data == NULL)
return CURLE_BAD_FUNCTION_ARGUMENT;
if(!data->set.connect_only) {
failf(data, "CONNECT_ONLY is required!");
return CURLE_UNSUPPORTED_PROTOCOL;
}
*sfd = Curl_getconnectinfo(data, connp);
if(*sfd == CURL_SOCKET_BAD) {
failf(data, "Failed to get recent socket");
return CURLE_UNSUPPORTED_PROTOCOL;
}
return CURLE_OK;
}
CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n)
{
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;
}
CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen,
size_t *n)
{
curl_socket_t sfd;
CURLcode ret;
ssize_t n1;
struct connectdata *c = NULL;
struct SessionHandle *data = (struct SessionHandle *)curl;
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;
if((CURLE_OK == ret) && (0 == n1))
return CURLE_AGAIN;
*n = (size_t)n1;
return ret;
}