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"
63 * @defgroup Ecore_Con_Url_Group Ecore URL Connection Functions
65 * Utility functions that set up, use and shut down the Ecore URL
67 * FIXME: write detailed description
70 int ECORE_CON_EVENT_URL_DATA = 0;
71 int ECORE_CON_EVENT_URL_COMPLETE = 0;
72 int ECORE_CON_EVENT_URL_PROGRESS = 0;
75 static Eina_Bool _ecore_con_url_fd_handler(void *data,
76 Ecore_Fd_Handler *fd_handler);
77 static int _ecore_con_url_perform(Ecore_Con_Url *url_con);
78 static size_t _ecore_con_url_header_cb(void *ptr, size_t size, size_t nitems,
80 static size_t _ecore_con_url_data_cb(void *buffer,
84 static int _ecore_con_url_progress_cb(void *clientp, double dltotal,
85 double dlnow, double ultotal,
87 static size_t _ecore_con_url_read_cb(void *ptr, size_t size, size_t nitems,
89 static void _ecore_con_event_url_free(void *data __UNUSED__, void *ev);
90 static int _ecore_con_url_process_completed_jobs(
91 Ecore_Con_Url *url_con_to_match);
92 static Eina_Bool _ecore_con_url_idler_handler(void *data __UNUSED__);
94 static Ecore_Idler *_fd_idler_handler = NULL;
95 static Eina_List *_url_con_list = NULL;
96 static CURLM *curlm = NULL;
97 static fd_set _current_fd_set;
98 static int init_count = 0;
99 static Ecore_Timer *_curl_timeout = NULL;
101 typedef struct _Ecore_Con_Url_Event Ecore_Con_Url_Event;
102 struct _Ecore_Con_Url_Event
109 _url_complete_idler_cb(void *data)
111 Ecore_Con_Url_Event *lev;
114 ecore_event_add(lev->type, lev->ev, _ecore_con_event_url_free, NULL);
117 return ECORE_CALLBACK_CANCEL;
121 _url_complete_push_event(int type, void *ev)
123 Ecore_Con_Url_Event *lev;
125 lev = malloc(sizeof(Ecore_Con_Url_Event));
129 ecore_idler_add(_url_complete_idler_cb, lev);
135 * Initialises the Ecore_Con_Url library.
136 * @return Number of times the library has been initialised without being
138 * @ingroup Ecore_Con_Url_Group
141 ecore_con_url_init(void)
149 if (!ECORE_CON_EVENT_URL_DATA)
151 ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
152 ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
153 ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
160 FD_ZERO(&_current_fd_set);
161 if (curl_global_init(CURL_GLOBAL_NOTHING))
163 while (_url_con_list)
164 ecore_con_url_destroy(eina_list_data_get(_url_con_list));
168 curlm = curl_multi_init();
171 while (_url_con_list)
172 ecore_con_url_destroy(eina_list_data_get(_url_con_list));
178 curl_multi_timeout(curlm, &ms);
183 ecore_timer_add((double)ms / 1000, _ecore_con_url_idler_handler,
185 ecore_timer_freeze(_curl_timeout);
195 * Shuts down the Ecore_Con_Url library.
196 * @return Number of calls that still uses Ecore_Con_Url
197 * @ingroup Ecore_Con_Url_Group
200 ecore_con_url_shutdown(void)
211 if (_fd_idler_handler)
212 ecore_idler_del(_fd_idler_handler);
214 _fd_idler_handler = NULL;
217 ecore_timer_del(_curl_timeout);
219 _curl_timeout = NULL;
221 while (_url_con_list)
222 ecore_con_url_destroy(eina_list_data_get(_url_con_list));
226 curl_multi_cleanup(curlm);
230 curl_global_cleanup();
236 * Creates and initializes a new Ecore_Con_Url connection object.
238 * Creates and initializes a new Ecore_Con_Url connection object that can be
239 * uesd for sending requests.
241 * @param url URL that will receive requests. Can be changed using
242 * ecore_con_url_url_set.
244 * @return NULL on error, a new Ecore_Con_Url on success.
246 * @ingroup Ecore_Con_Url_Group
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;
260 url_con = calloc(1, sizeof(Ecore_Con_Url));
264 url_con->curl_easy = curl_easy_init();
265 if (!url_con->curl_easy)
271 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
273 ecore_con_url_url_set(url_con, url);
275 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEFUNCTION,
276 _ecore_con_url_data_cb);
277 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEDATA, url_con);
279 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION,
280 _ecore_con_url_progress_cb);
281 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSDATA, url_con);
282 curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_FALSE);
284 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERFUNCTION,
285 _ecore_con_url_header_cb);
286 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERDATA, url_con);
289 * FIXME: Check that these timeouts are sensible defaults
290 * FIXME: Provide a means to change these timeouts
292 curl_easy_setopt(url_con->curl_easy, CURLOPT_CONNECTTIMEOUT, 30);
293 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEOUT, 300);
294 curl_easy_setopt(url_con->curl_easy, CURLOPT_FOLLOWLOCATION, 1);
296 curl_easy_setopt(url_con->curl_easy, CURLOPT_ENCODING, "gzip,deflate");
299 url_con->write_fd = -1;
300 url_con->additional_headers = NULL;
301 url_con->response_headers = NULL;
311 * Creates a custom connection object.
313 * Creates and initializes a new Ecore_Con_Url for a custom request (e.g. HEAD,
314 * SUBSCRIBE and other obscure HTTP requests). This object should be used like
315 * one created with ecore_con_url_new().
317 * @param url URL that will receive requests
318 * @param custom_request Custom request (e.g. GET, POST, HEAD, PUT, etc)
320 * @return NULL on error, a new Ecore_Con_Url on success.
322 * @ingroup Ecore_Con_Url_Group
324 * @see ecore_con_url_new()
325 * @see ecore_con_url_url_set()
328 ecore_con_url_custom_new(const char *url, const char *custom_request)
331 Ecore_Con_Url *url_con;
339 url_con = ecore_con_url_new(url);
344 curl_easy_setopt(url_con->curl_easy, CURLOPT_CUSTOMREQUEST, custom_request);
350 custom_request = NULL;
355 * Destroys a Ecore_Con_Url connection object.
357 * @ingroup Ecore_Con_Url_Group
359 * @see ecore_con_url_new()
362 ecore_con_url_destroy(Ecore_Con_Url *url_con)
370 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
372 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_destroy");
376 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
377 if(url_con->fd != -1)
379 FD_CLR(url_con->fd, &_current_fd_set);
380 if (url_con->fd_handler)
381 ecore_main_fd_handler_del(url_con->fd_handler);
384 url_con->fd_handler = NULL;
388 curl_formfree(url_con->post);
390 url_con->post = NULL;
392 if (url_con->curl_easy)
394 // FIXME: For an unknown reason, progress continue to arrive after destruction
395 // this prevent any further call to the callback.
396 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION, NULL);
402 curl_multi_remove_handle(curlm, url_con->curl_easy);
405 curl_easy_cleanup(url_con->curl_easy);
408 _url_con_list = eina_list_remove(_url_con_list, url_con);
409 curl_slist_free_all(url_con->headers);
410 EINA_LIST_FREE(url_con->additional_headers, s)
412 EINA_LIST_FREE(url_con->response_headers, s)
423 * Sets the URL to send the request to.
425 * @param url_con Connection object through which the request will be sent.
426 * @param url URL that will receive the request
428 * @return 1 on success, 0 on error.
430 * @ingroup Ecore_Con_Url_Group
433 ecore_con_url_url_set(Ecore_Con_Url *url_con, const char *url)
436 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
438 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_url_set");
450 url_con->url = strdup(url);
453 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL,
456 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL, "");
467 * Associates data with a connection object.
469 * Associates data with a connection object, which can be retrieved later with
470 * ecore_con_url_data_get()).
472 * @param url_con Connection object to associate data.
473 * @param data Data to be set.
475 * @ingroup Ecore_Con_Url_Group
477 * @see ecore_con_url_data_get()
480 ecore_con_url_data_set(Ecore_Con_Url *url_con, void *data)
483 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
485 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_set");
489 url_con->data = data;
498 * Adds an additional header to the request connection object.
500 * Adds an additional header to the request connection object. This addition
501 * will be valid for only one ecore_con_url_send() call.
503 * @param url_con Connection object
504 * @param key Header key
505 * @param value Header value
507 * @ingroup Ecore_Con_Url_Group
509 * @see ecore_con_url_send()
510 * @see ecore_con_url_additional_headers_clear()
513 ecore_con_url_additional_header_add(Ecore_Con_Url *url_con, const char *key,
519 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
521 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
522 "ecore_con_url_additional_header_add");
526 tmp = malloc(strlen(key) + strlen(value) + 3);
530 sprintf(tmp, "%s: %s", key, value);
531 url_con->additional_headers = eina_list_append(url_con->additional_headers,
542 * Cleans additional headers.
544 * Cleans additional headers associated with a connection object (previously
545 * added with ecore_con_url_additional_header_add()).
547 * @param url_con Connection object to clean additional headers.
549 * @ingroup Ecore_Con_Url_Group
551 * @see ecore_con_url_additional_header_add()
552 * @see ecore_con_url_send()
555 ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
560 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
562 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
563 "ecore_con_url_additional_headers_clear");
567 EINA_LIST_FREE(url_con->additional_headers, s)
576 * Retrieves data associated with a Ecore_Con_Url connection object.
578 * Retrieves data associated with a Ecore_Con_Url connection object (previously
579 * set with ecore_con_url_data_set()).
581 * @param Connection object to retrieve data from.
583 * @return Data associated with the given object.
585 * @ingroup Ecore_Con_Url_Group
587 * @see ecore_con_url_data_set()
590 ecore_con_url_data_get(Ecore_Con_Url *url_con)
593 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
595 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_get");
599 return url_con->data;
608 * Sets the @ref Ecore_Con_Url object's condition/time members.
609 * @ingroup Ecore_Con_Url_Group
612 ecore_con_url_time(Ecore_Con_Url *url_con, Ecore_Con_Url_Time condition,
616 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
618 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_time");
622 url_con->condition = condition;
633 * Setup a file for receiving request data.
635 * Setups a file to have response data written into. Note that
636 * ECORE_CON_EVENT_URL_DATA events will not be emitted if a file has been set to
637 * receive the response data.
639 * @param url_con Connection object to set file
640 * @param fd File descriptor associated with the file
642 * @ingroup Ecore_Con_Url_Group
645 ecore_con_url_fd_set(Ecore_Con_Url *url_con, int fd)
648 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
650 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_set");
654 url_con->write_fd = fd;
659 * Retrieves the number of bytes received.
661 * Retrieves the number of bytes received on the last request of the given
664 * @param url_con Connection object which the request was sent on.
666 * @return Number of bytes received on request.
668 * @ingroup Ecore_Con_Url_Group
670 * @see ecore_con_url_send()
673 ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
676 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
678 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
679 "ecore_con_url_received_bytes_get");
683 return url_con->received;
690 * Retrieves headers from last request sent.
692 * Retrieves a list containing the response headers. This function should be
693 * used after an ECORE_CON_EVENT_URL_COMPLETE event (headers should normally be
694 * ready at that time).
696 * @param url_con Connection object to retrieve response headers from.
698 * @return List of response headers. This list must not be modified by the user.
700 * @ingroup Ecore_Con_Url_Group
702 EAPI const Eina_List *
703 ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
706 return url_con->response_headers;
713 * Sets url_con to use http auth, with given username and password, "safely" or not.
715 * @param url_con Connection object to perform a request on, previously created
716 * with ecore_con_url_new() or ecore_con_url_custom_new().
717 * @param username Username to use in authentication
718 * @param password Password to use in authentication
719 * @param safe Whether to use "safer" methods (eg, NOT http basic auth)
721 * @return 1 on success, 0 on error.
723 * @ingroup Ecore_Con_Url_Group
726 ecore_con_url_httpauth_set(Ecore_Con_Url *url_con, const char *username,
727 const char *password,
731 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
733 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
734 "ecore_con_url_httpauth_set");
738 # if LIBCURL_VERSION_NUM >= 0x071301
739 if ((username) && (password))
742 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH,
745 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
747 curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
748 curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
760 * @param url_con Connection object to perform a request on, previously created
761 * with ecore_con_url_new() or ecore_con_url_custom_new().
762 * @param data Payload (data sent on the request)
763 * @param length Payload length
764 * @param content_type Content type of the payload (e.g. text/xml)
766 * @return 1 on success, 0 on error.
768 * @ingroup Ecore_Con_Url_Group
770 * @see ecore_con_url_custom_new()
771 * @see ecore_con_url_additional_headers_clear()
772 * @see ecore_con_url_additional_header_add()
773 * @see ecore_con_url_data_set()
774 * @see ecore_con_url_data_get()
775 * @see ecore_con_url_response_headers_get()
778 ecore_con_url_send(Ecore_Con_Url *url_con, const void *data, size_t length,
779 const char *content_type)
786 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
788 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_send");
798 /* Free response headers from previous send() calls */
799 EINA_LIST_FREE(url_con->response_headers, s) free((char *)s);
800 url_con->response_headers = NULL;
802 curl_slist_free_all(url_con->headers);
803 url_con->headers = NULL;
807 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDS, data);
808 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, length);
810 if (content_type && (strlen(content_type) < 200))
812 sprintf(tmp, "Content-type: %s", content_type);
813 url_con->headers = curl_slist_append(url_con->headers, tmp);
816 sprintf(tmp, "Content-length: %zu", length);
817 url_con->headers = curl_slist_append(url_con->headers, tmp);
820 switch (url_con->condition)
822 case ECORE_CON_URL_TIME_NONE:
823 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
827 case ECORE_CON_URL_TIME_IFMODSINCE:
828 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
829 CURL_TIMECOND_IFMODSINCE);
830 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE, url_con->time);
833 case ECORE_CON_URL_TIME_IFUNMODSINCE:
834 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
835 CURL_TIMECOND_IFUNMODSINCE);
836 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE, url_con->time);
839 case ECORE_CON_URL_TIME_LASTMOD:
840 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
841 CURL_TIMECOND_LASTMOD);
842 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE, url_con->time);
846 /* Additional headers */
847 EINA_LIST_FOREACH(url_con->additional_headers, l, s)
848 url_con->headers = curl_slist_append(url_con->headers, s);
850 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPHEADER, url_con->headers);
852 url_con->received = 0;
854 int res = _ecore_con_url_perform(url_con);
868 * @return FIXME: To be more documented.
869 * @ingroup Ecore_Con_Url_Group
872 ecore_con_url_ftp_upload(Ecore_Con_Url *url_con, const char *filename,
873 const char *user, const char *pass,
874 const char *upload_dir)
880 struct stat file_info;
882 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
884 ECORE_MAGIC_FAIL(url_con,
886 "ecore_con_url_ftp_upload");
900 snprintf(tmp, PATH_MAX, "%s", filename);
902 if (stat(filename, &file_info))
905 fd = fopen(filename, "rb");
907 snprintf(url, sizeof(url), "ftp://%s/%s/%s", url_con->url,
908 upload_dir, basename(tmp));
910 snprintf(url, sizeof(url), "ftp://%s/%s", url_con->url,
913 snprintf(userpwd, sizeof(userpwd), "%s:%s", user, pass);
914 curl_easy_setopt(url_con->curl_easy, CURLOPT_INFILESIZE_LARGE,
915 (curl_off_t)file_info.st_size);
916 curl_easy_setopt(url_con->curl_easy, CURLOPT_USERPWD, userpwd);
917 curl_easy_setopt(url_con->curl_easy, CURLOPT_UPLOAD, 1);
918 curl_easy_setopt(url_con->curl_easy, CURLOPT_READFUNCTION,
919 _ecore_con_url_read_cb);
920 curl_easy_setopt(url_con->curl_easy, CURLOPT_READDATA, fd);
921 ecore_con_url_url_set(url_con, url);
923 return _ecore_con_url_perform(url_con);
939 * Send a Curl httppost
940 * @return 1 on success, 0 on error.
941 * @ingroup Ecore_Con_Url_Group
944 ecore_con_url_http_post_send(Ecore_Con_Url *url_con, void *httppost)
948 curl_formfree(url_con->post);
950 url_con->post = NULL;
952 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
954 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
955 "ecore_con_url_http_post_send");
959 url_con->post = httppost;
967 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPPOST, httppost);
969 return ecore_con_url_send(url_con, NULL, 0, NULL);
977 * Enable or disable libcurl verbose output, useful for debug
978 * @return FIXME: To be more documented.
979 * @ingroup Ecore_Con_Url_Group
982 ecore_con_url_verbose_set(Ecore_Con_Url *url_con, int verbose)
985 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
987 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
988 "ecore_con_url_verbose_set");
1000 curl_easy_setopt(url_con->curl_easy, CURLOPT_VERBOSE,
1003 curl_easy_setopt(url_con->curl_easy, CURLOPT_VERBOSE, 0);
1009 * Enable or disable EPSV extension
1010 * @return FIXME: To be more documented.
1011 * @ingroup Ecore_Con_Url_Group
1014 ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con, int use_epsv)
1017 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1019 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1020 "ecore_con_url_ftp_use_epsv_set");
1024 if (url_con->active)
1032 curl_easy_setopt(url_con->curl_easy, CURLOPT_FTP_USE_EPSV,
1035 curl_easy_setopt(url_con->curl_easy, CURLOPT_FTP_USE_EPSV, 0);
1042 _ecore_con_url_suspend_fd_handler(void)
1045 Ecore_Con_Url *url_con;
1051 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1053 if (url_con->active && url_con->fd_handler)
1055 ecore_main_fd_handler_del(url_con->fd_handler);
1056 url_con->fd_handler = NULL;
1065 _ecore_con_url_restart_fd_handler(void)
1068 Ecore_Con_Url *url_con;
1074 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1076 if (!url_con->fd_handler && url_con->fd != -1)
1078 url_con->fd_handler =
1079 ecore_main_fd_handler_add(url_con->fd, url_con->flags,
1080 _ecore_con_url_fd_handler,
1090 _ecore_con_url_data_cb(void *buffer, size_t size, size_t nitems, void *userp)
1092 Ecore_Con_Url *url_con;
1093 Ecore_Con_Event_Url_Data *e;
1094 size_t real_size = size * nitems;
1096 url_con = (Ecore_Con_Url *)userp;
1101 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1103 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_cb");
1107 url_con->received += real_size;
1109 if (url_con->write_fd < 0)
1112 malloc(sizeof(Ecore_Con_Event_Url_Data) + sizeof(unsigned char) *
1116 e->url_con = url_con;
1117 e->size = real_size;
1118 memcpy(e->data, buffer, real_size);
1119 ecore_event_add(ECORE_CON_EVENT_URL_DATA, e,
1120 _ecore_con_event_url_free, NULL);
1126 size_t total_size = real_size;
1129 while (total_size > 0)
1131 count = write(url_con->write_fd,
1132 (char *)buffer + offset,
1136 if (errno != EAGAIN && errno != EINTR)
1141 total_size -= count;
1150 #define ECORE_CON_URL_TRANSMISSION(Transmit, Event, Url_con, Total, Now) \
1152 Ecore_Con_Event_Url_Progress *e; \
1153 if ((Total != 0) || (Now != 0)) \
1155 e = calloc(1, sizeof(Ecore_Con_Event_Url_Progress)); \
1158 e->url_con = url_con; \
1161 ecore_event_add(Event, e, _ecore_con_event_url_free, NULL); \
1167 _ecore_con_url_header_cb(void *ptr, size_t size, size_t nitems, void *stream)
1169 size_t real_size = size * nitems;
1170 Ecore_Con_Url *url_con = stream;
1172 char *header = malloc(sizeof(char) * (real_size + 1));
1176 memcpy(header, ptr, real_size);
1177 header[real_size] = '\0';
1179 url_con->response_headers = eina_list_append(url_con->response_headers,
1186 _ecore_con_url_progress_cb(void *clientp, double dltotal, double dlnow,
1190 Ecore_Con_Event_Url_Progress *e;
1191 Ecore_Con_Url *url_con;
1195 e = malloc(sizeof(Ecore_Con_Event_Url_Progress));
1198 e->url_con = url_con;
1199 e->down.total = dltotal;
1200 e->down.now = dlnow;
1201 e->up.total = ultotal;
1203 ecore_event_add(ECORE_CON_EVENT_URL_PROGRESS, e,
1204 _ecore_con_event_url_free, NULL);
1211 _ecore_con_url_read_cb(void *ptr, size_t size, size_t nitems, void *stream)
1213 size_t retcode = fread(ptr, size, nitems, stream);
1215 if (ferror((FILE *)stream))
1218 return CURL_READFUNC_ABORT;
1220 else if ((retcode == 0) || (retcode < nitems))
1222 fclose((FILE *)stream);
1226 INF("*** We read %zu bytes from file", retcode);
1231 _ecore_con_url_perform(Ecore_Con_Url *url_con)
1233 fd_set read_set, write_set, exc_set;
1235 int flags, still_running;
1236 int completed_immediately = 0;
1238 _url_con_list = eina_list_append(_url_con_list, url_con);
1240 url_con->active = 1;
1241 curl_multi_add_handle(curlm, url_con->curl_easy);
1242 /* This one can't be stopped, or the download never start. */
1243 while (curl_multi_perform(curlm, &still_running) == CURLM_CALL_MULTI_PERFORM) ;
1245 completed_immediately = _ecore_con_url_process_completed_jobs(url_con);
1247 if (!completed_immediately)
1249 if (url_con->fd_handler)
1250 ecore_main_fd_handler_del(url_con->fd_handler);
1252 url_con->fd_handler = NULL;
1254 /* url_con still active -- set up an fd_handler */
1256 FD_ZERO(&write_set);
1259 /* Stupid curl, why can't I get the fd to the current added job? */
1260 curl_multi_fdset(curlm, &read_set, &write_set, &exc_set, &fd_max);
1261 for (fd = 0; fd <= fd_max; fd++)
1263 if (!FD_ISSET(fd, &_current_fd_set))
1266 if (FD_ISSET(fd, &read_set))
1267 flags |= ECORE_FD_READ;
1269 if (FD_ISSET(fd, &write_set))
1270 flags |= ECORE_FD_WRITE;
1272 if (FD_ISSET(fd, &exc_set))
1273 flags |= ECORE_FD_ERROR;
1279 curl_multi_timeout(curlm, &ms);
1283 FD_SET(fd, &_current_fd_set);
1285 url_con->flags = flags;
1286 url_con->fd_handler =
1287 ecore_main_fd_handler_add(fd, flags,
1288 _ecore_con_url_fd_handler,
1294 if (!url_con->fd_handler)
1296 /* Failed to set up an fd_handler */
1297 ecore_timer_freeze(_curl_timeout);
1298 curl_multi_remove_handle(curlm, url_con->curl_easy);
1299 url_con->active = 0;
1304 ecore_timer_thaw(_curl_timeout);
1311 _ecore_con_url_idler_handler(void *data)
1314 int done = 1, still_running;
1316 start = ecore_time_get();
1317 while (curl_multi_perform(curlm, &still_running) == CURLM_CALL_MULTI_PERFORM)
1318 /* make this not more than a frametime to keep interactivity high */
1319 if ((ecore_time_get() - start) > ecore_animator_frametime_get())
1325 _ecore_con_url_process_completed_jobs(NULL);
1329 _ecore_con_url_restart_fd_handler();
1330 _fd_idler_handler = NULL;
1333 ecore_timer_freeze(_curl_timeout);
1336 (void *)0xACE ? ECORE_CALLBACK_RENEW : ECORE_CALLBACK_CANCEL;
1339 return ECORE_CALLBACK_RENEW;
1343 _ecore_con_url_fd_handler(void *data __UNUSED__,
1344 Ecore_Fd_Handler *fd_handler __UNUSED__)
1346 _ecore_con_url_suspend_fd_handler();
1348 if (!_fd_idler_handler)
1349 _fd_idler_handler = ecore_idler_add(
1350 _ecore_con_url_idler_handler, NULL);
1352 return ECORE_CALLBACK_RENEW;
1356 _ecore_con_url_process_completed_jobs(Ecore_Con_Url *url_con_to_match)
1359 Ecore_Con_Url *url_con;
1360 Ecore_Con_Event_Url_Complete *e;
1363 int job_matched = 0;
1365 /* Loop jobs and check if any are done */
1366 while ((curlmsg = curl_multi_info_read(curlm, &n_remaining)))
1368 if (curlmsg->msg != CURLMSG_DONE)
1371 /* find the job which is done */
1372 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1374 if (curlmsg->easy_handle == url_con->curl_easy)
1376 if (url_con_to_match &&
1377 (url_con == url_con_to_match))
1380 if(url_con->fd != -1)
1382 FD_CLR(url_con->fd, &_current_fd_set);
1383 if (url_con->fd_handler)
1384 ecore_main_fd_handler_del(
1385 url_con->fd_handler);
1388 url_con->fd_handler = NULL;
1391 _url_con_list = eina_list_remove(_url_con_list, url_con);
1392 url_con->active = 0;
1393 e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
1396 e->url_con = url_con;
1398 if (curlmsg->data.result == CURLE_OK)
1400 long status; /* curl API uses long, not int */
1403 curl_easy_getinfo(curlmsg->easy_handle,
1404 CURLINFO_RESPONSE_CODE,
1409 _url_complete_push_event(ECORE_CON_EVENT_URL_COMPLETE, e);
1412 curl_multi_remove_handle(curlm, url_con->curl_easy);
1422 _ecore_con_event_url_free(void *data __UNUSED__, void *ev)