2 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
6 * For info on how to use libcurl, see:
7 * http://curl.haxx.se/libcurl/c/libcurl-tutorial.html
12 * 1. Create an Ecore_Con_Url object
13 * 2. Register to receive the ECORE_CON_EVENT_URL_COMPLETE event
14 * (and optionally the ECORE_CON_EVENT_URL_DATA event to receive
15 * the response, e.g. for HTTP/FTP downloads)
16 * 3. Set the URL with ecore_con_url_url_set(...);
17 * 4. Perform the operation with ecore_con_url_send(...);
19 * Note that it is good to reuse Ecore_Con_Url objects wherever possible, but
20 * bear in mind that each one can only perform one operation at a time.
21 * You need to wait for the ECORE_CON_EVENT_URL_COMPLETE event before re-using
22 * or destroying the object.
24 * Example Usage 1 (HTTP GET):
25 * ecore_con_url_url_set(url_con, "http://www.google.com");
26 * ecore_con_url_send(url_con, NULL, 0, NULL);
28 * Example usage 2 (HTTP POST):
29 * ecore_con_url_url_set(url_con, "http://www.example.com/post_handler.cgi");
30 * ecore_con_url_send(url_con, data, data_length, "multipart/form-data");
32 * Example Usage 3 (FTP download):
33 * ecore_con_url_url_set(url_con, "ftp://ftp.example.com/pub/myfile");
34 * ecore_con_url_send(url_con, NULL, 0, NULL);
36 * Example Usage 4 (FTP upload as ftp://ftp.example.com/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", NULL);
40 * Example Usage 5 (FTP upload as ftp://ftp.example.com/dir/file):
41 * ecore_con_url_url_set(url_con, "ftp://ftp.example.com");
42 * ecore_con_url_ftp_upload(url_con, "/tmp/file", "user", "pass","dir");
44 * FIXME: Support more CURL features: Authentication, Progress callbacks and more...
54 #include <sys/types.h>
57 #ifdef HAVE_WS2TCPIP_H
58 # include <ws2tcpip.h>
62 #include "ecore_private.h"
63 #include "Ecore_Con.h"
64 #include "ecore_con_private.h"
67 * @defgroup Ecore_Con_Url_Group Ecore URL Connection Functions
69 * Utility functions that set up, use and shut down the Ecore URL
71 * FIXME: write detailed description
74 int ECORE_CON_EVENT_URL_DATA = 0;
75 int ECORE_CON_EVENT_URL_COMPLETE = 0;
76 int ECORE_CON_EVENT_URL_PROGRESS = 0;
79 static int _ecore_con_url_fd_handler(void *data, Ecore_Fd_Handler *fd_handler);
80 static int _ecore_con_url_perform(Ecore_Con_Url *url_con);
81 static size_t _ecore_con_url_header_cb(void *ptr, size_t size, size_t nitems, void *stream);
82 static size_t _ecore_con_url_data_cb(void *buffer, size_t size, size_t nitems, void *userp);
83 static int _ecore_con_url_progress_cb(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
84 static size_t _ecore_con_url_read_cb(void *ptr, size_t size, size_t nitems, void *stream);
85 static void _ecore_con_event_url_free(void *data __UNUSED__, void *ev);
86 static int _ecore_con_url_process_completed_jobs(Ecore_Con_Url *url_con_to_match);
87 static int _ecore_con_url_idler_handler(void *data __UNUSED__);
89 static Ecore_Idler *_fd_idler_handler = NULL;
90 static Eina_List *_url_con_list = NULL;
91 static CURLM *curlm = NULL;
92 static fd_set _current_fd_set;
93 static int init_count = 0;
94 static Ecore_Timer *_curl_timeout = NULL;
96 typedef struct _Ecore_Con_Url_Event Ecore_Con_Url_Event;
97 struct _Ecore_Con_Url_Event
104 _url_complete_idler_cb(void *data)
106 Ecore_Con_Url_Event *lev;
109 ecore_event_add(lev->type, lev->ev, _ecore_con_event_url_free, NULL);
116 _url_complete_push_event(int type, void *ev)
118 Ecore_Con_Url_Event *lev;
120 lev = malloc(sizeof(Ecore_Con_Url_Event));
124 ecore_idler_add(_url_complete_idler_cb, lev);
130 * Initialises the Ecore_Con_Url library.
131 * @return Number of times the library has been initialised without being
133 * @ingroup Ecore_Con_Url_Group
136 ecore_con_url_init(void)
141 if (init_count > 1) return init_count;
143 if (!ECORE_CON_EVENT_URL_DATA)
145 ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
146 ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
147 ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
154 FD_ZERO(&_current_fd_set);
155 if (curl_global_init(CURL_GLOBAL_NOTHING))
157 while (_url_con_list)
158 ecore_con_url_destroy(eina_list_data_get(_url_con_list));
162 curlm = curl_multi_init();
165 while (_url_con_list)
166 ecore_con_url_destroy(eina_list_data_get(_url_con_list));
172 curl_multi_timeout(curlm, &ms);
173 if (ms <= 0) ms = 1000;
175 _curl_timeout = ecore_timer_add((double) ms / 1000, _ecore_con_url_idler_handler, (void *) 0xACE);
176 ecore_timer_freeze(_curl_timeout);
185 * Shuts down the Ecore_Con_Url library.
186 * @return Number of calls that still uses Ecore_Con_Url
187 * @ingroup Ecore_Con_Url_Group
190 ecore_con_url_shutdown(void)
193 if (!init_count) return 0;
197 if (init_count != 0) return init_count;
199 if (_fd_idler_handler)
200 ecore_idler_del(_fd_idler_handler);
201 _fd_idler_handler = NULL;
204 ecore_timer_del(_curl_timeout);
205 _curl_timeout = NULL;
207 while (_url_con_list)
208 ecore_con_url_destroy(eina_list_data_get(_url_con_list));
212 curl_multi_cleanup(curlm);
216 curl_global_cleanup();
222 * Creates and initializes a new Ecore_Con_Url connection object.
224 * Creates and initializes a new Ecore_Con_Url connection object that can be
225 * uesd for sending requests.
227 * @param url URL that will receive requests. Can be changed using
228 * ecore_con_url_url_set.
230 * @return NULL on error, a new Ecore_Con_Url on success.
232 * @ingroup Ecore_Con_Url_Group
234 * @see ecore_con_url_custom_new()
235 * @see ecore_con_url_url_set()
238 ecore_con_url_new(const char *url)
241 Ecore_Con_Url *url_con;
243 if (!init_count) return NULL;
245 url_con = calloc(1, sizeof(Ecore_Con_Url));
246 if (!url_con) return NULL;
248 url_con->curl_easy = curl_easy_init();
249 if (!url_con->curl_easy)
255 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
257 ecore_con_url_url_set(url_con, url);
259 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEFUNCTION,
260 _ecore_con_url_data_cb);
261 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEDATA, url_con);
263 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION,
264 _ecore_con_url_progress_cb);
265 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSDATA, url_con);
266 curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_FALSE);
268 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERFUNCTION, _ecore_con_url_header_cb);
269 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERDATA, url_con);
272 * FIXME: Check that these timeouts are sensible defaults
273 * FIXME: Provide a means to change these timeouts
275 curl_easy_setopt(url_con->curl_easy, CURLOPT_CONNECTTIMEOUT, 30);
276 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEOUT, 300);
277 curl_easy_setopt(url_con->curl_easy, CURLOPT_FOLLOWLOCATION, 1);
279 curl_easy_setopt(url_con->curl_easy, CURLOPT_ENCODING, "gzip,deflate");
282 url_con->write_fd = -1;
283 url_con->additional_headers = NULL;
284 url_con->response_headers = NULL;
294 * Creates a custom connection object.
296 * Creates and initializes a new Ecore_Con_Url for a custom request (e.g. HEAD,
297 * SUBSCRIBE and other obscure HTTP requests). This object should be used like
298 * one created with ecore_con_url_new().
300 * @param url URL that will receive requests
301 * @param custom_request Custom request (e.g. GET, POST, HEAD, PUT, etc)
303 * @return NULL on error, a new Ecore_Con_Url on success.
305 * @ingroup Ecore_Con_Url_Group
307 * @see ecore_con_url_new()
308 * @see ecore_con_url_url_set()
311 ecore_con_url_custom_new(const char *url, const char *custom_request)
314 Ecore_Con_Url *url_con;
316 if (!url) return NULL;
317 if (!custom_request) return NULL;
319 url_con = ecore_con_url_new(url);
321 if (!url_con) return NULL;
323 curl_easy_setopt(url_con->curl_easy, CURLOPT_CUSTOMREQUEST, custom_request);
329 custom_request = NULL;
334 * Destroys a Ecore_Con_Url connection object.
336 * @ingroup Ecore_Con_Url_Group
338 * @see ecore_con_url_new()
341 ecore_con_url_destroy(Ecore_Con_Url *url_con)
346 if (!url_con) return;
347 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
349 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_destroy");
353 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
354 if(url_con->fd != -1)
356 FD_CLR(url_con->fd, &_current_fd_set);
357 if (url_con->fd_handler)
358 ecore_main_fd_handler_del(url_con->fd_handler);
360 url_con->fd_handler = NULL;
364 curl_formfree(url_con->post);
365 url_con->post = NULL;
367 if (url_con->curl_easy)
369 // FIXME: For an unknown reason, progress continue to arrive after destruction
370 // this prevent any further call to the callback.
371 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION, NULL);
377 curl_multi_remove_handle(curlm, url_con->curl_easy);
379 curl_easy_cleanup(url_con->curl_easy);
381 _url_con_list = eina_list_remove(_url_con_list, url_con);
382 curl_slist_free_all(url_con->headers);
383 EINA_LIST_FREE(url_con->additional_headers, s)
385 EINA_LIST_FREE(url_con->response_headers, s)
396 * Sets the URL to send the request to.
398 * @param url_con Connection object through which the request will be sent.
399 * @param url URL that will receive the request
401 * @return 1 on success, 0 on error.
403 * @ingroup Ecore_Con_Url_Group
406 ecore_con_url_url_set(Ecore_Con_Url *url_con, const char *url)
409 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
411 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_url_set");
415 if (url_con->active) return 0;
417 if (url_con->url) free(url_con->url);
419 if (url) url_con->url = strdup(url);
421 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL, url_con->url);
423 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL, "");
433 * Associates data with a connection object.
435 * Associates data with a connection object, which can be retrieved later with
436 * ecore_con_url_data_get()).
438 * @param url_con Connection object to associate data.
439 * @param data Data to be set.
441 * @ingroup Ecore_Con_Url_Group
443 * @see ecore_con_url_data_get()
446 ecore_con_url_data_set(Ecore_Con_Url *url_con, void *data)
449 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
451 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_set");
455 url_con->data = data;
464 * Adds an additional header to the request connection object.
466 * Adds an additional header to the request connection object. This addition
467 * will be valid for only one ecore_con_url_send() call.
469 * @param url_con Connection object
470 * @param key Header key
471 * @param value Header value
473 * @ingroup Ecore_Con_Url_Group
475 * @see ecore_con_url_send()
476 * @see ecore_con_url_additional_headers_clear()
479 ecore_con_url_additional_header_add(Ecore_Con_Url *url_con, const char *key, const char *value)
484 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
486 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_additional_header_add");
490 tmp = malloc(strlen(key) + strlen(value) + 3);
492 sprintf(tmp, "%s: %s", key, value);
493 url_con->additional_headers = eina_list_append(url_con->additional_headers, tmp);
503 * Cleans additional headers.
505 * Cleans additional headers associated with a connection object (previously
506 * added with ecore_con_url_additional_header_add()).
508 * @param url_con Connection object to clean additional headers.
510 * @ingroup Ecore_Con_Url_Group
512 * @see ecore_con_url_additional_header_add()
513 * @see ecore_con_url_send()
516 ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
521 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
523 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_additional_headers_clear");
527 EINA_LIST_FREE(url_con->additional_headers, s)
536 * Retrieves data associated with a Ecore_Con_Url connection object.
538 * Retrieves data associated with a Ecore_Con_Url connection object (previously
539 * set with ecore_con_url_data_set()).
541 * @param Connection object to retrieve data from.
543 * @return Data associated with the given object.
545 * @ingroup Ecore_Con_Url_Group
547 * @see ecore_con_url_data_set()
550 ecore_con_url_data_get(Ecore_Con_Url *url_con)
553 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
555 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_get");
559 return url_con->data;
567 * FIXME: To be documented.
568 * @return FIXME: To be documented.
569 * @ingroup Ecore_Con_Url_Group
572 ecore_con_url_time(Ecore_Con_Url *url_con, Ecore_Con_Url_Time condition, time_t tm)
575 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
577 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_time");
581 url_con->condition = condition;
592 * Setup a file for receiving request data.
594 * Setups a file to have response data written into. Note that
595 * ECORE_CON_EVENT_URL_DATA events will not be emitted if a file has been set to
596 * receive the response data.
598 * @param url_con Connection object to set file
599 * @param fd File descriptor associated with the file
601 * @ingroup Ecore_Con_Url_Group
604 ecore_con_url_fd_set(Ecore_Con_Url *url_con, int fd)
607 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
609 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_set");
612 url_con->write_fd = fd;
617 * Retrieves the number of bytes received.
619 * Retrieves the number of bytes received on the last request of the given
622 * @param url_con Connection object which the request was sent on.
624 * @return Number of bytes received on request.
626 * @ingroup Ecore_Con_Url_Group
628 * @see ecore_con_url_send()
631 ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
634 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
636 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_received_bytes_get");
640 return url_con->received;
647 * Retrieves headers from last request sent.
649 * Retrieves a list containing the response headers. This function should be
650 * used after an ECORE_CON_EVENT_URL_COMPLETE event (headers should normally be
651 * ready at that time).
653 * @param url_con Connection object to retrieve response headers from.
655 * @return List of response headers. This list must not be modified by the user.
657 * @ingroup Ecore_Con_Url_Group
659 EAPI const Eina_List *
660 ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
663 return url_con->response_headers;
670 * Sets url_con to use http auth, with given username and password, "safely" or not.
672 * @param url_con Connection object to perform a request on, previously created
673 * with ecore_con_url_new() or ecore_con_url_custom_new().
674 * @param username Username to use in authentication
675 * @param password Password to use in authentication
676 * @param safe Whether to use "safer" methods (eg, NOT http basic auth)
678 * @return 1 on success, 0 on error.
680 * @ingroup Ecore_Con_Url_Group
683 ecore_con_url_httpauth_set(Ecore_Con_Url *url_con, const char *username, const char *password, Eina_Bool safe)
686 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
688 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_httpauth_set");
691 # ifdef CURLOPT_USERNAME
692 # ifdef CURLOPT_PASSWORD
693 if ((username != NULL) && (password != NULL))
696 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
698 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
699 curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
700 curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
712 * @param url_con Connection object to perform a request on, previously created
713 * with ecore_con_url_new() or ecore_con_url_custom_new().
714 * @param data Payload (data sent on the request)
715 * @param length Payload length
716 * @param content_type Content type of the payload (e.g. text/xml)
718 * @return 1 on success, 0 on error.
720 * @ingroup Ecore_Con_Url_Group
722 * @see ecore_con_url_custom_new()
723 * @see ecore_con_url_additional_headers_clear()
724 * @see ecore_con_url_additional_header_add()
725 * @see ecore_con_url_data_set()
726 * @see ecore_con_url_data_get()
727 * @see ecore_con_url_response_headers_get()
730 ecore_con_url_send(Ecore_Con_Url *url_con, const void *data, size_t length, const char *content_type)
737 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
739 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_send");
743 if (url_con->active) return 0;
744 if (!url_con->url) return 0;
746 /* Free response headers from previous send() calls */
747 EINA_LIST_FREE(url_con->response_headers, s) free((char *)s);
748 url_con->response_headers = NULL;
750 curl_slist_free_all(url_con->headers);
751 url_con->headers = NULL;
755 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDS, data);
756 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, length);
758 if (content_type && (strlen(content_type) < 200))
760 sprintf(tmp, "Content-type: %s", content_type);
761 url_con->headers = curl_slist_append(url_con->headers, tmp);
763 sprintf(tmp, "Content-length: %zu", length);
764 url_con->headers = curl_slist_append(url_con->headers, tmp);
767 switch (url_con->condition)
769 case ECORE_CON_URL_TIME_NONE:
770 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
773 case ECORE_CON_URL_TIME_IFMODSINCE:
774 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
775 CURL_TIMECOND_IFMODSINCE);
776 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE, url_con->time);
778 case ECORE_CON_URL_TIME_IFUNMODSINCE:
779 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
780 CURL_TIMECOND_IFUNMODSINCE);
781 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE, url_con->time);
783 case ECORE_CON_URL_TIME_LASTMOD:
784 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
785 CURL_TIMECOND_LASTMOD);
786 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE, url_con->time);
790 /* Additional headers */
791 EINA_LIST_FOREACH(url_con->additional_headers, l, s)
792 url_con->headers = curl_slist_append(url_con->headers, s);
794 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPHEADER, url_con->headers);
796 url_con->received = 0;
798 int res = _ecore_con_url_perform(url_con);
812 * @return FIXME: To be more documented.
813 * @ingroup Ecore_Con_Url_Group
816 ecore_con_url_ftp_upload(Ecore_Con_Url *url_con, const char *filename, const char *user, const char *pass, const char *upload_dir)
822 struct stat file_info;
824 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
826 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_ftp_upload");
830 if (url_con->active) return 0;
831 if (!url_con->url) return 0;
836 snprintf(tmp, PATH_MAX, "%s", filename);
838 if (stat(filename, &file_info)) return 0;
839 fd = fopen(filename, "rb");
841 snprintf(url, sizeof(url), "ftp://%s/%s/%s", url_con->url,
842 upload_dir, basename(tmp));
844 snprintf(url, sizeof(url), "ftp://%s/%s", url_con->url,
846 snprintf(userpwd, sizeof(userpwd), "%s:%s", user, pass);
847 curl_easy_setopt(url_con->curl_easy, CURLOPT_INFILESIZE_LARGE,
848 (curl_off_t)file_info.st_size);
849 curl_easy_setopt(url_con->curl_easy, CURLOPT_USERPWD, userpwd);
850 curl_easy_setopt(url_con->curl_easy, CURLOPT_UPLOAD, 1);
851 curl_easy_setopt(url_con->curl_easy, CURLOPT_READFUNCTION,
852 _ecore_con_url_read_cb);
853 curl_easy_setopt(url_con->curl_easy, CURLOPT_READDATA, fd);
854 ecore_con_url_url_set(url_con, url);
856 return _ecore_con_url_perform(url_con);
871 * Send a Curl httppost
872 * @return 1 on success, 0 on error.
873 * @ingroup Ecore_Con_Url_Group
876 ecore_con_url_http_post_send(Ecore_Con_Url *url_con, void *httppost)
880 curl_formfree(url_con->post);
881 url_con->post = NULL;
883 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
885 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_http_post_send");
889 url_con->post = httppost;
891 if (url_con->active) return 0;
892 if (!url_con->url) return 0;
894 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPPOST, httppost);
896 return ecore_con_url_send(url_con, NULL, 0, NULL);
904 * Enable or disable libcurl verbose output, useful for debug
905 * @return FIXME: To be more documented.
906 * @ingroup Ecore_Con_Url_Group
909 ecore_con_url_verbose_set(Ecore_Con_Url *url_con, int verbose)
912 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
914 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_verbose_set");
918 if (url_con->active) return;
919 if (!url_con->url) return;
920 if (verbose == EINA_TRUE)
921 curl_easy_setopt(url_con->curl_easy, CURLOPT_VERBOSE, 1);
923 curl_easy_setopt(url_con->curl_easy, CURLOPT_VERBOSE, 0);
928 * Enable or disable EPSV extension
929 * @return FIXME: To be more documented.
930 * @ingroup Ecore_Con_Url_Group
933 ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con, int use_epsv)
936 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
938 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_ftp_use_epsv_set");
942 if (url_con->active) return;
943 if (!url_con->url) return;
944 if (use_epsv == EINA_TRUE)
945 curl_easy_setopt(url_con->curl_easy, CURLOPT_FTP_USE_EPSV, 1);
947 curl_easy_setopt(url_con->curl_easy, CURLOPT_FTP_USE_EPSV, 0);
953 _ecore_con_url_suspend_fd_handler(void)
956 Ecore_Con_Url *url_con;
959 if (!_url_con_list) return 0;
961 EINA_LIST_FOREACH(_url_con_list, l, url_con)
963 if (url_con->active && url_con->fd_handler)
965 ecore_main_fd_handler_del(url_con->fd_handler);
966 url_con->fd_handler = NULL;
975 _ecore_con_url_restart_fd_handler(void)
978 Ecore_Con_Url *url_con;
981 if (!_url_con_list) return 0;
983 EINA_LIST_FOREACH(_url_con_list, l, url_con)
985 if (url_con->fd_handler == NULL && url_con->fd != -1)
987 url_con->fd_handler =
988 ecore_main_fd_handler_add(url_con->fd, url_con->flags,
989 _ecore_con_url_fd_handler,
999 _ecore_con_url_data_cb(void *buffer, size_t size, size_t nitems, void *userp)
1001 Ecore_Con_Url *url_con;
1002 Ecore_Con_Event_Url_Data *e;
1003 size_t real_size = size * nitems;
1005 url_con = (Ecore_Con_Url *)userp;
1007 if (!url_con) return -1;
1008 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1010 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_cb");
1014 url_con->received += real_size;
1016 if (url_con->write_fd < 0)
1018 e = malloc(sizeof(Ecore_Con_Event_Url_Data) + sizeof(unsigned char) * (real_size - 1));
1021 e->url_con = url_con;
1022 e->size = real_size;
1023 memcpy(e->data, buffer, real_size);
1024 ecore_event_add(ECORE_CON_EVENT_URL_DATA, e,
1025 _ecore_con_event_url_free, NULL);
1031 size_t total_size = real_size;
1034 while (total_size > 0)
1036 count = write(url_con->write_fd, (char*) buffer + offset, total_size);
1039 if (errno != EAGAIN && errno != EINTR) return -1;
1043 total_size -= count;
1052 #define ECORE_CON_URL_TRANSMISSION(Transmit, Event, Url_con, Total, Now) \
1054 Ecore_Con_Event_Url_Progress *e; \
1055 if ((Total != 0) || (Now != 0)) \
1057 e = calloc(1, sizeof(Ecore_Con_Event_Url_Progress)); \
1060 e->url_con = url_con; \
1063 ecore_event_add(Event, e, _ecore_con_event_url_free, NULL); \
1069 _ecore_con_url_header_cb(void *ptr, size_t size, size_t nitems, void *stream)
1071 size_t real_size = size * nitems;
1072 Ecore_Con_Url *url_con = stream;
1074 char *header = malloc(sizeof(char)*(real_size + 1));
1075 if (!header) return real_size;
1076 memcpy(header, ptr, real_size);
1077 header[real_size] = '\0';
1079 url_con->response_headers = eina_list_append(url_con->response_headers,
1086 _ecore_con_url_progress_cb(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
1088 Ecore_Con_Event_Url_Progress *e;
1089 Ecore_Con_Url *url_con;
1093 e = malloc(sizeof(Ecore_Con_Event_Url_Progress));
1096 e->url_con = url_con;
1097 e->down.total = dltotal;
1098 e->down.now = dlnow;
1099 e->up.total = ultotal;
1101 ecore_event_add(ECORE_CON_EVENT_URL_PROGRESS, e,
1102 _ecore_con_event_url_free, NULL);
1109 _ecore_con_url_read_cb(void *ptr, size_t size, size_t nitems, void *stream)
1111 size_t retcode = fread(ptr, size, nitems, stream);
1113 if (ferror((FILE*)stream))
1116 return CURL_READFUNC_ABORT;
1118 else if ((retcode == 0) || (retcode < nitems))
1120 fclose((FILE*)stream);
1123 INF("*** We read %zu bytes from file", retcode);
1128 _ecore_con_url_perform(Ecore_Con_Url *url_con)
1130 fd_set read_set, write_set, exc_set;
1132 int flags, still_running;
1133 int completed_immediately = 0;
1135 _url_con_list = eina_list_append(_url_con_list, url_con);
1137 url_con->active = 1;
1138 curl_multi_add_handle(curlm, url_con->curl_easy);
1139 /* This one can't be stopped, or the download never start. */
1140 while (curl_multi_perform(curlm, &still_running) == CURLM_CALL_MULTI_PERFORM);
1142 completed_immediately = _ecore_con_url_process_completed_jobs(url_con);
1144 if (!completed_immediately)
1146 if (url_con->fd_handler)
1147 ecore_main_fd_handler_del(url_con->fd_handler);
1148 url_con->fd_handler = NULL;
1150 /* url_con still active -- set up an fd_handler */
1152 FD_ZERO(&write_set);
1155 /* Stupid curl, why can't I get the fd to the current added job? */
1156 curl_multi_fdset(curlm, &read_set, &write_set, &exc_set, &fd_max);
1157 for (fd = 0; fd <= fd_max; fd++)
1159 if (!FD_ISSET(fd, &_current_fd_set))
1162 if (FD_ISSET(fd, &read_set)) flags |= ECORE_FD_READ;
1163 if (FD_ISSET(fd, &write_set)) flags |= ECORE_FD_WRITE;
1164 if (FD_ISSET(fd, &exc_set)) flags |= ECORE_FD_ERROR;
1169 curl_multi_timeout(curlm, &ms);
1170 if (ms == 0) ms = 1000;
1172 FD_SET(fd, &_current_fd_set);
1174 url_con->flags = flags;
1175 url_con->fd_handler =
1176 ecore_main_fd_handler_add(fd, flags,
1177 _ecore_con_url_fd_handler,
1183 if (!url_con->fd_handler)
1185 /* Failed to set up an fd_handler */
1186 ecore_timer_freeze(_curl_timeout);
1187 curl_multi_remove_handle(curlm, url_con->curl_easy);
1188 url_con->active = 0;
1192 ecore_timer_thaw(_curl_timeout);
1199 _ecore_con_url_idler_handler(void *data)
1202 int done = 1, still_running;
1204 start = ecore_time_get();
1205 while (curl_multi_perform(curlm, &still_running) == CURLM_CALL_MULTI_PERFORM)
1206 /* make this not more than a frametime to keep interactivity high */
1207 if ((ecore_time_get() - start) > ecore_animator_frametime_get())
1213 _ecore_con_url_process_completed_jobs(NULL);
1217 _ecore_con_url_restart_fd_handler();
1218 _fd_idler_handler = NULL;
1221 ecore_timer_freeze(_curl_timeout);
1222 return data == (void*) 0xACE ? 1 : 0;
1229 _ecore_con_url_fd_handler(void *data __UNUSED__, Ecore_Fd_Handler *fd_handler __UNUSED__)
1231 _ecore_con_url_suspend_fd_handler();
1233 if (_fd_idler_handler == NULL)
1234 _fd_idler_handler = ecore_idler_add(_ecore_con_url_idler_handler, NULL);
1240 _ecore_con_url_process_completed_jobs(Ecore_Con_Url *url_con_to_match)
1243 Ecore_Con_Url *url_con;
1244 Ecore_Con_Event_Url_Complete *e;
1247 int job_matched = 0;
1249 /* Loop jobs and check if any are done */
1250 while ((curlmsg = curl_multi_info_read(curlm, &n_remaining)) != NULL)
1252 if (curlmsg->msg != CURLMSG_DONE) continue;
1254 /* find the job which is done */
1255 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1257 if (curlmsg->easy_handle == url_con->curl_easy)
1259 if (url_con_to_match && (url_con == url_con_to_match))
1261 if(url_con->fd != -1)
1263 FD_CLR(url_con->fd, &_current_fd_set);
1264 if (url_con->fd_handler)
1265 ecore_main_fd_handler_del(url_con->fd_handler);
1267 url_con->fd_handler = NULL;
1269 _url_con_list = eina_list_remove(_url_con_list, url_con);
1270 url_con->active = 0;
1271 e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
1274 e->url_con = url_con;
1276 if (curlmsg->data.result == CURLE_OK)
1278 long status; /* curl API uses long, not int */
1281 curl_easy_getinfo(curlmsg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
1285 _url_complete_push_event(ECORE_CON_EVENT_URL_COMPLETE, e);
1287 curl_multi_remove_handle(curlm, url_con->curl_easy);
1297 _ecore_con_event_url_free(void *data __UNUSED__, void *ev)