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