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_get(...);
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_get(url_con);
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");
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);
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: 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,
74 static size_t _ecore_con_url_data_cb(void *buffer,
78 static int _ecore_con_url_progress_cb(void *clientp,
83 static size_t _ecore_con_url_read_cb(void *ptr,
87 static void _ecore_con_event_url_free(void *data __UNUSED__,
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);
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;
100 typedef struct _Ecore_Con_Url_Event Ecore_Con_Url_Event;
101 struct _Ecore_Con_Url_Event
108 _url_complete_idler_cb(void *data)
110 Ecore_Con_Url_Event *lev;
113 ecore_event_add(lev->type, lev->ev, _ecore_con_event_url_free, NULL);
116 return ECORE_CALLBACK_CANCEL;
120 _url_complete_push_event(int type,
123 Ecore_Con_Url_Event *lev;
125 lev = malloc(sizeof(Ecore_Con_Url_Event));
129 ecore_idler_add(_url_complete_idler_cb, lev);
135 * @addtogroup Ecore_Con_Url_Group Ecore URL Connection Functions
137 * Utility functions that set up, use and shut down the Ecore URL
138 * Connection library.
140 * @todo write detailed description of Ecore_Con_Url
146 * Initialises the Ecore_Con_Url library.
147 * @return Number of times the library has been initialised without being
151 ecore_con_url_init(void)
159 if (!ECORE_CON_EVENT_URL_DATA)
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();
170 FD_ZERO(&_current_fd_set);
171 if (curl_global_init(CURL_GLOBAL_ALL))
173 while (_url_con_list)
174 ecore_con_url_free(eina_list_data_get(_url_con_list));
178 _curlm = curl_multi_init();
181 while (_url_con_list)
182 ecore_con_url_free(eina_list_data_get(_url_con_list));
188 curl_multi_timeout(_curlm, &ms);
193 ecore_timer_add((double)ms / 1000, _ecore_con_url_idler_handler,
195 ecore_timer_freeze(_curl_timeout);
205 * Shuts down the Ecore_Con_Url library.
206 * @return Number of calls that still uses Ecore_Con_Url
209 ecore_con_url_shutdown(void)
217 if (_init_count != 0)
220 if (_fd_idler_handler)
221 ecore_idler_del(_fd_idler_handler);
223 _fd_idler_handler = NULL;
226 ecore_timer_del(_curl_timeout);
228 _curl_timeout = NULL;
230 while (_url_con_list)
231 ecore_con_url_free(eina_list_data_get(_url_con_list));
235 curl_multi_cleanup(_curlm);
239 curl_global_cleanup();
245 * Creates and initializes a new Ecore_Con_Url connection object.
247 * Creates and initializes a new Ecore_Con_Url connection object that can be
248 * uesd for sending requests.
250 * @param url URL that will receive requests. Can be changed using
251 * ecore_con_url_url_set.
253 * @return NULL on error, a new Ecore_Con_Url on success.
256 * @see ecore_con_url_custom_new()
257 * @see ecore_con_url_url_set()
260 ecore_con_url_new(const char *url)
263 Ecore_Con_Url *url_con;
269 url_con = calloc(1, sizeof(Ecore_Con_Url));
274 url_con->write_fd = -1;
276 url_con->curl_easy = curl_easy_init();
277 if (!url_con->curl_easy)
283 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
285 if (!ecore_con_url_url_set(url_con, url))
287 ecore_con_url_free(url_con);
291 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_ENCODING, "gzip,deflate");
294 ERR("Could not set CURLOPT_ENCODING to \"gzip,deflate\": %s",
295 curl_easy_strerror(ret));
296 ecore_con_url_free(url_con);
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);
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);
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);
314 * FIXME: Check that these timeouts are sensible defaults
315 * FIXME: Provide a means to change these timeouts
317 curl_easy_setopt(url_con->curl_easy, CURLOPT_CONNECTTIMEOUT, 30);
318 curl_easy_setopt(url_con->curl_easy, CURLOPT_FOLLOWLOCATION, 1);
328 * Creates a custom connection object.
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().
334 * @param url URL that will receive requests
335 * @param custom_request Custom request (e.g. GET, POST, HEAD, PUT, etc)
337 * @return NULL on error, a new Ecore_Con_Url on success.
340 * @see ecore_con_url_new()
341 * @see ecore_con_url_url_set()
344 ecore_con_url_custom_new(const char *url,
345 const char *custom_request)
348 Ecore_Con_Url *url_con;
357 url_con = ecore_con_url_new(url);
362 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_CUSTOMREQUEST, custom_request);
365 ERR("Could not set a custom request string: %s",
366 curl_easy_strerror(ret));
367 ecore_con_url_free(url_con);
375 custom_request = NULL;
380 * Destroys a Ecore_Con_Url connection object.
382 * @param url_con Connection object to free.
384 * @see ecore_con_url_new()
387 ecore_con_url_free(Ecore_Con_Url *url_con)
396 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
398 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_free");
402 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
403 if(url_con->fd != -1)
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);
410 url_con->fd_handler = NULL;
413 if (url_con->curl_easy)
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);
421 url_con->active = EINA_FALSE;
423 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
425 ERR("curl_multi_remove_handle failed: %s",
426 curl_multi_strerror(ret));
429 curl_easy_cleanup(url_con->curl_easy);
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)
436 EINA_LIST_FREE(url_con->response_headers, s)
447 * Sets the URL to send the request to.
449 * @param url_con Connection object through which the request will be sent.
450 * @param url URL that will receive the request
452 * @return EINA_TRUE on success, EINA_FALSE on error.
456 ecore_con_url_url_set(Ecore_Con_Url *url_con,
460 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
462 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_url_set");
474 url_con->url = strdup(url);
477 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL,
480 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL, "");
491 * Associates data with a connection object.
493 * Associates data with a connection object, which can be retrieved later with
494 * ecore_con_url_data_get()).
496 * @param url_con Connection object to associate data.
497 * @param data Data to be set.
500 * @see ecore_con_url_data_get()
503 ecore_con_url_data_set(Ecore_Con_Url *url_con,
507 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
509 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_set");
513 url_con->data = data;
522 * Adds an additional header to the request connection object.
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.
527 * @param url_con Connection object
528 * @param key Header key
529 * @param value Header value
532 * @see ecore_con_url_get()
533 * @see ecore_con_url_post()
534 * @see ecore_con_url_additional_headers_clear()
537 ecore_con_url_additional_header_add(Ecore_Con_Url *url_con,
544 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
546 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
547 "ecore_con_url_additional_header_add");
551 tmp = malloc(strlen(key) + strlen(value) + 3);
555 sprintf(tmp, "%s: %s", key, value);
556 url_con->additional_headers = eina_list_append(url_con->additional_headers,
567 * Cleans additional headers.
569 * Cleans additional headers associated with a connection object (previously
570 * added with ecore_con_url_additional_header_add()).
572 * @param url_con Connection object to clean additional headers.
575 * @see ecore_con_url_additional_header_add()
576 * @see ecore_con_url_get()
577 * @see ecore_con_url_post()
580 ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
585 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
587 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
588 "ecore_con_url_additional_headers_clear");
592 EINA_LIST_FREE(url_con->additional_headers, s)
601 * Retrieves data associated with a Ecore_Con_Url connection object.
603 * Retrieves data associated with a Ecore_Con_Url connection object (previously
604 * set with ecore_con_url_data_set()).
606 * @param url_con Connection object to retrieve data from.
608 * @return Data associated with the given object.
611 * @see ecore_con_url_data_set()
614 ecore_con_url_data_get(Ecore_Con_Url *url_con)
617 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
619 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_get");
623 return url_con->data;
631 * Sets whether HTTP requests should be conditional, dependent on
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.
638 * @sa ecore_con_url_get()
639 * @sa ecore_con_url_post()
642 ecore_con_url_time(Ecore_Con_Url *url_con,
643 Ecore_Con_Url_Time condition,
647 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
649 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_time");
653 url_con->time_condition = condition;
654 url_con->timestamp = timestamp;
664 * Setup a file for receiving request data.
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.
670 * @param url_con Connection object to set file
671 * @param fd File descriptor associated with the file
675 ecore_con_url_fd_set(Ecore_Con_Url *url_con,
679 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
681 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_set");
685 url_con->write_fd = fd;
690 * Retrieves the number of bytes received.
692 * Retrieves the number of bytes received on the last request of the given
695 * @param url_con Connection object which the request was sent on.
697 * @return Number of bytes received on request.
700 * @see ecore_con_url_get()
701 * @see ecore_con_url_post()
704 ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
707 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
709 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
710 "ecore_con_url_received_bytes_get");
714 return url_con->received;
721 * Retrieves headers from last request sent.
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).
727 * @param url_con Connection object to retrieve response headers from.
729 * @return List of response headers. This list must not be modified by the user.
732 EAPI const Eina_List *
733 ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
736 return url_con->response_headers;
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.
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)
752 * @return #EINA_TRUE on success, #EINA_FALSE on error.
756 ecore_con_url_httpauth_set(Ecore_Con_Url *url_con,
757 const char *username,
758 const char *password,
764 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
766 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
767 "ecore_con_url_httpauth_set");
771 # if LIBCURL_VERSION_NUM >= 0x071301
772 if ((username) && (password))
775 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH,
778 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
780 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
783 ERR("Could not set username for HTTP authentication: %s",
784 curl_easy_strerror(ret));
788 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
791 ERR("Could not set password for HTTP authentication: %s",
792 curl_easy_strerror(ret));
809 _ecore_con_url_send(Ecore_Con_Url *url_con,
813 const char *content_type)
820 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
822 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_send");
832 /* Free response headers from previous send() calls */
833 EINA_LIST_FREE(url_con->response_headers, s)
835 url_con->response_headers = NULL;
837 curl_slist_free_all(url_con->headers);
838 url_con->headers = NULL;
840 if ((mode == MODE_POST) || (mode == MODE_AUTO))
844 if ((content_type) && (strlen(content_type) < 200))
846 snprintf(tmp, sizeof(tmp), "Content-Type: %s", content_type);
847 url_con->headers = curl_slist_append(url_con->headers, tmp);
850 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDS, data);
851 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, length);
855 switch (url_con->time_condition)
857 case ECORE_CON_URL_TIME_NONE:
858 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
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);
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);
877 /* Additional headers */
878 EINA_LIST_FOREACH(url_con->additional_headers, l, s)
879 url_con->headers = curl_slist_append(url_con->headers, s);
881 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPHEADER, url_con->headers);
883 url_con->received = 0;
885 return _ecore_con_url_perform(url_con);
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)
905 * @return #EINA_TRUE on success, #EINA_FALSE on error.
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()
917 EINA_DEPRECATED EAPI Eina_Bool
918 ecore_con_url_send(Ecore_Con_Url *url_con,
921 const char *content_type)
923 return _ecore_con_url_send(url_con, MODE_AUTO, data, length, content_type);
927 * Sends a get request.
929 * @param url_con Connection object to perform a request on, previously created
931 * @return #EINA_TRUE on success, #EINA_FALSE on error.
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()
943 ecore_con_url_get(Ecore_Con_Url *url_con)
945 return _ecore_con_url_send(url_con, MODE_GET, NULL, 0, NULL);
949 * Sends a post request.
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)
958 * @return #EINA_TRUE on success, #EINA_FALSE on error.
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()
970 ecore_con_url_post(Ecore_Con_Url *url_con,
973 const char *content_type)
975 return _ecore_con_url_send(url_con, MODE_POST, data, length, content_type);
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
990 ecore_con_url_ftp_upload(Ecore_Con_Url *url_con,
991 const char *filename,
994 const char *upload_dir)
1000 struct stat file_info;
1003 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1005 ECORE_MAGIC_FAIL(url_con,
1006 ECORE_MAGIC_CON_URL,
1007 "ecore_con_url_ftp_upload");
1011 if (url_con->active)
1019 if (stat(filename, &file_info))
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)
1026 ERR("Could not set username and password for FTP upload: %s",
1027 curl_easy_strerror(ret));
1032 snprintf(tmp, PATH_MAX, "%s", filename);
1035 snprintf(url, sizeof(url), "ftp://%s/%s/%s", url_con->url,
1036 upload_dir, basename(tmp));
1038 snprintf(url, sizeof(url), "ftp://%s/%s", url_con->url,
1041 if (!ecore_con_url_url_set(url_con, url))
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);
1050 fd = fopen(filename, "rb");
1053 ERR("Could not open \"%s\" for FTP upload", filename);
1056 curl_easy_setopt(url_con->curl_easy, CURLOPT_READDATA, fd);
1058 return _ecore_con_url_perform(url_con);
1073 * Enables the cookie engine for subsequent HTTP requests.
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
1079 * @note Even though this function is called @c ecore_con_url_cookies_init(),
1080 * there is no symmetrical shutdown operation.
1082 * @param url_con Ecore_Con_Url instance which will be acted upon.
1085 ecore_con_url_cookies_init(Ecore_Con_Url *url_con)
1091 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1093 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1094 "ecore_con_url_cookies_init");
1098 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEFILE, "");
1105 * Controls whether session cookies from previous sessions shall be loaded.
1107 * Session cookies are cookies with no expire date set, which usually means
1108 * they are removed after the current session is closed.
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.
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.
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.
1122 * @see ecore_con_url_cookies_file_add()
1125 ecore_con_url_cookies_ignore_old_session_set(Ecore_Con_Url *url_con, Eina_Bool ignore)
1131 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1133 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1134 "ecore_con_url_cookies_ignore_old_session_set");
1138 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIESESSION, ignore);
1146 * Clears currently loaded cookies.
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().
1152 * @note This function will initialize the cookie engine if it has not been
1155 * @param url_con Ecore_Con_Url instance which will be acted upon.
1157 * @see ecore_con_url_cookies_session_clear()
1158 * @see ecore_con_url_cookies_ignore_old_session_set()
1161 ecore_con_url_cookies_clear(Ecore_Con_Url *url_con)
1167 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1169 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1170 "ecore_con_url_cookies_clear");
1174 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "ALL");
1181 * Clears currently loaded session cookies.
1183 * Session cookies are cookies with no expire date set, which usually means
1184 * they are removed after the current session is closed.
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().
1190 * @note This function will initialize the cookie engine if it has not been
1193 * @param url_con Ecore_Con_Url instance which will be acted upon.
1195 * @see ecore_con_url_cookies_clear()
1196 * @see ecore_con_url_cookies_ignore_old_session_set()
1199 ecore_con_url_cookies_session_clear(Ecore_Con_Url *url_con)
1205 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1207 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1208 "ecore_con_url_cookies_session_clear");
1212 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "SESS");
1219 * Adds a file to the list of files from which to load cookies.
1221 * Files must contain cookies defined according to two possible formats:
1223 * @li HTTP-style header ("Set-Cookie: ...").
1224 * @li Netscape/Mozilla cookie data format.
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.
1229 * @note This function will initialize the cookie engine if it has not been
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.
1235 * @see ecore_con_url_cookies_ignore_old_session_set()
1238 ecore_con_url_cookies_file_add(Ecore_Con_Url *url_con, const char * const file_name)
1244 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1246 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1247 "ecore_con_url_cookies_file_add");
1251 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEFILE, file_name);
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.
1262 * Cookies are written following Netscape/Mozilla's data format, also known as
1265 * @note This function will initialize the cookie engine if it has not been
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.
1271 * @return @c EINA_TRUE is the file name has been set successfully,
1272 * @c EINA_FALSE otherwise.
1274 * @see ecore_con_url_cookies_jar_write()
1277 ecore_con_url_cookies_jar_file_set(Ecore_Con_Url *url_con, const char * const cookiejar_file)
1285 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1287 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1288 "ecore_con_url_cookies_jar_file_set");
1292 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEJAR,
1294 if (ret != CURLE_OK)
1296 ERR("Setting the cookie-jar name failed: %s",
1297 curl_easy_strerror(ret));
1305 (void)cookiejar_file;
1310 * Writes all current cookies to the cookie jar immediately.
1312 * A cookie-jar file must have been previously set by
1313 * @c ecore_con_url_jar_file_set, otherwise nothing will be done.
1315 * @note This function will initialize the cookie engine if it has not been
1318 * @param url_con Ecore_Con_Url instance which will be acted upon.
1320 * @see ecore_con_url_cookies_jar_file_set()
1323 ecore_con_url_cookies_jar_write(Ecore_Con_Url *url_con)
1329 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1331 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1332 "ecore_con_url_cookies_jar_write");
1336 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "FLUSH");
1343 * Toggle libcurl's verbose output.
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.
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.
1353 ecore_con_url_verbose_set(Ecore_Con_Url *url_con,
1357 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1359 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1360 "ecore_con_url_verbose_set");
1364 if (url_con->active)
1370 curl_easy_setopt(url_con->curl_easy, CURLOPT_VERBOSE, (int)verbose);
1378 * Enable or disable EPSV extension
1379 * @return FIXME: To be more documented.
1382 ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con,
1386 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1388 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1389 "ecore_con_url_ftp_use_epsv_set");
1393 if (url_con->active)
1399 curl_easy_setopt(url_con->curl_easy, CURLOPT_FTP_USE_EPSV, (int)use_epsv);
1406 * Toggle libcurl's verify peer's certificate option.
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.
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.
1418 ecore_con_url_ssl_verify_peer_set(Ecore_Con_Url *url_con,
1422 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1424 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1425 "ecore_con_url_ssl_verify_peer_set");
1429 if (url_con->active)
1435 curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, (int)verify);
1443 * Set a custom CA to trust for SSL/TLS connections.
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.
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).
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
1457 * @return @c 0 on success. When cURL is used, non-zero return values
1458 * are equal to cURL error codes.
1461 ecore_con_url_ssl_ca_set(Ecore_Con_Url *url_con, const char *ca_path)
1466 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1468 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_ssl_ca_set");
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);
1478 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, 1);
1480 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_CAINFO, ca_path);
1497 _ecore_con_url_suspend_fd_handler(void)
1500 Ecore_Con_Url *url_con;
1506 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1508 if (url_con->active && url_con->fd_handler)
1510 ecore_main_fd_handler_del(url_con->fd_handler);
1511 url_con->fd_handler = NULL;
1520 _ecore_con_url_restart_fd_handler(void)
1523 Ecore_Con_Url *url_con;
1529 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1531 if (!url_con->fd_handler && url_con->fd != -1)
1533 url_con->fd_handler =
1534 ecore_main_fd_handler_add(url_con->fd, url_con->flags,
1535 _ecore_con_url_fd_handler,
1545 _ecore_con_url_data_cb(void *buffer,
1550 Ecore_Con_Url *url_con;
1551 Ecore_Con_Event_Url_Data *e;
1552 size_t real_size = size * nitems;
1554 url_con = (Ecore_Con_Url *)userp;
1559 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1561 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_cb");
1565 url_con->received += real_size;
1567 if (url_con->write_fd < 0)
1570 malloc(sizeof(Ecore_Con_Event_Url_Data) + sizeof(unsigned char) *
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);
1584 size_t total_size = real_size;
1587 while (total_size > 0)
1589 count = write(url_con->write_fd,
1590 (char *)buffer + offset,
1594 if (errno != EAGAIN && errno != EINTR)
1599 total_size -= count;
1608 #define ECORE_CON_URL_TRANSMISSION(Transmit, Event, Url_con, Total, Now) \
1610 Ecore_Con_Event_Url_Progress *e; \
1611 if ((Total != 0) || (Now != 0)) \
1613 e = calloc(1, sizeof(Ecore_Con_Event_Url_Progress)); \
1616 e->url_con = url_con; \
1619 ecore_event_add(Event, e, _ecore_con_event_url_free, NULL); \
1625 _ecore_con_url_header_cb(void *ptr,
1630 size_t real_size = size * nitems;
1631 Ecore_Con_Url *url_con = stream;
1633 char *header = malloc(sizeof(char) * (real_size + 1));
1637 memcpy(header, ptr, real_size);
1638 header[real_size] = '\0';
1640 url_con->response_headers = eina_list_append(url_con->response_headers,
1647 _ecore_con_url_progress_cb(void *clientp,
1653 Ecore_Con_Event_Url_Progress *e;
1654 Ecore_Con_Url *url_con;
1658 e = malloc(sizeof(Ecore_Con_Event_Url_Progress));
1661 e->url_con = url_con;
1662 e->down.total = dltotal;
1663 e->down.now = dlnow;
1664 e->up.total = ultotal;
1666 ecore_event_add(ECORE_CON_EVENT_URL_PROGRESS, e,
1667 _ecore_con_event_url_free, NULL);
1674 _ecore_con_url_read_cb(void *ptr,
1679 size_t retcode = fread(ptr, size, nitems, stream);
1681 if (ferror((FILE *)stream))
1684 return CURL_READFUNC_ABORT;
1686 else if (retcode == 0)
1688 fclose((FILE *)stream);
1692 INF("*** We read %zu bytes from file", retcode);
1697 _ecore_con_url_perform(Ecore_Con_Url *url_con)
1699 fd_set read_set, write_set, exc_set;
1701 int flags, still_running;
1702 int completed_immediately = 0;
1705 _url_con_list = eina_list_append(_url_con_list, url_con);
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) ;
1712 completed_immediately = _ecore_con_url_process_completed_jobs(url_con);
1714 if (!completed_immediately)
1716 if (url_con->fd_handler)
1717 ecore_main_fd_handler_del(url_con->fd_handler);
1719 url_con->fd_handler = NULL;
1721 /* url_con still active -- set up an fd_handler */
1723 FD_ZERO(&write_set);
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,
1729 if (ret != CURLM_OK)
1731 ERR("curl_multi_fdset failed: %s", curl_multi_strerror(ret));
1735 for (fd = 0; fd <= fd_max; fd++)
1737 if (!FD_ISSET(fd, &_current_fd_set))
1740 if (FD_ISSET(fd, &read_set))
1741 flags |= ECORE_FD_READ;
1743 if (FD_ISSET(fd, &write_set))
1744 flags |= ECORE_FD_WRITE;
1746 if (FD_ISSET(fd, &exc_set))
1747 flags |= ECORE_FD_ERROR;
1753 ret = curl_multi_timeout(_curlm, &ms);
1754 if (ret != CURLM_OK)
1755 ERR("curl_multi_timeout failed: %s",
1756 curl_multi_strerror(ret));
1761 FD_SET(fd, &_current_fd_set);
1763 url_con->flags = flags;
1764 url_con->fd_handler =
1765 ecore_main_fd_handler_add(fd, flags,
1766 _ecore_con_url_fd_handler,
1772 if (!url_con->fd_handler)
1774 /* Failed to set up an fd_handler */
1775 ecore_timer_freeze(_curl_timeout);
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));
1782 url_con->active = EINA_FALSE;
1787 ecore_timer_thaw(_curl_timeout);
1794 _ecore_con_url_idler_handler(void *data)
1797 int done = 1, still_running;
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())
1808 _ecore_con_url_process_completed_jobs(NULL);
1812 _ecore_con_url_restart_fd_handler();
1813 _fd_idler_handler = NULL;
1816 ecore_timer_freeze(_curl_timeout);
1819 (void *)0xACE ? ECORE_CALLBACK_RENEW : ECORE_CALLBACK_CANCEL;
1822 return ECORE_CALLBACK_RENEW;
1826 _ecore_con_url_fd_handler(void *data __UNUSED__,
1827 Ecore_Fd_Handler *fd_handler __UNUSED__)
1829 _ecore_con_url_suspend_fd_handler();
1831 if (!_fd_idler_handler)
1832 _fd_idler_handler = ecore_idler_add(
1833 _ecore_con_url_idler_handler, NULL);
1835 return ECORE_CALLBACK_RENEW;
1839 _ecore_con_url_process_completed_jobs(Ecore_Con_Url *url_con_to_match)
1842 Ecore_Con_Url *url_con;
1843 Ecore_Con_Event_Url_Complete *e;
1847 int job_matched = 0;
1849 /* Loop jobs and check if any are done */
1850 while ((curlmsg = curl_multi_info_read(_curlm, &n_remaining)))
1852 if (curlmsg->msg != CURLMSG_DONE)
1855 /* find the job which is done */
1856 EINA_LIST_FOREACH(_url_con_list, l, url_con)
1858 if (curlmsg->easy_handle == url_con->curl_easy)
1860 if (url_con_to_match &&
1861 (url_con == url_con_to_match))
1864 if(url_con->fd != -1)
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);
1872 url_con->fd_handler = NULL;
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));
1880 e->url_con = url_con;
1882 if (curlmsg->data.result == CURLE_OK)
1884 long status; /* curl API uses long, not int */
1887 curl_easy_getinfo(curlmsg->easy_handle,
1888 CURLINFO_RESPONSE_CODE,
1893 _url_complete_push_event(ECORE_CON_EVENT_URL_COMPLETE, e);
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));
1910 _ecore_con_event_url_free(void *data __UNUSED__,