2 * For info on how to use libcurl, see:
3 * http://curl.haxx.se/libcurl/c/libcurl-tutorial.html
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_send(...);
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.
20 * Example Usage 1 (HTTP GET):
21 * ecore_con_url_url_set(url_con, "http://www.google.com");
22 * ecore_con_url_send(url_con, NULL, 0, NULL);
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_send(url_con, data, data_length, "multipart/form-data");
28 * Example Usage 3 (FTP download):
29 * ecore_con_url_url_set(url_con, "ftp://ftp.example.com/pub/myfile");
30 * ecore_con_url_send(url_con, NULL, 0, NULL);
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);
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");
40 * FIXME: Support more CURL features: Authentication, Progress callbacks and more...
50 #include <sys/types.h>
53 #ifdef HAVE_WS2TCPIP_H
54 # include <ws2tcpip.h>
58 #include "ecore_private.h"
59 #include "Ecore_Con.h"
60 #include "ecore_con_private.h"
62 int ECORE_CON_EVENT_URL_DATA = 0;
63 int ECORE_CON_EVENT_URL_COMPLETE = 0;
64 int ECORE_CON_EVENT_URL_PROGRESS = 0;
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, size_t size, size_t nitems,
72 static size_t _ecore_con_url_data_cb(void *buffer,
76 static int _ecore_con_url_progress_cb(void *clientp, double dltotal,
77 double dlnow, double ultotal,
79 static size_t _ecore_con_url_read_cb(void *ptr, size_t size, size_t nitems,
81 static void _ecore_con_event_url_free(void *data __UNUSED__, void *ev);
82 static int _ecore_con_url_process_completed_jobs(
83 Ecore_Con_Url *url_con_to_match);
84 static Eina_Bool _ecore_con_url_idler_handler(void *data);
86 static Ecore_Idler *_fd_idler_handler = NULL;
87 static Eina_List *_url_con_list = NULL;
88 static CURLM *_curlm = NULL;
89 static fd_set _current_fd_set;
90 static int _init_count = 0;
91 static Ecore_Timer *_curl_timeout = NULL;
93 typedef struct _Ecore_Con_Url_Event Ecore_Con_Url_Event;
94 struct _Ecore_Con_Url_Event
101 _url_complete_idler_cb(void *data)
103 Ecore_Con_Url_Event *lev;
106 ecore_event_add(lev->type, lev->ev, _ecore_con_event_url_free, NULL);
109 return ECORE_CALLBACK_CANCEL;
113 _url_complete_push_event(int type, void *ev)
115 Ecore_Con_Url_Event *lev;
117 lev = malloc(sizeof(Ecore_Con_Url_Event));
121 ecore_idler_add(_url_complete_idler_cb, lev);
127 * @addtogroup Ecore_Con_Url_Group Ecore URL Connection Functions
129 * Utility functions that set up, use and shut down the Ecore URL
130 * Connection library.
132 * @todo write detailed description of Ecore_Con_Url
138 * Initialises the Ecore_Con_Url library.
139 * @return Number of times the library has been initialised without being
143 ecore_con_url_init(void)
151 if (!ECORE_CON_EVENT_URL_DATA)
153 ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
154 ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
155 ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
162 FD_ZERO(&_current_fd_set);
163 if (curl_global_init(CURL_GLOBAL_NOTHING))
165 while (_url_con_list)
166 ecore_con_url_free(eina_list_data_get(_url_con_list));
170 _curlm = curl_multi_init();
173 while (_url_con_list)
174 ecore_con_url_free(eina_list_data_get(_url_con_list));
180 curl_multi_timeout(_curlm, &ms);
185 ecore_timer_add((double)ms / 1000, _ecore_con_url_idler_handler,
187 ecore_timer_freeze(_curl_timeout);
197 * Shuts down the Ecore_Con_Url library.
198 * @return Number of calls that still uses Ecore_Con_Url
201 ecore_con_url_shutdown(void)
209 if (_init_count != 0)
212 if (_fd_idler_handler)
213 ecore_idler_del(_fd_idler_handler);
215 _fd_idler_handler = NULL;
218 ecore_timer_del(_curl_timeout);
220 _curl_timeout = NULL;
222 while (_url_con_list)
223 ecore_con_url_free(eina_list_data_get(_url_con_list));
227 curl_multi_cleanup(_curlm);
231 curl_global_cleanup();
237 * Creates and initializes a new Ecore_Con_Url connection object.
239 * Creates and initializes a new Ecore_Con_Url connection object that can be
240 * uesd for sending requests.
242 * @param url URL that will receive requests. Can be changed using
243 * ecore_con_url_url_set.
245 * @return NULL on error, a new Ecore_Con_Url on success.
248 * @see ecore_con_url_custom_new()
249 * @see ecore_con_url_url_set()
252 ecore_con_url_new(const char *url)
255 Ecore_Con_Url *url_con;
261 url_con = calloc(1, sizeof(Ecore_Con_Url));
266 url_con->write_fd = -1;
268 url_con->curl_easy = curl_easy_init();
269 if (!url_con->curl_easy)
275 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
277 if (!ecore_con_url_url_set(url_con, url))
279 ecore_con_url_free(url_con);
283 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_ENCODING, "gzip,deflate");
286 ERR("Could not set CURLOPT_ENCODING to \"gzip,deflate\": %s",
287 curl_easy_strerror(ret));
288 ecore_con_url_free(url_con);
292 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEFUNCTION,
293 _ecore_con_url_data_cb);
294 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEDATA, url_con);
296 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION,
297 _ecore_con_url_progress_cb);
298 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSDATA, url_con);
299 curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_FALSE);
301 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERFUNCTION,
302 _ecore_con_url_header_cb);
303 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERDATA, url_con);
306 * FIXME: Check that these timeouts are sensible defaults
307 * FIXME: Provide a means to change these timeouts
309 curl_easy_setopt(url_con->curl_easy, CURLOPT_CONNECTTIMEOUT, 30);
310 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEOUT, 300);
311 curl_easy_setopt(url_con->curl_easy, CURLOPT_FOLLOWLOCATION, 1);
321 * Creates a custom connection object.
323 * Creates and initializes a new Ecore_Con_Url for a custom request (e.g. HEAD,
324 * SUBSCRIBE and other obscure HTTP requests). This object should be used like
325 * one created with ecore_con_url_new().
327 * @param url URL that will receive requests
328 * @param custom_request Custom request (e.g. GET, POST, HEAD, PUT, etc)
330 * @return NULL on error, a new Ecore_Con_Url on success.
333 * @see ecore_con_url_new()
334 * @see ecore_con_url_url_set()
337 ecore_con_url_custom_new(const char *url, const char *custom_request)
340 Ecore_Con_Url *url_con;
349 url_con = ecore_con_url_new(url);
354 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_CUSTOMREQUEST, custom_request);
357 ERR("Could not set a custom request string: %s",
358 curl_easy_strerror(ret));
359 ecore_con_url_free(url_con);
367 custom_request = NULL;
372 * Destroys a Ecore_Con_Url connection object.
374 * @param url_con Connection object to free.
376 * @see ecore_con_url_new()
379 ecore_con_url_free(Ecore_Con_Url *url_con)
388 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
390 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_free");
394 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
395 if(url_con->fd != -1)
397 FD_CLR(url_con->fd, &_current_fd_set);
398 if (url_con->fd_handler)
399 ecore_main_fd_handler_del(url_con->fd_handler);
402 url_con->fd_handler = NULL;
405 if (url_con->curl_easy)
407 // FIXME: For an unknown reason, progress continue to arrive after destruction
408 // this prevent any further call to the callback.
409 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION, NULL);
413 url_con->active = EINA_FALSE;
415 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
417 ERR("curl_multi_remove_handle failed: %s",
418 curl_multi_strerror(ret));
421 curl_easy_cleanup(url_con->curl_easy);
424 _url_con_list = eina_list_remove(_url_con_list, url_con);
425 curl_slist_free_all(url_con->headers);
426 EINA_LIST_FREE(url_con->additional_headers, s)
428 EINA_LIST_FREE(url_con->response_headers, s)
439 * Sets the URL to send the request to.
441 * @param url_con Connection object through which the request will be sent.
442 * @param url URL that will receive the request
444 * @return EINA_TRUE on success, EINA_FALSE on error.
448 ecore_con_url_url_set(Ecore_Con_Url *url_con, const char *url)
451 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
453 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_url_set");
465 url_con->url = strdup(url);
468 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL,
471 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL, "");
482 * Associates data with a connection object.
484 * Associates data with a connection object, which can be retrieved later with
485 * ecore_con_url_data_get()).
487 * @param url_con Connection object to associate data.
488 * @param data Data to be set.
491 * @see ecore_con_url_data_get()
494 ecore_con_url_data_set(Ecore_Con_Url *url_con, void *data)
497 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
499 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_set");
503 url_con->data = data;
512 * Adds an additional header to the request connection object.
514 * Adds an additional header to the request connection object. This addition
515 * will be valid for only one ecore_con_url_send() call.
517 * @param url_con Connection object
518 * @param key Header key
519 * @param value Header value
522 * @see ecore_con_url_send()
523 * @see ecore_con_url_additional_headers_clear()
526 ecore_con_url_additional_header_add(Ecore_Con_Url *url_con, const char *key,
532 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
534 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
535 "ecore_con_url_additional_header_add");
539 tmp = malloc(strlen(key) + strlen(value) + 3);
543 sprintf(tmp, "%s: %s", key, value);
544 url_con->additional_headers = eina_list_append(url_con->additional_headers,
555 * Cleans additional headers.
557 * Cleans additional headers associated with a connection object (previously
558 * added with ecore_con_url_additional_header_add()).
560 * @param url_con Connection object to clean additional headers.
563 * @see ecore_con_url_additional_header_add()
564 * @see ecore_con_url_send()
567 ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
572 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
574 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
575 "ecore_con_url_additional_headers_clear");
579 EINA_LIST_FREE(url_con->additional_headers, s)
588 * Retrieves data associated with a Ecore_Con_Url connection object.
590 * Retrieves data associated with a Ecore_Con_Url connection object (previously
591 * set with ecore_con_url_data_set()).
593 * @param url_con Connection object to retrieve data from.
595 * @return Data associated with the given object.
598 * @see ecore_con_url_data_set()
601 ecore_con_url_data_get(Ecore_Con_Url *url_con)
604 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
606 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_get");
610 return url_con->data;
618 * Sets whether HTTP requests should be conditional, dependent on
621 * @param url_con Ecore_Con_Url to act upon.
622 * @param condition Condition to use for HTTP requests.
623 * @param timestamp Time since 1 Jan 1970 to use in the condition.
625 * @sa ecore_con_url_send()
628 ecore_con_url_time(Ecore_Con_Url *url_con, Ecore_Con_Url_Time condition,
632 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
634 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_time");
638 url_con->time_condition = condition;
639 url_con->timestamp = timestamp;
649 * Setup a file for receiving request data.
651 * Setups a file to have response data written into. Note that
652 * ECORE_CON_EVENT_URL_DATA events will not be emitted if a file has been set to
653 * receive the response data.
655 * @param url_con Connection object to set file
656 * @param fd File descriptor associated with the file
660 ecore_con_url_fd_set(Ecore_Con_Url *url_con, int fd)
663 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
665 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_set");
669 url_con->write_fd = fd;
674 * Retrieves the number of bytes received.
676 * Retrieves the number of bytes received on the last request of the given
679 * @param url_con Connection object which the request was sent on.
681 * @return Number of bytes received on request.
684 * @see ecore_con_url_send()
687 ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
690 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
692 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
693 "ecore_con_url_received_bytes_get");
697 return url_con->received;
704 * Retrieves headers from last request sent.
706 * Retrieves a list containing the response headers. This function should be
707 * used after an ECORE_CON_EVENT_URL_COMPLETE event (headers should normally be
708 * ready at that time).
710 * @param url_con Connection object to retrieve response headers from.
712 * @return List of response headers. This list must not be modified by the user.
715 EAPI const Eina_List *
716 ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
719 return url_con->response_headers;
726 * Sets url_con to use http auth, with given username and password, "safely" or not.
728 * @param url_con Connection object to perform a request on, previously created
729 * with ecore_con_url_new() or ecore_con_url_custom_new().
730 * @param username Username to use in authentication
731 * @param password Password to use in authentication
732 * @param safe Whether to use "safer" methods (eg, NOT http basic auth)
734 * @return #EINA_TRUE on success, #EINA_FALSE on error.
738 ecore_con_url_httpauth_set(Ecore_Con_Url *url_con, const char *username,
739 const char *password, Eina_Bool safe)
744 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
746 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
747 "ecore_con_url_httpauth_set");
751 # if LIBCURL_VERSION_NUM >= 0x071301
752 if ((username) && (password))
755 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH,
758 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
760 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
763 ERR("Could not set username for HTTP authentication: %s",
764 curl_easy_strerror(ret));
768 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
771 ERR("Could not set password for HTTP authentication: %s",
772 curl_easy_strerror(ret));
787 * @param url_con Connection object to perform a request on, previously created
788 * with ecore_con_url_new() or ecore_con_url_custom_new().
789 * @param data Payload (data sent on the request)
790 * @param length Payload length. If @c -1, rely on automatic length
791 * calculation via @c strlen() on @p data.
792 * @param content_type Content type of the payload (e.g. text/xml)
794 * @return #EINA_TRUE on success, #EINA_FALSE on error.
796 * @see ecore_con_url_custom_new()
797 * @see ecore_con_url_additional_headers_clear()
798 * @see ecore_con_url_additional_header_add()
799 * @see ecore_con_url_data_set()
800 * @see ecore_con_url_data_get()
801 * @see ecore_con_url_response_headers_get()
802 * @see ecore_con_url_time()
805 ecore_con_url_send(Ecore_Con_Url *url_con, const void *data, long length,
806 const char *content_type)
813 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
815 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_send");
825 /* Free response headers from previous send() calls */
826 EINA_LIST_FREE(url_con->response_headers, s)
828 url_con->response_headers = NULL;
830 curl_slist_free_all(url_con->headers);
831 url_con->headers = NULL;
835 if ((content_type) && (strlen(content_type) < 200))
837 snprintf(tmp, sizeof(tmp), "Content-Type: %s", content_type);
838 url_con->headers = curl_slist_append(url_con->headers, tmp);
841 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDS, data);
842 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, length);
845 switch (url_con->time_condition)
847 case ECORE_CON_URL_TIME_NONE:
848 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
852 case ECORE_CON_URL_TIME_IFMODSINCE:
853 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
854 CURL_TIMECOND_IFMODSINCE);
855 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE,
856 (long)url_con->timestamp);
859 case ECORE_CON_URL_TIME_IFUNMODSINCE:
860 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
861 CURL_TIMECOND_IFUNMODSINCE);
862 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE,
863 (long)url_con->timestamp);
867 /* Additional headers */
868 EINA_LIST_FOREACH(url_con->additional_headers, l, s)
869 url_con->headers = curl_slist_append(url_con->headers, s);
871 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPHEADER, url_con->headers);
873 url_con->received = 0;
875 return _ecore_con_url_perform(url_con);
886 * @brief Uploads a file to an ftp site.
887 * @param url_con The Ecore_Con_Url object to send with
888 * @param filename The path to the file to send
889 * @param user The username to log in with
890 * @param pass The password to log in with
891 * @param upload_dir The directory to which the file should be uploaded
892 * @return #EINA_TRUE on success, else #EINA_FALSE.
893 * Upload @p filename to an ftp server set in @p url_con using @p user
894 * and @p pass to directory @p upload_dir
897 ecore_con_url_ftp_upload(Ecore_Con_Url *url_con, const char *filename,
898 const char *user, const char *pass,
899 const char *upload_dir)
905 struct stat file_info;
908 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
910 ECORE_MAGIC_FAIL(url_con,
912 "ecore_con_url_ftp_upload");
924 if (stat(filename, &file_info))
927 snprintf(userpwd, sizeof(userpwd), "%s:%s", user, pass);
928 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERPWD, userpwd);
931 ERR("Could not set username and password for FTP upload: %s",
932 curl_easy_strerror(ret));
937 snprintf(tmp, PATH_MAX, "%s", filename);
940 snprintf(url, sizeof(url), "ftp://%s/%s/%s", url_con->url,
941 upload_dir, basename(tmp));
943 snprintf(url, sizeof(url), "ftp://%s/%s", url_con->url,
946 if (!ecore_con_url_url_set(url_con, url))
949 curl_easy_setopt(url_con->curl_easy, CURLOPT_INFILESIZE_LARGE,
950 (curl_off_t)file_info.st_size);
951 curl_easy_setopt(url_con->curl_easy, CURLOPT_UPLOAD, 1);
952 curl_easy_setopt(url_con->curl_easy, CURLOPT_READFUNCTION,
953 _ecore_con_url_read_cb);
955 fd = fopen(filename, "rb");
958 ERR("Could not open \"%s\" for FTP upload", filename);
961 curl_easy_setopt(url_con->curl_easy, CURLOPT_READDATA, fd);
963 return _ecore_con_url_perform(url_con);
978 * Toggle libcurl's verbose output.
980 * If @p verbose is @c EINA_TRUE, libcurl will output a lot of verbose
981 * information about its operations, which is useful for
982 * debugging. The verbose information will be sent to stderr.
984 * @param url_con Ecore_Con_Url instance which will be acted upon.
985 * @param verbose Whether or not to enable libcurl's verbose output.
988 ecore_con_url_verbose_set(Ecore_Con_Url *url_con, Eina_Bool verbose)
991 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
993 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
994 "ecore_con_url_verbose_set");
1004 curl_easy_setopt(url_con->curl_easy, CURLOPT_VERBOSE, (int)verbose);
1012 * Enable or disable EPSV extension
1013 * @return FIXME: To be more documented.
1016 ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con, Eina_Bool use_epsv)
1019 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1021 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1022 "ecore_con_url_ftp_use_epsv_set");
1026 if (url_con->active)
1032 curl_easy_setopt(url_con->curl_easy, CURLOPT_FTP_USE_EPSV, (int)use_epsv);
1044 _ecore_con_url_suspend_fd_handler(void)
1047 Ecore_Con_Url *url_con;
1053 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1055 if (url_con->active && url_con->fd_handler)
1057 ecore_main_fd_handler_del(url_con->fd_handler);
1058 url_con->fd_handler = NULL;
1067 _ecore_con_url_restart_fd_handler(void)
1070 Ecore_Con_Url *url_con;
1076 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1078 if (!url_con->fd_handler && url_con->fd != -1)
1080 url_con->fd_handler =
1081 ecore_main_fd_handler_add(url_con->fd, url_con->flags,
1082 _ecore_con_url_fd_handler,
1092 _ecore_con_url_data_cb(void *buffer, size_t size, size_t nitems, void *userp)
1094 Ecore_Con_Url *url_con;
1095 Ecore_Con_Event_Url_Data *e;
1096 size_t real_size = size * nitems;
1098 url_con = (Ecore_Con_Url *)userp;
1103 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1105 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_cb");
1109 url_con->received += real_size;
1111 if (url_con->write_fd < 0)
1114 malloc(sizeof(Ecore_Con_Event_Url_Data) + sizeof(unsigned char) *
1118 e->url_con = url_con;
1119 e->size = real_size;
1120 memcpy(e->data, buffer, real_size);
1121 ecore_event_add(ECORE_CON_EVENT_URL_DATA, e,
1122 _ecore_con_event_url_free, NULL);
1128 size_t total_size = real_size;
1131 while (total_size > 0)
1133 count = write(url_con->write_fd,
1134 (char *)buffer + offset,
1138 if (errno != EAGAIN && errno != EINTR)
1143 total_size -= count;
1152 #define ECORE_CON_URL_TRANSMISSION(Transmit, Event, Url_con, Total, Now) \
1154 Ecore_Con_Event_Url_Progress *e; \
1155 if ((Total != 0) || (Now != 0)) \
1157 e = calloc(1, sizeof(Ecore_Con_Event_Url_Progress)); \
1160 e->url_con = url_con; \
1163 ecore_event_add(Event, e, _ecore_con_event_url_free, NULL); \
1169 _ecore_con_url_header_cb(void *ptr, size_t size, size_t nitems, void *stream)
1171 size_t real_size = size * nitems;
1172 Ecore_Con_Url *url_con = stream;
1174 char *header = malloc(sizeof(char) * (real_size + 1));
1178 memcpy(header, ptr, real_size);
1179 header[real_size] = '\0';
1181 url_con->response_headers = eina_list_append(url_con->response_headers,
1188 _ecore_con_url_progress_cb(void *clientp, double dltotal, double dlnow,
1192 Ecore_Con_Event_Url_Progress *e;
1193 Ecore_Con_Url *url_con;
1197 e = malloc(sizeof(Ecore_Con_Event_Url_Progress));
1200 e->url_con = url_con;
1201 e->down.total = dltotal;
1202 e->down.now = dlnow;
1203 e->up.total = ultotal;
1205 ecore_event_add(ECORE_CON_EVENT_URL_PROGRESS, e,
1206 _ecore_con_event_url_free, NULL);
1213 _ecore_con_url_read_cb(void *ptr, size_t size, size_t nitems, void *stream)
1215 size_t retcode = fread(ptr, size, nitems, stream);
1217 if (ferror((FILE *)stream))
1220 return CURL_READFUNC_ABORT;
1222 else if ((retcode == 0) || (retcode < nitems))
1224 fclose((FILE *)stream);
1228 INF("*** We read %zu bytes from file", retcode);
1233 _ecore_con_url_perform(Ecore_Con_Url *url_con)
1235 fd_set read_set, write_set, exc_set;
1237 int flags, still_running;
1238 int completed_immediately = 0;
1241 _url_con_list = eina_list_append(_url_con_list, url_con);
1243 url_con->active = EINA_TRUE;
1244 curl_multi_add_handle(_curlm, url_con->curl_easy);
1245 /* This one can't be stopped, or the download never start. */
1246 while (curl_multi_perform(_curlm, &still_running) == CURLM_CALL_MULTI_PERFORM) ;
1248 completed_immediately = _ecore_con_url_process_completed_jobs(url_con);
1250 if (!completed_immediately)
1252 if (url_con->fd_handler)
1253 ecore_main_fd_handler_del(url_con->fd_handler);
1255 url_con->fd_handler = NULL;
1257 /* url_con still active -- set up an fd_handler */
1259 FD_ZERO(&write_set);
1262 /* Stupid curl, why can't I get the fd to the current added job? */
1263 ret = curl_multi_fdset(_curlm, &read_set, &write_set, &exc_set,
1265 if (ret != CURLM_OK)
1267 ERR("curl_multi_fdset failed: %s", curl_multi_strerror(ret));
1271 for (fd = 0; fd <= fd_max; fd++)
1273 if (!FD_ISSET(fd, &_current_fd_set))
1276 if (FD_ISSET(fd, &read_set))
1277 flags |= ECORE_FD_READ;
1279 if (FD_ISSET(fd, &write_set))
1280 flags |= ECORE_FD_WRITE;
1282 if (FD_ISSET(fd, &exc_set))
1283 flags |= ECORE_FD_ERROR;
1289 ret = curl_multi_timeout(_curlm, &ms);
1290 if (ret != CURLM_OK)
1291 ERR("curl_multi_timeout failed: %s",
1292 curl_multi_strerror(ret));
1297 FD_SET(fd, &_current_fd_set);
1299 url_con->flags = flags;
1300 url_con->fd_handler =
1301 ecore_main_fd_handler_add(fd, flags,
1302 _ecore_con_url_fd_handler,
1308 if (!url_con->fd_handler)
1310 /* Failed to set up an fd_handler */
1311 ecore_timer_freeze(_curl_timeout);
1313 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
1314 if (ret != CURLM_OK)
1315 ERR("curl_multi_remove_handle failed: %s",
1316 curl_multi_strerror(ret));
1318 url_con->active = EINA_FALSE;
1323 ecore_timer_thaw(_curl_timeout);
1330 _ecore_con_url_idler_handler(void *data)
1333 int done = 1, still_running;
1335 start = ecore_time_get();
1336 while (curl_multi_perform(_curlm, &still_running) == CURLM_CALL_MULTI_PERFORM)
1337 /* make this not more than a frametime to keep interactivity high */
1338 if ((ecore_time_get() - start) > ecore_animator_frametime_get())
1344 _ecore_con_url_process_completed_jobs(NULL);
1348 _ecore_con_url_restart_fd_handler();
1349 _fd_idler_handler = NULL;
1352 ecore_timer_freeze(_curl_timeout);
1355 (void *)0xACE ? ECORE_CALLBACK_RENEW : ECORE_CALLBACK_CANCEL;
1358 return ECORE_CALLBACK_RENEW;
1362 _ecore_con_url_fd_handler(void *data __UNUSED__,
1363 Ecore_Fd_Handler *fd_handler __UNUSED__)
1365 _ecore_con_url_suspend_fd_handler();
1367 if (!_fd_idler_handler)
1368 _fd_idler_handler = ecore_idler_add(
1369 _ecore_con_url_idler_handler, NULL);
1371 return ECORE_CALLBACK_RENEW;
1375 _ecore_con_url_process_completed_jobs(Ecore_Con_Url *url_con_to_match)
1378 Ecore_Con_Url *url_con;
1379 Ecore_Con_Event_Url_Complete *e;
1383 int job_matched = 0;
1385 /* Loop jobs and check if any are done */
1386 while ((curlmsg = curl_multi_info_read(_curlm, &n_remaining)))
1388 if (curlmsg->msg != CURLMSG_DONE)
1391 /* find the job which is done */
1392 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1394 if (curlmsg->easy_handle == url_con->curl_easy)
1396 if (url_con_to_match &&
1397 (url_con == url_con_to_match))
1400 if(url_con->fd != -1)
1402 FD_CLR(url_con->fd, &_current_fd_set);
1403 if (url_con->fd_handler)
1404 ecore_main_fd_handler_del(
1405 url_con->fd_handler);
1408 url_con->fd_handler = NULL;
1411 _url_con_list = eina_list_remove(_url_con_list, url_con);
1412 url_con->active = EINA_FALSE;
1413 e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
1416 e->url_con = url_con;
1418 if (curlmsg->data.result == CURLE_OK)
1420 long status; /* curl API uses long, not int */
1423 curl_easy_getinfo(curlmsg->easy_handle,
1424 CURLINFO_RESPONSE_CODE,
1429 _url_complete_push_event(ECORE_CON_EVENT_URL_COMPLETE, e);
1432 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
1433 if (ret != CURLM_OK)
1434 ERR("curl_multi_remove_handle failed: %s",
1435 curl_multi_strerror(ret));
1446 _ecore_con_event_url_free(void *data __UNUSED__, void *ev)