efl/src/lib/ecore_con/ecore_con_url_curl.c

356 lines
12 KiB
C
Raw Normal View History

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef _WIN32
# include <ws2tcpip.h>
#endif
#include "Ecore.h"
#include "ecore_private.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#include "ecore_con_url_curl.h"
Ecore_Con_Curl *_c = NULL;
Eina_Bool _c_fail = EINA_FALSE;
double _c_timeout = 0.0;
ecore_con: Rename EAPI macro to ECORE_CON_API in Ecore Con library Patch from a series of patches to rename EAPI symbols to specific library DSOs. EAPI was designed to be able to pass ```__attribute__ ((visibility ("default")))``` for symbols with GCC, which would mean that even if -fvisibility=hidden was used when compiling the library, the needed symbols would get exported. MSVC __almost__ works like GCC (or mingw) in which you can declare everything as export and it will just work (slower, but it will work). But there's a caveat: global variables will not work the same way for MSVC, but works for mingw and GCC. For global variables (as opposed to functions), MSVC requires correct DSO visibility for MSVC: instead of declaring a symbol as export for everything, you need to declare it as import when importing from another DSO and export when defining it locally. With current EAPI definitions, we get the following example working in mingw and MSVC (observe it doesn't define any global variables as exported symbols). Example 1: dll1: ``` EAPI void foo(void); EAPI void bar() { foo(); } ``` dll2: ``` EAPI void foo() { printf ("foo\n"); } ``` This works fine with API defined as __declspec(dllexport) in both cases and for gcc defining as ```__atttribute__((visibility("default")))```. However, the following: Example 2: dll1: ``` EAPI extern int foo; EAPI void foobar(void); EAPI void bar() { foo = 5; foobar(); } ``` dll2: ``` EAPI int foo = 0; EAPI void foobar() { printf ("foo %d\n", foo); } ``` This will work on mingw but will not work for MSVC. And that's why EAPI is the only solution that worked for MSVC. Co-authored-by: João Paulo Taylor Ienczak Zanette <jpaulotiz@gmail.com> Co-authored-by: Ricardo Campos <ricardo.campos@expertise.dev> Co-authored-by: Lucas Cavalcante de Sousa <lucks.sousa@gmail.com>
2020-09-15 06:16:24 -07:00
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_BAD_CONTENT_ENCODING = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_BAD_DOWNLOAD_RESUME = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_BAD_FUNCTION_ARGUMENT = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_CHUNK_FAILED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_CONV_FAILED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_CONV_REQD = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_FAILED_INIT = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_FILE_COULDNT_READ_FILE = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_FILESIZE_EXCEEDED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_FUNCTION_NOT_FOUND = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_GOT_NOTHING = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_HTTP2 = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_HTTP2_STREAM = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_HTTP_POST_ERROR = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_HTTP_RETURNED_ERROR = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_INTERFACE_FAILED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_LOGIN_DENIED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_NO_CONNECTION_AVAILABLE = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_NOT_BUILT_IN = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_OPERATION_TIMEDOUT = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_PARTIAL_FILE = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_PEER_FAILED_VERIFICATION = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_RANGE_ERROR = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_READ_ERROR = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_RECV_ERROR = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_REMOTE_ACCESS_DENIED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_REMOTE_DISK_FULL = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_REMOTE_FILE_EXISTS = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_REMOTE_FILE_NOT_FOUND = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SEND_ERROR = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SEND_FAIL_REWIND = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_CACERT = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_CACERT_BADFILE = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_CERTPROBLEM = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_CIPHER = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_CONNECT_ERROR = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_CRL_BADFILE = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_ENGINE_INITFAILED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_ENGINE_NOTFOUND = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_ENGINE_SETFAILED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_INVALIDCERTSTATUS = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_ISSUER_ERROR = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_PINNEDPUBKEYNOTMATCH = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_SSL_SHUTDOWN_FAILED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_TOO_MANY_REDIRECTS = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_UNKNOWN_OPTION = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_UNSUPPORTED_PROTOCOL = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_UPLOAD_FAILED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_URL_MALFORMAT = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_USE_SSL_FAILED = 0;
ECORE_CON_API ECORE_CON_API_WEAK Eina_Error EFL_NET_HTTP_ERROR_WRITE_ERROR = 0;
Eina_Error
_curlcode_to_eina_error(const CURLcode code)
{
switch (code) {
case CURLE_OK: return 0;
case CURLE_ABORTED_BY_CALLBACK: return ECONNABORTED;
case CURLE_AGAIN: return EAGAIN;
case CURLE_OUT_OF_MEMORY: return ENOMEM;
case CURLE_COULDNT_CONNECT: return EFL_NET_DIALER_ERROR_COULDNT_CONNECT;
case CURLE_COULDNT_RESOLVE_PROXY: return EFL_NET_DIALER_ERROR_COULDNT_RESOLVE_PROXY;
case CURLE_COULDNT_RESOLVE_HOST: return EFL_NET_ERROR_COULDNT_RESOLVE_HOST;
#define _MAP(n) case CURLE_ ## n: return EFL_NET_HTTP_ERROR_ ## n
_MAP(BAD_CONTENT_ENCODING);
_MAP(BAD_DOWNLOAD_RESUME);
_MAP(BAD_FUNCTION_ARGUMENT);
_MAP(CHUNK_FAILED);
_MAP(CONV_FAILED);
_MAP(CONV_REQD);
_MAP(FAILED_INIT);
_MAP(FILE_COULDNT_READ_FILE);
_MAP(FILESIZE_EXCEEDED);
_MAP(FUNCTION_NOT_FOUND);
_MAP(GOT_NOTHING);
_MAP(HTTP2);
_MAP(HTTP2_STREAM);
_MAP(HTTP_POST_ERROR);
_MAP(HTTP_RETURNED_ERROR);
_MAP(INTERFACE_FAILED);
_MAP(LOGIN_DENIED);
_MAP(NO_CONNECTION_AVAILABLE);
_MAP(NOT_BUILT_IN);
_MAP(OPERATION_TIMEDOUT);
_MAP(PARTIAL_FILE);
_MAP(PEER_FAILED_VERIFICATION);
_MAP(RANGE_ERROR);
_MAP(READ_ERROR);
_MAP(RECV_ERROR);
_MAP(REMOTE_ACCESS_DENIED);
_MAP(REMOTE_DISK_FULL);
_MAP(REMOTE_FILE_EXISTS);
_MAP(REMOTE_FILE_NOT_FOUND);
_MAP(SEND_ERROR);
_MAP(SEND_FAIL_REWIND);
_MAP(SSL_CACERT);
_MAP(SSL_CACERT_BADFILE);
_MAP(SSL_CERTPROBLEM);
_MAP(SSL_CIPHER);
_MAP(SSL_CONNECT_ERROR);
_MAP(SSL_CRL_BADFILE);
_MAP(SSL_ENGINE_INITFAILED);
_MAP(SSL_ENGINE_NOTFOUND);
_MAP(SSL_ENGINE_SETFAILED);
_MAP(SSL_INVALIDCERTSTATUS);
_MAP(SSL_ISSUER_ERROR);
_MAP(SSL_PINNEDPUBKEYNOTMATCH);
_MAP(SSL_SHUTDOWN_FAILED);
_MAP(TOO_MANY_REDIRECTS);
_MAP(UNKNOWN_OPTION);
_MAP(UNSUPPORTED_PROTOCOL);
_MAP(UPLOAD_FAILED);
_MAP(URL_MALFORMAT);
_MAP(USE_SSL_FAILED);
_MAP(WRITE_ERROR);
#undef _MAP
default:
ERR("unexpected error CURLcode=%d '%s', not mapped",
code, _c->curl_easy_strerror(code));
return EINVAL;
}
}
Eina_Error
_curlmcode_to_eina_error(const CURLMcode code)
{
switch (code) {
case CURLM_OK: return 0;
case CURLM_BAD_HANDLE: return EBADF;
case CURLM_BAD_EASY_HANDLE: return EBADF;
case CURLM_OUT_OF_MEMORY: return ENOMEM;
case CURLM_INTERNAL_ERROR: EIO; /* not exact, but the error should happen so not much of a problem */
case CURLM_BAD_SOCKET: return ENOTSOCK;
case CURLM_UNKNOWN_OPTION: return EINVAL;
case CURLM_ADDED_ALREADY: return EALREADY;
default:
ERR("unexpected error CURLMcode=%d '%s', not mapped",
code, _c->curl_multi_strerror(code));
return EINVAL;
}
}
static void
_c_init_errors(void)
{
/* use from system: */
// CURLE_ABORTED_BY_CALLBACK = ECONNABORTED
// CURLE_AGAIN = EAGAIN
// CURLE_OUT_OF_MEMORY = ENOMEM
if (EFL_NET_HTTP_ERROR_BAD_CONTENT_ENCODING) return; /* only once in the whole runtime */
#define _MAP(n) EFL_NET_HTTP_ERROR_ ## n = eina_error_msg_static_register(_c->curl_easy_strerror(CURLE_ ## n))
_MAP(BAD_CONTENT_ENCODING);
_MAP(BAD_DOWNLOAD_RESUME);
_MAP(BAD_FUNCTION_ARGUMENT);
_MAP(CHUNK_FAILED);
_MAP(CONV_FAILED);
_MAP(CONV_REQD);
_MAP(FAILED_INIT);
_MAP(FILE_COULDNT_READ_FILE);
_MAP(FILESIZE_EXCEEDED);
_MAP(FUNCTION_NOT_FOUND);
_MAP(GOT_NOTHING);
_MAP(HTTP2);
_MAP(HTTP2_STREAM);
_MAP(HTTP_POST_ERROR);
_MAP(HTTP_RETURNED_ERROR);
_MAP(INTERFACE_FAILED);
_MAP(LOGIN_DENIED);
_MAP(NO_CONNECTION_AVAILABLE);
_MAP(NOT_BUILT_IN);
_MAP(OPERATION_TIMEDOUT);
_MAP(PARTIAL_FILE);
_MAP(PEER_FAILED_VERIFICATION);
_MAP(RANGE_ERROR);
_MAP(READ_ERROR);
_MAP(RECV_ERROR);
_MAP(REMOTE_ACCESS_DENIED);
_MAP(REMOTE_DISK_FULL);
_MAP(REMOTE_FILE_EXISTS);
_MAP(REMOTE_FILE_NOT_FOUND);
_MAP(SEND_ERROR);
_MAP(SEND_FAIL_REWIND);
_MAP(SSL_CACERT);
_MAP(SSL_CACERT_BADFILE);
_MAP(SSL_CERTPROBLEM);
_MAP(SSL_CIPHER);
_MAP(SSL_CONNECT_ERROR);
_MAP(SSL_CRL_BADFILE);
_MAP(SSL_ENGINE_INITFAILED);
_MAP(SSL_ENGINE_NOTFOUND);
_MAP(SSL_ENGINE_SETFAILED);
_MAP(SSL_INVALIDCERTSTATUS);
_MAP(SSL_ISSUER_ERROR);
_MAP(SSL_PINNEDPUBKEYNOTMATCH);
_MAP(SSL_SHUTDOWN_FAILED);
_MAP(TOO_MANY_REDIRECTS);
_MAP(UNKNOWN_OPTION);
_MAP(UNSUPPORTED_PROTOCOL);
_MAP(UPLOAD_FAILED);
_MAP(URL_MALFORMAT);
_MAP(USE_SSL_FAILED);
_MAP(WRITE_ERROR);
#undef _MAP
}
Eina_Bool
_c_init(void)
{
long ms = 0;
if (_c)
{
_c->ref++;
return EINA_TRUE;
}
if (_c_fail)
{
ERR("Cannot find libcurl at runtime!");
return EINA_FALSE;
}
_c = calloc(1, sizeof(Ecore_Con_Curl));
if (!_c) goto error;
_c->ref++;
#define LOAD(x) \
if (!_c->mod) { \
if ((_c->mod = eina_module_new(x))) { \
if (!eina_module_load(_c->mod)) { \
eina_module_free(_c->mod); \
_c->mod = NULL; \
} \
} \
}
#if defined(_WIN32) || defined(__CYGWIN__)
LOAD("libcurl-5.dll"); // try correct dll first
LOAD("libcurl-4.dll"); // try correct dll first
LOAD("libcurl.dll"); // try 1
LOAD("curllib.dll"); // if fail try 2
if (!_c->mod)
ERR("Could not find libcurl-5.dll, libcurl-4.dll, libcurl.dll, curllib.dll");
#elif defined(__APPLE__) && defined(__MACH__)
LOAD("libcurl.5.dylib"); // try 1
LOAD("libcurl.4.dylib"); // try 1
LOAD("libcurl.so.5"); // if fail try 2
LOAD("libcurl.so.4"); // if fail try 2
if (!_c->mod)
ERR("Could not find libcurl.5.dylib, libcurl.4.dylib, libcurl.so.5, libcurl.so.4");
#else
LOAD("libcurl.so.5"); // try only
LOAD("libcurl.so.4"); // try only
if (!_c->mod)
ERR("Could not find libcurl.so.5, libcurl.so.4");
#endif
if (!_c->mod) goto error;
#define SYM(x) \
if (!(_c->x = eina_module_symbol_get(_c->mod, #x))) { \
ERR("Cannot find symbol '%s' in'%s", #x, eina_module_file_get(_c->mod)); \
goto error; \
}
SYM(curl_global_init);
SYM(curl_global_cleanup);
SYM(curl_multi_init);
SYM(curl_multi_timeout);
SYM(curl_multi_cleanup);
SYM(curl_multi_remove_handle);
SYM(curl_multi_strerror);
SYM(curl_multi_info_read);
SYM(curl_multi_fdset);
SYM(curl_multi_perform);
SYM(curl_multi_add_handle);
SYM(curl_multi_setopt);
SYM(curl_multi_socket_action);
SYM(curl_multi_assign);
SYM(curl_easy_init);
SYM(curl_easy_setopt);
SYM(curl_easy_strerror);
SYM(curl_easy_cleanup);
SYM(curl_easy_getinfo);
SYM(curl_easy_pause);
SYM(curl_slist_free_all);
SYM(curl_slist_append);
SYM(curl_version_info);
SYM(curl_getdate);
_c_init_errors();
// curl_global_init() is not thread safe!
if (_c->curl_global_init(CURL_GLOBAL_ALL)) goto error;
_c->_curlm = _c->curl_multi_init();
if (!_c->_curlm)
{
_c->curl_global_cleanup();
goto error;
}
_c->curl_multi_timeout(_c->_curlm, &ms);
if ((ms >= CURL_MIN_TIMEOUT) || (ms < 0)) ms = CURL_MIN_TIMEOUT;
_c_timeout = ms / 1000.0;
return EINA_TRUE;
error:
if (_c)
{
if (_c->mod) eina_module_free(_c->mod);
free(_c);
_c = NULL;
}
_c_fail = EINA_TRUE;
return EINA_FALSE;
}
void
_c_shutdown(void)
{
if (!_c || --_c->ref) return;
if (_c->_curlm)
{
_c->curl_multi_cleanup(_c->_curlm);
}
_c->curl_global_cleanup();
if (_c->mod) eina_module_free(_c->mod);
free(_c);
_c = NULL;
}