Merge remote-tracking branch 'origin/upstream'
[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 #include "Ecore.h"
25 #include "ecore_private.h"
26 #include "Ecore_Con.h"
27 #include "ecore_con_private.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 #ifdef HAVE_CURL
34 static Eina_Bool _ecore_con_url_fd_handler(void             *data,
35                                            Ecore_Fd_Handler *fd_handler);
36 static Eina_Bool _ecore_con_url_perform(Ecore_Con_Url *url_con);
37 static size_t    _ecore_con_url_header_cb(void  *ptr,
38                                           size_t size,
39                                           size_t nitems,
40                                           void  *stream);
41 static size_t _ecore_con_url_data_cb(void  *buffer,
42                                      size_t size,
43                                      size_t nitems,
44                                      void  *userp);
45 static int _ecore_con_url_progress_cb(void  *clientp,
46                                       double dltotal,
47                                       double dlnow,
48                                       double ultotal,
49                                       double ulnow);
50 static size_t _ecore_con_url_read_cb(void  *ptr,
51                                      size_t size,
52                                      size_t nitems,
53                                      void  *stream);
54 static void      _ecore_con_event_url_free(void *data __UNUSED__,
55                                            void      *ev);
56 static int       _ecore_con_url_process_completed_jobs(
57   Ecore_Con_Url *url_con_to_match);
58 static Eina_Bool _ecore_con_url_idler_handler(void *data);
59
60 static Ecore_Idler *_fd_idler_handler = NULL;
61 static Eina_List *_url_con_list = NULL;
62 static CURLM *_curlm = NULL;
63 static fd_set _current_fd_set;
64 static int _init_count = 0;
65 static Ecore_Timer *_curl_timeout = NULL;
66 static Eina_Bool pipelining = EINA_FALSE;
67
68 typedef struct _Ecore_Con_Url_Event Ecore_Con_Url_Event;
69 struct _Ecore_Con_Url_Event
70 {
71    int   type;
72    void *ev;
73 };
74
75 static Eina_Bool
76 _url_complete_idler_cb(void *data)
77 {
78    Ecore_Con_Url_Event *lev;
79
80    lev = data;
81    ecore_event_add(lev->type, lev->ev, _ecore_con_event_url_free, NULL);
82    free(lev);
83
84    return ECORE_CALLBACK_CANCEL;
85 }
86
87 static void
88 _url_complete_push_event(int   type,
89                          void *ev)
90 {
91    Ecore_Con_Url_Event *lev;
92
93    lev = malloc(sizeof(Ecore_Con_Url_Event));
94    lev->type = type;
95    lev->ev = ev;
96
97    ecore_idler_add(_url_complete_idler_cb, lev);
98 }
99
100 #endif
101
102 /**
103  * @addtogroup Ecore_Con_Url_Group Ecore URL Connection Functions
104  *
105  * @{
106  */
107
108 EAPI int
109 ecore_con_url_init(void)
110 {
111 #ifdef HAVE_CURL
112    _init_count++;
113
114    if (_init_count > 1)
115      return _init_count;
116
117    if (!ECORE_CON_EVENT_URL_DATA)
118      {
119         ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
120         ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
121         ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
122      }
123
124    if (!_curlm)
125      {
126         long ms;
127
128         FD_ZERO(&_current_fd_set);
129         if (curl_global_init(CURL_GLOBAL_ALL))
130           {
131              while (_url_con_list)
132                ecore_con_url_free(eina_list_data_get(_url_con_list));
133              return 0;
134           }
135
136         _curlm = curl_multi_init();
137         if (!_curlm)
138           {
139              while (_url_con_list)
140                ecore_con_url_free(eina_list_data_get(_url_con_list));
141
142              _init_count--;
143              return 0;
144           }
145
146         curl_multi_timeout(_curlm, &ms);
147         if (ms <= 0)
148           ms = 1000;
149
150         _curl_timeout =
151           ecore_timer_add((double)ms / 1000, _ecore_con_url_idler_handler,
152                           (void *)0xACE);
153         ecore_timer_freeze(_curl_timeout);
154      }
155
156    return 1;
157 #else
158    return 0;
159 #endif
160 }
161
162 EAPI int
163 ecore_con_url_shutdown(void)
164 {
165 #ifdef HAVE_CURL
166    if (!_init_count)
167      return 0;
168
169    _init_count--;
170
171    if (_init_count != 0)
172      return _init_count;
173
174    if (_fd_idler_handler)
175      ecore_idler_del(_fd_idler_handler);
176
177    _fd_idler_handler = NULL;
178
179    if (_curl_timeout)
180      ecore_timer_del(_curl_timeout);
181
182    _curl_timeout = NULL;
183
184    while (_url_con_list)
185      ecore_con_url_free(eina_list_data_get(_url_con_list));
186
187    if (_curlm)
188      {
189         curl_multi_cleanup(_curlm);
190         _curlm = NULL;
191      }
192
193    curl_global_cleanup();
194 #endif
195    return 1;
196 }
197
198 EAPI void
199 ecore_con_url_pipeline_set(Eina_Bool enable)
200 {
201 #ifdef HAVE_CURL
202   if (enable)
203     curl_multi_setopt(_curlm, CURLMOPT_PIPELINING, 1);
204   else
205     curl_multi_setopt(_curlm, CURLMOPT_PIPELINING, 0);
206   pipelining = enable;
207 #else
208   return;
209   (void)enable;
210 #endif
211 }
212
213 EAPI Eina_Bool
214 ecore_con_url_pipeline_get(void)
215 {
216 #ifdef HAVE_CURL
217   return pipelining;
218 #endif
219   return EINA_FALSE;
220 }
221
222 EAPI Ecore_Con_Url *
223 ecore_con_url_new(const char *url)
224 {
225 #ifdef HAVE_CURL
226    Ecore_Con_Url *url_con;
227    CURLcode ret;
228
229    if (!_init_count)
230      return NULL;
231
232    url_con = calloc(1, sizeof(Ecore_Con_Url));
233    if (!url_con)
234      return NULL;
235
236    url_con->fd = -1;
237    url_con->write_fd = -1;
238
239    url_con->curl_easy = curl_easy_init();
240    if (!url_con->curl_easy)
241      {
242         free(url_con);
243         return NULL;
244      }
245
246    ECORE_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
247
248    if (!ecore_con_url_url_set(url_con, url))
249      {
250         ecore_con_url_free(url_con);
251         return NULL;
252      }
253
254    ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_ENCODING, "gzip,deflate");
255    if (ret != CURLE_OK)
256      {
257         ERR("Could not set CURLOPT_ENCODING to \"gzip,deflate\": %s",
258             curl_easy_strerror(ret));
259         ecore_con_url_free(url_con);
260         return NULL;
261      }
262
263    curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEFUNCTION,
264                     _ecore_con_url_data_cb);
265    curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEDATA, url_con);
266
267    curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION,
268                     _ecore_con_url_progress_cb);
269    curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSDATA, url_con);
270    curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_FALSE);
271
272    curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERFUNCTION,
273                     _ecore_con_url_header_cb);
274    curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERDATA, url_con);
275
276    /*
277     * FIXME: Check that these timeouts are sensible defaults
278     * FIXME: Provide a means to change these timeouts
279     */
280    curl_easy_setopt(url_con->curl_easy, CURLOPT_MAXCONNECTS, 100);
281    curl_easy_setopt(url_con->curl_easy, CURLOPT_FORBID_REUSE, 1);
282    curl_easy_setopt(url_con->curl_easy, CURLOPT_CONNECTTIMEOUT, 30);
283    curl_easy_setopt(url_con->curl_easy, CURLOPT_FOLLOWLOCATION, 1);
284
285    return url_con;
286 #else
287    return NULL;
288    url = NULL;
289 #endif
290 }
291
292 EAPI Ecore_Con_Url *
293 ecore_con_url_custom_new(const char *url,
294                          const char *custom_request)
295 {
296 #ifdef HAVE_CURL
297    Ecore_Con_Url *url_con;
298    CURLcode ret;
299
300    if (!url)
301      return NULL;
302
303    if (!custom_request)
304      return NULL;
305
306    url_con = ecore_con_url_new(url);
307
308    if (!url_con)
309      return NULL;
310
311    ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_CUSTOMREQUEST, custom_request);
312    if (ret != CURLE_OK)
313      {
314         ERR("Could not set a custom request string: %s",
315             curl_easy_strerror(ret));
316         ecore_con_url_free(url_con);
317         return NULL;
318      }
319
320    return url_con;
321 #else
322    return NULL;
323    url = NULL;
324    custom_request = NULL;
325 #endif
326 }
327
328 EAPI void
329 ecore_con_url_free(Ecore_Con_Url *url_con)
330 {
331 #ifdef HAVE_CURL
332    char *s;
333    CURLMcode ret;
334
335    if (!url_con)
336      return;
337
338    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
339      {
340         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_free");
341         return;
342      }
343
344    ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
345    if(url_con->fd != -1)
346      {
347         FD_CLR(url_con->fd, &_current_fd_set);
348         if (url_con->fd_handler)
349           ecore_main_fd_handler_del(url_con->fd_handler);
350
351         url_con->fd = -1;
352         url_con->fd_handler = NULL;
353      }
354
355    if (url_con->curl_easy)
356      {
357         // FIXME: For an unknown reason, progress continue to arrive after destruction
358         // this prevent any further call to the callback.
359         curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_TRUE);
360         curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION, NULL);
361         curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_TRUE);
362
363         if (url_con->active)
364           {
365              url_con->active = EINA_FALSE;
366
367              ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
368              if (ret != CURLM_OK)
369                 ERR("curl_multi_remove_handle failed: %s",
370                     curl_multi_strerror(ret));
371           }
372
373         curl_easy_cleanup(url_con->curl_easy);
374      }
375
376    _url_con_list = eina_list_remove(_url_con_list, url_con);
377    curl_slist_free_all(url_con->headers);
378    EINA_LIST_FREE(url_con->additional_headers, s)
379      free(s);
380    EINA_LIST_FREE(url_con->response_headers, s)
381      free(s);
382    eina_stringshare_del(url_con->url);
383    free(url_con);
384 #else
385    return;
386    (void)url_con;
387 #endif
388 }
389
390 EAPI const char *
391 ecore_con_url_url_get(Ecore_Con_Url *url_con)
392 {
393 #ifdef HAVE_CURL
394    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
395      {
396         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, __func__);
397         return NULL;
398      }
399    return url_con->url;
400 #else
401    return NULL;
402    (void)url_con;
403 #endif
404 }
405
406 EAPI Eina_Bool
407 ecore_con_url_url_set(Ecore_Con_Url *url_con,
408                       const char    *url)
409 {
410 #ifdef HAVE_CURL
411    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
412      {
413         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_url_set");
414         return EINA_FALSE;
415      }
416
417    if (url_con->active)
418      return EINA_FALSE;
419
420    eina_stringshare_replace(&url_con->url, url);
421
422    if (url_con->url)
423      curl_easy_setopt(url_con->curl_easy, CURLOPT_URL,
424                       url_con->url);
425    else
426      curl_easy_setopt(url_con->curl_easy, CURLOPT_URL, "");
427
428    return EINA_TRUE;
429 #else
430    return EINA_FALSE;
431    (void)url;
432    (void)url_con;
433 #endif
434 }
435
436 EAPI void
437 ecore_con_url_data_set(Ecore_Con_Url *url_con,
438                        void          *data)
439 {
440 #ifdef HAVE_CURL
441    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
442      {
443         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_set");
444         return;
445      }
446
447    url_con->data = data;
448 #else
449    return;
450    url_con = NULL;
451    data = NULL;
452 #endif
453 }
454
455 EAPI void
456 ecore_con_url_additional_header_add(Ecore_Con_Url *url_con,
457                                     const char    *key,
458                                     const char    *value)
459 {
460 #ifdef HAVE_CURL
461    char *tmp;
462
463    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
464      {
465         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
466                          "ecore_con_url_additional_header_add");
467         return;
468      }
469
470    tmp = malloc(strlen(key) + strlen(value) + 3);
471    if (!tmp)
472      return;
473
474    sprintf(tmp, "%s: %s", key, value);
475    url_con->additional_headers = eina_list_append(url_con->additional_headers,
476                                                   tmp);
477 #else
478    return;
479    url_con = NULL;
480    key = NULL;
481    value = NULL;
482 #endif
483 }
484
485 EAPI void
486 ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
487 {
488 #ifdef HAVE_CURL
489    char *s;
490
491    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
492      {
493         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
494                          "ecore_con_url_additional_headers_clear");
495         return;
496      }
497
498    EINA_LIST_FREE(url_con->additional_headers, s)
499      free(s);
500 #else
501    return;
502    url_con = NULL;
503 #endif
504 }
505
506 EAPI void *
507 ecore_con_url_data_get(Ecore_Con_Url *url_con)
508 {
509 #ifdef HAVE_CURL
510    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
511      {
512         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_get");
513         return NULL;
514      }
515
516    return url_con->data;
517 #else
518    return NULL;
519    url_con = NULL;
520 #endif
521 }
522
523 EAPI void
524 ecore_con_url_time(Ecore_Con_Url     *url_con,
525                    Ecore_Con_Url_Time condition,
526                    double             timestamp)
527 {
528 #ifdef HAVE_CURL
529    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
530      {
531         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_time");
532         return;
533      }
534
535    url_con->time_condition = condition;
536    url_con->timestamp = timestamp;
537 #else
538    return;
539    (void)url_con;
540    (void)condition;
541    (void)timestamp;
542 #endif
543 }
544
545 EAPI void
546 ecore_con_url_fd_set(Ecore_Con_Url *url_con,
547                      int            fd)
548 {
549 #ifdef HAVE_CURL
550    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
551      {
552         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_set");
553         return;
554      }
555
556    url_con->write_fd = fd;
557 #else
558    return;
559    (void)url_con;
560    (void)fd;
561 #endif
562 }
563
564 EAPI int
565 ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
566 {
567 #ifdef HAVE_CURL
568    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
569      {
570         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
571                          "ecore_con_url_received_bytes_get");
572         return -1;
573      }
574
575    return url_con->received;
576 #else
577    return 0;
578    (void)url_con;
579 #endif
580 }
581
582 EAPI const Eina_List *
583 ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
584 {
585 #ifdef HAVE_CURL
586    return url_con->response_headers;
587 #else
588    return NULL;
589    (void)url_con;
590 #endif
591 }
592
593 EAPI Eina_Bool
594 ecore_con_url_httpauth_set(Ecore_Con_Url *url_con,
595                            const char    *username,
596                            const char    *password,
597                            Eina_Bool      safe)
598 {
599 #ifdef HAVE_CURL
600    CURLcode ret;
601
602    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
603      {
604         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
605                          "ecore_con_url_httpauth_set");
606         return EINA_FALSE;
607      }
608
609 # if LIBCURL_VERSION_NUM >= 0x071301
610    if ((username) && (password))
611      {
612         if (safe)
613           curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH,
614                            CURLAUTH_ANYSAFE);
615         else
616           curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
617
618         ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
619         if (ret != CURLE_OK)
620           {
621              ERR("Could not set username for HTTP authentication: %s",
622                  curl_easy_strerror(ret));
623              return EINA_FALSE;
624           }
625
626         ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
627         if (ret != CURLE_OK)
628           {
629              ERR("Could not set password for HTTP authentication: %s",
630                  curl_easy_strerror(ret));
631              return EINA_FALSE;
632           }
633
634         return EINA_TRUE;
635      }
636 # endif
637 #else
638    return EINA_FALSE;
639    (void)url_con;
640    (void)username;
641    (void)password;
642    (void)safe;
643 #endif
644
645    return EINA_FALSE;
646 }
647
648 #define MODE_AUTO 0
649 #define MODE_GET  1
650 #define MODE_POST 2
651
652 static Eina_Bool
653 _ecore_con_url_send(Ecore_Con_Url *url_con,
654                     int            mode,
655                     const void    *data,
656                     long           length,
657                     const char    *content_type)
658 {
659 #ifdef HAVE_CURL
660    Eina_List *l;
661    const char *s;
662    char tmp[256];
663
664    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
665      {
666         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_send");
667         return EINA_FALSE;
668      }
669
670    if (url_con->active)
671      return EINA_FALSE;
672
673    if (!url_con->url)
674      return EINA_FALSE;
675
676    /* Free response headers from previous send() calls */
677    EINA_LIST_FREE(url_con->response_headers, s)
678      free((char *)s);
679    url_con->response_headers = NULL;
680
681    curl_slist_free_all(url_con->headers);
682    url_con->headers = NULL;
683
684    if ((mode == MODE_POST) || (mode == MODE_AUTO))
685      {
686         if (data)
687           {
688              if ((content_type) && (strlen(content_type) < 200))
689                {
690                   snprintf(tmp, sizeof(tmp), "Content-Type: %s", content_type);
691                   url_con->headers = curl_slist_append(url_con->headers, tmp);
692                }
693
694              curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDS, data);
695              curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, length);
696           }
697         else if (mode == MODE_POST)
698           {
699              curl_easy_setopt(url_con->curl_easy, CURLOPT_POST, 1);
700           }
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
744 EINA_DEPRECATED EAPI Eina_Bool
745 ecore_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
753 EAPI Eina_Bool
754 ecore_con_url_get(Ecore_Con_Url *url_con)
755 {
756    return _ecore_con_url_send(url_con, MODE_GET, NULL, 0, NULL);
757 }
758
759 EAPI Eina_Bool
760 ecore_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
768 EAPI Eina_Bool
769 ecore_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
851 EAPI void
852 ecore_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
872 EAPI void
873 ecore_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
894 EAPI void
895 ecore_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
915 EAPI void
916 ecore_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
936 EAPI void
937 ecore_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
958 EAPI Eina_Bool
959 ecore_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
991 EAPI void
992 ecore_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
1012 EAPI void
1013 ecore_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
1038 EAPI void
1039 ecore_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  */
1076 EAPI void
1077 ecore_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  */
1120 EAPI int
1121 ecore_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
1157 static 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
1180 static 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
1205 static 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
1285 static 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
1307 static 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
1334 static 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
1361 static 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
1457 static 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
1481 static 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
1494 static 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
1565 static void
1566 _ecore_con_event_url_free(void *data __UNUSED__,
1567                           void      *ev)
1568 {
1569    free(ev);
1570 }
1571
1572 #endif