aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/ecore/src/lib/ecore_con/ecore_con_url.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/ecore/src/lib/ecore_con/ecore_con_url.c')
-rw-r--r--libraries/ecore/src/lib/ecore_con/ecore_con_url.c1572
1 files changed, 1572 insertions, 0 deletions
diff --git a/libraries/ecore/src/lib/ecore_con/ecore_con_url.c b/libraries/ecore/src/lib/ecore_con/ecore_con_url.c
new file mode 100644
index 0000000..cfcf095
--- /dev/null
+++ b/libraries/ecore/src/lib/ecore_con/ecore_con_url.c
@@ -0,0 +1,1572 @@
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
20#ifdef HAVE_WS2TCPIP_H
21# include <ws2tcpip.h>
22#endif
23
24#ifdef HAVE_ESCAPE
25# include <Escape.h>
26#endif
27
28#include "Ecore.h"
29#include "ecore_private.h"
30#include "Ecore_Con.h"
31#include "ecore_con_private.h"
32
33int ECORE_CON_EVENT_URL_DATA = 0;
34int ECORE_CON_EVENT_URL_COMPLETE = 0;
35int ECORE_CON_EVENT_URL_PROGRESS = 0;
36
37#ifdef HAVE_CURL
38static Eina_Bool _ecore_con_url_fd_handler(void *data,
39 Ecore_Fd_Handler *fd_handler);
40static Eina_Bool _ecore_con_url_perform(Ecore_Con_Url *url_con);
41static size_t _ecore_con_url_header_cb(void *ptr,
42 size_t size,
43 size_t nitems,
44 void *stream);
45static size_t _ecore_con_url_data_cb(void *buffer,
46 size_t size,
47 size_t nitems,
48 void *userp);
49static int _ecore_con_url_progress_cb(void *clientp,
50 double dltotal,
51 double dlnow,
52 double ultotal,
53 double ulnow);
54static size_t _ecore_con_url_read_cb(void *ptr,
55 size_t size,
56 size_t nitems,
57 void *stream);
58static void _ecore_con_event_url_free(void *data __UNUSED__,
59 void *ev);
60static int _ecore_con_url_process_completed_jobs(
61 Ecore_Con_Url *url_con_to_match);
62static Eina_Bool _ecore_con_url_idler_handler(void *data);
63
64static Ecore_Idler *_fd_idler_handler = NULL;
65static Eina_List *_url_con_list = NULL;
66static CURLM *_curlm = NULL;
67static fd_set _current_fd_set;
68static int _init_count = 0;
69static Ecore_Timer *_curl_timeout = NULL;
70static Eina_Bool pipelining = EINA_FALSE;
71
72typedef struct _Ecore_Con_Url_Event Ecore_Con_Url_Event;
73struct _Ecore_Con_Url_Event
74{
75 int type;
76 void *ev;
77};
78
79static Eina_Bool
80_url_complete_idler_cb(void *data)
81{
82 Ecore_Con_Url_Event *lev;
83
84 lev = data;
85 ecore_event_add(lev->type, lev->ev, _ecore_con_event_url_free, NULL);
86 free(lev);
87
88 return ECORE_CALLBACK_CANCEL;
89}
90
91static void
92_url_complete_push_event(int type,
93 void *ev)
94{
95 Ecore_Con_Url_Event *lev;
96
97 lev = malloc(sizeof(Ecore_Con_Url_Event));
98 lev->type = type;
99 lev->ev = ev;
100
101 ecore_idler_add(_url_complete_idler_cb, lev);
102}
103
104#endif
105
106/**
107 * @addtogroup Ecore_Con_Url_Group Ecore URL Connection Functions
108 *
109 * @{
110 */
111
112EAPI int
113ecore_con_url_init(void)
114{
115#ifdef HAVE_CURL
116 _init_count++;
117
118 if (_init_count > 1)
119 return _init_count;
120
121 if (!ECORE_CON_EVENT_URL_DATA)
122 {
123 ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
124 ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
125 ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
126 }
127
128 if (!_curlm)
129 {
130 long ms;
131
132 FD_ZERO(&_current_fd_set);
133 if (curl_global_init(CURL_GLOBAL_ALL))
134 {
135 while (_url_con_list)
136 ecore_con_url_free(eina_list_data_get(_url_con_list));
137 return 0;
138 }
139
140 _curlm = curl_multi_init();
141 if (!_curlm)
142 {
143 while (_url_con_list)
144 ecore_con_url_free(eina_list_data_get(_url_con_list));
145
146 _init_count--;
147 return 0;
148 }
149
150 curl_multi_timeout(_curlm, &ms);
151 if (ms <= 0)
152 ms = 1000;
153
154 _curl_timeout =
155 ecore_timer_add((double)ms / 1000, _ecore_con_url_idler_handler,
156 (void *)0xACE);
157 ecore_timer_freeze(_curl_timeout);
158 }
159
160 return 1;
161#else
162 return 0;
163#endif
164}
165
166EAPI int
167ecore_con_url_shutdown(void)
168{
169#ifdef HAVE_CURL
170 if (!_init_count)
171 return 0;
172
173 _init_count--;
174
175 if (_init_count != 0)
176 return _init_count;
177
178 if (_fd_idler_handler)
179 ecore_idler_del(_fd_idler_handler);
180
181 _fd_idler_handler = NULL;
182
183 if (_curl_timeout)
184 ecore_timer_del(_curl_timeout);
185
186 _curl_timeout = NULL;
187
188 while (_url_con_list)
189 ecore_con_url_free(eina_list_data_get(_url_con_list));
190
191 if (_curlm)
192 {
193 curl_multi_cleanup(_curlm);
194 _curlm = NULL;
195 }
196
197 curl_global_cleanup();
198#endif
199 return 1;
200}
201
202EAPI void
203ecore_con_url_pipeline_set(Eina_Bool enable)
204{
205#ifdef HAVE_CURL
206 if (enable)
207 curl_multi_setopt(_curlm, CURLMOPT_PIPELINING, 1);
208 else
209 curl_multi_setopt(_curlm, CURLMOPT_PIPELINING, 0);
210 pipelining = enable;
211#else
212 return;
213 (void)enable;
214#endif
215}
216
217EAPI Eina_Bool
218ecore_con_url_pipeline_get(void)
219{
220#ifdef HAVE_CURL
221 return pipelining;
222#endif
223 return EINA_FALSE;
224}
225
226EAPI Ecore_Con_Url *
227ecore_con_url_new(const char *url)
228{
229#ifdef HAVE_CURL
230 Ecore_Con_Url *url_con;
231 CURLcode ret;
232
233 if (!_init_count)
234 return NULL;
235
236 url_con = calloc(1, sizeof(Ecore_Con_Url));
237 if (!url_con)
238 return NULL;
239
240 url_con->fd = -1;
241 url_con->write_fd = -1;
242
243 url_con->curl_easy = curl_easy_init();
244 if (!url_con->curl_easy)
245 {
246 free(url_con);
247 return NULL;
248 }
249
250 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
251
252 if (!ecore_con_url_url_set(url_con, url))
253 {
254 ecore_con_url_free(url_con);
255 return NULL;
256 }
257
258 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_ENCODING, "gzip,deflate");
259 if (ret != CURLE_OK)
260 {
261 ERR("Could not set CURLOPT_ENCODING to \"gzip,deflate\": %s",
262 curl_easy_strerror(ret));
263 ecore_con_url_free(url_con);
264 return NULL;
265 }
266
267 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEFUNCTION,
268 _ecore_con_url_data_cb);
269 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEDATA, url_con);
270
271 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION,
272 _ecore_con_url_progress_cb);
273 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSDATA, url_con);
274 curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_FALSE);
275
276 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERFUNCTION,
277 _ecore_con_url_header_cb);
278 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERDATA, url_con);
279
280 /*
281 * FIXME: Check that these timeouts are sensible defaults
282 * FIXME: Provide a means to change these timeouts
283 */
284 curl_easy_setopt(url_con->curl_easy, CURLOPT_CONNECTTIMEOUT, 30);
285 curl_easy_setopt(url_con->curl_easy, CURLOPT_FOLLOWLOCATION, 1);
286
287 return url_con;
288#else
289 return NULL;
290 url = NULL;
291#endif
292}
293
294EAPI Ecore_Con_Url *
295ecore_con_url_custom_new(const char *url,
296 const char *custom_request)
297{
298#ifdef HAVE_CURL
299 Ecore_Con_Url *url_con;
300 CURLcode ret;
301
302 if (!url)
303 return NULL;
304
305 if (!custom_request)
306 return NULL;
307
308 url_con = ecore_con_url_new(url);
309
310 if (!url_con)
311 return NULL;
312
313 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_CUSTOMREQUEST, custom_request);
314 if (ret != CURLE_OK)
315 {
316 ERR("Could not set a custom request string: %s",
317 curl_easy_strerror(ret));
318 ecore_con_url_free(url_con);
319 return NULL;
320 }
321
322 return url_con;
323#else
324 return NULL;
325 url = NULL;
326 custom_request = NULL;
327#endif
328}
329
330EAPI void
331ecore_con_url_free(Ecore_Con_Url *url_con)
332{
333#ifdef HAVE_CURL
334 char *s;
335 CURLMcode ret;
336
337 if (!url_con)
338 return;
339
340 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
341 {
342 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_free");
343 return;
344 }
345
346 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
347 if(url_con->fd != -1)
348 {
349 FD_CLR(url_con->fd, &_current_fd_set);
350 if (url_con->fd_handler)
351 ecore_main_fd_handler_del(url_con->fd_handler);
352
353 url_con->fd = -1;
354 url_con->fd_handler = NULL;
355 }
356
357 if (url_con->curl_easy)
358 {
359 // FIXME: For an unknown reason, progress continue to arrive after destruction
360 // this prevent any further call to the callback.
361 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION, NULL);
362 curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_TRUE);
363
364 if (url_con->active)
365 {
366 url_con->active = EINA_FALSE;
367
368 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
369 if (ret != CURLM_OK)
370 ERR("curl_multi_remove_handle failed: %s",
371 curl_multi_strerror(ret));
372 }
373
374 curl_easy_cleanup(url_con->curl_easy);
375 }
376
377 _url_con_list = eina_list_remove(_url_con_list, url_con);
378 curl_slist_free_all(url_con->headers);
379 EINA_LIST_FREE(url_con->additional_headers, s)
380 free(s);
381 EINA_LIST_FREE(url_con->response_headers, s)
382 free(s);
383 eina_stringshare_del(url_con->url);
384 free(url_con);
385#else
386 return;
387 (void)url_con;
388#endif
389}
390
391EAPI const char *
392ecore_con_url_url_get(Ecore_Con_Url *url_con)
393{
394#ifdef HAVE_CURL
395 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
396 {
397 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, __func__);
398 return NULL;
399 }
400 return url_con->url;
401#else
402 return NULL;
403 (void)url_con;
404#endif
405}
406
407EAPI Eina_Bool
408ecore_con_url_url_set(Ecore_Con_Url *url_con,
409 const char *url)
410{
411#ifdef HAVE_CURL
412 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
413 {
414 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_url_set");
415 return EINA_FALSE;
416 }
417
418 if (url_con->active)
419 return EINA_FALSE;
420
421 eina_stringshare_replace(&url_con->url, url);
422
423 if (url_con->url)
424 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL,
425 url_con->url);
426 else
427 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL, "");
428
429 return EINA_TRUE;
430#else
431 return EINA_FALSE;
432 (void)url;
433 (void)url_con;
434#endif
435}
436
437EAPI void
438ecore_con_url_data_set(Ecore_Con_Url *url_con,
439 void *data)
440{
441#ifdef HAVE_CURL
442 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
443 {
444 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_set");
445 return;
446 }
447
448 url_con->data = data;
449#else
450 return;
451 url_con = NULL;
452 data = NULL;
453#endif
454}
455
456EAPI void
457ecore_con_url_additional_header_add(Ecore_Con_Url *url_con,
458 const char *key,
459 const char *value)
460{
461#ifdef HAVE_CURL
462 char *tmp;
463
464 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
465 {
466 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
467 "ecore_con_url_additional_header_add");
468 return;
469 }
470
471 tmp = malloc(strlen(key) + strlen(value) + 3);
472 if (!tmp)
473 return;
474
475 sprintf(tmp, "%s: %s", key, value);
476 url_con->additional_headers = eina_list_append(url_con->additional_headers,
477 tmp);
478#else
479 return;
480 url_con = NULL;
481 key = NULL;
482 value = NULL;
483#endif
484}
485
486EAPI void
487ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
488{
489#ifdef HAVE_CURL
490 char *s;
491
492 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
493 {
494 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
495 "ecore_con_url_additional_headers_clear");
496 return;
497 }
498
499 EINA_LIST_FREE(url_con->additional_headers, s)
500 free(s);
501#else
502 return;
503 url_con = NULL;
504#endif
505}
506
507EAPI void *
508ecore_con_url_data_get(Ecore_Con_Url *url_con)
509{
510#ifdef HAVE_CURL
511 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
512 {
513 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_get");
514 return NULL;
515 }
516
517 return url_con->data;
518#else
519 return NULL;
520 url_con = NULL;
521#endif
522}
523
524EAPI void
525ecore_con_url_time(Ecore_Con_Url *url_con,
526 Ecore_Con_Url_Time condition,
527 double timestamp)
528{
529#ifdef HAVE_CURL
530 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
531 {
532 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_time");
533 return;
534 }
535
536 url_con->time_condition = condition;
537 url_con->timestamp = timestamp;
538#else
539 return;
540 (void)url_con;
541 (void)condition;
542 (void)timestamp;
543#endif
544}
545
546EAPI void
547ecore_con_url_fd_set(Ecore_Con_Url *url_con,
548 int fd)
549{
550#ifdef HAVE_CURL
551 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
552 {
553 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_set");
554 return;
555 }
556
557 url_con->write_fd = fd;
558#else
559 return;
560 (void)url_con;
561 (void)fd;
562#endif
563}
564
565EAPI int
566ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
567{
568#ifdef HAVE_CURL
569 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
570 {
571 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
572 "ecore_con_url_received_bytes_get");
573 return -1;
574 }
575
576 return url_con->received;
577#else
578 return 0;
579 (void)url_con;
580#endif
581}
582
583EAPI const Eina_List *
584ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
585{
586#ifdef HAVE_CURL
587 return url_con->response_headers;
588#else
589 return NULL;
590 (void)url_con;
591#endif
592}
593
594EAPI Eina_Bool
595ecore_con_url_httpauth_set(Ecore_Con_Url *url_con,
596 const char *username,
597 const char *password,
598 Eina_Bool safe)
599{
600#ifdef HAVE_CURL
601 CURLcode ret;
602
603 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
604 {
605 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
606 "ecore_con_url_httpauth_set");
607 return EINA_FALSE;
608 }
609
610# if LIBCURL_VERSION_NUM >= 0x071301
611 if ((username) && (password))
612 {
613 if (safe)
614 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH,
615 CURLAUTH_ANYSAFE);
616 else
617 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
618
619 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
620 if (ret != CURLE_OK)
621 {
622 ERR("Could not set username for HTTP authentication: %s",
623 curl_easy_strerror(ret));
624 return EINA_FALSE;
625 }
626
627 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
628 if (ret != CURLE_OK)
629 {
630 ERR("Could not set password for HTTP authentication: %s",
631 curl_easy_strerror(ret));
632 return EINA_FALSE;
633 }
634
635 return EINA_TRUE;
636 }
637# endif
638#else
639 return EINA_FALSE;
640 (void)url_con;
641 (void)username;
642 (void)password;
643 (void)safe;
644#endif
645
646 return EINA_FALSE;
647}
648
649#define MODE_AUTO 0
650#define MODE_GET 1
651#define MODE_POST 2
652
653static Eina_Bool
654_ecore_con_url_send(Ecore_Con_Url *url_con,
655 int mode,
656 const void *data,
657 long length,
658 const char *content_type)
659{
660#ifdef HAVE_CURL
661 Eina_List *l;
662 const char *s;
663 char tmp[256];
664
665 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
666 {
667 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_send");
668 return EINA_FALSE;
669 }
670
671 if (url_con->active)
672 return EINA_FALSE;
673
674 if (!url_con->url)
675 return EINA_FALSE;
676
677 /* Free response headers from previous send() calls */
678 EINA_LIST_FREE(url_con->response_headers, s)
679 free((char *)s);
680 url_con->response_headers = NULL;
681
682 curl_slist_free_all(url_con->headers);
683 url_con->headers = NULL;
684
685 if ((mode == MODE_POST) || (mode == MODE_AUTO))
686 {
687 if (data)
688 {
689 if ((content_type) && (strlen(content_type) < 200))
690 {
691 snprintf(tmp, sizeof(tmp), "Content-Type: %s", content_type);
692 url_con->headers = curl_slist_append(url_con->headers, tmp);
693 }
694
695 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDS, data);
696 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, length);
697 }
698 else curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, 0);
699 if (mode == MODE_POST)
700 curl_easy_setopt(url_con->curl_easy, CURLOPT_POST, 1);
701 }
702
703 switch (url_con->time_condition)
704 {
705 case ECORE_CON_URL_TIME_NONE:
706 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
707 CURL_TIMECOND_NONE);
708 break;
709
710 case ECORE_CON_URL_TIME_IFMODSINCE:
711 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
712 CURL_TIMECOND_IFMODSINCE);
713 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE,
714 (long)url_con->timestamp);
715 break;
716
717 case ECORE_CON_URL_TIME_IFUNMODSINCE:
718 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
719 CURL_TIMECOND_IFUNMODSINCE);
720 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE,
721 (long)url_con->timestamp);
722 break;
723 }
724
725 /* Additional headers */
726 EINA_LIST_FOREACH(url_con->additional_headers, l, s)
727 url_con->headers = curl_slist_append(url_con->headers, s);
728
729 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPHEADER, url_con->headers);
730
731 url_con->received = 0;
732
733 return _ecore_con_url_perform(url_con);
734#else
735 return EINA_FALSE;
736 (void)url_con;
737 (void)mode;
738 (void)data;
739 (void)length;
740 (void)content_type;
741#endif
742}
743
744EINA_DEPRECATED EAPI Eina_Bool
745ecore_con_url_send(Ecore_Con_Url *url_con,
746 const void *data,
747 long length,
748 const char *content_type)
749{
750 return _ecore_con_url_send(url_con, MODE_AUTO, data, length, content_type);
751}
752
753EAPI Eina_Bool
754ecore_con_url_get(Ecore_Con_Url *url_con)
755{
756 return _ecore_con_url_send(url_con, MODE_GET, NULL, 0, NULL);
757}
758
759EAPI Eina_Bool
760ecore_con_url_post(Ecore_Con_Url *url_con,
761 const void *data,
762 long length,
763 const char *content_type)
764{
765 return _ecore_con_url_send(url_con, MODE_POST, data, length, content_type);
766}
767
768EAPI Eina_Bool
769ecore_con_url_ftp_upload(Ecore_Con_Url *url_con,
770 const char *filename,
771 const char *user,
772 const char *pass,
773 const char *upload_dir)
774{
775#ifdef HAVE_CURL
776 char url[4096];
777 char userpwd[4096];
778 FILE *fd;
779 struct stat file_info;
780 CURLcode ret;
781
782 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
783 {
784 ECORE_MAGIC_FAIL(url_con,
785 ECORE_MAGIC_CON_URL,
786 "ecore_con_url_ftp_upload");
787 return EINA_FALSE;
788 }
789
790 if (url_con->active)
791 return EINA_FALSE;
792
793 if (!url_con->url)
794 return EINA_FALSE;
795
796 if (filename)
797 {
798 if (stat(filename, &file_info))
799 return EINA_FALSE;
800
801 snprintf(userpwd, sizeof(userpwd), "%s:%s", user, pass);
802 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERPWD, userpwd);
803 if (ret != CURLE_OK)
804 {
805 ERR("Could not set username and password for FTP upload: %s",
806 curl_easy_strerror(ret));
807 return EINA_FALSE;
808 }
809
810 char tmp[PATH_MAX];
811 snprintf(tmp, PATH_MAX, "%s", filename);
812
813 if (upload_dir)
814 snprintf(url, sizeof(url), "ftp://%s/%s/%s", url_con->url,
815 upload_dir, basename(tmp));
816 else
817 snprintf(url, sizeof(url), "ftp://%s/%s", url_con->url,
818 basename(tmp));
819
820 if (!ecore_con_url_url_set(url_con, url))
821 return EINA_FALSE;
822
823 curl_easy_setopt(url_con->curl_easy, CURLOPT_INFILESIZE_LARGE,
824 (curl_off_t)file_info.st_size);
825 curl_easy_setopt(url_con->curl_easy, CURLOPT_UPLOAD, 1);
826 curl_easy_setopt(url_con->curl_easy, CURLOPT_READFUNCTION,
827 _ecore_con_url_read_cb);
828
829 fd = fopen(filename, "rb");
830 if (!fd)
831 {
832 ERR("Could not open \"%s\" for FTP upload", filename);
833 return EINA_FALSE;
834 }
835 curl_easy_setopt(url_con->curl_easy, CURLOPT_READDATA, fd);
836
837 return _ecore_con_url_perform(url_con);
838 }
839#else
840 return EINA_FALSE;
841 (void)url_con;
842 (void)filename;
843 (void)user;
844 (void)pass;
845 (void)upload_dir;
846#endif
847
848 return EINA_FALSE;
849}
850
851EAPI void
852ecore_con_url_cookies_init(Ecore_Con_Url *url_con)
853{
854#ifdef HAVE_CURL
855 if (!url_con)
856 return;
857
858 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
859 {
860 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
861 "ecore_con_url_cookies_init");
862 return;
863 }
864
865 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEFILE, "");
866#else
867 return;
868 (void)url_con;
869#endif
870}
871
872EAPI void
873ecore_con_url_cookies_ignore_old_session_set(Ecore_Con_Url *url_con, Eina_Bool ignore)
874{
875#ifdef HAVE_CURL
876 if (!url_con)
877 return;
878
879 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
880 {
881 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
882 "ecore_con_url_cookies_ignore_old_session_set");
883 return;
884 }
885
886 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIESESSION, ignore);
887#else
888 return;
889 (void)url_con;
890 (void)ignore;
891#endif
892}
893
894EAPI void
895ecore_con_url_cookies_clear(Ecore_Con_Url *url_con)
896{
897#ifdef HAVE_CURL
898 if (!url_con)
899 return;
900
901 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
902 {
903 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
904 "ecore_con_url_cookies_clear");
905 return;
906 }
907
908 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "ALL");
909#else
910 return;
911 (void)url_con;
912#endif
913}
914
915EAPI void
916ecore_con_url_cookies_session_clear(Ecore_Con_Url *url_con)
917{
918#ifdef HAVE_CURL
919 if (!url_con)
920 return;
921
922 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
923 {
924 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
925 "ecore_con_url_cookies_session_clear");
926 return;
927 }
928
929 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "SESS");
930#else
931 return;
932 (void)url_con;
933#endif
934}
935
936EAPI void
937ecore_con_url_cookies_file_add(Ecore_Con_Url *url_con, const char * const file_name)
938{
939#ifdef HAVE_CURL
940 if (!url_con)
941 return;
942
943 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
944 {
945 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
946 "ecore_con_url_cookies_file_add");
947 return;
948 }
949
950 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEFILE, file_name);
951#else
952 return;
953 (void)url_con;
954 (void)file_name;
955#endif
956}
957
958EAPI Eina_Bool
959ecore_con_url_cookies_jar_file_set(Ecore_Con_Url *url_con, const char * const cookiejar_file)
960{
961#ifdef HAVE_CURL
962 CURLcode ret;
963
964 if (!url_con)
965 return EINA_FALSE;
966
967 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
968 {
969 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
970 "ecore_con_url_cookies_jar_file_set");
971 return EINA_FALSE;
972 }
973
974 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEJAR,
975 cookiejar_file);
976 if (ret != CURLE_OK)
977 {
978 ERR("Setting the cookie-jar name failed: %s",
979 curl_easy_strerror(ret));
980 return EINA_FALSE;
981 }
982
983 return EINA_TRUE;
984#else
985 return EINA_FALSE;
986 (void)url_con;
987 (void)cookiejar_file;
988#endif
989}
990
991EAPI void
992ecore_con_url_cookies_jar_write(Ecore_Con_Url *url_con)
993{
994#ifdef HAVE_CURL
995 if (!url_con)
996 return;
997
998 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
999 {
1000 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1001 "ecore_con_url_cookies_jar_write");
1002 return;
1003 }
1004
1005 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "FLUSH");
1006#else
1007 return;
1008 (void)url_con;
1009#endif
1010}
1011
1012EAPI void
1013ecore_con_url_verbose_set(Ecore_Con_Url *url_con,
1014 Eina_Bool verbose)
1015{
1016#ifdef HAVE_CURL
1017 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1018 {
1019 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1020 "ecore_con_url_verbose_set");
1021 return;
1022 }
1023
1024 if (url_con->active)
1025 return;
1026
1027 if (!url_con->url)
1028 return;
1029
1030 curl_easy_setopt(url_con->curl_easy, CURLOPT_VERBOSE, (int)verbose);
1031#else
1032 return;
1033 (void)url_con;
1034 (void)verbose;
1035#endif
1036}
1037
1038EAPI void
1039ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con,
1040 Eina_Bool use_epsv)
1041{
1042#ifdef HAVE_CURL
1043 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1044 {
1045 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1046 "ecore_con_url_ftp_use_epsv_set");
1047 return;
1048 }
1049
1050 if (url_con->active)
1051 return;
1052
1053 if (!url_con->url)
1054 return;
1055
1056 curl_easy_setopt(url_con->curl_easy, CURLOPT_FTP_USE_EPSV, (int)use_epsv);
1057#else
1058 return;
1059 (void)url_con;
1060 (void)use_epsv;
1061#endif
1062}
1063
1064/**
1065 * Toggle libcurl's verify peer's certificate option.
1066 *
1067 * If @p verify is @c EINA_TRUE, libcurl will verify
1068 * the authenticity of the peer's certificate, otherwise
1069 * it will not. Default behavior of libcurl is to check
1070 * peer's certificate.
1071 *
1072 * @param url_con Ecore_Con_Url instance which will be acted upon.
1073 * @param verify Whether or not libcurl will check peer's certificate.
1074 * @since 1.1.0
1075 */
1076EAPI void
1077ecore_con_url_ssl_verify_peer_set(Ecore_Con_Url *url_con,
1078 Eina_Bool verify)
1079{
1080#ifdef HAVE_CURL
1081 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1082 {
1083 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1084 "ecore_con_url_ssl_verify_peer_set");
1085 return;
1086 }
1087
1088 if (url_con->active)
1089 return;
1090
1091 if (!url_con->url)
1092 return;
1093
1094 curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, (int)verify);
1095#else
1096 return;
1097 (void)url_con;
1098 (void)verify;
1099#endif
1100}
1101
1102/**
1103 * Set a custom CA to trust for SSL/TLS connections.
1104 *
1105 * Specify the path of a file (in PEM format) containing one or more
1106 * CA certificate(s) to use for the validation of the server certificate.
1107 *
1108 * This function can also disable CA validation if @p ca_path is @c NULL.
1109 * However, the server certificate still needs to be valid for the connection
1110 * to succeed (i.e., the certificate must concern the server the
1111 * connection is made to).
1112 *
1113 * @param url_con Connection object that will use the custom CA.
1114 * @param ca_path Path to a CA certificate(s) file or @c NULL to disable
1115 * CA validation.
1116 *
1117 * @return @c 0 on success. When cURL is used, non-zero return values
1118 * are equal to cURL error codes.
1119 */
1120EAPI int
1121ecore_con_url_ssl_ca_set(Ecore_Con_Url *url_con, const char *ca_path)
1122{
1123 int res = -1;
1124
1125#ifdef HAVE_CURL
1126 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1127 {
1128 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_ssl_ca_set");
1129 return -1;
1130 }
1131
1132 if (url_con->active) return -1;
1133 if (!url_con->url) return -1;
1134 if (ca_path == NULL)
1135 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, 0);
1136 else
1137 {
1138 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, 1);
1139 if (!res)
1140 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_CAINFO, ca_path);
1141 }
1142#else
1143 return -1;
1144 (void)url_con;
1145 (void)ca_path;
1146#endif
1147
1148 return res;
1149}
1150
1151
1152/**
1153 * @}
1154 */
1155
1156#ifdef HAVE_CURL
1157static int
1158_ecore_con_url_suspend_fd_handler(void)
1159{
1160 Eina_List *l;
1161 Ecore_Con_Url *url_con;
1162 int deleted = 0;
1163
1164 if (!_url_con_list)
1165 return 0;
1166
1167 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1168 {
1169 if (url_con->active && url_con->fd_handler)
1170 {
1171 ecore_main_fd_handler_del(url_con->fd_handler);
1172 url_con->fd_handler = NULL;
1173 deleted++;
1174 }
1175 }
1176
1177 return deleted;
1178}
1179
1180static int
1181_ecore_con_url_restart_fd_handler(void)
1182{
1183 Eina_List *l;
1184 Ecore_Con_Url *url_con;
1185 int activated = 0;
1186
1187 if (!_url_con_list)
1188 return 0;
1189
1190 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1191 {
1192 if (!url_con->fd_handler && url_con->fd != -1)
1193 {
1194 url_con->fd_handler =
1195 ecore_main_fd_handler_add(url_con->fd, url_con->flags,
1196 _ecore_con_url_fd_handler,
1197 NULL, NULL, NULL);
1198 activated++;
1199 }
1200 }
1201
1202 return activated;
1203}
1204
1205static size_t
1206_ecore_con_url_data_cb(void *buffer,
1207 size_t size,
1208 size_t nitems,
1209 void *userp)
1210{
1211 Ecore_Con_Url *url_con;
1212 Ecore_Con_Event_Url_Data *e;
1213 size_t real_size = size * nitems;
1214
1215 url_con = (Ecore_Con_Url *)userp;
1216
1217 if (!url_con)
1218 return -1;
1219
1220 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1221 {
1222 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_cb");
1223 return -1;
1224 }
1225
1226 url_con->received += real_size;
1227
1228 if (url_con->write_fd < 0)
1229 {
1230 e =
1231 malloc(sizeof(Ecore_Con_Event_Url_Data) + sizeof(unsigned char) *
1232 (real_size - 1));
1233 if (e)
1234 {
1235 e->url_con = url_con;
1236 e->size = real_size;
1237 memcpy(e->data, buffer, real_size);
1238 ecore_event_add(ECORE_CON_EVENT_URL_DATA, e,
1239 _ecore_con_event_url_free, NULL);
1240 }
1241 }
1242 else
1243 {
1244 ssize_t count = 0;
1245 size_t total_size = real_size;
1246 size_t offset = 0;
1247
1248 while (total_size > 0)
1249 {
1250 count = write(url_con->write_fd,
1251 (char *)buffer + offset,
1252 total_size);
1253 if (count < 0)
1254 {
1255 if (errno != EAGAIN && errno != EINTR)
1256 return -1;
1257 }
1258 else
1259 {
1260 total_size -= count;
1261 offset += count;
1262 }
1263 }
1264 }
1265
1266 return real_size;
1267}
1268
1269#define ECORE_CON_URL_TRANSMISSION(Transmit, Event, Url_con, Total, Now) \
1270 { \
1271 Ecore_Con_Event_Url_Progress *e; \
1272 if ((Total != 0) || (Now != 0)) \
1273 { \
1274 e = calloc(1, sizeof(Ecore_Con_Event_Url_Progress)); \
1275 if (e) \
1276 { \
1277 e->url_con = url_con; \
1278 e->total = Total; \
1279 e->now = Now; \
1280 ecore_event_add(Event, e, _ecore_con_event_url_free, NULL); \
1281 } \
1282 } \
1283 }
1284
1285static size_t
1286_ecore_con_url_header_cb(void *ptr,
1287 size_t size,
1288 size_t nitems,
1289 void *stream)
1290{
1291 size_t real_size = size * nitems;
1292 Ecore_Con_Url *url_con = stream;
1293
1294 char *header = malloc(sizeof(char) * (real_size + 1));
1295 if (!header)
1296 return real_size;
1297
1298 memcpy(header, ptr, real_size);
1299 header[real_size] = '\0';
1300
1301 url_con->response_headers = eina_list_append(url_con->response_headers,
1302 header);
1303
1304 return real_size;
1305}
1306
1307static int
1308_ecore_con_url_progress_cb(void *clientp,
1309 double dltotal,
1310 double dlnow,
1311 double ultotal,
1312 double ulnow)
1313{
1314 Ecore_Con_Event_Url_Progress *e;
1315 Ecore_Con_Url *url_con;
1316
1317 url_con = clientp;
1318
1319 e = malloc(sizeof(Ecore_Con_Event_Url_Progress));
1320 if (e)
1321 {
1322 e->url_con = url_con;
1323 e->down.total = dltotal;
1324 e->down.now = dlnow;
1325 e->up.total = ultotal;
1326 e->up.now = ulnow;
1327 ecore_event_add(ECORE_CON_EVENT_URL_PROGRESS, e,
1328 _ecore_con_event_url_free, NULL);
1329 }
1330
1331 return 0;
1332}
1333
1334static size_t
1335_ecore_con_url_read_cb(void *ptr,
1336 size_t size,
1337 size_t nitems,
1338 void *stream)
1339{
1340 size_t retcode = fread(ptr, size, nitems, stream);
1341
1342 if (ferror((FILE *)stream))
1343 {
1344 fclose(stream);
1345 return CURL_READFUNC_ABORT;
1346 }
1347 else if (retcode == 0)
1348 {
1349 fclose((FILE *)stream);
1350 return 0;
1351 }
1352
1353#ifdef _WIN32
1354 INF("*** We read %Iu bytes from file", retcode);
1355#else
1356 INF("*** We read %zu bytes from file", retcode);
1357#endif
1358 return retcode;
1359}
1360
1361static Eina_Bool
1362_ecore_con_url_perform(Ecore_Con_Url *url_con)
1363{
1364 fd_set read_set, write_set, exc_set;
1365 int fd_max, fd;
1366 int flags, still_running;
1367 int completed_immediately = 0;
1368 CURLMcode ret;
1369
1370 _url_con_list = eina_list_append(_url_con_list, url_con);
1371
1372 url_con->active = EINA_TRUE;
1373 curl_multi_add_handle(_curlm, url_con->curl_easy);
1374 curl_multi_perform(_curlm, &still_running);
1375
1376 completed_immediately = _ecore_con_url_process_completed_jobs(url_con);
1377
1378 if (!completed_immediately)
1379 {
1380 if (url_con->fd_handler)
1381 ecore_main_fd_handler_del(url_con->fd_handler);
1382
1383 url_con->fd_handler = NULL;
1384
1385 /* url_con still active -- set up an fd_handler */
1386 FD_ZERO(&read_set);
1387 FD_ZERO(&write_set);
1388 FD_ZERO(&exc_set);
1389
1390 /* Stupid curl, why can't I get the fd to the current added job? */
1391 ret = curl_multi_fdset(_curlm, &read_set, &write_set, &exc_set,
1392 &fd_max);
1393 if (ret != CURLM_OK)
1394 {
1395 ERR("curl_multi_fdset failed: %s", curl_multi_strerror(ret));
1396 return EINA_FALSE;
1397 }
1398
1399 for (fd = 0; fd <= fd_max; fd++)
1400 {
1401 if (!FD_ISSET(fd, &_current_fd_set))
1402 {
1403 flags = 0;
1404 if (FD_ISSET(fd, &read_set))
1405 flags |= ECORE_FD_READ;
1406
1407 if (FD_ISSET(fd, &write_set))
1408 flags |= ECORE_FD_WRITE;
1409
1410 if (FD_ISSET(fd, &exc_set))
1411 flags |= ECORE_FD_ERROR;
1412
1413 if (flags)
1414 {
1415 long ms = 0;
1416
1417 ret = curl_multi_timeout(_curlm, &ms);
1418 if (ret != CURLM_OK)
1419 ERR("curl_multi_timeout failed: %s",
1420 curl_multi_strerror(ret));
1421
1422 if (ms == 0)
1423 ms = 1000;
1424
1425 FD_SET(fd, &_current_fd_set);
1426 url_con->fd = fd;
1427 url_con->flags = flags;
1428 url_con->fd_handler =
1429 ecore_main_fd_handler_add(fd, flags,
1430 _ecore_con_url_fd_handler,
1431 NULL, NULL, NULL);
1432 break;
1433 }
1434 }
1435 }
1436 if (!url_con->fd_handler)
1437 {
1438 /* Failed to set up an fd_handler */
1439 ecore_timer_freeze(_curl_timeout);
1440
1441 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
1442 if (ret != CURLM_OK)
1443 ERR("curl_multi_remove_handle failed: %s",
1444 curl_multi_strerror(ret));
1445
1446 url_con->active = EINA_FALSE;
1447 url_con->fd = -1;
1448 return EINA_FALSE;
1449 }
1450
1451 ecore_timer_thaw(_curl_timeout);
1452 }
1453
1454 return EINA_TRUE;
1455}
1456
1457static Eina_Bool
1458_ecore_con_url_idler_handler(void *data)
1459{
1460 int done, still_running;
1461
1462 done = (curl_multi_perform(_curlm, &still_running) != CURLM_CALL_MULTI_PERFORM);
1463
1464 _ecore_con_url_process_completed_jobs(NULL);
1465
1466 if (done)
1467 {
1468 _ecore_con_url_restart_fd_handler();
1469 _fd_idler_handler = NULL;
1470
1471 if (!_url_con_list)
1472 ecore_timer_freeze(_curl_timeout);
1473
1474 return data ==
1475 (void *)0xACE ? ECORE_CALLBACK_RENEW : ECORE_CALLBACK_CANCEL;
1476 }
1477
1478 return ECORE_CALLBACK_RENEW;
1479}
1480
1481static Eina_Bool
1482_ecore_con_url_fd_handler(void *data __UNUSED__,
1483 Ecore_Fd_Handler *fd_handler __UNUSED__)
1484{
1485 _ecore_con_url_suspend_fd_handler();
1486
1487 if (!_fd_idler_handler)
1488 _fd_idler_handler = ecore_idler_add(
1489 _ecore_con_url_idler_handler, NULL);
1490
1491 return ECORE_CALLBACK_RENEW;
1492}
1493
1494static int
1495_ecore_con_url_process_completed_jobs(Ecore_Con_Url *url_con_to_match)
1496{
1497 Eina_List *l;
1498 Ecore_Con_Url *url_con;
1499 Ecore_Con_Event_Url_Complete *e;
1500 CURLMsg *curlmsg;
1501 CURLMcode ret;
1502 int n_remaining;
1503 int job_matched = 0;
1504
1505 /* Loop jobs and check if any are done */
1506 while ((curlmsg = curl_multi_info_read(_curlm, &n_remaining)))
1507 {
1508 if (curlmsg->msg != CURLMSG_DONE)
1509 continue;
1510
1511 /* find the job which is done */
1512 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1513 {
1514 if (curlmsg->easy_handle == url_con->curl_easy)
1515 {
1516 if (url_con_to_match &&
1517 (url_con == url_con_to_match))
1518 job_matched = 1;
1519
1520 if(url_con->fd != -1)
1521 {
1522 FD_CLR(url_con->fd, &_current_fd_set);
1523 if (url_con->fd_handler)
1524 ecore_main_fd_handler_del(
1525 url_con->fd_handler);
1526
1527 url_con->fd = -1;
1528 url_con->fd_handler = NULL;
1529 }
1530
1531 _url_con_list = eina_list_remove(_url_con_list, url_con);
1532 url_con->active = EINA_FALSE;
1533 e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
1534 if (e)
1535 {
1536 e->url_con = url_con;
1537 e->status = 0;
1538 if (curlmsg->data.result == CURLE_OK)
1539 {
1540 long status; /* curl API uses long, not int */
1541
1542 status = 0;
1543 curl_easy_getinfo(curlmsg->easy_handle,
1544 CURLINFO_RESPONSE_CODE,
1545 &status);
1546 e->status = status;
1547 }
1548
1549 _url_complete_push_event(ECORE_CON_EVENT_URL_COMPLETE, e);
1550 }
1551
1552 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
1553 if (ret != CURLM_OK)
1554 ERR("curl_multi_remove_handle failed: %s",
1555 curl_multi_strerror(ret));
1556
1557 break;
1558 }
1559 }
1560 }
1561
1562 return job_matched;
1563}
1564
1565static void
1566_ecore_con_event_url_free(void *data __UNUSED__,
1567 void *ev)
1568{
1569 free(ev);
1570}
1571
1572#endif