forked from enlightenment/efl
1427 lines
41 KiB
C
1427 lines
41 KiB
C
/*
|
|
* For info on how to use libcurl, see:
|
|
* http://curl.haxx.se/libcurl/c/libcurl-tutorial.html
|
|
*/
|
|
|
|
/*
|
|
* FIXME: Support more CURL features...
|
|
*/
|
|
|
|
#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>
|
|
#include <fcntl.h>
|
|
#include <libgen.h>
|
|
|
|
#include "Ecore.h"
|
|
#include "ecore_private.h"
|
|
#include "Ecore_Con.h"
|
|
#include "ecore_con_private.h"
|
|
#include "ecore_con_url_curl.h"
|
|
#include "Emile.h"
|
|
|
|
int ECORE_CON_EVENT_URL_DATA = 0;
|
|
int ECORE_CON_EVENT_URL_COMPLETE = 0;
|
|
int ECORE_CON_EVENT_URL_PROGRESS = 0;
|
|
|
|
static int _init_count = 0;
|
|
static Eina_Bool pipelining = EINA_FALSE;
|
|
|
|
static Eina_List *_url_con_url_list = NULL;
|
|
|
|
/**
|
|
* @addtogroup Ecore_Con_Url_Group Ecore URL Connection Functions
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
EAPI int
|
|
ecore_con_url_init(void)
|
|
{
|
|
if (++_init_count > 1) return _init_count;
|
|
if (!ecore_init()) goto ecore_init_failed;
|
|
if (!ecore_con_init()) goto ecore_con_init_failed;
|
|
if (!emile_init()) goto emile_init_failed;
|
|
if (!emile_cipher_init()) goto emile_cipher_init_failed;
|
|
_c_init();
|
|
ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
|
|
ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
|
|
ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
|
|
return _init_count;
|
|
|
|
emile_cipher_init_failed:
|
|
emile_shutdown();
|
|
emile_init_failed:
|
|
ecore_con_shutdown();
|
|
ecore_con_init_failed:
|
|
ecore_shutdown();
|
|
ecore_init_failed:
|
|
return --_init_count;
|
|
}
|
|
|
|
EAPI int
|
|
ecore_con_url_shutdown(void)
|
|
{
|
|
Ecore_Con_Url *url_con_url;
|
|
if (_init_count == 0) return 0;
|
|
--_init_count;
|
|
if (_init_count) return _init_count;
|
|
EINA_LIST_FREE(_url_con_url_list, url_con_url)
|
|
ecore_con_url_free(url_con_url);
|
|
|
|
ecore_event_type_flush(ECORE_CON_EVENT_URL_DATA,
|
|
ECORE_CON_EVENT_URL_COMPLETE,
|
|
ECORE_CON_EVENT_URL_PROGRESS);
|
|
|
|
_c_shutdown();
|
|
emile_shutdown(); /* no emile_cipher_shutdown(), handled here */
|
|
ecore_con_shutdown();
|
|
ecore_shutdown();
|
|
return 0;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_pipeline_set(Eina_Bool enable)
|
|
{
|
|
if (!_c_init()) return;
|
|
if (enable == pipelining) return;
|
|
_c->curl_multi_setopt(_c->_curlm, CURLMOPT_PIPELINING, !!enable);
|
|
pipelining = enable;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_con_url_pipeline_get(void)
|
|
{
|
|
return pipelining;
|
|
}
|
|
|
|
|
|
/* The rest of this file exists solely to provide ABI compatibility */
|
|
|
|
struct _Ecore_Con_Url
|
|
{
|
|
ECORE_MAGIC;
|
|
Eo *dialer;
|
|
Eo *send_copier;
|
|
Eo *input;
|
|
Ecore_Timer *timer;
|
|
struct {
|
|
Ecore_Animator *animator;
|
|
struct {
|
|
uint64_t total;
|
|
uint64_t now;
|
|
} download, upload;
|
|
} progress;
|
|
Eina_Stringshare *url;
|
|
Eina_Stringshare *custom_request;
|
|
void *data;
|
|
struct {
|
|
Eina_List *files; /* of Eina_Stringshare - read locations */
|
|
Eina_List *cmds; /* of static-const strings - COOKIELIST commands */
|
|
Eina_Stringshare *jar; /* write location */
|
|
Eina_Bool ignore_old_session;
|
|
} cookies;
|
|
struct {
|
|
Eina_Stringshare *url;
|
|
Eina_Stringshare *username;
|
|
Eina_Stringshare *password;
|
|
} proxy;
|
|
struct {
|
|
Ecore_Con_Url_Time condition;
|
|
double stamp;
|
|
} time;
|
|
struct {
|
|
Eina_Stringshare *username;
|
|
Eina_Stringshare *password;
|
|
Efl_Net_Http_Authentication_Method method;
|
|
Eina_Bool restricted;
|
|
} httpauth;
|
|
Eina_Stringshare *ca_path;
|
|
Eina_List *request_headers;
|
|
Eina_List *response_headers;
|
|
unsigned event_count;
|
|
int received_bytes;
|
|
int status;
|
|
int write_fd;
|
|
Efl_Net_Http_Version http_version;
|
|
Eina_Bool ssl_verify_peer;
|
|
Eina_Bool verbose;
|
|
Eina_Bool ftp_use_epsv;
|
|
Eina_Bool delete_me;
|
|
};
|
|
|
|
#define ECORE_CON_URL_CHECK_RETURN(u, ...) \
|
|
do \
|
|
{ \
|
|
if (!EINA_MAGIC_CHECK(u, ECORE_MAGIC_CON_URL)) \
|
|
{ \
|
|
ECORE_MAGIC_FAIL(u, ECORE_MAGIC_CON_URL, __func__); \
|
|
return __VA_ARGS__; \
|
|
} \
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(u->delete_me, __VA_ARGS__); \
|
|
} \
|
|
while (0)
|
|
|
|
|
|
static void
|
|
_ecore_con_url_dialer_close(Ecore_Con_Url *url_con)
|
|
{
|
|
if (url_con->send_copier)
|
|
{
|
|
efl_del(url_con->send_copier);
|
|
url_con->send_copier = NULL;
|
|
}
|
|
|
|
if (url_con->input)
|
|
{
|
|
efl_del(url_con->input);
|
|
url_con->input = NULL;
|
|
}
|
|
|
|
if (url_con->timer)
|
|
{
|
|
ecore_timer_del(url_con->timer);
|
|
url_con->timer = NULL;
|
|
}
|
|
|
|
if (url_con->progress.animator)
|
|
{
|
|
ecore_animator_del(url_con->progress.animator);
|
|
url_con->progress.animator = NULL;
|
|
}
|
|
|
|
if (!url_con->dialer) return;
|
|
|
|
if (!efl_io_closer_closed_get(url_con->dialer))
|
|
efl_io_closer_close(url_con->dialer);
|
|
efl_del(url_con->dialer);
|
|
url_con->dialer = NULL;
|
|
}
|
|
|
|
static void
|
|
_ecore_con_url_response_headers_free(Ecore_Con_Url *url_con)
|
|
{
|
|
char *str;
|
|
EINA_LIST_FREE(url_con->response_headers, str)
|
|
free(str);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_url_request_headers_free(Ecore_Con_Url *url_con)
|
|
{
|
|
Efl_Net_Http_Header *header;
|
|
EINA_LIST_FREE(url_con->response_headers, header)
|
|
free(header); /* key and value are inline */
|
|
}
|
|
|
|
static void
|
|
_ecore_con_url_free_internal(Ecore_Con_Url *url_con)
|
|
{
|
|
const char *s;
|
|
|
|
url_con->delete_me = EINA_TRUE;
|
|
if (url_con->event_count > 0) return;
|
|
|
|
_ecore_con_url_dialer_close(url_con);
|
|
|
|
eina_stringshare_replace(&url_con->url, NULL);
|
|
eina_stringshare_replace(&url_con->custom_request, NULL);
|
|
|
|
url_con->data = NULL;
|
|
|
|
EINA_LIST_FREE(url_con->cookies.files, s)
|
|
eina_stringshare_del(s);
|
|
eina_list_free(url_con->cookies.cmds); /* data is not to be freed! */
|
|
eina_stringshare_replace(&url_con->cookies.jar, NULL);
|
|
|
|
eina_stringshare_replace(&url_con->proxy.url, NULL);
|
|
eina_stringshare_replace(&url_con->proxy.username, NULL);
|
|
eina_stringshare_replace(&url_con->proxy.password, NULL);
|
|
|
|
eina_stringshare_replace(&url_con->httpauth.username, NULL);
|
|
eina_stringshare_replace(&url_con->httpauth.password, NULL);
|
|
|
|
eina_stringshare_replace(&url_con->ca_path, NULL);
|
|
|
|
_ecore_con_url_request_headers_free(url_con);
|
|
_ecore_con_url_response_headers_free(url_con);
|
|
|
|
ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
|
|
free(url_con);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_event_url_progress_free(void *data EINA_UNUSED, void *event)
|
|
{
|
|
Ecore_Con_Event_Url_Progress *ev = event;
|
|
Ecore_Con_Url *url_con = ev->url_con;
|
|
|
|
EINA_SAFETY_ON_TRUE_GOTO(url_con->event_count == 0, end);
|
|
|
|
url_con->event_count--;
|
|
if ((url_con->event_count == 0) && (url_con->delete_me))
|
|
_ecore_con_url_free_internal(url_con);
|
|
|
|
end:
|
|
free(ev);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_event_url_progress_add(Ecore_Con_Url *url_con)
|
|
{
|
|
Ecore_Con_Event_Url_Progress *ev;
|
|
|
|
if (url_con->delete_me) return;
|
|
|
|
ev = malloc(sizeof(*ev));
|
|
EINA_SAFETY_ON_NULL_RETURN(ev);
|
|
|
|
ev->url_con = url_con;
|
|
ev->down.total = url_con->progress.download.total;
|
|
ev->down.now = url_con->progress.download.now;
|
|
ev->up.total = url_con->progress.upload.total;
|
|
ev->up.now = url_con->progress.upload.now;
|
|
url_con->event_count++;
|
|
ecore_event_add(ECORE_CON_EVENT_URL_PROGRESS, ev, _ecore_con_event_url_progress_free, NULL);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_event_url_complete_free(void *data EINA_UNUSED, void *event)
|
|
{
|
|
Ecore_Con_Event_Url_Complete *ev = event;
|
|
Ecore_Con_Url *url_con = ev->url_con;
|
|
|
|
EINA_SAFETY_ON_TRUE_GOTO(url_con->event_count == 0, end);
|
|
|
|
url_con->event_count--;
|
|
if ((url_con->event_count == 0) && (url_con->delete_me))
|
|
_ecore_con_url_free_internal(url_con);
|
|
|
|
end:
|
|
free(ev);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_event_url_complete_add(Ecore_Con_Url *url_con, int status)
|
|
{
|
|
Ecore_Con_Event_Url_Complete *ev;
|
|
|
|
if (url_con->delete_me) return;
|
|
|
|
if (url_con->progress.animator)
|
|
{
|
|
ecore_animator_del(url_con->progress.animator);
|
|
url_con->progress.animator = NULL;
|
|
_ecore_con_event_url_progress_add(url_con);
|
|
}
|
|
|
|
if (url_con->status)
|
|
{
|
|
DBG("URL '%s' was already complete with status=%d, new=%d", url_con->url, url_con->status, status);
|
|
goto end;
|
|
}
|
|
|
|
url_con->status = status;
|
|
|
|
ev = malloc(sizeof(Ecore_Con_Event_Url_Complete));
|
|
EINA_SAFETY_ON_NULL_GOTO(ev, end);
|
|
|
|
ev->url_con = url_con;
|
|
ev->status = status;
|
|
url_con->event_count++;
|
|
ecore_event_add(ECORE_CON_EVENT_URL_COMPLETE, ev, _ecore_con_event_url_complete_free, NULL);
|
|
|
|
end:
|
|
_ecore_con_url_dialer_close(url_con);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_url_dialer_error(void *data, const Efl_Event *event)
|
|
{
|
|
Ecore_Con_Url *url_con = data;
|
|
Eina_Error *perr = event->info;
|
|
int status;
|
|
|
|
status = efl_net_dialer_http_response_status_get(url_con->dialer);
|
|
if ((status < 500) || (status > 599))
|
|
{
|
|
DBG("HTTP error %d reset to 1", status);
|
|
status = 1; /* not a real HTTP error */
|
|
}
|
|
|
|
WRN("HTTP dialer error url='%s': %s",
|
|
efl_net_dialer_address_dial_get(url_con->dialer),
|
|
eina_error_msg_get(*perr));
|
|
|
|
_ecore_con_event_url_complete_add(url_con, status);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_event_url_data_free(void *data EINA_UNUSED, void *event)
|
|
{
|
|
Ecore_Con_Event_Url_Data *ev = event;
|
|
Ecore_Con_Url *url_con = ev->url_con;
|
|
|
|
EINA_SAFETY_ON_TRUE_GOTO(url_con->event_count == 0, end);
|
|
|
|
url_con->event_count--;
|
|
if ((url_con->event_count == 0) && (url_con->delete_me))
|
|
_ecore_con_url_free_internal(url_con);
|
|
|
|
end:
|
|
free(ev);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_url_dialer_can_read_changed(void *data, const Efl_Event *event EINA_UNUSED)
|
|
{
|
|
Ecore_Con_Url *url_con = data;
|
|
Eina_Bool can_read;
|
|
Ecore_Con_Event_Url_Data *ev;
|
|
Eina_Rw_Slice slice;
|
|
Eina_Error err;
|
|
|
|
if (url_con->delete_me) return;
|
|
|
|
can_read = efl_io_reader_can_read_get(url_con->dialer);
|
|
if (!can_read) return;
|
|
|
|
ev = malloc(sizeof(Ecore_Con_Event_Url_Data) + EFL_NET_DIALER_HTTP_BUFFER_RECEIVE_SIZE);
|
|
EINA_SAFETY_ON_NULL_RETURN(ev);
|
|
|
|
slice.mem = ev->data;
|
|
slice.len = EFL_NET_DIALER_HTTP_BUFFER_RECEIVE_SIZE;
|
|
|
|
err = efl_io_reader_read(url_con->dialer, &slice);
|
|
if (err)
|
|
{
|
|
free(ev);
|
|
if (err == EAGAIN) return;
|
|
WRN("Error reading data from HTTP url='%s': %s",
|
|
efl_net_dialer_address_dial_get(url_con->dialer),
|
|
eina_error_msg_get(err));
|
|
return;
|
|
}
|
|
|
|
ev->size = slice.len;
|
|
ev->url_con = url_con;
|
|
url_con->received_bytes += ev->size;
|
|
|
|
if (url_con->write_fd == -1)
|
|
{
|
|
url_con->event_count++;
|
|
ecore_event_add(ECORE_CON_EVENT_URL_DATA, ev, _ecore_con_event_url_data_free, NULL);
|
|
return;
|
|
}
|
|
|
|
while (slice.len > 0)
|
|
{
|
|
ssize_t r = write(url_con->write_fd, slice.bytes, slice.len);
|
|
if (r == -1)
|
|
{
|
|
ERR("Could not write to fd=%d: %s", url_con->write_fd, eina_error_msg_get(errno));
|
|
break;
|
|
}
|
|
slice.bytes += r;
|
|
slice.len -= r;
|
|
}
|
|
free(ev);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_url_dialer_eos(void *data, const Efl_Event *event EINA_UNUSED)
|
|
{
|
|
Ecore_Con_Url *url_con = data;
|
|
|
|
DBG("HTTP EOS url='%s'", efl_net_dialer_address_dial_get(url_con->dialer));
|
|
|
|
if (url_con->send_copier && (!efl_io_copier_done_get(url_con->send_copier)))
|
|
{
|
|
DBG("done receiving, waiting for send copier...");
|
|
return;
|
|
}
|
|
|
|
_ecore_con_event_url_complete_add(url_con, efl_net_dialer_http_response_status_get(url_con->dialer));
|
|
}
|
|
|
|
static void
|
|
_ecore_con_url_dialer_headers_done(void *data, const Efl_Event *event EINA_UNUSED)
|
|
{
|
|
Ecore_Con_Url *url_con = data;
|
|
Eina_Iterator *it;
|
|
Efl_Net_Http_Header *header;
|
|
size_t len;
|
|
int status = efl_net_dialer_http_response_status_get(url_con->dialer);
|
|
char *str;
|
|
|
|
DBG("HTTP headers done, status=%d url='%s'",
|
|
status,
|
|
efl_net_dialer_address_dial_get(url_con->dialer));
|
|
|
|
_ecore_con_url_response_headers_free(url_con);
|
|
|
|
it = efl_net_dialer_http_response_headers_all_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_RETURN(it);
|
|
EINA_ITERATOR_FOREACH(it, header)
|
|
{
|
|
if (header->key)
|
|
{
|
|
len = strlen(header->key) + strlen(header->value) + strlen(": \r\n") + 1;
|
|
str = malloc(len);
|
|
EINA_SAFETY_ON_NULL_GOTO(str, end);
|
|
snprintf(str, len, "%s: %s\r\n", header->key, header->value);
|
|
url_con->response_headers = eina_list_append(url_con->response_headers, str);
|
|
}
|
|
else
|
|
{
|
|
if (url_con->response_headers)
|
|
{
|
|
str = malloc(strlen("\r\n") + 1);
|
|
EINA_SAFETY_ON_NULL_GOTO(str, end);
|
|
memcpy(str, "\r\n", strlen("\r\n") + 1);
|
|
url_con->response_headers = eina_list_append(url_con->response_headers, str);
|
|
}
|
|
|
|
len = strlen(header->value) + strlen("\r\n") + 1;
|
|
str = malloc(len);
|
|
EINA_SAFETY_ON_NULL_GOTO(str, end);
|
|
snprintf(str, len, "%s\r\n", header->value);
|
|
url_con->response_headers = eina_list_append(url_con->response_headers, str);
|
|
}
|
|
}
|
|
|
|
str = malloc(strlen("\r\n") + 1);
|
|
EINA_SAFETY_ON_NULL_GOTO(str, end);
|
|
memcpy(str, "\r\n", strlen("\r\n") + 1);
|
|
url_con->response_headers = eina_list_append(url_con->response_headers, str);
|
|
|
|
end:
|
|
eina_iterator_free(it);
|
|
}
|
|
|
|
EFL_CALLBACKS_ARRAY_DEFINE(ecore_con_url_dialer_cbs,
|
|
{ EFL_IO_READER_EVENT_CAN_READ_CHANGED, _ecore_con_url_dialer_can_read_changed },
|
|
{ EFL_IO_READER_EVENT_EOS, _ecore_con_url_dialer_eos },
|
|
{ EFL_NET_DIALER_EVENT_DIALER_ERROR, _ecore_con_url_dialer_error },
|
|
{ EFL_NET_DIALER_HTTP_EVENT_HEADERS_DONE, _ecore_con_url_dialer_headers_done });
|
|
|
|
static Eina_Bool
|
|
_ecore_con_url_progress_animator_cb(void *data)
|
|
{
|
|
Ecore_Con_Url *url_con = data;
|
|
uint64_t dn, dt, un, ut;
|
|
|
|
efl_net_dialer_http_progress_download_get(url_con->dialer, &dn, &dt);
|
|
efl_net_dialer_http_progress_upload_get(url_con->dialer, &un, &ut);
|
|
|
|
if ((dn == url_con->progress.download.now) &&
|
|
(dt == url_con->progress.download.total) &&
|
|
(un == url_con->progress.upload.now) &&
|
|
(ut == url_con->progress.upload.total))
|
|
return EINA_TRUE;
|
|
|
|
url_con->progress.download.now = dn;
|
|
url_con->progress.download.total = dt;
|
|
url_con->progress.upload.now = un;
|
|
url_con->progress.upload.total = ut;
|
|
|
|
_ecore_con_event_url_progress_add(url_con);
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
/*
|
|
* creates a new efl_net_dialer_proxy_set() URL based on legacy parameters:
|
|
* - proxy url (that could contain user + pass encoded, optional protocol)
|
|
* - no protocol = http://
|
|
* - username
|
|
* - password
|
|
*
|
|
* May return NULL (= use envvar http_proxy)
|
|
*/
|
|
static char *
|
|
_ecore_con_url_proxy_url_new(const Ecore_Con_Url *url_con)
|
|
{
|
|
Eina_Slice protocol, user = {}, pass = {}, address;
|
|
char buf[4096];
|
|
const char *p;
|
|
|
|
if (!url_con->proxy.url) return NULL; /* use from envvar */
|
|
|
|
p = strstr(url_con->proxy.url, "://");
|
|
if (!p)
|
|
{
|
|
protocol = (Eina_Slice)EINA_SLICE_STR_LITERAL("http");
|
|
address = (Eina_Slice)EINA_SLICE_STR(url_con->proxy.url);
|
|
}
|
|
else
|
|
{
|
|
const char *s;
|
|
|
|
protocol.mem = url_con->proxy.url;
|
|
protocol.len = p - url_con->proxy.url;
|
|
if (protocol.len == 0)
|
|
protocol = (Eina_Slice)EINA_SLICE_STR_LITERAL("http");
|
|
|
|
p += strlen("://");
|
|
s = strchr(p, '@');
|
|
if (!s)
|
|
{
|
|
address = (Eina_Slice)EINA_SLICE_STR(p);
|
|
}
|
|
else
|
|
{
|
|
address = (Eina_Slice)EINA_SLICE_STR(s + 1);
|
|
|
|
user.mem = p;
|
|
user.len = s - p;
|
|
|
|
s = eina_slice_strchr(user, ':');
|
|
if (s)
|
|
{
|
|
pass.mem = s + 1;
|
|
pass.len = user.len - (user.bytes - pass.bytes);
|
|
user.len = s - (const char *)user.bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (url_con->proxy.username)
|
|
user = eina_stringshare_slice_get(url_con->proxy.username);
|
|
|
|
if (url_con->proxy.password)
|
|
pass = eina_stringshare_slice_get(url_con->proxy.password);
|
|
|
|
if (user.len && pass.len)
|
|
{
|
|
snprintf(buf, sizeof(buf),
|
|
EINA_SLICE_STR_FMT "://"
|
|
EINA_SLICE_STR_FMT ":"
|
|
EINA_SLICE_STR_FMT "@"
|
|
EINA_SLICE_STR_FMT,
|
|
EINA_SLICE_STR_PRINT(protocol),
|
|
EINA_SLICE_STR_PRINT(user),
|
|
EINA_SLICE_STR_PRINT(pass),
|
|
EINA_SLICE_STR_PRINT(address));
|
|
}
|
|
else if (user.len)
|
|
{
|
|
snprintf(buf, sizeof(buf),
|
|
EINA_SLICE_STR_FMT "://"
|
|
EINA_SLICE_STR_FMT "@"
|
|
EINA_SLICE_STR_FMT,
|
|
EINA_SLICE_STR_PRINT(protocol),
|
|
EINA_SLICE_STR_PRINT(user),
|
|
EINA_SLICE_STR_PRINT(address));
|
|
}
|
|
else
|
|
{
|
|
snprintf(buf, sizeof(buf),
|
|
EINA_SLICE_STR_FMT "://" EINA_SLICE_STR_FMT,
|
|
EINA_SLICE_STR_PRINT(protocol),
|
|
EINA_SLICE_STR_PRINT(address));
|
|
}
|
|
|
|
return strdup(buf);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_url_copier_done(void *data, const Efl_Event *event)
|
|
{
|
|
Ecore_Con_Url *url_con = data;
|
|
int status = efl_net_dialer_http_response_status_get(url_con->dialer);
|
|
|
|
DBG("copier %s %p for url='%s' is done", efl_name_get(event->object), event->object, efl_net_dialer_address_dial_get(url_con->dialer));
|
|
|
|
if (!efl_io_reader_eos_get(url_con->dialer))
|
|
{
|
|
DBG("done sending, waiting for dialer EOS...");
|
|
return;
|
|
}
|
|
|
|
_ecore_con_event_url_complete_add(url_con, status);
|
|
}
|
|
|
|
static void
|
|
_ecore_con_url_copier_error(void *data, const Efl_Event *event)
|
|
{
|
|
Ecore_Con_Url *url_con = data;
|
|
Eina_Error *perr = event->info;
|
|
int status;
|
|
|
|
status = efl_net_dialer_http_response_status_get(url_con->dialer);
|
|
if ((status < 500) || (status > 599))
|
|
{
|
|
DBG("HTTP error %d reset to 1", status);
|
|
status = 1; /* not a real HTTP error */
|
|
}
|
|
|
|
WRN("HTTP copier %s %p error url='%s': %s",
|
|
efl_name_get(event->object), event->object,
|
|
efl_net_dialer_address_dial_get(url_con->dialer),
|
|
eina_error_msg_get(*perr));
|
|
|
|
_ecore_con_event_url_complete_add(url_con, status);
|
|
}
|
|
|
|
EFL_CALLBACKS_ARRAY_DEFINE(_ecore_con_url_copier_cbs,
|
|
{ EFL_IO_COPIER_EVENT_ERROR, _ecore_con_url_copier_error },
|
|
{ EFL_IO_COPIER_EVENT_DONE, _ecore_con_url_copier_done });
|
|
|
|
/*
|
|
* Ecore_Con_Url is documented as 'reusable', while Efl.Net.Dialers
|
|
* are one-shot and must be recreated on every usage.
|
|
*
|
|
* Then _ecore_con_url_request_prepare() will close (cancel) any
|
|
* previous dialer and create a new one with all parameters set.
|
|
*/
|
|
static Eina_Bool
|
|
_ecore_con_url_request_prepare(Ecore_Con_Url *url_con, const char *method)
|
|
{
|
|
const Efl_Net_Http_Header *header;
|
|
const Eina_List *n;
|
|
const char *s;
|
|
char *proxy_url = NULL;
|
|
CURL *curl_easy;
|
|
|
|
_ecore_con_url_dialer_close(url_con);
|
|
|
|
url_con->status = 0;
|
|
url_con->received_bytes = 0;
|
|
url_con->progress.download.now = 0;
|
|
url_con->progress.download.total = 0;
|
|
url_con->progress.upload.now = 0;
|
|
url_con->progress.upload.total = 0;
|
|
_ecore_con_url_response_headers_free(url_con);
|
|
|
|
proxy_url = _ecore_con_url_proxy_url_new(url_con);
|
|
if (proxy_url)
|
|
DBG("proxy_url='%s'", proxy_url);
|
|
|
|
url_con->dialer = efl_add(EFL_NET_DIALER_HTTP_CLASS, efl_main_loop_get(),
|
|
efl_net_dialer_http_method_set(efl_added, url_con->custom_request ? url_con->custom_request : method),
|
|
efl_net_dialer_http_primary_mode_set(efl_added, (strcmp(method, "PUT") == 0) ? EFL_NET_DIALER_HTTP_PRIMARY_MODE_UPLOAD : EFL_NET_DIALER_HTTP_PRIMARY_MODE_DOWNLOAD),
|
|
efl_net_dialer_proxy_set(efl_added, proxy_url),
|
|
efl_net_dialer_http_authentication_set(efl_added, url_con->httpauth.username, url_con->httpauth.password, url_con->httpauth.method, url_con->httpauth.restricted),
|
|
efl_net_dialer_http_version_set(efl_added, url_con->http_version),
|
|
efl_net_dialer_http_allow_redirects_set(efl_added, EINA_TRUE),
|
|
efl_net_dialer_http_ssl_verify_set(efl_added, url_con->ssl_verify_peer, url_con->ssl_verify_peer),
|
|
efl_net_dialer_http_ssl_certificate_authority_set(efl_added, url_con->ca_path),
|
|
efl_event_callback_array_add(efl_added, ecore_con_url_dialer_cbs(), url_con));
|
|
EINA_SAFETY_ON_NULL_GOTO(url_con->dialer, error);
|
|
|
|
curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_GOTO(curl_easy, error_curl_easy);
|
|
|
|
if (url_con->verbose)
|
|
{
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_DEBUGFUNCTION, NULL);
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_DEBUGDATA, NULL);
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_VERBOSE, 1L);
|
|
DBG("HTTP Dialer %p is set to legacy debug function (CURL's default, no eina_log)", url_con->dialer);
|
|
}
|
|
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_FTP_USE_EPSV, (long)url_con->ftp_use_epsv);
|
|
|
|
/* previously always set encoding to gzip,deflate */
|
|
efl_net_dialer_http_request_header_add(url_con->dialer, "Accept-Encoding", "gzip,deflate");
|
|
|
|
if (url_con->time.condition != ECORE_CON_URL_TIME_NONE)
|
|
{
|
|
char *ts = efl_net_dialer_http_date_serialize(url_con->time.stamp);
|
|
if (ts)
|
|
{
|
|
efl_net_dialer_http_request_header_add(url_con->dialer,
|
|
url_con->time.condition == ECORE_CON_URL_TIME_IFMODSINCE ? "If-Modified-Since" : "If-Unmodified-Since",
|
|
ts);
|
|
free(ts);
|
|
}
|
|
}
|
|
|
|
EINA_LIST_FOREACH(url_con->request_headers, n, header)
|
|
efl_net_dialer_http_request_header_add(url_con->dialer, header->key, header->value);
|
|
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_COOKIESESSION, (long)url_con->cookies.ignore_old_session);
|
|
|
|
EINA_LIST_FOREACH(url_con->cookies.files, n, s)
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_COOKIEFILE, s);
|
|
|
|
if (url_con->cookies.jar)
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_COOKIEJAR, url_con->cookies.jar);
|
|
|
|
EINA_LIST_FREE(url_con->cookies.cmds, s) /* free: only to execute once! */
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_COOKIELIST, s);
|
|
|
|
// Users should hook to their window animator if they want to show in real-time,
|
|
// or have a slower timer... but the old API requested a period event, so add it
|
|
// based on global animator timeout
|
|
url_con->progress.animator = ecore_animator_add(_ecore_con_url_progress_animator_cb, url_con);
|
|
|
|
DBG("prepared %p %s (%s), proxy=%s, primary_mode=%d",
|
|
url_con->dialer,
|
|
method,
|
|
efl_net_dialer_http_method_get(url_con->dialer),
|
|
efl_net_dialer_proxy_get(url_con->dialer),
|
|
efl_net_dialer_http_primary_mode_get(url_con->dialer));
|
|
|
|
free(proxy_url);
|
|
return EINA_TRUE;
|
|
|
|
error_curl_easy:
|
|
_ecore_con_url_dialer_close(url_con);
|
|
error:
|
|
free(proxy_url);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
EAPI Ecore_Con_Url *
|
|
ecore_con_url_new(const char *url)
|
|
{
|
|
Ecore_Con_Url *url_con;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(url, NULL);
|
|
|
|
url_con = calloc(1, sizeof(Ecore_Con_Url));
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(url_con, NULL);
|
|
|
|
url_con->url = eina_stringshare_add(url);
|
|
url_con->http_version = EFL_NET_HTTP_VERSION_V1_1;
|
|
url_con->write_fd = -1;
|
|
|
|
EINA_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
|
|
_url_con_url_list = eina_list_append(_url_con_url_list, url_con);
|
|
|
|
return url_con;
|
|
}
|
|
|
|
EAPI Ecore_Con_Url *
|
|
ecore_con_url_custom_new(const char *url,
|
|
const char *custom_request)
|
|
{
|
|
Ecore_Con_Url *url_con;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(url, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(custom_request, NULL);
|
|
|
|
url_con = ecore_con_url_new(url);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(url_con, NULL);
|
|
|
|
url_con->custom_request = eina_stringshare_add(custom_request);
|
|
|
|
return url_con;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_free(Ecore_Con_Url *url_con)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
/* remove from list as early as possible, we don't want to call
|
|
* ecore_con_url_free() again on pending handles in ecore_con_url_shutdown()
|
|
*/
|
|
_url_con_url_list = eina_list_remove(_url_con_url_list, url_con);
|
|
_ecore_con_url_free_internal(url_con);
|
|
}
|
|
|
|
EAPI void *
|
|
ecore_con_url_data_get(Ecore_Con_Url *url_con)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, NULL);
|
|
return url_con->data;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_data_set(Ecore_Con_Url *url_con,
|
|
void *data)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
url_con->data = data;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_con_url_url_set(Ecore_Con_Url *url_con,
|
|
const char *url)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
eina_stringshare_replace(&url_con->url, url ? url : "");
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
EAPI const char *
|
|
ecore_con_url_url_get(Ecore_Con_Url *url_con)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, NULL);
|
|
return url_con->url;
|
|
}
|
|
|
|
/* LEGACY: HTTP requests */
|
|
EAPI Eina_Bool
|
|
ecore_con_url_get(Ecore_Con_Url *url_con)
|
|
{
|
|
Eina_Error err;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
|
|
if (!_ecore_con_url_request_prepare(url_con, "GET"))
|
|
return EINA_FALSE;
|
|
|
|
err = efl_net_dialer_dial(url_con->dialer, url_con->url);
|
|
if (err)
|
|
{
|
|
WRN("failed to HTTP GET '%s': %s", url_con->url, eina_error_msg_get(err));
|
|
_ecore_con_url_dialer_close(url_con);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_con_url_head(Ecore_Con_Url *url_con)
|
|
{
|
|
Eina_Error err;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
|
|
if (!_ecore_con_url_request_prepare(url_con, "HEAD"))
|
|
return EINA_FALSE;
|
|
|
|
err = efl_net_dialer_dial(url_con->dialer, url_con->url);
|
|
if (err)
|
|
{
|
|
WRN("failed to HTTP HEAD '%s': %s", url_con->url, eina_error_msg_get(err));
|
|
_ecore_con_url_dialer_close(url_con);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_con_url_post(Ecore_Con_Url *url_con,
|
|
const void *data,
|
|
long length,
|
|
const char *content_type)
|
|
{
|
|
Eo *buffer, *copier;
|
|
Eina_Slice slice = { .mem = data, .len = length };
|
|
Eina_Error err;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
|
|
if (!_ecore_con_url_request_prepare(url_con, "POST"))
|
|
return EINA_FALSE;
|
|
|
|
if (content_type)
|
|
efl_net_dialer_http_request_header_add(url_con->dialer, "Content-Type", content_type);
|
|
|
|
buffer = efl_add(EFL_IO_BUFFER_CLASS, efl_loop_get(url_con->dialer),
|
|
efl_name_set(efl_added, "post-buffer"),
|
|
efl_io_closer_close_on_invalidate_set(efl_added, EINA_TRUE),
|
|
efl_io_closer_close_on_exec_set(efl_added, EINA_TRUE));
|
|
EINA_SAFETY_ON_NULL_GOTO(buffer, error_buffer);
|
|
|
|
err = efl_io_writer_write(buffer, &slice, NULL);
|
|
if (err)
|
|
{
|
|
WRN("could not populate buffer %p with %ld bytes: %s",
|
|
buffer, length, eina_error_msg_get(err));
|
|
goto error_copier;
|
|
}
|
|
|
|
copier = efl_add(EFL_IO_COPIER_CLASS, efl_loop_get(url_con->dialer),
|
|
efl_name_set(efl_added, "send-copier"),
|
|
efl_io_copier_source_set(efl_added, buffer),
|
|
efl_io_copier_destination_set(efl_added, url_con->dialer),
|
|
efl_io_closer_close_on_invalidate_set(efl_added, EINA_FALSE),
|
|
efl_event_callback_array_add(efl_added, _ecore_con_url_copier_cbs(), url_con));
|
|
EINA_SAFETY_ON_NULL_GOTO(copier, error_copier);
|
|
|
|
err = efl_net_dialer_dial(url_con->dialer, url_con->url);
|
|
if (err)
|
|
{
|
|
WRN("failed to post to '%s': %s", url_con->url, eina_error_msg_get(err));
|
|
goto error_dialer;
|
|
}
|
|
|
|
url_con->input = buffer;
|
|
url_con->send_copier = copier;
|
|
DBG("posting to '%s' using an Efl.Io.Copier=%p", url_con->url, copier);
|
|
|
|
return EINA_TRUE;
|
|
|
|
error_dialer:
|
|
efl_del(copier);
|
|
error_copier:
|
|
efl_del(buffer);
|
|
error_buffer:
|
|
_ecore_con_url_dialer_close(url_con);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
/* LEGACY: headers */
|
|
EAPI void
|
|
ecore_con_url_additional_header_add(Ecore_Con_Url *url_con,
|
|
const char *key,
|
|
const char *value)
|
|
{
|
|
Efl_Net_Http_Header *header;
|
|
char *s;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
EINA_SAFETY_ON_NULL_RETURN(key);
|
|
EINA_SAFETY_ON_NULL_RETURN(value);
|
|
|
|
header = malloc(sizeof(Efl_Net_Http_Header) +
|
|
strlen(key) + 1 +
|
|
strlen(value) + 1);
|
|
EINA_SAFETY_ON_NULL_RETURN(header);
|
|
|
|
header->key = s = (char *)header + sizeof(Efl_Net_Http_Header);
|
|
memcpy(s, key, strlen(key) + 1);
|
|
|
|
header->value = s = s + strlen(key) + 1;
|
|
memcpy(s, value, strlen(value) + 1);
|
|
|
|
url_con->request_headers = eina_list_append(url_con->request_headers,
|
|
header);
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
_ecore_con_url_request_headers_free(url_con);
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_time(Ecore_Con_Url *url_con,
|
|
Ecore_Con_Url_Time time_condition,
|
|
double timestamp)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
url_con->time.condition = time_condition;
|
|
url_con->time.stamp = timestamp;
|
|
}
|
|
|
|
/* LEGACY: cookies */
|
|
EAPI void
|
|
ecore_con_url_cookies_init(Ecore_Con_Url *url_con)
|
|
{
|
|
CURL *curl_easy;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
|
|
/* meaningful before and after dial, persist and apply */
|
|
url_con->cookies.files = eina_list_append(url_con->cookies.files, eina_stringshare_add(""));
|
|
|
|
if (!url_con->dialer) return;
|
|
|
|
curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_RETURN(curl_easy);
|
|
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_COOKIEFILE, "");
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_cookies_file_add(Ecore_Con_Url *url_con,
|
|
const char * const file_name)
|
|
{
|
|
CURL *curl_easy;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
EINA_SAFETY_ON_NULL_RETURN(file_name);
|
|
|
|
/* meaningful before and after dial, persist and apply */
|
|
url_con->cookies.files = eina_list_append(url_con->cookies.files, eina_stringshare_add(file_name));
|
|
|
|
if (!url_con->dialer) return;
|
|
|
|
curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_RETURN(curl_easy);
|
|
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_COOKIEFILE, file_name);
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_cookies_clear(Ecore_Con_Url *url_con)
|
|
{
|
|
static const char cookielist_cmd_all[] = "ALL";
|
|
CURL *curl_easy;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
|
|
/* only meaningful once, if not dialed, queue, otherwise execute */
|
|
if (!url_con->dialer)
|
|
{
|
|
url_con->cookies.cmds = eina_list_append(url_con->cookies.cmds, cookielist_cmd_all);
|
|
return;
|
|
}
|
|
|
|
curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_RETURN(curl_easy);
|
|
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_COOKIELIST, cookielist_cmd_all);
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_cookies_session_clear(Ecore_Con_Url *url_con)
|
|
{
|
|
static const char cookielist_cmd_sess[] = "SESS";
|
|
CURL *curl_easy;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
|
|
/* only meaningful once, if not dialed, queue, otherwise execute */
|
|
if (!url_con->dialer)
|
|
{
|
|
url_con->cookies.cmds = eina_list_append(url_con->cookies.cmds, cookielist_cmd_sess);
|
|
return;
|
|
}
|
|
|
|
curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_RETURN(curl_easy);
|
|
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_COOKIELIST, cookielist_cmd_sess);
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_cookies_ignore_old_session_set(Ecore_Con_Url *url_con,
|
|
Eina_Bool ignore)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
url_con->cookies.ignore_old_session = ignore;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_con_url_cookies_jar_file_set(Ecore_Con_Url *url_con,
|
|
const char * const cookiejar_file)
|
|
{
|
|
CURL *curl_easy;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cookiejar_file, EINA_FALSE);
|
|
|
|
/* meaningful before and after dial, persist and apply */
|
|
eina_stringshare_replace(&url_con->cookies.jar, cookiejar_file);
|
|
|
|
if (!url_con->dialer) return EINA_TRUE;
|
|
|
|
curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(curl_easy, EINA_FALSE);
|
|
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_COOKIEJAR, url_con->cookies.jar);
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_cookies_jar_write(Ecore_Con_Url *url_con)
|
|
{
|
|
CURL *curl_easy;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
|
|
/* only meaningful after dialed */
|
|
if (!url_con->dialer) return;
|
|
|
|
curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_RETURN(curl_easy);
|
|
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_COOKIELIST, "FLUSH");
|
|
}
|
|
|
|
/* LEGACY: file upload/download */
|
|
EAPI void
|
|
ecore_con_url_fd_set(Ecore_Con_Url *url_con, int fd)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
|
|
if (url_con->write_fd == fd) return;
|
|
|
|
url_con->write_fd = fd;
|
|
if (!url_con->dialer) return;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_con_url_ftp_upload(Ecore_Con_Url *url_con,
|
|
const char *filename,
|
|
const char *user,
|
|
const char *pass,
|
|
const char *upload_dir)
|
|
{
|
|
char tmp[4096];
|
|
char *bname;
|
|
Eo *file, *copier;
|
|
Eina_Error err;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(filename, EINA_FALSE);
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(filename[0] == '\0', EINA_FALSE);
|
|
|
|
bname = strdup(filename);
|
|
if (upload_dir)
|
|
snprintf(tmp, sizeof(tmp), "%s/%s/%s", url_con->url, upload_dir, basename(bname));
|
|
else
|
|
snprintf(tmp, sizeof(tmp), "%s/%s", url_con->url, basename(bname));
|
|
free(bname);
|
|
|
|
if (!_ecore_con_url_request_prepare(url_con, "PUT"))
|
|
return EINA_FALSE;
|
|
|
|
efl_net_dialer_http_authentication_set(url_con->dialer, user, pass, EFL_NET_HTTP_AUTHENTICATION_METHOD_ANY, EINA_FALSE);
|
|
|
|
file = efl_add(EFL_IO_FILE_CLASS, efl_loop_get(url_con->dialer),
|
|
efl_name_set(efl_added, "upload-file"),
|
|
efl_file_set(efl_added, filename),
|
|
efl_io_file_flags_set(efl_added, O_RDONLY),
|
|
efl_io_closer_close_on_invalidate_set(efl_added, EINA_TRUE),
|
|
efl_io_closer_close_on_exec_set(efl_added, EINA_TRUE));
|
|
EINA_SAFETY_ON_NULL_GOTO(file, error_file);
|
|
|
|
copier = efl_add(EFL_IO_COPIER_CLASS, efl_loop_get(url_con->dialer),
|
|
efl_name_set(efl_added, "send-copier"),
|
|
efl_io_copier_source_set(efl_added, file),
|
|
efl_io_copier_destination_set(efl_added, url_con->dialer),
|
|
efl_io_closer_close_on_invalidate_set(efl_added, EINA_FALSE),
|
|
efl_event_callback_array_add(efl_added, _ecore_con_url_copier_cbs(), url_con));
|
|
EINA_SAFETY_ON_NULL_GOTO(copier, error_copier);
|
|
|
|
err = efl_net_dialer_dial(url_con->dialer, tmp);
|
|
if (err)
|
|
{
|
|
WRN("failed to upload file '%s' to '%s': %s", filename, tmp, eina_error_msg_get(err));
|
|
goto error_dialer;
|
|
}
|
|
|
|
url_con->input = file;
|
|
url_con->send_copier = copier;
|
|
DBG("uploading file '%s' to '%s' using an Efl.Io.Copier=%p", filename, tmp, copier);
|
|
|
|
return EINA_TRUE;
|
|
|
|
error_dialer:
|
|
efl_del(copier);
|
|
error_copier:
|
|
efl_del(file);
|
|
error_file:
|
|
_ecore_con_url_dialer_close(url_con);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con,
|
|
Eina_Bool use_epsv)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
url_con->ftp_use_epsv = use_epsv;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_limit_upload_speed(Ecore_Con_Url *url_con, off_t max_speed)
|
|
{
|
|
CURL *curl_easy;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
EINA_SAFETY_ON_NULL_RETURN(_c);
|
|
|
|
curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_RETURN(curl_easy);
|
|
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_MAX_SEND_SPEED_LARGE, max_speed);
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_limit_download_speed(Ecore_Con_Url *url_con, off_t max_speed)
|
|
{
|
|
CURL *curl_easy;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
EINA_SAFETY_ON_NULL_RETURN(_c);
|
|
|
|
curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_RETURN(curl_easy);
|
|
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_MAX_RECV_SPEED_LARGE, max_speed);
|
|
}
|
|
|
|
/* LEGACY: proxy */
|
|
EAPI Eina_Bool
|
|
ecore_con_url_proxy_password_set(Ecore_Con_Url *url_con, const char *password)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(password, EINA_FALSE);
|
|
eina_stringshare_replace(&url_con->proxy.password, password);
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_con_url_proxy_username_set(Ecore_Con_Url *url_con, const char *username)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(username, EINA_FALSE);
|
|
eina_stringshare_replace(&url_con->proxy.username, username);
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_con_url_proxy_set(Ecore_Con_Url *url_con, const char *proxy_url)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
eina_stringshare_replace(&url_con->proxy.url, proxy_url);
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
/* LEGACY: response */
|
|
EAPI int
|
|
ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
return url_con->received_bytes;
|
|
}
|
|
|
|
EAPI int
|
|
ecore_con_url_status_code_get(Ecore_Con_Url *url_con)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, 0);
|
|
if (!url_con->dialer) return url_con->status;
|
|
return efl_net_dialer_http_response_status_get(url_con->dialer);
|
|
}
|
|
|
|
EAPI const Eina_List *
|
|
ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, NULL);
|
|
return url_con->response_headers;
|
|
}
|
|
|
|
/* LEGACY: SSL */
|
|
EAPI int
|
|
ecore_con_url_ssl_ca_set(Ecore_Con_Url *url_con,
|
|
const char *ca_path)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, -1);
|
|
eina_stringshare_replace(&url_con->ca_path, ca_path);
|
|
url_con->ssl_verify_peer = !!ca_path;
|
|
return 0;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_ssl_verify_peer_set(Ecore_Con_Url *url_con,
|
|
Eina_Bool verify)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
url_con->ssl_verify_peer = !!verify;
|
|
}
|
|
|
|
/* LEGACY: misc */
|
|
EAPI Eina_Bool
|
|
ecore_con_url_httpauth_set(Ecore_Con_Url *url_con,
|
|
const char *username,
|
|
const char *password,
|
|
Eina_Bool safe)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(username, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(password, EINA_FALSE);
|
|
|
|
eina_stringshare_replace(&url_con->httpauth.username, username);
|
|
eina_stringshare_replace(&url_con->httpauth.password, password);
|
|
url_con->httpauth.method = safe ? EFL_NET_HTTP_AUTHENTICATION_METHOD_ANY_SAFE : EFL_NET_HTTP_AUTHENTICATION_METHOD_ANY;
|
|
url_con->httpauth.restricted = safe;
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_con_url_http_version_set(Ecore_Con_Url *url_con, Ecore_Con_Url_Http_Version version)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
|
|
switch (version)
|
|
{
|
|
case ECORE_CON_URL_HTTP_VERSION_1_0:
|
|
url_con->http_version = EFL_NET_HTTP_VERSION_V1_0;
|
|
break;
|
|
case ECORE_CON_URL_HTTP_VERSION_1_1:
|
|
url_con->http_version = EFL_NET_HTTP_VERSION_V1_1;
|
|
break;
|
|
default:
|
|
ERR("unknown HTTP version enum value %d", version);
|
|
return EINA_FALSE;
|
|
}
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_ecore_con_url_timeout_cb(void *data)
|
|
{
|
|
Ecore_Con_Url *url_con = data;
|
|
int status;
|
|
|
|
url_con->timer = NULL;
|
|
|
|
WRN("HTTP timeout url='%s'", efl_net_dialer_address_dial_get(url_con->dialer));
|
|
|
|
status = efl_net_dialer_http_response_status_get(url_con->dialer);
|
|
if ((status < 500) || (status > 599))
|
|
{
|
|
DBG("HTTP error %d reset to 1", status);
|
|
status = 1; /* not a real HTTP error */
|
|
}
|
|
|
|
_ecore_con_event_url_complete_add(url_con, status);
|
|
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_timeout_set(Ecore_Con_Url *url_con, double timeout)
|
|
{
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
|
|
if (url_con->timer)
|
|
{
|
|
ecore_timer_del(url_con->timer);
|
|
url_con->timer = NULL;
|
|
}
|
|
|
|
if (timeout <= 0.0) return;
|
|
|
|
// NOTE: it is weird to start the timeout right away here, but it
|
|
// was done like that and we're keeping it for compatibility
|
|
url_con->timer = ecore_timer_add(timeout, _ecore_con_url_timeout_cb, url_con);
|
|
}
|
|
|
|
EAPI void
|
|
ecore_con_url_verbose_set(Ecore_Con_Url *url_con,
|
|
Eina_Bool verbose)
|
|
{
|
|
CURL *curl_easy;
|
|
|
|
ECORE_CON_URL_CHECK_RETURN(url_con);
|
|
|
|
/* meaningful before and after dial, persist and apply */
|
|
url_con->verbose = !!verbose;
|
|
|
|
if (!url_con->dialer) return;
|
|
|
|
curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
|
|
EINA_SAFETY_ON_NULL_RETURN(curl_easy);
|
|
|
|
if (url_con->verbose)
|
|
{
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_DEBUGFUNCTION, NULL);
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_DEBUGDATA, NULL);
|
|
DBG("HTTP Dialer %p is set to legacy debug function (CURL's default, no eina_log)", url_con->dialer);
|
|
}
|
|
_c->curl_easy_setopt(curl_easy, CURLOPT_VERBOSE, (long)url_con->verbose);
|
|
}
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|