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