1 /*
2  * For info on how to use libcurl, see:
3  * http://curl.haxx.se/libcurl/c/libcurl-tutorial.html
4  */
5 
6 /*
7  * FIXME: Support more CURL features...
8  */
9 
10 #ifdef HAVE_CONFIG_H
11 # include <config.h>
12 #endif
13 
14 #include <string.h>
15 #include <errno.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <libgen.h>
21 
22 #include "Ecore.h"
23 #include "ecore_private.h"
24 #include "Ecore_Con.h"
25 #include "ecore_con_private.h"
26 #include "ecore_con_url_curl.h"
27 #include "Emile.h"
28 
29 int ECORE_CON_EVENT_URL_DATA = 0;
30 int ECORE_CON_EVENT_URL_COMPLETE = 0;
31 int ECORE_CON_EVENT_URL_PROGRESS = 0;
32 
33 static int _init_count = 0;
34 static Eina_Bool pipelining = EINA_FALSE;
35 
36 static Eina_List *_url_con_url_list = NULL;
37 
38 /**
39  * @addtogroup Ecore_Con_Url_Group Ecore URL Connection Functions
40  *
41  * @{
42  */
43 
44 EAPI int
ecore_con_url_init(void)45 ecore_con_url_init(void)
46 {
47    if (++_init_count > 1) return _init_count;
48    if (!ecore_init()) goto ecore_init_failed;
49    if (!ecore_con_init()) goto ecore_con_init_failed;
50    if (!emile_init()) goto emile_init_failed;
51    if (!emile_cipher_init()) goto emile_cipher_init_failed;
52    ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
53    ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
54    ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
55    return _init_count;
56 
57  emile_cipher_init_failed:
58    emile_shutdown();
59  emile_init_failed:
60    ecore_con_shutdown();
61  ecore_con_init_failed:
62    ecore_shutdown();
63  ecore_init_failed:
64    return --_init_count;
65 }
66 
67 EAPI int
ecore_con_url_shutdown(void)68 ecore_con_url_shutdown(void)
69 {
70    Ecore_Con_Url *url_con_url;
71    if (_init_count == 0) return 0;
72    --_init_count;
73    if (_init_count) return _init_count;
74    EINA_LIST_FREE(_url_con_url_list, url_con_url)
75      ecore_con_url_free(url_con_url);
76 
77    ecore_event_type_flush(ECORE_CON_EVENT_URL_DATA,
78                           ECORE_CON_EVENT_URL_COMPLETE,
79                           ECORE_CON_EVENT_URL_PROGRESS);
80 
81    emile_shutdown(); /* no emile_cipher_shutdown(), handled here */
82    ecore_con_shutdown();
83    ecore_shutdown();
84    return 0;
85 }
86 
87 EAPI void
ecore_con_url_pipeline_set(Eina_Bool enable)88 ecore_con_url_pipeline_set(Eina_Bool enable)
89 {
90    if (!_c_init()) return;
91    if (enable == pipelining) return;
92    _c->curl_multi_setopt(_c->_curlm, CURLMOPT_PIPELINING, !!enable);
93    pipelining = enable;
94 }
95 
96 EAPI Eina_Bool
ecore_con_url_pipeline_get(void)97 ecore_con_url_pipeline_get(void)
98 {
99    return pipelining;
100 }
101 
102 
103 /* The rest of this file exists solely to provide ABI compatibility */
104 
105 struct _Ecore_Con_Url
106 {
107    ECORE_MAGIC;
108    Eo *dialer;
109    Eo *send_copier;
110    Eo *input;
111    Ecore_Timer *timer;
112    struct {
113       Ecore_Animator *animator;
114       struct {
115          uint64_t total;
116          uint64_t now;
117       } download, upload;
118    } progress;
119    Eina_Stringshare *url;
120    Eina_Stringshare *custom_request;
121    void *data;
122    struct {
123       Eina_List *files; /* of Eina_Stringshare - read locations */
124       Eina_List *cmds; /* of static-const strings - COOKIELIST commands */
125       Eina_Stringshare *jar; /* write location */
126       Eina_Bool ignore_old_session;
127    } cookies;
128    struct {
129       Eina_Stringshare *url;
130       Eina_Stringshare *username;
131       Eina_Stringshare *password;
132    } proxy;
133    struct {
134       Ecore_Con_Url_Time condition;
135       double stamp;
136    } time;
137    struct {
138       Eina_Stringshare *username;
139       Eina_Stringshare *password;
140       Efl_Net_Http_Authentication_Method method;
141       Eina_Bool restricted;
142    } httpauth;
143    Eina_Stringshare *ca_path;
144    Eina_List *request_headers;
145    Eina_List *response_headers;
146    unsigned event_count;
147    int received_bytes;
148    int status;
149    int write_fd;
150    Efl_Net_Http_Version http_version;
151    Eina_Bool ssl_verify_peer;
152    Eina_Bool verbose;
153    Eina_Bool ftp_use_epsv;
154    Eina_Bool delete_me;
155 };
156 
157 #define ECORE_CON_URL_CHECK_RETURN(u, ...) \
158   do \
159     { \
160        if (!EINA_MAGIC_CHECK(u, ECORE_MAGIC_CON_URL)) \
161          { \
162             ECORE_MAGIC_FAIL(u, ECORE_MAGIC_CON_URL, __func__); \
163             return __VA_ARGS__; \
164          } \
165        EINA_SAFETY_ON_TRUE_RETURN_VAL(u->delete_me, __VA_ARGS__); \
166     } \
167   while (0)
168 
169 
170 static void
_ecore_con_url_dialer_close(Ecore_Con_Url * url_con)171 _ecore_con_url_dialer_close(Ecore_Con_Url *url_con)
172 {
173    if (url_con->send_copier)
174      {
175         efl_del(url_con->send_copier);
176         url_con->send_copier = NULL;
177      }
178 
179    if (url_con->input)
180      {
181         efl_del(url_con->input);
182         url_con->input = NULL;
183      }
184 
185    if (url_con->timer)
186      {
187         ecore_timer_del(url_con->timer);
188         url_con->timer = NULL;
189      }
190 
191    if (url_con->progress.animator)
192      {
193         ecore_animator_del(url_con->progress.animator);
194         url_con->progress.animator = NULL;
195      }
196 
197    if (!url_con->dialer) return;
198 
199    if (!efl_io_closer_closed_get(url_con->dialer))
200      efl_io_closer_close(url_con->dialer);
201    efl_del(url_con->dialer);
202    url_con->dialer = NULL;
203 }
204 
205 static void
_ecore_con_url_response_headers_free(Ecore_Con_Url * url_con)206 _ecore_con_url_response_headers_free(Ecore_Con_Url *url_con)
207 {
208    char *str;
209    EINA_LIST_FREE(url_con->response_headers, str)
210      free(str);
211 }
212 
213 static void
_ecore_con_url_request_headers_free(Ecore_Con_Url * url_con)214 _ecore_con_url_request_headers_free(Ecore_Con_Url *url_con)
215 {
216    Efl_Net_Http_Header *header;
217    EINA_LIST_FREE(url_con->response_headers, header)
218      free(header); /* key and value are inline */
219 }
220 
221 static void
_ecore_con_url_free_internal(Ecore_Con_Url * url_con)222 _ecore_con_url_free_internal(Ecore_Con_Url *url_con)
223 {
224    const char *s;
225 
226    url_con->delete_me = EINA_TRUE;
227    if (url_con->event_count > 0) return;
228 
229    _ecore_con_url_dialer_close(url_con);
230 
231    eina_stringshare_replace(&url_con->url, NULL);
232    eina_stringshare_replace(&url_con->custom_request, NULL);
233 
234    url_con->data = NULL;
235 
236    EINA_LIST_FREE(url_con->cookies.files, s)
237      eina_stringshare_del(s);
238    eina_list_free(url_con->cookies.cmds); /* data is not to be freed! */
239    eina_stringshare_replace(&url_con->cookies.jar, NULL);
240 
241    eina_stringshare_replace(&url_con->proxy.url, NULL);
242    eina_stringshare_replace(&url_con->proxy.username, NULL);
243    eina_stringshare_replace(&url_con->proxy.password, NULL);
244 
245    eina_stringshare_replace(&url_con->httpauth.username, NULL);
246    eina_stringshare_replace(&url_con->httpauth.password, NULL);
247 
248    eina_stringshare_replace(&url_con->ca_path, NULL);
249 
250    _ecore_con_url_request_headers_free(url_con);
251    _ecore_con_url_response_headers_free(url_con);
252 
253    ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
254    free(url_con);
255 }
256 
257 static void
_ecore_con_event_url_progress_free(void * data EINA_UNUSED,void * event)258 _ecore_con_event_url_progress_free(void *data EINA_UNUSED, void *event)
259 {
260    Ecore_Con_Event_Url_Progress *ev = event;
261    Ecore_Con_Url *url_con = ev->url_con;
262 
263    EINA_SAFETY_ON_TRUE_GOTO(url_con->event_count == 0, end);
264 
265    url_con->event_count--;
266    if ((url_con->event_count == 0) && (url_con->delete_me))
267      _ecore_con_url_free_internal(url_con);
268 
269  end:
270    free(ev);
271 }
272 
273 static void
_ecore_con_event_url_progress_add(Ecore_Con_Url * url_con)274 _ecore_con_event_url_progress_add(Ecore_Con_Url *url_con)
275 {
276    Ecore_Con_Event_Url_Progress *ev;
277 
278    if (url_con->delete_me) return;
279 
280    ev = malloc(sizeof(*ev));
281    EINA_SAFETY_ON_NULL_RETURN(ev);
282 
283    ev->url_con = url_con;
284    ev->down.total = url_con->progress.download.total;
285    ev->down.now = url_con->progress.download.now;
286    ev->up.total = url_con->progress.upload.total;
287    ev->up.now = url_con->progress.upload.now;
288    url_con->event_count++;
289    ecore_event_add(ECORE_CON_EVENT_URL_PROGRESS, ev, _ecore_con_event_url_progress_free, NULL);
290 }
291 
292 static void
_ecore_con_event_url_complete_free(void * data EINA_UNUSED,void * event)293 _ecore_con_event_url_complete_free(void *data EINA_UNUSED, void *event)
294 {
295    Ecore_Con_Event_Url_Complete *ev = event;
296    Ecore_Con_Url *url_con = ev->url_con;
297 
298    EINA_SAFETY_ON_TRUE_GOTO(url_con->event_count == 0, end);
299 
300    url_con->event_count--;
301    if ((url_con->event_count == 0) && (url_con->delete_me))
302      _ecore_con_url_free_internal(url_con);
303 
304  end:
305    free(ev);
306 }
307 
308 static void
_ecore_con_event_url_complete_add(Ecore_Con_Url * url_con,int status)309 _ecore_con_event_url_complete_add(Ecore_Con_Url *url_con, int status)
310 {
311    Ecore_Con_Event_Url_Complete *ev;
312 
313    if (url_con->delete_me) return;
314 
315    if (url_con->progress.animator)
316      {
317         ecore_animator_del(url_con->progress.animator);
318         url_con->progress.animator = NULL;
319         _ecore_con_event_url_progress_add(url_con);
320      }
321 
322    if (url_con->status)
323      {
324         DBG("URL '%s' was already complete with status=%d, new=%d", url_con->url, url_con->status, status);
325         goto end;
326      }
327 
328    url_con->status = status;
329 
330    ev = malloc(sizeof(Ecore_Con_Event_Url_Complete));
331    EINA_SAFETY_ON_NULL_GOTO(ev, end);
332 
333    ev->url_con = url_con;
334    ev->status = status;
335    url_con->event_count++;
336    ecore_event_add(ECORE_CON_EVENT_URL_COMPLETE, ev, _ecore_con_event_url_complete_free, NULL);
337 
338  end:
339    _ecore_con_url_dialer_close(url_con);
340 }
341 
342 static void
_ecore_con_url_dialer_error(void * data,const Efl_Event * event)343 _ecore_con_url_dialer_error(void *data, const Efl_Event *event)
344 {
345    Ecore_Con_Url *url_con = data;
346    Eina_Error *perr = event->info;
347    int status;
348 
349    status = efl_net_dialer_http_response_status_get(url_con->dialer);
350    if ((status < 500) || (status > 599))
351      {
352         DBG("HTTP error %d reset to 1", status);
353         status = 1; /* not a real HTTP error */
354      }
355 
356    WRN("HTTP dialer error url='%s': %s",
357        efl_net_dialer_address_dial_get(url_con->dialer),
358        eina_error_msg_get(*perr));
359 
360    _ecore_con_event_url_complete_add(url_con, status);
361 }
362 
363 static void
_ecore_con_event_url_data_free(void * data EINA_UNUSED,void * event)364 _ecore_con_event_url_data_free(void *data EINA_UNUSED, void *event)
365 {
366    Ecore_Con_Event_Url_Data *ev = event;
367    Ecore_Con_Url *url_con = ev->url_con;
368 
369    EINA_SAFETY_ON_TRUE_GOTO(url_con->event_count == 0, end);
370 
371    url_con->event_count--;
372    if ((url_con->event_count == 0) && (url_con->delete_me))
373      _ecore_con_url_free_internal(url_con);
374 
375  end:
376    free(ev);
377 }
378 
379 static void
_ecore_con_url_dialer_can_read_changed(void * data,const Efl_Event * event EINA_UNUSED)380 _ecore_con_url_dialer_can_read_changed(void *data, const Efl_Event *event EINA_UNUSED)
381 {
382    Ecore_Con_Url *url_con = data;
383    Eina_Bool can_read;
384    Ecore_Con_Event_Url_Data *ev;
385    Eina_Rw_Slice slice;
386    Eina_Error err;
387 
388    if (url_con->delete_me) return;
389 
390    can_read = efl_io_reader_can_read_get(url_con->dialer);
391    if (!can_read) return;
392 
393    ev = malloc(sizeof(Ecore_Con_Event_Url_Data) + EFL_NET_DIALER_HTTP_BUFFER_RECEIVE_SIZE);
394    EINA_SAFETY_ON_NULL_RETURN(ev);
395 
396    slice.mem = ev->data;
397    slice.len = EFL_NET_DIALER_HTTP_BUFFER_RECEIVE_SIZE;
398 
399    err = efl_io_reader_read(url_con->dialer, &slice);
400    if (err)
401      {
402         free(ev);
403         if (err == EAGAIN) return;
404         WRN("Error reading data from HTTP url='%s': %s",
405             efl_net_dialer_address_dial_get(url_con->dialer),
406             eina_error_msg_get(err));
407         return;
408      }
409 
410    ev->size = slice.len;
411    ev->url_con = url_con;
412    url_con->received_bytes += ev->size;
413 
414    if (url_con->write_fd == -1)
415      {
416         url_con->event_count++;
417         ecore_event_add(ECORE_CON_EVENT_URL_DATA, ev, _ecore_con_event_url_data_free, NULL);
418         return;
419      }
420 
421    while (slice.len > 0)
422      {
423         ssize_t r = write(url_con->write_fd, slice.bytes, slice.len);
424         if (r == -1)
425           {
426              ERR("Could not write to fd=%d: %s", url_con->write_fd, eina_error_msg_get(errno));
427              break;
428           }
429         slice.bytes += r;
430         slice.len -= r;
431      }
432    free(ev);
433 }
434 
435 static void
_ecore_con_url_dialer_eos(void * data,const Efl_Event * event EINA_UNUSED)436 _ecore_con_url_dialer_eos(void *data, const Efl_Event *event EINA_UNUSED)
437 {
438    Ecore_Con_Url *url_con = data;
439 
440    DBG("HTTP EOS url='%s'", efl_net_dialer_address_dial_get(url_con->dialer));
441 
442    if (url_con->send_copier && (!efl_io_copier_done_get(url_con->send_copier)))
443      {
444         DBG("done receiving, waiting for send copier...");
445         return;
446      }
447 
448    _ecore_con_event_url_complete_add(url_con, efl_net_dialer_http_response_status_get(url_con->dialer));
449 }
450 
451 static void
_ecore_con_url_dialer_headers_done(void * data,const Efl_Event * event EINA_UNUSED)452 _ecore_con_url_dialer_headers_done(void *data, const Efl_Event *event EINA_UNUSED)
453 {
454    Ecore_Con_Url *url_con = data;
455    Eina_Iterator *it;
456    Efl_Net_Http_Header *header;
457    size_t len;
458    int status = efl_net_dialer_http_response_status_get(url_con->dialer);
459    char *str;
460 
461    DBG("HTTP headers done, status=%d url='%s'",
462        status,
463        efl_net_dialer_address_dial_get(url_con->dialer));
464 
465    _ecore_con_url_response_headers_free(url_con);
466 
467    it = efl_net_dialer_http_response_headers_all_get(url_con->dialer);
468    EINA_SAFETY_ON_NULL_RETURN(it);
469    EINA_ITERATOR_FOREACH(it, header)
470      {
471         if (header->key)
472           {
473              len = strlen(header->key) + strlen(header->value) + strlen(": \r\n") + 1;
474              str = malloc(len);
475              EINA_SAFETY_ON_NULL_GOTO(str, end);
476              snprintf(str, len, "%s: %s\r\n", header->key, header->value);
477              url_con->response_headers = eina_list_append(url_con->response_headers, str);
478           }
479         else
480           {
481              if (url_con->response_headers)
482                {
483                   str = malloc(strlen("\r\n") + 1);
484                   EINA_SAFETY_ON_NULL_GOTO(str, end);
485                   memcpy(str, "\r\n", strlen("\r\n") + 1);
486                   url_con->response_headers = eina_list_append(url_con->response_headers, str);
487                }
488 
489              len = strlen(header->value) + strlen("\r\n") + 1;
490              str = malloc(len);
491              EINA_SAFETY_ON_NULL_GOTO(str, end);
492              snprintf(str, len, "%s\r\n", header->value);
493              url_con->response_headers = eina_list_append(url_con->response_headers, str);
494           }
495      }
496 
497    str = malloc(strlen("\r\n") + 1);
498    EINA_SAFETY_ON_NULL_GOTO(str, end);
499    memcpy(str, "\r\n", strlen("\r\n") + 1);
500    url_con->response_headers = eina_list_append(url_con->response_headers, str);
501 
502  end:
503    eina_iterator_free(it);
504 }
505 
506 EFL_CALLBACKS_ARRAY_DEFINE(ecore_con_url_dialer_cbs,
507                            { EFL_IO_READER_EVENT_CAN_READ_CHANGED, _ecore_con_url_dialer_can_read_changed },
508                            { EFL_IO_READER_EVENT_EOS, _ecore_con_url_dialer_eos },
509                            { EFL_NET_DIALER_EVENT_DIALER_ERROR, _ecore_con_url_dialer_error },
510                            { EFL_NET_DIALER_HTTP_EVENT_HEADERS_DONE, _ecore_con_url_dialer_headers_done });
511 
512 static Eina_Bool
_ecore_con_url_progress_animator_cb(void * data)513 _ecore_con_url_progress_animator_cb(void *data)
514 {
515    Ecore_Con_Url *url_con = data;
516    uint64_t dn, dt, un, ut;
517 
518    efl_net_dialer_http_progress_download_get(url_con->dialer, &dn, &dt);
519    efl_net_dialer_http_progress_upload_get(url_con->dialer, &un, &ut);
520 
521    if ((dn == url_con->progress.download.now) &&
522        (dt == url_con->progress.download.total) &&
523        (un == url_con->progress.upload.now) &&
524        (ut == url_con->progress.upload.total))
525      return EINA_TRUE;
526 
527    url_con->progress.download.now = dn;
528    url_con->progress.download.total = dt;
529    url_con->progress.upload.now = un;
530    url_con->progress.upload.total = ut;
531 
532    _ecore_con_event_url_progress_add(url_con);
533 
534    return EINA_TRUE;
535 }
536 
537 /*
538  * creates a new efl_net_dialer_proxy_set() URL based on legacy parameters:
539  *  - proxy url (that could contain user + pass encoded, optional protocol)
540  *    - no protocol = http://
541  *  - username
542  *  - password
543  *
544  * May return NULL (= use envvar http_proxy)
545  */
546 static char *
_ecore_con_url_proxy_url_new(const Ecore_Con_Url * url_con)547 _ecore_con_url_proxy_url_new(const Ecore_Con_Url *url_con)
548 {
549    Eina_Slice protocol, user = {}, pass = {}, address;
550    char buf[4096];
551    const char *p;
552 
553    if (!url_con->proxy.url) return NULL; /* use from envvar */
554 
555    p = strstr(url_con->proxy.url, "://");
556    if (!p)
557      {
558         protocol = (Eina_Slice)EINA_SLICE_STR_LITERAL("http");
559         address = (Eina_Slice)EINA_SLICE_STR(url_con->proxy.url);
560      }
561    else
562      {
563         const char *s;
564 
565         protocol.mem = url_con->proxy.url;
566         protocol.len = p - url_con->proxy.url;
567         if (protocol.len == 0)
568           protocol = (Eina_Slice)EINA_SLICE_STR_LITERAL("http");
569 
570         p += strlen("://");
571         s = strchr(p, '@');
572         if (!s)
573           {
574              address = (Eina_Slice)EINA_SLICE_STR(p);
575           }
576         else
577           {
578              address = (Eina_Slice)EINA_SLICE_STR(s + 1);
579 
580              user.mem = p;
581              user.len = s - p;
582 
583              s = eina_slice_strchr(user, ':');
584              if (s)
585                {
586                   pass.mem = s + 1;
587                   pass.len = user.len - (user.bytes - pass.bytes);
588                   user.len = s - (const char *)user.bytes;
589                }
590           }
591      }
592 
593    if (url_con->proxy.username)
594      user = eina_stringshare_slice_get(url_con->proxy.username);
595 
596    if (url_con->proxy.password)
597      pass = eina_stringshare_slice_get(url_con->proxy.password);
598 
599    if (user.len && pass.len)
600      {
601         snprintf(buf, sizeof(buf),
602                  EINA_SLICE_STR_FMT "://"
603                  EINA_SLICE_STR_FMT ":"
604                  EINA_SLICE_STR_FMT "@"
605                  EINA_SLICE_STR_FMT,
606                  EINA_SLICE_STR_PRINT(protocol),
607                  EINA_SLICE_STR_PRINT(user),
608                  EINA_SLICE_STR_PRINT(pass),
609                  EINA_SLICE_STR_PRINT(address));
610      }
611    else if (user.len)
612      {
613         snprintf(buf, sizeof(buf),
614                  EINA_SLICE_STR_FMT "://"
615                  EINA_SLICE_STR_FMT "@"
616                  EINA_SLICE_STR_FMT,
617                  EINA_SLICE_STR_PRINT(protocol),
618                  EINA_SLICE_STR_PRINT(user),
619                  EINA_SLICE_STR_PRINT(address));
620      }
621    else
622      {
623         snprintf(buf, sizeof(buf),
624                  EINA_SLICE_STR_FMT "://" EINA_SLICE_STR_FMT,
625                  EINA_SLICE_STR_PRINT(protocol),
626                  EINA_SLICE_STR_PRINT(address));
627      }
628 
629    return strdup(buf);
630 }
631 
632 static void
_ecore_con_url_copier_done(void * data,const Efl_Event * event)633 _ecore_con_url_copier_done(void *data, const Efl_Event *event)
634 {
635    Ecore_Con_Url *url_con = data;
636    int status = efl_net_dialer_http_response_status_get(url_con->dialer);
637 
638    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));
639 
640    if (!efl_io_reader_eos_get(url_con->dialer))
641      {
642         DBG("done sending, waiting for dialer EOS...");
643         return;
644      }
645 
646    _ecore_con_event_url_complete_add(url_con, status);
647 }
648 
649 static void
_ecore_con_url_copier_error(void * data,const Efl_Event * event)650 _ecore_con_url_copier_error(void *data, const Efl_Event *event)
651 {
652    Ecore_Con_Url *url_con = data;
653    Eina_Error *perr = event->info;
654    int status;
655 
656    status = efl_net_dialer_http_response_status_get(url_con->dialer);
657    if ((status < 500) || (status > 599))
658      {
659         DBG("HTTP error %d reset to 1", status);
660         status = 1; /* not a real HTTP error */
661      }
662 
663    WRN("HTTP copier %s %p error url='%s': %s",
664        efl_name_get(event->object), event->object,
665        efl_net_dialer_address_dial_get(url_con->dialer),
666        eina_error_msg_get(*perr));
667 
668    _ecore_con_event_url_complete_add(url_con, status);
669 }
670 
671 EFL_CALLBACKS_ARRAY_DEFINE(_ecore_con_url_copier_cbs,
672                            { EFL_IO_COPIER_EVENT_ERROR, _ecore_con_url_copier_error },
673                            { EFL_IO_COPIER_EVENT_DONE, _ecore_con_url_copier_done });
674 
675 /*
676  * Ecore_Con_Url is documented as 'reusable', while Efl.Net.Dialers
677  * are one-shot and must be recreated on every usage.
678  *
679  * Then _ecore_con_url_request_prepare() will close (cancel) any
680  * previous dialer and create a new one with all parameters set.
681  */
682 static Eina_Bool
_ecore_con_url_request_prepare(Ecore_Con_Url * url_con,const char * method)683 _ecore_con_url_request_prepare(Ecore_Con_Url *url_con, const char *method)
684 {
685    const Efl_Net_Http_Header *header;
686    const Eina_List *n;
687    const char *s;
688    char *proxy_url = NULL;
689    CURL *curl_easy;
690 
691    _ecore_con_url_dialer_close(url_con);
692 
693    url_con->status = 0;
694    url_con->received_bytes = 0;
695    url_con->progress.download.now = 0;
696    url_con->progress.download.total = 0;
697    url_con->progress.upload.now = 0;
698    url_con->progress.upload.total = 0;
699    _ecore_con_url_response_headers_free(url_con);
700 
701    proxy_url = _ecore_con_url_proxy_url_new(url_con);
702    if (proxy_url)
703      DBG("proxy_url='%s'", proxy_url);
704 
705    url_con->dialer = efl_add(EFL_NET_DIALER_HTTP_CLASS, efl_main_loop_get(),
706                              efl_net_dialer_http_method_set(efl_added, url_con->custom_request ? url_con->custom_request : method),
707                              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),
708                              efl_net_dialer_proxy_set(efl_added, proxy_url),
709                              efl_net_dialer_http_authentication_set(efl_added, url_con->httpauth.username, url_con->httpauth.password, url_con->httpauth.method, url_con->httpauth.restricted),
710                              efl_net_dialer_http_version_set(efl_added, url_con->http_version),
711                              efl_net_dialer_http_allow_redirects_set(efl_added, EINA_TRUE),
712                              efl_net_dialer_http_ssl_verify_set(efl_added, url_con->ssl_verify_peer, url_con->ssl_verify_peer),
713                              efl_net_dialer_http_ssl_certificate_authority_set(efl_added, url_con->ca_path),
714                              efl_event_callback_array_add(efl_added, ecore_con_url_dialer_cbs(), url_con));
715    EINA_SAFETY_ON_NULL_GOTO(url_con->dialer, error);
716 
717    curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
718    EINA_SAFETY_ON_NULL_GOTO(curl_easy, error_curl_easy);
719 
720    if (url_con->verbose)
721      {
722         _c->curl_easy_setopt(curl_easy, CURLOPT_DEBUGFUNCTION, NULL);
723         _c->curl_easy_setopt(curl_easy, CURLOPT_DEBUGDATA, NULL);
724         _c->curl_easy_setopt(curl_easy, CURLOPT_VERBOSE, 1L);
725         DBG("HTTP Dialer %p is set to legacy debug function (CURL's default, no eina_log)", url_con->dialer);
726      }
727 
728    _c->curl_easy_setopt(curl_easy, CURLOPT_FTP_USE_EPSV, (long)url_con->ftp_use_epsv);
729 
730    /* previously always set encoding to gzip,deflate */
731    efl_net_dialer_http_request_header_add(url_con->dialer, "Accept-Encoding", "gzip,deflate");
732 
733    if (url_con->time.condition != ECORE_CON_URL_TIME_NONE)
734      {
735         char *ts = efl_net_dialer_http_date_serialize(url_con->time.stamp);
736         if (ts)
737           {
738              efl_net_dialer_http_request_header_add(url_con->dialer,
739                                                     url_con->time.condition == ECORE_CON_URL_TIME_IFMODSINCE ? "If-Modified-Since" : "If-Unmodified-Since",
740                                                     ts);
741              free(ts);
742           }
743      }
744 
745    EINA_LIST_FOREACH(url_con->request_headers, n, header)
746      efl_net_dialer_http_request_header_add(url_con->dialer, header->key, header->value);
747 
748    _c->curl_easy_setopt(curl_easy, CURLOPT_COOKIESESSION, (long)url_con->cookies.ignore_old_session);
749 
750    EINA_LIST_FOREACH(url_con->cookies.files, n, s)
751      _c->curl_easy_setopt(curl_easy, CURLOPT_COOKIEFILE, s);
752 
753    if (url_con->cookies.jar)
754      _c->curl_easy_setopt(curl_easy, CURLOPT_COOKIEJAR, url_con->cookies.jar);
755 
756    EINA_LIST_FREE(url_con->cookies.cmds, s) /* free: only to execute once! */
757      _c->curl_easy_setopt(curl_easy, CURLOPT_COOKIELIST, s);
758 
759    // Users should hook to their window animator if they want to show in real-time,
760    // or have a slower timer... but the old API requested a period event, so add it
761    // based on global animator timeout
762    url_con->progress.animator = ecore_animator_add(_ecore_con_url_progress_animator_cb, url_con);
763 
764    DBG("prepared %p %s (%s), proxy=%s, primary_mode=%d",
765        url_con->dialer,
766        method,
767        efl_net_dialer_http_method_get(url_con->dialer),
768        efl_net_dialer_proxy_get(url_con->dialer),
769        efl_net_dialer_http_primary_mode_get(url_con->dialer));
770 
771    free(proxy_url);
772    return EINA_TRUE;
773 
774  error_curl_easy:
775    _ecore_con_url_dialer_close(url_con);
776  error:
777    free(proxy_url);
778    return EINA_FALSE;
779 }
780 
781 EAPI Ecore_Con_Url *
ecore_con_url_new(const char * url)782 ecore_con_url_new(const char *url)
783 {
784    Ecore_Con_Url *url_con;
785 
786    EINA_SAFETY_ON_NULL_RETURN_VAL(url, NULL);
787 
788    url_con = calloc(1, sizeof(Ecore_Con_Url));
789    EINA_SAFETY_ON_NULL_RETURN_VAL(url_con, NULL);
790 
791    url_con->url = eina_stringshare_add(url);
792    url_con->http_version = EFL_NET_HTTP_VERSION_V1_1;
793    url_con->write_fd = -1;
794 
795    EINA_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
796    _url_con_url_list = eina_list_append(_url_con_url_list, url_con);
797 
798    return url_con;
799 }
800 
801 EAPI Ecore_Con_Url *
ecore_con_url_custom_new(const char * url,const char * custom_request)802 ecore_con_url_custom_new(const char *url,
803                          const char *custom_request)
804 {
805    Ecore_Con_Url *url_con;
806 
807    EINA_SAFETY_ON_NULL_RETURN_VAL(url, NULL);
808    EINA_SAFETY_ON_NULL_RETURN_VAL(custom_request, NULL);
809 
810    url_con = ecore_con_url_new(url);
811    EINA_SAFETY_ON_NULL_RETURN_VAL(url_con, NULL);
812 
813    url_con->custom_request = eina_stringshare_add(custom_request);
814 
815    return url_con;
816 }
817 
818 EAPI void
ecore_con_url_free(Ecore_Con_Url * url_con)819 ecore_con_url_free(Ecore_Con_Url *url_con)
820 {
821    ECORE_CON_URL_CHECK_RETURN(url_con);
822    /* remove from list as early as possible, we don't want to call
823     * ecore_con_url_free() again on pending handles in ecore_con_url_shutdown()
824     */
825    _url_con_url_list = eina_list_remove(_url_con_url_list, url_con);
826    _ecore_con_url_free_internal(url_con);
827 }
828 
829 EAPI void *
ecore_con_url_data_get(Ecore_Con_Url * url_con)830 ecore_con_url_data_get(Ecore_Con_Url *url_con)
831 {
832    ECORE_CON_URL_CHECK_RETURN(url_con, NULL);
833    return url_con->data;
834 }
835 
836 EAPI void
ecore_con_url_data_set(Ecore_Con_Url * url_con,void * data)837 ecore_con_url_data_set(Ecore_Con_Url *url_con,
838                        void *data)
839 {
840    ECORE_CON_URL_CHECK_RETURN(url_con);
841    url_con->data = data;
842 }
843 
844 EAPI Eina_Bool
ecore_con_url_url_set(Ecore_Con_Url * url_con,const char * url)845 ecore_con_url_url_set(Ecore_Con_Url *url_con,
846                       const char *url)
847 {
848    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
849    eina_stringshare_replace(&url_con->url, url ? url : "");
850    return EINA_TRUE;
851 }
852 
853 EAPI const char *
ecore_con_url_url_get(Ecore_Con_Url * url_con)854 ecore_con_url_url_get(Ecore_Con_Url *url_con)
855 {
856    ECORE_CON_URL_CHECK_RETURN(url_con, NULL);
857    return url_con->url;
858 }
859 
860 /* LEGACY: HTTP requests */
861 EAPI Eina_Bool
ecore_con_url_get(Ecore_Con_Url * url_con)862 ecore_con_url_get(Ecore_Con_Url *url_con)
863 {
864    Eina_Error err;
865 
866    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
867 
868    if (!_ecore_con_url_request_prepare(url_con, "GET"))
869      return EINA_FALSE;
870 
871    err = efl_net_dialer_dial(url_con->dialer, url_con->url);
872    if (err)
873      {
874         WRN("failed to HTTP GET '%s': %s", url_con->url, eina_error_msg_get(err));
875         _ecore_con_url_dialer_close(url_con);
876         return EINA_FALSE;
877      }
878 
879    return EINA_TRUE;
880 }
881 
882 EAPI Eina_Bool
ecore_con_url_head(Ecore_Con_Url * url_con)883 ecore_con_url_head(Ecore_Con_Url *url_con)
884 {
885    Eina_Error err;
886 
887    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
888 
889    if (!_ecore_con_url_request_prepare(url_con, "HEAD"))
890      return EINA_FALSE;
891 
892    err = efl_net_dialer_dial(url_con->dialer, url_con->url);
893    if (err)
894      {
895         WRN("failed to HTTP HEAD '%s': %s", url_con->url, eina_error_msg_get(err));
896         _ecore_con_url_dialer_close(url_con);
897         return EINA_FALSE;
898      }
899 
900    return EINA_TRUE;
901 }
902 
903 EAPI Eina_Bool
ecore_con_url_post(Ecore_Con_Url * url_con,const void * data,long length,const char * content_type)904 ecore_con_url_post(Ecore_Con_Url *url_con,
905                    const void *data,
906                    long length,
907                    const char *content_type)
908 {
909    Eo *buffer, *copier;
910    Eina_Slice slice = { .mem = data, .len = length };
911    Eina_Error err;
912 
913    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
914 
915    if (!_ecore_con_url_request_prepare(url_con, "POST"))
916      return EINA_FALSE;
917 
918    if (content_type)
919      efl_net_dialer_http_request_header_add(url_con->dialer, "Content-Type", content_type);
920 
921    buffer = efl_add(EFL_IO_BUFFER_CLASS, efl_loop_get(url_con->dialer),
922                     efl_name_set(efl_added, "post-buffer"),
923                     efl_io_closer_close_on_invalidate_set(efl_added, EINA_TRUE),
924                     efl_io_closer_close_on_exec_set(efl_added, EINA_TRUE));
925    EINA_SAFETY_ON_NULL_GOTO(buffer, error_buffer);
926 
927    err = efl_io_writer_write(buffer, &slice, NULL);
928    if (err)
929      {
930         WRN("could not populate buffer %p with %ld bytes: %s",
931             buffer, length, eina_error_msg_get(err));
932         goto error_copier;
933      }
934 
935    copier = efl_add(EFL_IO_COPIER_CLASS, efl_loop_get(url_con->dialer),
936                     efl_name_set(efl_added, "send-copier"),
937                     efl_io_copier_source_set(efl_added, buffer),
938                     efl_io_copier_destination_set(efl_added, url_con->dialer),
939                     efl_io_closer_close_on_invalidate_set(efl_added, EINA_FALSE),
940                     efl_event_callback_array_add(efl_added, _ecore_con_url_copier_cbs(), url_con));
941    EINA_SAFETY_ON_NULL_GOTO(copier, error_copier);
942 
943    err = efl_net_dialer_dial(url_con->dialer, url_con->url);
944    if (err)
945      {
946         WRN("failed to post to '%s': %s", url_con->url, eina_error_msg_get(err));
947         goto error_dialer;
948      }
949 
950    url_con->input = buffer;
951    url_con->send_copier = copier;
952    DBG("posting to '%s' using an Efl.Io.Copier=%p", url_con->url, copier);
953 
954    return EINA_TRUE;
955 
956  error_dialer:
957    efl_del(copier);
958  error_copier:
959    efl_del(buffer);
960  error_buffer:
961    _ecore_con_url_dialer_close(url_con);
962    return EINA_FALSE;
963 }
964 
965 /* LEGACY: headers */
966 EAPI void
ecore_con_url_additional_header_add(Ecore_Con_Url * url_con,const char * key,const char * value)967 ecore_con_url_additional_header_add(Ecore_Con_Url *url_con,
968                                     const char *key,
969                                     const char *value)
970 {
971    Efl_Net_Http_Header *header;
972    char *s;
973 
974    ECORE_CON_URL_CHECK_RETURN(url_con);
975    EINA_SAFETY_ON_NULL_RETURN(key);
976    EINA_SAFETY_ON_NULL_RETURN(value);
977 
978    header = malloc(sizeof(Efl_Net_Http_Header) +
979                    strlen(key) + 1 +
980                    strlen(value) + 1);
981    EINA_SAFETY_ON_NULL_RETURN(header);
982 
983    header->key = s = (char *)header + sizeof(Efl_Net_Http_Header);
984    memcpy(s, key, strlen(key) + 1);
985 
986    header->value = s = s + strlen(key) + 1;
987    memcpy(s, value, strlen(value) + 1);
988 
989    url_con->request_headers = eina_list_append(url_con->request_headers,
990                                                header);
991 }
992 
993 EAPI void
ecore_con_url_additional_headers_clear(Ecore_Con_Url * url_con)994 ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
995 {
996    ECORE_CON_URL_CHECK_RETURN(url_con);
997    _ecore_con_url_request_headers_free(url_con);
998 }
999 
1000 EAPI void
ecore_con_url_time(Ecore_Con_Url * url_con,Ecore_Con_Url_Time time_condition,double timestamp)1001 ecore_con_url_time(Ecore_Con_Url *url_con,
1002                    Ecore_Con_Url_Time time_condition,
1003                    double timestamp)
1004 {
1005    ECORE_CON_URL_CHECK_RETURN(url_con);
1006    url_con->time.condition = time_condition;
1007    url_con->time.stamp = timestamp;
1008 }
1009 
1010 /* LEGACY: cookies */
1011 EAPI void
ecore_con_url_cookies_init(Ecore_Con_Url * url_con)1012 ecore_con_url_cookies_init(Ecore_Con_Url *url_con)
1013 {
1014    CURL *curl_easy;
1015 
1016    ECORE_CON_URL_CHECK_RETURN(url_con);
1017 
1018    /* meaningful before and after dial, persist and apply */
1019    url_con->cookies.files = eina_list_append(url_con->cookies.files, eina_stringshare_add(""));
1020 
1021    if (!url_con->dialer) return;
1022 
1023    curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
1024    EINA_SAFETY_ON_NULL_RETURN(curl_easy);
1025 
1026    _c->curl_easy_setopt(curl_easy, CURLOPT_COOKIEFILE, "");
1027 }
1028 
1029 EAPI void
ecore_con_url_cookies_file_add(Ecore_Con_Url * url_con,const char * const file_name)1030 ecore_con_url_cookies_file_add(Ecore_Con_Url *url_con,
1031                                const char * const file_name)
1032 {
1033    CURL *curl_easy;
1034 
1035    ECORE_CON_URL_CHECK_RETURN(url_con);
1036    EINA_SAFETY_ON_NULL_RETURN(file_name);
1037 
1038    /* meaningful before and after dial, persist and apply */
1039    url_con->cookies.files = eina_list_append(url_con->cookies.files, eina_stringshare_add(file_name));
1040 
1041    if (!url_con->dialer) return;
1042 
1043    curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
1044    EINA_SAFETY_ON_NULL_RETURN(curl_easy);
1045 
1046    _c->curl_easy_setopt(curl_easy, CURLOPT_COOKIEFILE, file_name);
1047 }
1048 
1049 EAPI void
ecore_con_url_cookies_clear(Ecore_Con_Url * url_con)1050 ecore_con_url_cookies_clear(Ecore_Con_Url *url_con)
1051 {
1052    static const char cookielist_cmd_all[] = "ALL";
1053    CURL *curl_easy;
1054 
1055    ECORE_CON_URL_CHECK_RETURN(url_con);
1056 
1057    /* only meaningful once, if not dialed, queue, otherwise execute */
1058    if (!url_con->dialer)
1059      {
1060         url_con->cookies.cmds = eina_list_append(url_con->cookies.cmds, cookielist_cmd_all);
1061         return;
1062      }
1063 
1064    curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
1065    EINA_SAFETY_ON_NULL_RETURN(curl_easy);
1066 
1067    _c->curl_easy_setopt(curl_easy, CURLOPT_COOKIELIST, cookielist_cmd_all);
1068 }
1069 
1070 EAPI void
ecore_con_url_cookies_session_clear(Ecore_Con_Url * url_con)1071 ecore_con_url_cookies_session_clear(Ecore_Con_Url *url_con)
1072 {
1073    static const char cookielist_cmd_sess[] = "SESS";
1074    CURL *curl_easy;
1075 
1076    ECORE_CON_URL_CHECK_RETURN(url_con);
1077 
1078    /* only meaningful once, if not dialed, queue, otherwise execute */
1079    if (!url_con->dialer)
1080      {
1081         url_con->cookies.cmds = eina_list_append(url_con->cookies.cmds, cookielist_cmd_sess);
1082         return;
1083      }
1084 
1085    curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
1086    EINA_SAFETY_ON_NULL_RETURN(curl_easy);
1087 
1088    _c->curl_easy_setopt(curl_easy, CURLOPT_COOKIELIST, cookielist_cmd_sess);
1089 }
1090 
1091 EAPI void
ecore_con_url_cookies_ignore_old_session_set(Ecore_Con_Url * url_con,Eina_Bool ignore)1092 ecore_con_url_cookies_ignore_old_session_set(Ecore_Con_Url *url_con,
1093                                              Eina_Bool ignore)
1094 {
1095    ECORE_CON_URL_CHECK_RETURN(url_con);
1096    url_con->cookies.ignore_old_session = ignore;
1097 }
1098 
1099 EAPI Eina_Bool
ecore_con_url_cookies_jar_file_set(Ecore_Con_Url * url_con,const char * const cookiejar_file)1100 ecore_con_url_cookies_jar_file_set(Ecore_Con_Url *url_con,
1101                                    const char * const cookiejar_file)
1102 {
1103    CURL *curl_easy;
1104 
1105    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
1106    EINA_SAFETY_ON_NULL_RETURN_VAL(cookiejar_file, EINA_FALSE);
1107 
1108    /* meaningful before and after dial, persist and apply */
1109    eina_stringshare_replace(&url_con->cookies.jar, cookiejar_file);
1110 
1111    if (!url_con->dialer) return EINA_TRUE;
1112 
1113    curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
1114    EINA_SAFETY_ON_NULL_RETURN_VAL(curl_easy, EINA_FALSE);
1115 
1116    _c->curl_easy_setopt(curl_easy, CURLOPT_COOKIEJAR, url_con->cookies.jar);
1117    return EINA_TRUE;
1118 }
1119 
1120 EAPI void
ecore_con_url_cookies_jar_write(Ecore_Con_Url * url_con)1121 ecore_con_url_cookies_jar_write(Ecore_Con_Url *url_con)
1122 {
1123    CURL *curl_easy;
1124 
1125    ECORE_CON_URL_CHECK_RETURN(url_con);
1126 
1127    /* only meaningful after dialed */
1128    if (!url_con->dialer) return;
1129 
1130    curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
1131    EINA_SAFETY_ON_NULL_RETURN(curl_easy);
1132 
1133    _c->curl_easy_setopt(curl_easy, CURLOPT_COOKIELIST, "FLUSH");
1134 }
1135 
1136 /* LEGACY: file upload/download */
1137 EAPI void
ecore_con_url_fd_set(Ecore_Con_Url * url_con,int fd)1138 ecore_con_url_fd_set(Ecore_Con_Url *url_con, int fd)
1139 {
1140    ECORE_CON_URL_CHECK_RETURN(url_con);
1141 
1142    if (url_con->write_fd == fd) return;
1143 
1144    url_con->write_fd = fd;
1145    if (!url_con->dialer) return;
1146 }
1147 
1148 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)1149 ecore_con_url_ftp_upload(Ecore_Con_Url *url_con,
1150                          const char *filename,
1151                          const char *user,
1152                          const char *pass,
1153                          const char *upload_dir)
1154 {
1155    char tmp[4096];
1156    char *bname;
1157    Eo *file, *copier;
1158    Eina_Error err;
1159 
1160    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
1161    EINA_SAFETY_ON_NULL_RETURN_VAL(filename, EINA_FALSE);
1162    EINA_SAFETY_ON_TRUE_RETURN_VAL(filename[0] == '\0', EINA_FALSE);
1163 
1164    bname = strdup(filename);
1165    if (upload_dir)
1166      snprintf(tmp, sizeof(tmp), "%s/%s/%s", url_con->url, upload_dir, basename(bname));
1167    else
1168      snprintf(tmp, sizeof(tmp), "%s/%s", url_con->url, basename(bname));
1169    free(bname);
1170 
1171    if (!_ecore_con_url_request_prepare(url_con, "PUT"))
1172      return EINA_FALSE;
1173 
1174    efl_net_dialer_http_authentication_set(url_con->dialer, user, pass, EFL_NET_HTTP_AUTHENTICATION_METHOD_ANY, EINA_FALSE);
1175 
1176    file = efl_add(EFL_IO_FILE_CLASS, efl_loop_get(url_con->dialer),
1177                   efl_name_set(efl_added, "upload-file"),
1178                   efl_file_set(efl_added, filename),
1179                   efl_io_file_flags_set(efl_added, O_RDONLY),
1180                   efl_io_closer_close_on_invalidate_set(efl_added, EINA_TRUE),
1181                   efl_io_closer_close_on_exec_set(efl_added, EINA_TRUE));
1182    EINA_SAFETY_ON_NULL_GOTO(file, error_file);
1183 
1184    copier = efl_add(EFL_IO_COPIER_CLASS, efl_loop_get(url_con->dialer),
1185                     efl_name_set(efl_added, "send-copier"),
1186                     efl_io_copier_source_set(efl_added, file),
1187                     efl_io_copier_destination_set(efl_added, url_con->dialer),
1188                     efl_io_closer_close_on_invalidate_set(efl_added, EINA_FALSE),
1189                     efl_event_callback_array_add(efl_added, _ecore_con_url_copier_cbs(), url_con));
1190    EINA_SAFETY_ON_NULL_GOTO(copier, error_copier);
1191 
1192    err = efl_net_dialer_dial(url_con->dialer, tmp);
1193    if (err)
1194      {
1195         WRN("failed to upload file '%s' to '%s': %s", filename, tmp, eina_error_msg_get(err));
1196         goto error_dialer;
1197      }
1198 
1199    url_con->input = file;
1200    url_con->send_copier = copier;
1201    DBG("uploading file '%s' to '%s' using an Efl.Io.Copier=%p", filename, tmp, copier);
1202 
1203    return EINA_TRUE;
1204 
1205  error_dialer:
1206    efl_del(copier);
1207  error_copier:
1208    efl_del(file);
1209  error_file:
1210    _ecore_con_url_dialer_close(url_con);
1211    return EINA_FALSE;
1212 }
1213 
1214 EAPI void
ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url * url_con,Eina_Bool use_epsv)1215 ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con,
1216                                Eina_Bool use_epsv)
1217 {
1218    ECORE_CON_URL_CHECK_RETURN(url_con);
1219    url_con->ftp_use_epsv = use_epsv;
1220 }
1221 
1222 EAPI void
ecore_con_url_limit_upload_speed(Ecore_Con_Url * url_con,off_t max_speed)1223 ecore_con_url_limit_upload_speed(Ecore_Con_Url *url_con, off_t max_speed)
1224 {
1225    CURL *curl_easy;
1226 
1227    ECORE_CON_URL_CHECK_RETURN(url_con);
1228    EINA_SAFETY_ON_NULL_RETURN(_c);
1229 
1230    curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
1231    EINA_SAFETY_ON_NULL_RETURN(curl_easy);
1232 
1233    _c->curl_easy_setopt(curl_easy, CURLOPT_MAX_SEND_SPEED_LARGE, max_speed);
1234 }
1235 
1236 EAPI void
ecore_con_url_limit_download_speed(Ecore_Con_Url * url_con,off_t max_speed)1237 ecore_con_url_limit_download_speed(Ecore_Con_Url *url_con, off_t max_speed)
1238 {
1239    CURL *curl_easy;
1240 
1241    ECORE_CON_URL_CHECK_RETURN(url_con);
1242    EINA_SAFETY_ON_NULL_RETURN(_c);
1243 
1244    curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
1245    EINA_SAFETY_ON_NULL_RETURN(curl_easy);
1246 
1247    _c->curl_easy_setopt(curl_easy, CURLOPT_MAX_RECV_SPEED_LARGE, max_speed);
1248 }
1249 
1250 /* LEGACY: proxy */
1251 EAPI Eina_Bool
ecore_con_url_proxy_password_set(Ecore_Con_Url * url_con,const char * password)1252 ecore_con_url_proxy_password_set(Ecore_Con_Url *url_con, const char *password)
1253 {
1254    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
1255    EINA_SAFETY_ON_NULL_RETURN_VAL(password, EINA_FALSE);
1256    eina_stringshare_replace(&url_con->proxy.password, password);
1257    return EINA_TRUE;
1258 }
1259 
1260 EAPI Eina_Bool
ecore_con_url_proxy_username_set(Ecore_Con_Url * url_con,const char * username)1261 ecore_con_url_proxy_username_set(Ecore_Con_Url *url_con, const char *username)
1262 {
1263    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
1264    EINA_SAFETY_ON_NULL_RETURN_VAL(username, EINA_FALSE);
1265    eina_stringshare_replace(&url_con->proxy.username, username);
1266    return EINA_TRUE;
1267 }
1268 
1269 EAPI Eina_Bool
ecore_con_url_proxy_set(Ecore_Con_Url * url_con,const char * proxy_url)1270 ecore_con_url_proxy_set(Ecore_Con_Url *url_con, const char *proxy_url)
1271 {
1272    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
1273    eina_stringshare_replace(&url_con->proxy.url, proxy_url);
1274    return EINA_TRUE;
1275 }
1276 
1277 /* LEGACY: response */
1278 EAPI int
ecore_con_url_received_bytes_get(Ecore_Con_Url * url_con)1279 ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
1280 {
1281    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
1282    return url_con->received_bytes;
1283 }
1284 
1285 EAPI int
ecore_con_url_status_code_get(Ecore_Con_Url * url_con)1286 ecore_con_url_status_code_get(Ecore_Con_Url *url_con)
1287 {
1288    ECORE_CON_URL_CHECK_RETURN(url_con, 0);
1289    if (!url_con->dialer) return url_con->status;
1290    return efl_net_dialer_http_response_status_get(url_con->dialer);
1291 }
1292 
1293 EAPI const Eina_List *
ecore_con_url_response_headers_get(Ecore_Con_Url * url_con)1294 ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
1295 {
1296    ECORE_CON_URL_CHECK_RETURN(url_con, NULL);
1297    return url_con->response_headers;
1298 }
1299 
1300 /* LEGACY: SSL */
1301 EAPI int
ecore_con_url_ssl_ca_set(Ecore_Con_Url * url_con,const char * ca_path)1302 ecore_con_url_ssl_ca_set(Ecore_Con_Url *url_con,
1303                          const char *ca_path)
1304 {
1305    ECORE_CON_URL_CHECK_RETURN(url_con, -1);
1306    eina_stringshare_replace(&url_con->ca_path, ca_path);
1307    url_con->ssl_verify_peer = !!ca_path;
1308    return 0;
1309 }
1310 
1311 EAPI void
ecore_con_url_ssl_verify_peer_set(Ecore_Con_Url * url_con,Eina_Bool verify)1312 ecore_con_url_ssl_verify_peer_set(Ecore_Con_Url *url_con,
1313                                   Eina_Bool verify)
1314 {
1315    ECORE_CON_URL_CHECK_RETURN(url_con);
1316    url_con->ssl_verify_peer = !!verify;
1317 }
1318 
1319 /* LEGACY: misc */
1320 EAPI Eina_Bool
ecore_con_url_httpauth_set(Ecore_Con_Url * url_con,const char * username,const char * password,Eina_Bool safe)1321 ecore_con_url_httpauth_set(Ecore_Con_Url *url_con,
1322                            const char *username,
1323                            const char *password,
1324                            Eina_Bool safe)
1325 {
1326    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
1327    EINA_SAFETY_ON_NULL_RETURN_VAL(username, EINA_FALSE);
1328    EINA_SAFETY_ON_NULL_RETURN_VAL(password, EINA_FALSE);
1329 
1330    eina_stringshare_replace(&url_con->httpauth.username, username);
1331    eina_stringshare_replace(&url_con->httpauth.password, password);
1332    url_con->httpauth.method = safe ? EFL_NET_HTTP_AUTHENTICATION_METHOD_ANY_SAFE : EFL_NET_HTTP_AUTHENTICATION_METHOD_ANY;
1333    url_con->httpauth.restricted = safe;
1334    return EINA_TRUE;
1335 }
1336 
1337 EAPI Eina_Bool
ecore_con_url_http_version_set(Ecore_Con_Url * url_con,Ecore_Con_Url_Http_Version version)1338 ecore_con_url_http_version_set(Ecore_Con_Url *url_con, Ecore_Con_Url_Http_Version version)
1339 {
1340    ECORE_CON_URL_CHECK_RETURN(url_con, EINA_FALSE);
1341    switch (version)
1342      {
1343       case ECORE_CON_URL_HTTP_VERSION_1_0:
1344          url_con->http_version = EFL_NET_HTTP_VERSION_V1_0;
1345          break;
1346       case ECORE_CON_URL_HTTP_VERSION_1_1:
1347          url_con->http_version = EFL_NET_HTTP_VERSION_V1_1;
1348          break;
1349       default:
1350          ERR("unknown HTTP version enum value %d", version);
1351          return EINA_FALSE;
1352      }
1353    return EINA_TRUE;
1354 }
1355 
1356 static Eina_Bool
_ecore_con_url_timeout_cb(void * data)1357 _ecore_con_url_timeout_cb(void *data)
1358 {
1359    Ecore_Con_Url *url_con = data;
1360    int status;
1361 
1362    url_con->timer = NULL;
1363 
1364    WRN("HTTP timeout url='%s'", efl_net_dialer_address_dial_get(url_con->dialer));
1365 
1366    status = efl_net_dialer_http_response_status_get(url_con->dialer);
1367    if ((status < 500) || (status > 599))
1368      {
1369         DBG("HTTP error %d reset to 1", status);
1370         status = 1; /* not a real HTTP error */
1371      }
1372 
1373    _ecore_con_event_url_complete_add(url_con, status);
1374 
1375    return EINA_FALSE;
1376 }
1377 
1378 EAPI void
ecore_con_url_timeout_set(Ecore_Con_Url * url_con,double timeout)1379 ecore_con_url_timeout_set(Ecore_Con_Url *url_con, double timeout)
1380 {
1381    ECORE_CON_URL_CHECK_RETURN(url_con);
1382 
1383    if (url_con->timer)
1384      {
1385         ecore_timer_del(url_con->timer);
1386         url_con->timer = NULL;
1387      }
1388 
1389    if (timeout <= 0.0) return;
1390 
1391    // NOTE: it is weird to start the timeout right away here, but it
1392    // was done like that and we're keeping it for compatibility
1393    url_con->timer = ecore_timer_add(timeout, _ecore_con_url_timeout_cb, url_con);
1394 }
1395 
1396 EAPI void
ecore_con_url_verbose_set(Ecore_Con_Url * url_con,Eina_Bool verbose)1397 ecore_con_url_verbose_set(Ecore_Con_Url *url_con,
1398                           Eina_Bool verbose)
1399 {
1400    CURL *curl_easy;
1401 
1402    ECORE_CON_URL_CHECK_RETURN(url_con);
1403 
1404    /* meaningful before and after dial, persist and apply */
1405    url_con->verbose = !!verbose;
1406 
1407    if (!url_con->dialer) return;
1408 
1409    curl_easy = efl_net_dialer_http_curl_get(url_con->dialer);
1410    EINA_SAFETY_ON_NULL_RETURN(curl_easy);
1411 
1412    if (url_con->verbose)
1413      {
1414         _c->curl_easy_setopt(curl_easy, CURLOPT_DEBUGFUNCTION, NULL);
1415         _c->curl_easy_setopt(curl_easy, CURLOPT_DEBUGDATA, NULL);
1416         DBG("HTTP Dialer %p is set to legacy debug function (CURL's default, no eina_log)", url_con->dialer);
1417      }
1418    _c->curl_easy_setopt(curl_easy, CURLOPT_VERBOSE, (long)url_con->verbose);
1419 }
1420 
1421 /**
1422  * @}
1423  */
1424 
1425