svn update: 58224 (latest:58224)
[profile/ivi/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  * Brief usage:
8  * 1. Create an Ecore_Con_Url object
9  * 2. Register to receive the ECORE_CON_EVENT_URL_COMPLETE event
10  *    (and optionally the ECORE_CON_EVENT_URL_DATA event to receive
11  *    the response, e.g. for HTTP/FTP downloads)
12  * 3. Set the URL with ecore_con_url_url_set(...);
13  * 4. Perform the operation with ecore_con_url_get(...);
14  *
15  * Note that it is good to reuse Ecore_Con_Url objects wherever possible, but
16  * bear in mind that each one can only perform one operation at a time.
17  * You need to wait for the ECORE_CON_EVENT_URL_COMPLETE event before re-using
18  * or destroying the object.
19  *
20  * Example Usage 1 (HTTP GET):
21  *   ecore_con_url_url_set(url_con, "http://www.google.com");
22  *   ecore_con_url_get(url_con);
23  *
24  * Example usage 2 (HTTP POST):
25  *   ecore_con_url_url_set(url_con, "http://www.example.com/post_handler.cgi");
26  *   ecore_con_url_post(url_con, data, data_length, "multipart/form-data");
27  *
28  * Example Usage 3 (FTP download):
29  *   ecore_con_url_url_set(url_con, "ftp://ftp.example.com/pub/myfile");
30  *   ecore_con_url_get(url_con);
31  *
32  * Example Usage 4 (FTP upload as ftp://ftp.example.com/file):
33  *   ecore_con_url_url_set(url_con, "ftp://ftp.example.com");
34  *   ecore_con_url_ftp_upload(url_con, "/tmp/file", "user", "pass", NULL);
35  *
36  * Example Usage 5 (FTP upload as ftp://ftp.example.com/dir/file):
37  *   ecore_con_url_url_set(url_con, "ftp://ftp.example.com");
38  *   ecore_con_url_ftp_upload(url_con, "/tmp/file", "user", "pass","dir");
39  *
40  * FIXME: Support more CURL features: Progress callbacks and more...
41  */
42
43 #ifdef HAVE_CONFIG_H
44 # include <config.h>
45 #endif
46
47 #include <string.h>
48 #include <errno.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51 #include <unistd.h>
52
53 #ifdef HAVE_WS2TCPIP_H
54 # include <ws2tcpip.h>
55 #endif
56
57 #include "Ecore.h"
58 #include "ecore_private.h"
59 #include "Ecore_Con.h"
60 #include "ecore_con_private.h"
61
62 int ECORE_CON_EVENT_URL_DATA = 0;
63 int ECORE_CON_EVENT_URL_COMPLETE = 0;
64 int ECORE_CON_EVENT_URL_PROGRESS = 0;
65
66 #ifdef HAVE_CURL
67 static Eina_Bool _ecore_con_url_fd_handler(void             *data,
68                                            Ecore_Fd_Handler *fd_handler);
69 static Eina_Bool _ecore_con_url_perform(Ecore_Con_Url *url_con);
70 static size_t    _ecore_con_url_header_cb(void  *ptr,
71                                           size_t size,
72                                           size_t nitems,
73                                           void  *stream);
74 static size_t _ecore_con_url_data_cb(void  *buffer,
75                                      size_t size,
76                                      size_t nitems,
77                                      void  *userp);
78 static int _ecore_con_url_progress_cb(void  *clientp,
79                                       double dltotal,
80                                       double dlnow,
81                                       double ultotal,
82                                       double ulnow);
83 static size_t _ecore_con_url_read_cb(void  *ptr,
84                                      size_t size,
85                                      size_t nitems,
86                                      void  *stream);
87 static void      _ecore_con_event_url_free(void *data __UNUSED__,
88                                            void      *ev);
89 static int       _ecore_con_url_process_completed_jobs(
90   Ecore_Con_Url *url_con_to_match);
91 static Eina_Bool _ecore_con_url_idler_handler(void *data);
92
93 static Ecore_Idler *_fd_idler_handler = NULL;
94 static Eina_List *_url_con_list = NULL;
95 static CURLM *_curlm = NULL;
96 static fd_set _current_fd_set;
97 static int _init_count = 0;
98 static Ecore_Timer *_curl_timeout = NULL;
99
100 typedef struct _Ecore_Con_Url_Event Ecore_Con_Url_Event;
101 struct _Ecore_Con_Url_Event
102 {
103    int   type;
104    void *ev;
105 };
106
107 static Eina_Bool
108 _url_complete_idler_cb(void *data)
109 {
110    Ecore_Con_Url_Event *lev;
111
112    lev = data;
113    ecore_event_add(lev->type, lev->ev, _ecore_con_event_url_free, NULL);
114    free(lev);
115
116    return ECORE_CALLBACK_CANCEL;
117 }
118
119 static void
120 _url_complete_push_event(int   type,
121                          void *ev)
122 {
123    Ecore_Con_Url_Event *lev;
124
125    lev = malloc(sizeof(Ecore_Con_Url_Event));
126    lev->type = type;
127    lev->ev = ev;
128
129    ecore_idler_add(_url_complete_idler_cb, lev);
130 }
131
132 #endif
133
134 /**
135  * @addtogroup Ecore_Con_Url_Group Ecore URL Connection Functions
136  *
137  * Utility functions that set up, use and shut down the Ecore URL
138  * Connection library.
139  *
140  * @todo write detailed description of Ecore_Con_Url
141  *
142  * @{
143  */
144
145 /**
146  * Initialises the Ecore_Con_Url library.
147  * @return Number of times the library has been initialised without being
148  *          shut down.
149  */
150 EAPI int
151 ecore_con_url_init(void)
152 {
153 #ifdef HAVE_CURL
154    _init_count++;
155
156    if (_init_count > 1)
157      return _init_count;
158
159    if (!ECORE_CON_EVENT_URL_DATA)
160      {
161         ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
162         ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
163         ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
164      }
165
166    if (!_curlm)
167      {
168         long ms;
169
170         FD_ZERO(&_current_fd_set);
171         if (curl_global_init(CURL_GLOBAL_NOTHING))
172           {
173              while (_url_con_list)
174                ecore_con_url_free(eina_list_data_get(_url_con_list));
175              return 0;
176           }
177
178         _curlm = curl_multi_init();
179         if (!_curlm)
180           {
181              while (_url_con_list)
182                ecore_con_url_free(eina_list_data_get(_url_con_list));
183
184              _init_count--;
185              return 0;
186           }
187
188         curl_multi_timeout(_curlm, &ms);
189         if (ms <= 0)
190           ms = 1000;
191
192         _curl_timeout =
193           ecore_timer_add((double)ms / 1000, _ecore_con_url_idler_handler,
194                           (void *)0xACE);
195         ecore_timer_freeze(_curl_timeout);
196      }
197
198    return 1;
199 #else
200    return 0;
201 #endif
202 }
203
204 /**
205  * Shuts down the Ecore_Con_Url library.
206  * @return  Number of calls that still uses Ecore_Con_Url
207  */
208 EAPI int
209 ecore_con_url_shutdown(void)
210 {
211 #ifdef HAVE_CURL
212    if (!_init_count)
213      return 0;
214
215    _init_count--;
216
217    if (_init_count != 0)
218      return _init_count;
219
220    if (_fd_idler_handler)
221      ecore_idler_del(_fd_idler_handler);
222
223    _fd_idler_handler = NULL;
224
225    if (_curl_timeout)
226      ecore_timer_del(_curl_timeout);
227
228    _curl_timeout = NULL;
229
230    while (_url_con_list)
231      ecore_con_url_free(eina_list_data_get(_url_con_list));
232
233    if (_curlm)
234      {
235         curl_multi_cleanup(_curlm);
236         _curlm = NULL;
237      }
238
239    curl_global_cleanup();
240 #endif
241    return 1;
242 }
243
244 /**
245  * Creates and initializes a new Ecore_Con_Url connection object.
246  *
247  * Creates and initializes a new Ecore_Con_Url connection object that can be
248  * uesd for sending requests.
249  *
250  * @param url URL that will receive requests. Can be changed using
251  *            ecore_con_url_url_set.
252  *
253  * @return NULL on error, a new Ecore_Con_Url on success.
254  *
255  *
256  * @see ecore_con_url_custom_new()
257  * @see ecore_con_url_url_set()
258  */
259 EAPI Ecore_Con_Url *
260 ecore_con_url_new(const char *url)
261 {
262 #ifdef HAVE_CURL
263    Ecore_Con_Url *url_con;
264    CURLcode ret;
265
266    if (!_init_count)
267      return NULL;
268
269    url_con = calloc(1, sizeof(Ecore_Con_Url));
270    if (!url_con)
271      return NULL;
272
273    url_con->fd = -1;
274    url_con->write_fd = -1;
275
276    url_con->curl_easy = curl_easy_init();
277    if (!url_con->curl_easy)
278      {
279         free(url_con);
280         return NULL;
281      }
282
283    ECORE_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
284
285    if (!ecore_con_url_url_set(url_con, url))
286      {
287         ecore_con_url_free(url_con);
288         return NULL;
289      }
290
291    ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_ENCODING, "gzip,deflate");
292    if (ret != CURLE_OK)
293      {
294         ERR("Could not set CURLOPT_ENCODING to \"gzip,deflate\": %s",
295             curl_easy_strerror(ret));
296         ecore_con_url_free(url_con);
297         return NULL;
298      }
299
300    curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEFUNCTION,
301                     _ecore_con_url_data_cb);
302    curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEDATA, url_con);
303
304    curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION,
305                     _ecore_con_url_progress_cb);
306    curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSDATA, url_con);
307    curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_FALSE);
308
309    curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERFUNCTION,
310                     _ecore_con_url_header_cb);
311    curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERDATA, url_con);
312
313    /*
314     * FIXME: Check that these timeouts are sensible defaults
315     * FIXME: Provide a means to change these timeouts
316     */
317    curl_easy_setopt(url_con->curl_easy, CURLOPT_CONNECTTIMEOUT, 30);
318    curl_easy_setopt(url_con->curl_easy, CURLOPT_FOLLOWLOCATION, 1);
319
320    return url_con;
321 #else
322    return NULL;
323    url = NULL;
324 #endif
325 }
326
327 /**
328  * Creates a custom connection object.
329  *
330  * Creates and initializes a new Ecore_Con_Url for a custom request (e.g. HEAD,
331  * SUBSCRIBE and other obscure HTTP requests). This object should be used like
332  * one created with ecore_con_url_new().
333  *
334  * @param url URL that will receive requests
335  * @param custom_request Custom request (e.g. GET, POST, HEAD, PUT, etc)
336  *
337  * @return NULL on error, a new Ecore_Con_Url on success.
338  *
339  *
340  * @see ecore_con_url_new()
341  * @see ecore_con_url_url_set()
342  */
343 EAPI Ecore_Con_Url *
344 ecore_con_url_custom_new(const char *url,
345                          const char *custom_request)
346 {
347 #ifdef HAVE_CURL
348    Ecore_Con_Url *url_con;
349    CURLcode ret;
350
351    if (!url)
352      return NULL;
353
354    if (!custom_request)
355      return NULL;
356
357    url_con = ecore_con_url_new(url);
358
359    if (!url_con)
360      return NULL;
361
362    ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_CUSTOMREQUEST, custom_request);
363    if (ret != CURLE_OK)
364      {
365         ERR("Could not set a custom request string: %s",
366             curl_easy_strerror(ret));
367         ecore_con_url_free(url_con);
368         return NULL;
369      }
370
371    return url_con;
372 #else
373    return NULL;
374    url = NULL;
375    custom_request = NULL;
376 #endif
377 }
378
379 /**
380  * Destroys a Ecore_Con_Url connection object.
381  *
382  * @param url_con Connection object to free.
383  *
384  * @see ecore_con_url_new()
385  */
386 EAPI void
387 ecore_con_url_free(Ecore_Con_Url *url_con)
388 {
389 #ifdef HAVE_CURL
390    char *s;
391    CURLMcode ret;
392
393    if (!url_con)
394      return;
395
396    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
397      {
398         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_free");
399         return;
400      }
401
402    ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
403    if(url_con->fd != -1)
404      {
405         FD_CLR(url_con->fd, &_current_fd_set);
406         if (url_con->fd_handler)
407           ecore_main_fd_handler_del(url_con->fd_handler);
408
409         url_con->fd = -1;
410         url_con->fd_handler = NULL;
411      }
412
413    if (url_con->curl_easy)
414      {
415         // FIXME: For an unknown reason, progress continue to arrive after destruction
416         // this prevent any further call to the callback.
417         curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION, NULL);
418         
419         if (url_con->active)
420           {
421              url_con->active = EINA_FALSE;
422              
423              ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
424              if (ret != CURLM_OK)
425                 ERR("curl_multi_remove_handle failed: %s",
426                     curl_multi_strerror(ret));
427           }
428         
429         curl_easy_cleanup(url_con->curl_easy);
430      }
431
432    _url_con_list = eina_list_remove(_url_con_list, url_con);
433    curl_slist_free_all(url_con->headers);
434    EINA_LIST_FREE(url_con->additional_headers, s)
435      free(s);
436    EINA_LIST_FREE(url_con->response_headers, s)
437      free(s);
438    free(url_con->url);
439    free(url_con);
440 #else
441    return;
442    url_con = NULL;
443 #endif
444 }
445
446 /**
447  * Sets the URL to send the request to.
448  *
449  * @param url_con Connection object through which the request will be sent.
450  * @param url URL that will receive the request
451  *
452  * @return EINA_TRUE on success, EINA_FALSE on error.
453  *
454  */
455 EAPI Eina_Bool
456 ecore_con_url_url_set(Ecore_Con_Url *url_con,
457                       const char    *url)
458 {
459 #ifdef HAVE_CURL
460    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
461      {
462         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_url_set");
463         return EINA_FALSE;
464      }
465
466    if (url_con->active)
467      return EINA_FALSE;
468
469    if (url_con->url)
470      free(url_con->url);
471
472    url_con->url = NULL;
473    if (url)
474      url_con->url = strdup(url);
475
476    if (url_con->url)
477      curl_easy_setopt(url_con->curl_easy, CURLOPT_URL,
478                       url_con->url);
479    else
480      curl_easy_setopt(url_con->curl_easy, CURLOPT_URL, "");
481
482    return EINA_TRUE;
483 #else
484    return EINA_FALSE;
485    (void)url;
486    (void)url_con;
487 #endif
488 }
489
490 /**
491  * Associates data with a connection object.
492  *
493  * Associates data with a connection object, which can be retrieved later with
494  * ecore_con_url_data_get()).
495  *
496  * @param url_con Connection object to associate data.
497  * @param data Data to be set.
498  *
499  *
500  * @see ecore_con_url_data_get()
501  */
502 EAPI void
503 ecore_con_url_data_set(Ecore_Con_Url *url_con,
504                        void          *data)
505 {
506 #ifdef HAVE_CURL
507    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
508      {
509         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_set");
510         return;
511      }
512
513    url_con->data = data;
514 #else
515    return;
516    url_con = NULL;
517    data = NULL;
518 #endif
519 }
520
521 /**
522  * Adds an additional header to the request connection object.
523  *
524  * Adds an additional header to the request connection object. This addition
525  * will be valid for only one ecore_con_url_get() or ecore_con_url_post() call.
526  *
527  * @param url_con Connection object
528  * @param key Header key
529  * @param value Header value
530  *
531  *
532  * @see ecore_con_url_get()
533  * @see ecore_con_url_post()
534  * @see ecore_con_url_additional_headers_clear()
535  */
536 EAPI void
537 ecore_con_url_additional_header_add(Ecore_Con_Url *url_con,
538                                     const char    *key,
539                                     const char    *value)
540 {
541 #ifdef HAVE_CURL
542    char *tmp;
543
544    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
545      {
546         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
547                          "ecore_con_url_additional_header_add");
548         return;
549      }
550
551    tmp = malloc(strlen(key) + strlen(value) + 3);
552    if (!tmp)
553      return;
554
555    sprintf(tmp, "%s: %s", key, value);
556    url_con->additional_headers = eina_list_append(url_con->additional_headers,
557                                                   tmp);
558 #else
559    return;
560    url_con = NULL;
561    key = NULL;
562    value = NULL;
563 #endif
564 }
565
566 /**
567  * Cleans additional headers.
568  *
569  * Cleans additional headers associated with a connection object (previously
570  * added with ecore_con_url_additional_header_add()).
571  *
572  * @param url_con Connection object to clean additional headers.
573  *
574  *
575  * @see ecore_con_url_additional_header_add()
576  * @see ecore_con_url_get()
577  * @see ecore_con_url_post()
578  */
579 EAPI void
580 ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
581 {
582 #ifdef HAVE_CURL
583    char *s;
584
585    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
586      {
587         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
588                          "ecore_con_url_additional_headers_clear");
589         return;
590      }
591
592    EINA_LIST_FREE(url_con->additional_headers, s)
593      free(s);
594 #else
595    return;
596    url_con = NULL;
597 #endif
598 }
599
600 /**
601  * Retrieves data associated with a Ecore_Con_Url connection object.
602  *
603  * Retrieves data associated with a Ecore_Con_Url connection object (previously
604  * set with ecore_con_url_data_set()).
605  *
606  * @param url_con Connection object to retrieve data from.
607  *
608  * @return Data associated with the given object.
609  *
610  *
611  * @see ecore_con_url_data_set()
612  */
613 EAPI void *
614 ecore_con_url_data_get(Ecore_Con_Url *url_con)
615 {
616 #ifdef HAVE_CURL
617    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
618      {
619         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_get");
620         return NULL;
621      }
622
623    return url_con->data;
624 #else
625    return NULL;
626    url_con = NULL;
627 #endif
628 }
629
630 /**
631  * Sets whether HTTP requests should be conditional, dependent on
632  * modification time.
633  *
634  * @param url_con   Ecore_Con_Url to act upon.
635  * @param condition Condition to use for HTTP requests.
636  * @param timestamp Time since 1 Jan 1970 to use in the condition.
637  *
638  * @sa ecore_con_url_get()
639  * @sa ecore_con_url_post()
640  */
641 EAPI void
642 ecore_con_url_time(Ecore_Con_Url     *url_con,
643                    Ecore_Con_Url_Time condition,
644                    double             timestamp)
645 {
646 #ifdef HAVE_CURL
647    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
648      {
649         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_time");
650         return;
651      }
652
653    url_con->time_condition = condition;
654    url_con->timestamp = timestamp;
655 #else
656    return;
657    (void)url_con;
658    (void)condition;
659    (void)timestamp;
660 #endif
661 }
662
663 /**
664  * Setup a file for receiving request data.
665  *
666  * Setups a file to have response data written into. Note that
667  * ECORE_CON_EVENT_URL_DATA events will not be emitted if a file has been set to
668  * receive the response data.
669  *
670  * @param url_con Connection object to set file
671  * @param fd File descriptor associated with the file
672  *
673  */
674 EAPI void
675 ecore_con_url_fd_set(Ecore_Con_Url *url_con,
676                      int            fd)
677 {
678 #ifdef HAVE_CURL
679    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
680      {
681         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_set");
682         return;
683      }
684
685    url_con->write_fd = fd;
686 #endif
687 }
688
689 /**
690  * Retrieves the number of bytes received.
691  *
692  * Retrieves the number of bytes received on the last request of the given
693  * connection object.
694  *
695  * @param url_con Connection object which the request was sent on.
696  *
697  * @return Number of bytes received on request.
698  *
699  *
700  * @see ecore_con_url_get()
701  * @see ecore_con_url_post()
702  */
703 EAPI int
704 ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
705 {
706 #ifdef HAVE_CURL
707    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
708      {
709         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
710                          "ecore_con_url_received_bytes_get");
711         return -1;
712      }
713
714    return url_con->received;
715 #else
716    return 0;
717 #endif
718 }
719
720 /**
721  * Retrieves headers from last request sent.
722  *
723  * Retrieves a list containing the response headers. This function should be
724  * used after an ECORE_CON_EVENT_URL_COMPLETE event (headers should normally be
725  * ready at that time).
726  *
727  * @param url_con Connection object to retrieve response headers from.
728  *
729  * @return List of response headers. This list must not be modified by the user.
730  *
731  */
732 EAPI const Eina_List *
733 ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
734 {
735 #ifdef HAVE_CURL
736    return url_con->response_headers;
737 #else
738    return NULL;
739 #endif
740 }
741
742 /**
743  * Sets url_con to use http auth, with given username and password, "safely" or not.
744  * ATTENTION: requires libcurl >= 7.19.1 to work, otherwise will always return 0.
745  *
746  * @param url_con Connection object to perform a request on, previously created
747  *    with ecore_con_url_new() or ecore_con_url_custom_new().
748  * @param username Username to use in authentication
749  * @param password Password to use in authentication
750  * @param safe Whether to use "safer" methods (eg, NOT http basic auth)
751  *
752  * @return #EINA_TRUE on success, #EINA_FALSE on error.
753  *
754  */
755 EAPI Eina_Bool
756 ecore_con_url_httpauth_set(Ecore_Con_Url *url_con,
757                            const char    *username,
758                            const char    *password,
759                            Eina_Bool      safe)
760 {
761 #ifdef HAVE_CURL
762    CURLcode ret;
763
764    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
765      {
766         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
767                          "ecore_con_url_httpauth_set");
768         return EINA_FALSE;
769      }
770
771 # if LIBCURL_VERSION_NUM >= 0x071301
772    if ((username) && (password))
773      {
774         if (safe)
775           curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH,
776                            CURLAUTH_ANYSAFE);
777         else
778           curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
779
780         ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
781         if (ret != CURLE_OK)
782           {
783              ERR("Could not set username for HTTP authentication: %s",
784                  curl_easy_strerror(ret));
785              return EINA_FALSE;
786           }
787
788         ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
789         if (ret != CURLE_OK)
790           {
791              ERR("Could not set password for HTTP authentication: %s",
792                  curl_easy_strerror(ret));
793              return EINA_FALSE;
794           }
795
796         return EINA_TRUE;
797      }
798 # endif
799 #endif
800
801    return EINA_FALSE;
802 }
803
804 #define MODE_AUTO 0
805 #define MODE_GET  1
806 #define MODE_POST 2
807
808 static Eina_Bool
809 _ecore_con_url_send(Ecore_Con_Url *url_con,
810                     int            mode,
811                     const void    *data,
812                     long           length,
813                     const char    *content_type)
814 {
815 #ifdef HAVE_CURL
816    Eina_List *l;
817    const char *s;
818    char tmp[256];
819
820    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
821      {
822         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_send");
823         return EINA_FALSE;
824      }
825
826    if (url_con->active)
827      return EINA_FALSE;
828
829    if (!url_con->url)
830      return EINA_FALSE;
831
832    /* Free response headers from previous send() calls */
833    EINA_LIST_FREE(url_con->response_headers, s)
834      free((char *)s);
835    url_con->response_headers = NULL;
836
837    curl_slist_free_all(url_con->headers);
838    url_con->headers = NULL;
839
840    if ((mode == MODE_POST) || (mode == MODE_AUTO))
841      {
842         if (data)
843           {
844              if ((content_type) && (strlen(content_type) < 200))
845                {
846                   snprintf(tmp, sizeof(tmp), "Content-Type: %s", content_type);
847                   url_con->headers = curl_slist_append(url_con->headers, tmp);
848                }
849              
850              curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDS, data);
851              curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, length);
852           }
853      }
854    
855    switch (url_con->time_condition)
856      {
857       case ECORE_CON_URL_TIME_NONE:
858         curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
859                          CURL_TIMECOND_NONE);
860         break;
861
862       case ECORE_CON_URL_TIME_IFMODSINCE:
863         curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
864                          CURL_TIMECOND_IFMODSINCE);
865         curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE,
866                          (long)url_con->timestamp);
867         break;
868
869       case ECORE_CON_URL_TIME_IFUNMODSINCE:
870         curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
871                          CURL_TIMECOND_IFUNMODSINCE);
872         curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE,
873                          (long)url_con->timestamp);
874         break;
875      }
876
877    /* Additional headers */
878    EINA_LIST_FOREACH(url_con->additional_headers, l, s)
879      url_con->headers = curl_slist_append(url_con->headers, s);
880
881    curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPHEADER, url_con->headers);
882
883    url_con->received = 0;
884
885    return _ecore_con_url_perform(url_con);
886 #else
887    return EINA_FALSE;
888    url_con = NULL;
889    data = NULL;
890    length = 0;
891    content_type = NULL;
892 #endif
893 }
894
895 /**
896  * Sends a request.
897  *
898  * @param url_con Connection object to perform a request on, previously created
899  *                with ecore_con_url_new() or ecore_con_url_custom_new().
900  * @param data    Payload (data sent on the request)
901  * @param length  Payload length. If @c -1, rely on automatic length
902  *                calculation via @c strlen() on @p data.
903  * @param content_type Content type of the payload (e.g. text/xml)
904  *
905  * @return #EINA_TRUE on success, #EINA_FALSE on error.
906  *
907  * @see ecore_con_url_custom_new()
908  * @see ecore_con_url_additional_headers_clear()
909  * @see ecore_con_url_additional_header_add()
910  * @see ecore_con_url_data_set()
911  * @see ecore_con_url_data_get()
912  * @see ecore_con_url_response_headers_get()
913  * @see ecore_con_url_time()
914  * @see ecore_con_url_get()
915  * @see ecore_con_url_post()
916  */
917 EINA_DEPRECATED EAPI Eina_Bool
918 ecore_con_url_send(Ecore_Con_Url *url_con,
919                    const void    *data,
920                    long           length,
921                    const char    *content_type)
922 {
923    return _ecore_con_url_send(url_con, MODE_AUTO, data, length, content_type);
924 }
925
926 /**
927  * Sends a get request.
928  *
929  * @param url_con Connection object to perform a request on, previously created
930  * 
931  * @return #EINA_TRUE on success, #EINA_FALSE on error.
932  *
933  * @see ecore_con_url_custom_new()
934  * @see ecore_con_url_additional_headers_clear()
935  * @see ecore_con_url_additional_header_add()
936  * @see ecore_con_url_data_set()
937  * @see ecore_con_url_data_get()
938  * @see ecore_con_url_response_headers_get()
939  * @see ecore_con_url_time()
940  * @see ecore_con_url_post()
941  */
942 EAPI Eina_Bool
943 ecore_con_url_get(Ecore_Con_Url *url_con)
944 {
945    return _ecore_con_url_send(url_con, MODE_GET, NULL, 0, NULL);
946 }
947
948 /**
949  * Sends a post request.
950  *
951  * @param url_con Connection object to perform a request on, previously created
952  *                with ecore_con_url_new() or ecore_con_url_custom_new().
953  * @param data    Payload (data sent on the request)
954  * @param length  Payload length. If @c -1, rely on automatic length
955  *                calculation via @c strlen() on @p data.
956  * @param content_type Content type of the payload (e.g. text/xml)
957  *
958  * @return #EINA_TRUE on success, #EINA_FALSE on error.
959  *
960  * @see ecore_con_url_custom_new()
961  * @see ecore_con_url_additional_headers_clear()
962  * @see ecore_con_url_additional_header_add()
963  * @see ecore_con_url_data_set()
964  * @see ecore_con_url_data_get()
965  * @see ecore_con_url_response_headers_get()
966  * @see ecore_con_url_time()
967  * @see ecore_con_url_get()
968  */
969 EAPI Eina_Bool
970 ecore_con_url_post(Ecore_Con_Url *url_con,
971                    const void    *data,
972                    long           length,
973                    const char    *content_type)
974 {
975    return _ecore_con_url_send(url_con, MODE_POST, data, length, content_type);
976 }
977
978 /**
979  * @brief Uploads a file to an ftp site.
980  * @param url_con The Ecore_Con_Url object to send with
981  * @param filename The path to the file to send
982  * @param user The username to log in with
983  * @param pass The password to log in with
984  * @param upload_dir The directory to which the file should be uploaded
985  * @return #EINA_TRUE on success, else #EINA_FALSE.
986  * Upload @p filename to an ftp server set in @p url_con using @p user
987  * and @p pass to directory @p upload_dir
988  */
989 EAPI Eina_Bool
990 ecore_con_url_ftp_upload(Ecore_Con_Url *url_con,
991                          const char    *filename,
992                          const char    *user,
993                          const char    *pass,
994                          const char    *upload_dir)
995 {
996 #ifdef HAVE_CURL
997    char url[4096];
998    char userpwd[4096];
999    FILE *fd;
1000    struct stat file_info;
1001    CURLcode ret;
1002
1003    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1004      {
1005         ECORE_MAGIC_FAIL(url_con,
1006                          ECORE_MAGIC_CON_URL,
1007                          "ecore_con_url_ftp_upload");
1008         return EINA_FALSE;
1009      }
1010
1011    if (url_con->active)
1012      return EINA_FALSE;
1013
1014    if (!url_con->url)
1015      return EINA_FALSE;
1016
1017    if (filename)
1018      {
1019         if (stat(filename, &file_info))
1020           return EINA_FALSE;
1021
1022         snprintf(userpwd, sizeof(userpwd), "%s:%s", user, pass);
1023         ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERPWD, userpwd);
1024         if (ret != CURLE_OK)
1025           {
1026              ERR("Could not set username and password for FTP upload: %s",
1027                  curl_easy_strerror(ret));
1028              return EINA_FALSE;
1029           }
1030
1031         char tmp[PATH_MAX];
1032         snprintf(tmp, PATH_MAX, "%s", filename);
1033
1034         if (upload_dir)
1035           snprintf(url, sizeof(url), "ftp://%s/%s/%s", url_con->url,
1036                    upload_dir, basename(tmp));
1037         else
1038           snprintf(url, sizeof(url), "ftp://%s/%s", url_con->url,
1039                    basename(tmp));
1040
1041         if (!ecore_con_url_url_set(url_con, url))
1042           return EINA_FALSE;
1043
1044         curl_easy_setopt(url_con->curl_easy, CURLOPT_INFILESIZE_LARGE,
1045                          (curl_off_t)file_info.st_size);
1046         curl_easy_setopt(url_con->curl_easy, CURLOPT_UPLOAD, 1);
1047         curl_easy_setopt(url_con->curl_easy, CURLOPT_READFUNCTION,
1048                          _ecore_con_url_read_cb);
1049
1050         fd = fopen(filename, "rb");
1051         if (!fd)
1052           {
1053              ERR("Could not open \"%s\" for FTP upload", filename);
1054              return EINA_FALSE;
1055           }
1056         curl_easy_setopt(url_con->curl_easy, CURLOPT_READDATA, fd);
1057
1058         return _ecore_con_url_perform(url_con);
1059      }
1060
1061    return EINA_FALSE;
1062 #else
1063    return EINA_FALSE;
1064    (void)url_con;
1065    (void)filename;
1066    (void)user;
1067    (void)pass;
1068    (void)upload_dir;
1069 #endif
1070 }
1071
1072 /**
1073  * Enables the cookie engine for subsequent HTTP requests.
1074  *
1075  * After this function is called, cookies set by the server in HTTP responses
1076  * will be parsed and stored, as well as sent back to the server in new HTTP
1077  * requests.
1078  *
1079  * @note Even though this function is called @c ecore_con_url_cookies_init(),
1080  * there is no symmetrical shutdown operation.
1081  *
1082  * @param url_con Ecore_Con_Url instance which will be acted upon.
1083  */
1084 EAPI void
1085 ecore_con_url_cookies_init(Ecore_Con_Url *url_con)
1086 {
1087 #ifdef HAVE_CURL
1088    if (!url_con)
1089      return;
1090
1091    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1092      {
1093         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1094                          "ecore_con_url_cookies_init");
1095         return;
1096      }
1097
1098    curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEFILE, "");
1099 #else
1100    (void)url_con;
1101 #endif
1102 }
1103
1104 /**
1105  * Controls whether session cookies from previous sessions shall be loaded.
1106  *
1107  * Session cookies are cookies with no expire date set, which usually means
1108  * they are removed after the current session is closed.
1109  *
1110  * By default, when Ecore_Con_Url loads cookies from a file, all cookies are
1111  * loaded, including session cookies, which, most of the time, were supposed
1112  * to be loaded and valid only for that session.
1113  *
1114  * If @p ignore is set to @c EINA_TRUE, when Ecore_Con_Url loads cookies from
1115  * the files passed to @c ecore_con_url_cookies_file_add(), session cookies
1116  * will not be loaded.
1117  *
1118  * @param url_con Ecore_Con_Url instance which will be acted upon.
1119  * @param ignore  If @c EINA_TRUE, ignore session cookies when loading cookies
1120  *                from files. If @c EINA_FALSE, all cookies will be loaded.
1121  *
1122  * @see ecore_con_url_cookies_file_add()
1123  */
1124 EAPI void
1125 ecore_con_url_cookies_ignore_old_session_set(Ecore_Con_Url *url_con, Eina_Bool ignore)
1126 {
1127 #ifdef HAVE_CURL
1128    if (!url_con)
1129      return;
1130
1131    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1132      {
1133         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1134                          "ecore_con_url_cookies_ignore_old_session_set");
1135         return;
1136      }
1137
1138    curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIESESSION, ignore);
1139 #else
1140    (void)url_con;
1141    (void)ignore;
1142 #endif
1143 }
1144
1145 /**
1146  * Clears currently loaded cookies.
1147  *
1148  * The cleared cookies are removed and will not be sent in subsequent HTTP
1149  * requests, nor will they be written to the cookiejar file set via
1150  * @c ecore_con_url_cookies_jar_file_set().
1151  *
1152  * @note This function will initialize the cookie engine if it has not been
1153  *       initialized yet.
1154  *
1155  * @param url_con      Ecore_Con_Url instance which will be acted upon.
1156  *
1157  * @see ecore_con_url_cookies_session_clear()
1158  * @see ecore_con_url_cookies_ignore_old_session_set()
1159  */
1160 EAPI void
1161 ecore_con_url_cookies_clear(Ecore_Con_Url *url_con)
1162 {
1163 #ifdef HAVE_CURL
1164    if (!url_con)
1165      return;
1166
1167    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1168      {
1169         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1170                          "ecore_con_url_cookies_clear");
1171         return;
1172      }
1173
1174    curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "ALL");
1175 #else
1176    (void)url_con;
1177 #endif
1178 }
1179
1180 /**
1181  * Clears currently loaded session cookies.
1182  *
1183  * Session cookies are cookies with no expire date set, which usually means
1184  * they are removed after the current session is closed.
1185  *
1186  * The cleared cookies are removed and will not be sent in subsequent HTTP
1187  * requests, nor will they be written to the cookiejar file set via
1188  * @c ecore_con_url_cookies_jar_file_set().
1189  *
1190  * @note This function will initialize the cookie engine if it has not been
1191  *       initialized yet.
1192  *
1193  * @param url_con      Ecore_Con_Url instance which will be acted upon.
1194  *
1195  * @see ecore_con_url_cookies_clear()
1196  * @see ecore_con_url_cookies_ignore_old_session_set()
1197  */
1198 EAPI void
1199 ecore_con_url_cookies_session_clear(Ecore_Con_Url *url_con)
1200 {
1201 #ifdef HAVE_CURL
1202    if (!url_con)
1203      return;
1204
1205    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1206      {
1207         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1208                          "ecore_con_url_cookies_session_clear");
1209         return;
1210      }
1211
1212    curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "SESS");
1213 #else
1214    (void)url_con;
1215 #endif
1216 }
1217
1218 /**
1219  * Adds a file to the list of files from which to load cookies.
1220  *
1221  * Files must contain cookies defined according to two possible formats:
1222  *
1223  * @li HTTP-style header ("Set-Cookie: ...").
1224  * @li Netscape/Mozilla cookie data format.
1225  *
1226  * Please notice that the file will not be read immediately, but rather added
1227  * to a list of files that will be loaded and parsed at a later time.
1228  *
1229  * @note This function will initialize the cookie engine if it has not been
1230  *       initialized yet.
1231  *
1232  * @param url_con   Ecore_Con_Url instance which will be acted upon.
1233  * @param file_name Name of the file that will be added to the list.
1234  *
1235  * @see ecore_con_url_cookies_ignore_old_session_set()
1236  */
1237 EAPI void
1238 ecore_con_url_cookies_file_add(Ecore_Con_Url *url_con, const char * const file_name)
1239 {
1240 #ifdef HAVE_CURL
1241    if (!url_con)
1242      return;
1243
1244    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1245      {
1246         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1247                          "ecore_con_url_cookies_file_add");
1248         return;
1249      }
1250
1251    curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEFILE, file_name);
1252 #else
1253    (void)url_con;
1254    (void)file_name;
1255 #endif
1256 }
1257
1258 /**
1259  * Sets the name of the file to which all current cookies will be written when
1260  * either cookies are flushed or Ecore_Con is shut down.
1261  *
1262  * Cookies are written following Netscape/Mozilla's data format, also known as
1263  * cookie-jar.
1264  *
1265  * @note This function will initialize the cookie engine if it has not been
1266  *       initialized yet.
1267  *
1268  * @param url_con        Ecore_Con_Url instance which will be acted upon.
1269  * @param cookiejar_file File to which the cookies will be written.
1270  *
1271  * @return @c EINA_TRUE is the file name has been set successfully,
1272  *         @c EINA_FALSE otherwise.
1273  *
1274  * @see ecore_con_url_cookies_jar_write()
1275  */
1276 EAPI Eina_Bool
1277 ecore_con_url_cookies_jar_file_set(Ecore_Con_Url *url_con, const char * const cookiejar_file)
1278 {
1279 #ifdef HAVE_CURL
1280    CURLcode ret;
1281
1282    if (!url_con)
1283      return EINA_FALSE;
1284
1285    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1286      {
1287         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1288                          "ecore_con_url_cookies_jar_file_set");
1289         return EINA_FALSE;
1290      }
1291
1292    ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEJAR,
1293                           cookiejar_file);
1294    if (ret != CURLE_OK)
1295      {
1296         ERR("Setting the cookie-jar name failed: %s",
1297             curl_easy_strerror(ret));
1298         return EINA_FALSE;
1299      }
1300
1301    return EINA_TRUE;
1302 #else
1303    return EINA_FALSE;
1304    (void)url_con;
1305    (void)cookiejar_file;
1306 #endif
1307 }
1308
1309 /**
1310  * Writes all current cookies to the cookie jar immediately.
1311  *
1312  * A cookie-jar file must have been previously set by
1313  * @c ecore_con_url_jar_file_set, otherwise nothing will be done.
1314  *
1315  * @note This function will initialize the cookie engine if it has not been
1316  *       initialized yet.
1317  *
1318  * @param url_con Ecore_Con_Url instance which will be acted upon.
1319  *
1320  * @see ecore_con_url_cookies_jar_file_set()
1321  */
1322 EAPI void
1323 ecore_con_url_cookies_jar_write(Ecore_Con_Url *url_con)
1324 {
1325 #ifdef HAVE_CURL
1326    if (!url_con)
1327      return;
1328
1329    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1330      {
1331         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1332                          "ecore_con_url_cookies_jar_write");
1333         return;
1334      }
1335
1336    curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "FLUSH");
1337 #else
1338    (void)url_con;
1339 #endif
1340 }
1341
1342 /**
1343  * Toggle libcurl's verbose output.
1344  *
1345  * If @p verbose is @c EINA_TRUE, libcurl will output a lot of verbose
1346  * information about its operations, which is useful for
1347  * debugging. The verbose information will be sent to stderr.
1348  *
1349  * @param url_con Ecore_Con_Url instance which will be acted upon.
1350  * @param verbose Whether or not to enable libcurl's verbose output.
1351  */
1352 EAPI void
1353 ecore_con_url_verbose_set(Ecore_Con_Url *url_con,
1354                           Eina_Bool      verbose)
1355 {
1356 #ifdef HAVE_CURL
1357    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1358      {
1359         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1360                          "ecore_con_url_verbose_set");
1361         return;
1362      }
1363
1364    if (url_con->active)
1365      return;
1366
1367    if (!url_con->url)
1368      return;
1369
1370    curl_easy_setopt(url_con->curl_easy, CURLOPT_VERBOSE, (int)verbose);
1371 #else
1372    (void)url_con;
1373    (void)verbose;
1374 #endif
1375 }
1376
1377 /**
1378  * Enable or disable EPSV extension
1379  * @return  FIXME: To be more documented.
1380  */
1381 EAPI void
1382 ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con,
1383                                Eina_Bool      use_epsv)
1384 {
1385 #ifdef HAVE_CURL
1386    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1387      {
1388         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1389                          "ecore_con_url_ftp_use_epsv_set");
1390         return;
1391      }
1392
1393    if (url_con->active)
1394      return;
1395
1396    if (!url_con->url)
1397      return;
1398
1399    curl_easy_setopt(url_con->curl_easy, CURLOPT_FTP_USE_EPSV, (int)use_epsv);
1400 #endif
1401    (void)url_con;
1402    (void)use_epsv;
1403 }
1404
1405 /**
1406  * Toggle libcurl's verify peer's certificate option.
1407  *
1408  * If @p verify is @c EINA_TRUE, libcurl will verify
1409  * the authenticity of the peer's certificate, otherwise
1410  * it will not. Default behavior of libcurl is to check
1411  * peer's certificate.
1412  *
1413  * @param url_con Ecore_Con_Url instance which will be acted upon.
1414  * @param verify Whether or not libcurl will check peer's certificate.
1415  * @since 1.1.0
1416  */
1417 EAPI void
1418 ecore_con_url_ssl_verify_peer_set(Ecore_Con_Url *url_con,
1419                                   Eina_Bool      verify)
1420 {
1421 #ifdef HAVE_CURL
1422    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1423      {
1424         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1425                          "ecore_con_url_ssl_verify_peer_set");
1426         return;
1427      }
1428
1429    if (url_con->active)
1430      return;
1431
1432    if (!url_con->url)
1433      return;
1434
1435    curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, (int)verify);
1436 #else
1437    (void)url_con;
1438    (void)verify;
1439 #endif
1440 }
1441
1442 /**
1443  * Set a custom CA to trust for SSL/TLS connections.
1444  * 
1445  * Specify the path of a file (in PEM format) containing one or more
1446  * CA certificate(s) to use for the validation of the server certificate.
1447  * 
1448  * This function can also disable CA validation if @p ca_path is @c NULL.
1449  * However, the server certificate still needs to be valid for the connection
1450  * to succeed (i.e., the certificate must concern the server the
1451  * connection is made to).
1452  * 
1453  * @param url_con Connection object that will use the custom CA.
1454  * @param ca_path Path to a CA certificate(s) file or @c NULL to disable
1455  *                CA validation.
1456  * 
1457  * @return  @c 0 on success. When cURL is used, non-zero return values
1458  *          are equal to cURL error codes.
1459  */
1460 EAPI int
1461 ecore_con_url_ssl_ca_set(Ecore_Con_Url *url_con, const char *ca_path)
1462 {
1463    int res = -1;
1464
1465 #ifdef HAVE_CURL
1466    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1467      {
1468        ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_ssl_ca_set");
1469               return -1;
1470      }
1471
1472    if (url_con->active) return -1;
1473    if (!url_con->url) return -1;
1474    if (ca_path == NULL)
1475      res = curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, 0);
1476    else
1477      {
1478        res = curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, 1);
1479        if (!res)
1480          res = curl_easy_setopt(url_con->curl_easy, CURLOPT_CAINFO, ca_path);
1481      }
1482 #else
1483    (void)url_con;
1484    (void)ca_path;
1485 #endif
1486
1487    return res;
1488 }
1489
1490
1491 /**
1492  * @}
1493  */
1494
1495 #ifdef HAVE_CURL
1496 static int
1497 _ecore_con_url_suspend_fd_handler(void)
1498 {
1499    Eina_List *l;
1500    Ecore_Con_Url *url_con;
1501    int deleted = 0;
1502
1503    if (!_url_con_list)
1504      return 0;
1505
1506    EINA_LIST_FOREACH(_url_con_list, l, url_con)
1507      {
1508         if (url_con->active && url_con->fd_handler)
1509           {
1510              ecore_main_fd_handler_del(url_con->fd_handler);
1511              url_con->fd_handler = NULL;
1512              deleted++;
1513           }
1514      }
1515
1516    return deleted;
1517 }
1518
1519 static int
1520 _ecore_con_url_restart_fd_handler(void)
1521 {
1522    Eina_List *l;
1523    Ecore_Con_Url *url_con;
1524    int activated = 0;
1525
1526    if (!_url_con_list)
1527      return 0;
1528
1529    EINA_LIST_FOREACH(_url_con_list, l, url_con)
1530      {
1531         if (!url_con->fd_handler && url_con->fd != -1)
1532           {
1533              url_con->fd_handler =
1534                ecore_main_fd_handler_add(url_con->fd, url_con->flags,
1535                                          _ecore_con_url_fd_handler,
1536                                          NULL, NULL, NULL);
1537              activated++;
1538           }
1539      }
1540
1541    return activated;
1542 }
1543
1544 static size_t
1545 _ecore_con_url_data_cb(void  *buffer,
1546                        size_t size,
1547                        size_t nitems,
1548                        void  *userp)
1549 {
1550    Ecore_Con_Url *url_con;
1551    Ecore_Con_Event_Url_Data *e;
1552    size_t real_size = size * nitems;
1553
1554    url_con = (Ecore_Con_Url *)userp;
1555
1556    if (!url_con)
1557      return -1;
1558
1559    if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1560      {
1561         ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_cb");
1562         return -1;
1563      }
1564
1565    url_con->received += real_size;
1566
1567    if (url_con->write_fd < 0)
1568      {
1569         e =
1570           malloc(sizeof(Ecore_Con_Event_Url_Data) + sizeof(unsigned char) *
1571                  (real_size - 1));
1572         if (e)
1573           {
1574              e->url_con = url_con;
1575              e->size = real_size;
1576              memcpy(e->data, buffer, real_size);
1577              ecore_event_add(ECORE_CON_EVENT_URL_DATA, e,
1578                              _ecore_con_event_url_free, NULL);
1579           }
1580      }
1581    else
1582      {
1583         ssize_t count = 0;
1584         size_t total_size = real_size;
1585         size_t offset = 0;
1586
1587         while (total_size > 0)
1588           {
1589              count = write(url_con->write_fd,
1590                            (char *)buffer + offset,
1591                            total_size);
1592              if (count < 0)
1593                {
1594                   if (errno != EAGAIN && errno != EINTR)
1595                     return -1;
1596                }
1597              else
1598                {
1599                   total_size -= count;
1600                   offset += count;
1601                }
1602           }
1603      }
1604
1605    return real_size;
1606 }
1607
1608 #define ECORE_CON_URL_TRANSMISSION(Transmit, Event, Url_con, Total, Now)   \
1609   {                                                                        \
1610      Ecore_Con_Event_Url_Progress *e;                                      \
1611      if ((Total != 0) || (Now != 0))                                       \
1612        {                                                                   \
1613           e = calloc(1, sizeof(Ecore_Con_Event_Url_Progress));             \
1614           if (e)                                                           \
1615             {                                                              \
1616                e->url_con = url_con;                                       \
1617                e->total = Total;                                           \
1618                e->now = Now;                                               \
1619                ecore_event_add(Event, e, _ecore_con_event_url_free, NULL); \
1620             }                                                              \
1621        }                                                                   \
1622   }
1623
1624 static size_t
1625 _ecore_con_url_header_cb(void  *ptr,
1626                          size_t size,
1627                          size_t nitems,
1628                          void  *stream)
1629 {
1630    size_t real_size = size * nitems;
1631    Ecore_Con_Url *url_con = stream;
1632
1633    char *header = malloc(sizeof(char) * (real_size + 1));
1634    if (!header)
1635      return real_size;
1636
1637    memcpy(header, ptr, real_size);
1638    header[real_size] = '\0';
1639
1640    url_con->response_headers = eina_list_append(url_con->response_headers,
1641                                                 header);
1642
1643    return real_size;
1644 }
1645
1646 static int
1647 _ecore_con_url_progress_cb(void  *clientp,
1648                            double dltotal,
1649                            double dlnow,
1650                            double ultotal,
1651                            double ulnow)
1652 {
1653    Ecore_Con_Event_Url_Progress *e;
1654    Ecore_Con_Url *url_con;
1655
1656    url_con = clientp;
1657
1658    e = malloc(sizeof(Ecore_Con_Event_Url_Progress));
1659    if (e)
1660      {
1661         e->url_con = url_con;
1662         e->down.total = dltotal;
1663         e->down.now = dlnow;
1664         e->up.total = ultotal;
1665         e->up.now = ulnow;
1666         ecore_event_add(ECORE_CON_EVENT_URL_PROGRESS, e,
1667                         _ecore_con_event_url_free, NULL);
1668      }
1669
1670    return 0;
1671 }
1672
1673 static size_t
1674 _ecore_con_url_read_cb(void  *ptr,
1675                        size_t size,
1676                        size_t nitems,
1677                        void  *stream)
1678 {
1679    size_t retcode = fread(ptr, size, nitems, stream);
1680
1681    if (ferror((FILE *)stream))
1682      {
1683         fclose(stream);
1684         return CURL_READFUNC_ABORT;
1685      }
1686    else if (retcode == 0)
1687      {
1688         fclose((FILE *)stream);
1689         return 0;
1690      }
1691
1692    INF("*** We read %zu bytes from file", retcode);
1693    return retcode;
1694 }
1695
1696 static Eina_Bool
1697 _ecore_con_url_perform(Ecore_Con_Url *url_con)
1698 {
1699    fd_set read_set, write_set, exc_set;
1700    int fd_max, fd;
1701    int flags, still_running;
1702    int completed_immediately = 0;
1703    CURLMcode ret;
1704
1705    _url_con_list = eina_list_append(_url_con_list, url_con);
1706
1707    url_con->active = EINA_TRUE;
1708    curl_multi_add_handle(_curlm, url_con->curl_easy);
1709    /* This one can't be stopped, or the download never start. */
1710    while (curl_multi_perform(_curlm, &still_running) == CURLM_CALL_MULTI_PERFORM) ;
1711
1712    completed_immediately = _ecore_con_url_process_completed_jobs(url_con);
1713
1714    if (!completed_immediately)
1715      {
1716         if (url_con->fd_handler)
1717           ecore_main_fd_handler_del(url_con->fd_handler);
1718
1719         url_con->fd_handler = NULL;
1720
1721         /* url_con still active -- set up an fd_handler */
1722         FD_ZERO(&read_set);
1723         FD_ZERO(&write_set);
1724         FD_ZERO(&exc_set);
1725
1726         /* Stupid curl, why can't I get the fd to the current added job? */
1727         ret = curl_multi_fdset(_curlm, &read_set, &write_set, &exc_set,
1728                                &fd_max);
1729         if (ret != CURLM_OK)
1730           {
1731              ERR("curl_multi_fdset failed: %s", curl_multi_strerror(ret));
1732              return EINA_FALSE;
1733           }
1734
1735         for (fd = 0; fd <= fd_max; fd++)
1736           {
1737              if (!FD_ISSET(fd, &_current_fd_set))
1738                {
1739                   flags = 0;
1740                   if (FD_ISSET(fd, &read_set))
1741                     flags |= ECORE_FD_READ;
1742
1743                   if (FD_ISSET(fd, &write_set))
1744                     flags |= ECORE_FD_WRITE;
1745
1746                   if (FD_ISSET(fd, &exc_set))
1747                     flags |= ECORE_FD_ERROR;
1748
1749                   if (flags)
1750                     {
1751                        long ms = 0;
1752
1753                        ret = curl_multi_timeout(_curlm, &ms);
1754                        if (ret != CURLM_OK)
1755                          ERR("curl_multi_timeout failed: %s",
1756                              curl_multi_strerror(ret));
1757
1758                        if (ms == 0)
1759                          ms = 1000;
1760
1761                        FD_SET(fd, &_current_fd_set);
1762                        url_con->fd = fd;
1763                        url_con->flags = flags;
1764                        url_con->fd_handler =
1765                          ecore_main_fd_handler_add(fd, flags,
1766                                                    _ecore_con_url_fd_handler,
1767                                                    NULL, NULL, NULL);
1768                        break;
1769                     }
1770                }
1771           }
1772         if (!url_con->fd_handler)
1773           {
1774              /* Failed to set up an fd_handler */
1775               ecore_timer_freeze(_curl_timeout);
1776
1777               ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
1778               if (ret != CURLM_OK)
1779                 ERR("curl_multi_remove_handle failed: %s",
1780                     curl_multi_strerror(ret));
1781
1782               url_con->active = EINA_FALSE;
1783               url_con->fd = -1;
1784               return EINA_FALSE;
1785           }
1786
1787         ecore_timer_thaw(_curl_timeout);
1788      }
1789
1790    return EINA_TRUE;
1791 }
1792
1793 static Eina_Bool
1794 _ecore_con_url_idler_handler(void *data)
1795 {
1796    double start;
1797    int done = 1, still_running;
1798
1799    start = ecore_time_get();
1800    while (curl_multi_perform(_curlm, &still_running) == CURLM_CALL_MULTI_PERFORM)
1801      /* make this not more than a frametime to keep interactivity high */
1802      if ((ecore_time_get() - start) > ecore_animator_frametime_get())
1803        {
1804           done = 0;
1805           break;
1806        }
1807
1808    _ecore_con_url_process_completed_jobs(NULL);
1809
1810    if (done)
1811      {
1812         _ecore_con_url_restart_fd_handler();
1813         _fd_idler_handler = NULL;
1814
1815         if (!_url_con_list)
1816           ecore_timer_freeze(_curl_timeout);
1817
1818         return data ==
1819                (void *)0xACE ? ECORE_CALLBACK_RENEW : ECORE_CALLBACK_CANCEL;
1820      }
1821
1822    return ECORE_CALLBACK_RENEW;
1823 }
1824
1825 static Eina_Bool
1826 _ecore_con_url_fd_handler(void *data                   __UNUSED__,
1827                           Ecore_Fd_Handler *fd_handler __UNUSED__)
1828 {
1829    _ecore_con_url_suspend_fd_handler();
1830
1831    if (!_fd_idler_handler)
1832      _fd_idler_handler = ecore_idler_add(
1833          _ecore_con_url_idler_handler, NULL);
1834
1835    return ECORE_CALLBACK_RENEW;
1836 }
1837
1838 static int
1839 _ecore_con_url_process_completed_jobs(Ecore_Con_Url *url_con_to_match)
1840 {
1841    Eina_List *l;
1842    Ecore_Con_Url *url_con;
1843    Ecore_Con_Event_Url_Complete *e;
1844    CURLMsg *curlmsg;
1845    CURLMcode ret;
1846    int n_remaining;
1847    int job_matched = 0;
1848
1849    /* Loop jobs and check if any are done */
1850    while ((curlmsg = curl_multi_info_read(_curlm, &n_remaining)))
1851      {
1852         if (curlmsg->msg != CURLMSG_DONE)
1853           continue;
1854
1855         /* find the job which is done */
1856         EINA_LIST_FOREACH(_url_con_list, l, url_con)
1857           {
1858              if (curlmsg->easy_handle == url_con->curl_easy)
1859                {
1860                   if (url_con_to_match &&
1861                       (url_con == url_con_to_match))
1862                     job_matched = 1;
1863
1864                   if(url_con->fd != -1)
1865                     {
1866                        FD_CLR(url_con->fd, &_current_fd_set);
1867                        if (url_con->fd_handler)
1868                          ecore_main_fd_handler_del(
1869                            url_con->fd_handler);
1870
1871                        url_con->fd = -1;
1872                        url_con->fd_handler = NULL;
1873                     }
1874
1875                   _url_con_list = eina_list_remove(_url_con_list, url_con);
1876                   url_con->active = EINA_FALSE;
1877                   e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
1878                   if (e)
1879                     {
1880                        e->url_con = url_con;
1881                        e->status = 0;
1882                        if (curlmsg->data.result == CURLE_OK)
1883                          {
1884                             long status; /* curl API uses long, not int */
1885
1886                             status = 0;
1887                             curl_easy_getinfo(curlmsg->easy_handle,
1888                                               CURLINFO_RESPONSE_CODE,
1889                                               &status);
1890                             e->status = status;
1891                          }
1892
1893                        _url_complete_push_event(ECORE_CON_EVENT_URL_COMPLETE, e);
1894                     }
1895
1896                   ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
1897                   if (ret != CURLM_OK)
1898                     ERR("curl_multi_remove_handle failed: %s",
1899                         curl_multi_strerror(ret));
1900
1901                   break;
1902                }
1903           }
1904      }
1905
1906    return job_matched;
1907 }
1908
1909 static void
1910 _ecore_con_event_url_free(void *data __UNUSED__,
1911                           void      *ev)
1912 {
1913    free(ev);
1914 }
1915
1916 #endif