2 * For info on how to use libcurl, see:
3 * http://curl.haxx.se/libcurl/c/libcurl-tutorial.html
7 * FIXME: Support more CURL features...
17 #include <sys/types.h>
20 #ifdef HAVE_WS2TCPIP_H
21 # include <ws2tcpip.h>
29 #include "ecore_private.h"
30 #include "Ecore_Con.h"
31 #include "ecore_con_private.h"
33 int ECORE_CON_EVENT_URL_DATA = 0;
34 int ECORE_CON_EVENT_URL_COMPLETE = 0;
35 int ECORE_CON_EVENT_URL_PROGRESS = 0;
38 static Eina_Bool _ecore_con_url_perform(Ecore_Con_Url *url_con);
39 static size_t _ecore_con_url_header_cb(void *ptr,
43 static size_t _ecore_con_url_data_cb(void *buffer,
47 static int _ecore_con_url_progress_cb(void *clientp,
52 static size_t _ecore_con_url_read_cb(void *ptr,
56 static void _ecore_con_event_url_free(void *data __UNUSED__,
58 static Eina_Bool _ecore_con_url_idler_handler(void *data);
59 static Eina_Bool _ecore_con_url_fd_handler(void *data __UNUSED__, Ecore_Fd_Handler *fd_handler __UNUSED__);
60 static Eina_Bool _ecore_con_url_timeout_cb(void *data);
62 static Eina_List *_url_con_list = NULL;
63 static Eina_List *_fd_hd_list = NULL;
64 static CURLM *_curlm = NULL;
65 static fd_set _current_fd_set;
66 static int _init_count = 0;
67 static Ecore_Timer *_curl_timeout = NULL;
68 static Eina_Bool pipelining = EINA_FALSE;
73 * @addtogroup Ecore_Con_Url_Group Ecore URL Connection Functions
79 ecore_con_url_init(void)
82 if (++_init_count > 1) return _init_count;
84 if (!ECORE_CON_EVENT_URL_DATA) ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
85 if (!ECORE_CON_EVENT_URL_COMPLETE) ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
86 if (!ECORE_CON_EVENT_URL_PROGRESS) ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
92 // curl_global_init() is not thread safe!
93 if (curl_global_init(CURL_GLOBAL_ALL)) return --_init_count;
95 _curlm = curl_multi_init();
96 if (!_curlm) return --_init_count;
98 curl_multi_timeout(_curlm, &ms);
99 if (ms <= 0) ms = 100;
101 _curl_timeout = ecore_timer_add((double)ms / 1000, _ecore_con_url_idler_handler, (void *)0xACE);
102 ecore_timer_freeze(_curl_timeout);
112 ecore_con_url_shutdown(void)
115 if (_init_count == 0) return 0;
117 if (--_init_count == 0)
119 Ecore_Con_Url *con_url;
120 Ecore_Fd_Handler *fd_handler;
124 ecore_timer_del(_curl_timeout);
125 _curl_timeout = NULL;
128 FD_ZERO(&_current_fd_set);
129 EINA_LIST_FREE(_url_con_list, con_url) ecore_con_url_free(con_url);
130 EINA_LIST_FREE(_fd_hd_list, fd_handler) ecore_main_fd_handler_del(fd_handler);
134 curl_multi_cleanup(_curlm);
137 curl_global_cleanup();
145 ecore_con_url_pipeline_set(Eina_Bool enable)
149 curl_multi_setopt(_curlm, CURLMOPT_PIPELINING, 1);
151 curl_multi_setopt(_curlm, CURLMOPT_PIPELINING, 0);
160 ecore_con_url_pipeline_get(void)
169 ecore_con_url_new(const char *url)
172 Ecore_Con_Url *url_con;
178 url_con = calloc(1, sizeof(Ecore_Con_Url));
182 url_con->write_fd = -1;
184 url_con->curl_easy = curl_easy_init();
185 if (!url_con->curl_easy)
191 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
193 if (!ecore_con_url_url_set(url_con, url))
195 ecore_con_url_free(url_con);
199 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_ENCODING, "gzip,deflate");
202 ERR("Could not set CURLOPT_ENCODING to \"gzip,deflate\": %s",
203 curl_easy_strerror(ret));
204 ecore_con_url_free(url_con);
208 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEFUNCTION,
209 _ecore_con_url_data_cb);
210 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEDATA, url_con);
212 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION,
213 _ecore_con_url_progress_cb);
214 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSDATA, url_con);
215 curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_FALSE);
217 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERFUNCTION,
218 _ecore_con_url_header_cb);
219 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERDATA, url_con);
222 * FIXME: Check that these timeouts are sensible defaults
223 * FIXME: Provide a means to change these timeouts
225 curl_easy_setopt(url_con->curl_easy, CURLOPT_CONNECTTIMEOUT, 30);
226 curl_easy_setopt(url_con->curl_easy, CURLOPT_FOLLOWLOCATION, 1);
236 ecore_con_url_custom_new(const char *url,
237 const char *custom_request)
240 Ecore_Con_Url *url_con;
249 url_con = ecore_con_url_new(url);
254 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_CUSTOMREQUEST, custom_request);
257 ERR("Could not set a custom request string: %s",
258 curl_easy_strerror(ret));
259 ecore_con_url_free(url_con);
267 custom_request = NULL;
272 ecore_con_url_free(Ecore_Con_Url *url_con)
281 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
283 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_free");
287 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
289 if (url_con->curl_easy)
291 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION, NULL);
292 curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_TRUE);
294 if (eina_list_data_find(_url_con_list, url_con))
296 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
297 if (ret != CURLM_OK) ERR("curl_multi_remove_handle failed: %s", curl_multi_strerror(ret));
298 _url_con_list = eina_list_remove(_url_con_list, url_con);
301 curl_easy_cleanup(url_con->curl_easy);
303 if (url_con->timer) ecore_timer_del(url_con->timer);
305 curl_slist_free_all(url_con->headers);
306 EINA_LIST_FREE(url_con->additional_headers, s)
308 EINA_LIST_FREE(url_con->response_headers, s)
310 eina_stringshare_del(url_con->url);
319 ecore_con_url_url_get(Ecore_Con_Url *url_con)
322 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
324 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, __func__);
335 ecore_con_url_url_set(Ecore_Con_Url *url_con,
339 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
341 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_url_set");
345 if (eina_list_data_find(_url_con_list, url_con)) return EINA_FALSE;
347 eina_stringshare_replace(&url_con->url, url);
350 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL,
353 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL, "");
364 ecore_con_url_data_set(Ecore_Con_Url *url_con,
368 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
370 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_set");
374 url_con->data = data;
383 ecore_con_url_additional_header_add(Ecore_Con_Url *url_con,
390 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
392 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
393 "ecore_con_url_additional_header_add");
397 tmp = malloc(strlen(key) + strlen(value) + 3);
401 sprintf(tmp, "%s: %s", key, value);
402 url_con->additional_headers = eina_list_append(url_con->additional_headers,
413 ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
418 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
420 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
421 "ecore_con_url_additional_headers_clear");
425 EINA_LIST_FREE(url_con->additional_headers, s)
434 ecore_con_url_data_get(Ecore_Con_Url *url_con)
437 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
439 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_get");
443 return url_con->data;
451 ecore_con_url_time(Ecore_Con_Url *url_con,
452 Ecore_Con_Url_Time condition,
456 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
458 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_time");
462 url_con->time_condition = condition;
463 url_con->timestamp = timestamp;
473 ecore_con_url_fd_set(Ecore_Con_Url *url_con,
477 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
479 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_set");
483 url_con->write_fd = fd;
492 ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
495 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
497 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
498 "ecore_con_url_received_bytes_get");
502 return url_con->received;
509 EAPI const Eina_List *
510 ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
513 return url_con->response_headers;
521 ecore_con_url_httpauth_set(Ecore_Con_Url *url_con,
522 const char *username,
523 const char *password,
529 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
531 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
532 "ecore_con_url_httpauth_set");
536 # if LIBCURL_VERSION_NUM >= 0x071301
537 if ((username) && (password))
540 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH,
543 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
545 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
548 ERR("Could not set username for HTTP authentication: %s",
549 curl_easy_strerror(ret));
553 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
556 ERR("Could not set password for HTTP authentication: %s",
557 curl_easy_strerror(ret));
580 _ecore_con_url_send(Ecore_Con_Url *url_con,
584 const char *content_type)
591 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
593 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_send");
597 if (eina_list_data_find(_url_con_list, url_con)) return EINA_FALSE;
602 /* Free response headers from previous send() calls */
603 EINA_LIST_FREE(url_con->response_headers, s)
605 url_con->response_headers = NULL;
607 curl_slist_free_all(url_con->headers);
608 url_con->headers = NULL;
610 if ((mode == MODE_POST) || (mode == MODE_AUTO))
614 if ((content_type) && (strlen(content_type) < 200))
616 snprintf(tmp, sizeof(tmp), "Content-Type: %s", content_type);
617 url_con->headers = curl_slist_append(url_con->headers, tmp);
620 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDS, data);
621 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, length);
623 else curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, 0);
624 if (mode == MODE_POST)
625 curl_easy_setopt(url_con->curl_easy, CURLOPT_POST, 1);
628 switch (url_con->time_condition)
630 case ECORE_CON_URL_TIME_NONE:
631 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
635 case ECORE_CON_URL_TIME_IFMODSINCE:
636 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
637 CURL_TIMECOND_IFMODSINCE);
638 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE,
639 (long)url_con->timestamp);
642 case ECORE_CON_URL_TIME_IFUNMODSINCE:
643 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
644 CURL_TIMECOND_IFUNMODSINCE);
645 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE,
646 (long)url_con->timestamp);
650 /* Additional headers */
651 EINA_LIST_FOREACH(url_con->additional_headers, l, s)
652 url_con->headers = curl_slist_append(url_con->headers, s);
654 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPHEADER, url_con->headers);
656 url_con->received = 0;
658 return _ecore_con_url_perform(url_con);
669 EINA_DEPRECATED EAPI Eina_Bool
670 ecore_con_url_send(Ecore_Con_Url *url_con,
673 const char *content_type)
675 return _ecore_con_url_send(url_con, MODE_AUTO, data, length, content_type);
679 ecore_con_url_get(Ecore_Con_Url *url_con)
681 return _ecore_con_url_send(url_con, MODE_GET, NULL, 0, NULL);
685 ecore_con_url_post(Ecore_Con_Url *url_con,
688 const char *content_type)
690 return _ecore_con_url_send(url_con, MODE_POST, data, length, content_type);
694 ecore_con_url_ftp_upload(Ecore_Con_Url *url_con,
695 const char *filename,
698 const char *upload_dir)
704 struct stat file_info;
707 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
709 ECORE_MAGIC_FAIL(url_con,
711 "ecore_con_url_ftp_upload");
715 if (eina_list_data_find(_url_con_list, url_con)) return EINA_FALSE;
722 if (stat(filename, &file_info))
725 snprintf(userpwd, sizeof(userpwd), "%s:%s", user, pass);
726 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERPWD, userpwd);
729 ERR("Could not set username and password for FTP upload: %s",
730 curl_easy_strerror(ret));
735 snprintf(tmp, PATH_MAX, "%s", filename);
738 snprintf(url, sizeof(url), "ftp://%s/%s/%s", url_con->url,
739 upload_dir, basename(tmp));
741 snprintf(url, sizeof(url), "ftp://%s/%s", url_con->url,
744 if (!ecore_con_url_url_set(url_con, url))
747 curl_easy_setopt(url_con->curl_easy, CURLOPT_INFILESIZE_LARGE,
748 (curl_off_t)file_info.st_size);
749 curl_easy_setopt(url_con->curl_easy, CURLOPT_UPLOAD, 1);
750 curl_easy_setopt(url_con->curl_easy, CURLOPT_READFUNCTION,
751 _ecore_con_url_read_cb);
753 fd = fopen(filename, "rb");
756 ERR("Could not open \"%s\" for FTP upload", filename);
759 curl_easy_setopt(url_con->curl_easy, CURLOPT_READDATA, fd);
761 return _ecore_con_url_perform(url_con);
776 ecore_con_url_cookies_init(Ecore_Con_Url *url_con)
782 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
784 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
785 "ecore_con_url_cookies_init");
789 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEFILE, "");
797 ecore_con_url_cookies_ignore_old_session_set(Ecore_Con_Url *url_con, Eina_Bool ignore)
803 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
805 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
806 "ecore_con_url_cookies_ignore_old_session_set");
810 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIESESSION, ignore);
819 ecore_con_url_cookies_clear(Ecore_Con_Url *url_con)
825 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
827 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
828 "ecore_con_url_cookies_clear");
832 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "ALL");
840 ecore_con_url_cookies_session_clear(Ecore_Con_Url *url_con)
846 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
848 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
849 "ecore_con_url_cookies_session_clear");
853 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "SESS");
861 ecore_con_url_cookies_file_add(Ecore_Con_Url *url_con, const char * const file_name)
867 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
869 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
870 "ecore_con_url_cookies_file_add");
874 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEFILE, file_name);
883 ecore_con_url_cookies_jar_file_set(Ecore_Con_Url *url_con, const char * const cookiejar_file)
891 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
893 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
894 "ecore_con_url_cookies_jar_file_set");
898 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEJAR,
902 ERR("Setting the cookie-jar name failed: %s",
903 curl_easy_strerror(ret));
911 (void)cookiejar_file;
916 ecore_con_url_cookies_jar_write(Ecore_Con_Url *url_con)
922 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
924 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
925 "ecore_con_url_cookies_jar_write");
929 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "FLUSH");
937 ecore_con_url_verbose_set(Ecore_Con_Url *url_con,
941 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
943 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
944 "ecore_con_url_verbose_set");
948 if (eina_list_data_find(_url_con_list, url_con)) return;
953 curl_easy_setopt(url_con->curl_easy, CURLOPT_VERBOSE, (int)verbose);
962 ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con,
966 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
968 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
969 "ecore_con_url_ftp_use_epsv_set");
973 if (eina_list_data_find(_url_con_list, url_con)) return;
978 curl_easy_setopt(url_con->curl_easy, CURLOPT_FTP_USE_EPSV, (int)use_epsv);
987 * Toggle libcurl's verify peer's certificate option.
989 * If @p verify is @c EINA_TRUE, libcurl will verify
990 * the authenticity of the peer's certificate, otherwise
991 * it will not. Default behavior of libcurl is to check
992 * peer's certificate.
994 * @param url_con Ecore_Con_Url instance which will be acted upon.
995 * @param verify Whether or not libcurl will check peer's certificate.
999 ecore_con_url_ssl_verify_peer_set(Ecore_Con_Url *url_con,
1003 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1005 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1006 "ecore_con_url_ssl_verify_peer_set");
1010 if (eina_list_data_find(_url_con_list, url_con)) return;
1015 curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, (int)verify);
1024 * Set a custom CA to trust for SSL/TLS connections.
1026 * Specify the path of a file (in PEM format) containing one or more
1027 * CA certificate(s) to use for the validation of the server certificate.
1029 * This function can also disable CA validation if @p ca_path is @c NULL.
1030 * However, the server certificate still needs to be valid for the connection
1031 * to succeed (i.e., the certificate must concern the server the
1032 * connection is made to).
1034 * @param url_con Connection object that will use the custom CA.
1035 * @param ca_path Path to a CA certificate(s) file or @c NULL to disable
1038 * @return @c 0 on success. When cURL is used, non-zero return values
1039 * are equal to cURL error codes.
1042 ecore_con_url_ssl_ca_set(Ecore_Con_Url *url_con, const char *ca_path)
1047 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1049 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_ssl_ca_set");
1053 if (eina_list_data_find(_url_con_list, url_con)) return -1;
1054 if (!url_con->url) return -1;
1055 if (ca_path == NULL)
1056 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, 0);
1059 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, 1);
1061 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_CAINFO, ca_path);
1073 ecore_con_url_proxy_set(Ecore_Con_Url *url_con, const char *proxy)
1077 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1079 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_proxy_set");
1083 if (eina_list_data_find(_url_con_list, url_con)) return EINA_FALSE;
1084 if (!url_con->url) return EINA_FALSE;
1086 if (proxy == NULL) res = curl_easy_setopt(url_con->curl_easy, CURLOPT_PROXY, "");
1087 else res = curl_easy_setopt(url_con->curl_easy, CURLOPT_PROXY, proxy);
1089 if (res != CURLE_OK)
1091 ERR("curl_easy_setopt() failed");
1104 ecore_con_url_timeout_set(Ecore_Con_Url *url_con, double timeout)
1107 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1109 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_timeout_set");
1113 if (eina_list_data_find(_url_con_list, url_con)) return;
1114 if (!url_con->url || timeout < 0) return;
1115 if (url_con->timer) ecore_timer_del(url_con->timer);
1116 url_con->timer = ecore_timer_add(timeout, _ecore_con_url_timeout_cb, url_con);
1130 _ecore_con_url_timeout_cb(void *data)
1132 Ecore_Con_Url *url_con = data;
1134 Ecore_Con_Event_Url_Complete *e;
1136 if (!url_con) return ECORE_CALLBACK_CANCEL;
1137 if (!url_con->curl_easy) return ECORE_CALLBACK_CANCEL;
1138 if (!eina_list_data_find(_url_con_list, url_con)) return ECORE_CALLBACK_CANCEL;
1140 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
1141 if (ret != CURLM_OK) ERR("curl_multi_remove_handle failed: %s", curl_multi_strerror(ret));
1142 _url_con_list = eina_list_remove(_url_con_list, url_con);
1144 curl_slist_free_all(url_con->headers);
1145 url_con->headers = NULL;
1147 url_con->timer = NULL;
1149 e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
1152 e->url_con = url_con;
1154 ecore_event_add(ECORE_CON_EVENT_URL_COMPLETE, e, _ecore_con_event_url_free, NULL);
1156 return ECORE_CALLBACK_CANCEL;
1160 _ecore_con_url_data_cb(void *buffer,
1165 Ecore_Con_Url *url_con;
1166 Ecore_Con_Event_Url_Data *e;
1167 size_t real_size = size * nitems;
1169 url_con = (Ecore_Con_Url *)userp;
1174 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1176 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_cb");
1180 url_con->received += real_size;
1182 if (url_con->write_fd < 0)
1185 malloc(sizeof(Ecore_Con_Event_Url_Data) + sizeof(unsigned char) *
1189 e->url_con = url_con;
1190 e->size = real_size;
1191 memcpy(e->data, buffer, real_size);
1192 ecore_event_add(ECORE_CON_EVENT_URL_DATA, e, _ecore_con_event_url_free, NULL);
1198 size_t total_size = real_size;
1201 while (total_size > 0)
1203 count = write(url_con->write_fd,
1204 (char *)buffer + offset,
1208 if (errno != EAGAIN && errno != EINTR)
1213 total_size -= count;
1223 _ecore_con_url_header_cb(void *ptr,
1228 size_t real_size = size * nitems;
1229 Ecore_Con_Url *url_con = stream;
1231 char *header = malloc(sizeof(char) * (real_size + 1));
1235 memcpy(header, ptr, real_size);
1236 header[real_size] = '\0';
1238 url_con->response_headers = eina_list_append(url_con->response_headers,
1245 _ecore_con_url_progress_cb(void *clientp,
1251 Ecore_Con_Event_Url_Progress *e;
1252 Ecore_Con_Url *url_con;
1256 e = malloc(sizeof(Ecore_Con_Event_Url_Progress));
1259 e->url_con = url_con;
1260 e->down.total = dltotal;
1261 e->down.now = dlnow;
1262 e->up.total = ultotal;
1264 ecore_event_add(ECORE_CON_EVENT_URL_PROGRESS, e, _ecore_con_event_url_free, NULL);
1271 _ecore_con_url_read_cb(void *ptr,
1276 size_t retcode = fread(ptr, size, nitems, stream);
1278 if (ferror((FILE *)stream))
1281 return CURL_READFUNC_ABORT;
1283 else if (retcode == 0)
1285 fclose((FILE *)stream);
1290 INF("*** We read %Iu bytes from file", retcode);
1292 INF("*** We read %zu bytes from file", retcode);
1298 _ecore_con_url_info_read(void)
1303 while ((curlmsg = curl_multi_info_read(_curlm, &n_remaining)))
1305 if (curlmsg->msg == CURLMSG_DONE)
1308 Ecore_Con_Url *url_con;
1310 EINA_LIST_FOREACH_SAFE(_url_con_list, l, ll, url_con)
1312 if (curlmsg->easy_handle == url_con->curl_easy)
1315 Ecore_Con_Event_Url_Complete *e;
1317 e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
1320 e->url_con = url_con;
1322 if (curlmsg->data.result == CURLE_OK)
1324 long status; /* curl API uses long, not int */
1326 curl_easy_getinfo(curlmsg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
1329 ecore_event_add(ECORE_CON_EVENT_URL_COMPLETE, e, _ecore_con_event_url_free, NULL);
1332 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
1333 if (ret != CURLM_OK) ERR("curl_multi_remove_handle failed: %s", curl_multi_strerror(ret));
1334 _url_con_list = eina_list_remove(_url_con_list, url_con);
1343 _ecore_con_url_curl_clear(void)
1345 Ecore_Con_Url *url_con;
1347 FD_ZERO(&_current_fd_set);
1350 Ecore_Fd_Handler *fd_handler;
1351 EINA_LIST_FREE(_fd_hd_list, fd_handler)
1353 int fd = ecore_main_fd_handler_fd_get(fd_handler);
1354 FD_CLR(fd, &_current_fd_set);
1355 // FIXME: ecore_main_fd_handler_del() sometimes give errors
1356 // because curl do not make fd itself controlled by users, but it can be ignored.
1357 ecore_main_fd_handler_del(fd_handler);
1361 EINA_LIST_FREE(_url_con_list, url_con)
1364 Ecore_Con_Event_Url_Complete *e;
1366 e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
1369 e->url_con = url_con;
1371 ecore_event_add(ECORE_CON_EVENT_URL_COMPLETE, e, _ecore_con_event_url_free, NULL);
1373 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
1374 if (ret != CURLM_OK) ERR("curl_multi_remove_handle failed: %s", curl_multi_strerror(ret));
1379 _ecore_con_url_fd_handler(void *data __UNUSED__, Ecore_Fd_Handler *fd_handler __UNUSED__)
1383 Ecore_Fd_Handler *fd_handler;
1384 EINA_LIST_FREE(_fd_hd_list, fd_handler)
1386 int fd = ecore_main_fd_handler_fd_get(fd_handler);
1387 FD_CLR(fd, &_current_fd_set);
1388 // FIXME: ecore_main_fd_handler_del() sometimes give errors
1389 // because curl do not make fd itself controlled by users, but it can be ignored.
1390 ecore_main_fd_handler_del(fd_handler);
1393 ecore_timer_thaw(_curl_timeout);
1394 return ECORE_CALLBACK_RENEW;
1398 _ecore_con_url_fdset(void)
1401 fd_set read_set, write_set, exc_set;
1403 Ecore_Fd_Handler *fd_handler;
1406 FD_ZERO(&write_set);
1409 ret = curl_multi_fdset(_curlm, &read_set, &write_set, &exc_set, &fd_max);
1410 if (ret != CURLM_OK)
1412 ERR("curl_multi_fdset failed: %s", curl_multi_strerror(ret));
1416 for (fd = 0; fd <= fd_max; fd++)
1419 if (FD_ISSET(fd, &read_set)) flags |= ECORE_FD_READ;
1420 if (FD_ISSET(fd, &write_set)) flags |= ECORE_FD_WRITE;
1421 if (FD_ISSET(fd, &exc_set)) flags |= ECORE_FD_ERROR;
1424 if (!FD_ISSET(fd, &_current_fd_set))
1426 FD_SET(fd, &_current_fd_set);
1427 fd_handler = ecore_main_fd_handler_add(fd, flags, _ecore_con_url_fd_handler, NULL, NULL, NULL);
1428 if (fd_handler) _fd_hd_list = eina_list_append(_fd_hd_list, fd_handler);
1429 ecore_timer_freeze(_curl_timeout);
1436 _ecore_con_url_idler_handler(void *data __UNUSED__)
1441 ret = curl_multi_perform(_curlm, &still_running);
1442 if (ret != CURLM_OK)
1444 ERR("curl_multi_perform() failed: %s", curl_multi_strerror(ret));
1445 _ecore_con_url_curl_clear();
1446 ecore_timer_freeze(_curl_timeout);
1447 return ECORE_CALLBACK_RENEW;
1449 if (ret == CURLM_CALL_MULTI_PERFORM)
1451 DBG("Call multiperform again");
1452 return ECORE_CALLBACK_RENEW;
1455 _ecore_con_url_info_read();
1458 DBG("multiperform is still_running");
1459 _ecore_con_url_fdset();
1463 DBG("multiperform ended");
1464 _ecore_con_url_curl_clear();
1465 ecore_timer_freeze(_curl_timeout);
1468 return ECORE_CALLBACK_RENEW;
1472 _ecore_con_url_perform(Ecore_Con_Url *url_con)
1476 ret = curl_multi_add_handle(_curlm, url_con->curl_easy);
1477 if (ret != CURLM_OK)
1479 ERR("curl_multi_add_handle() failed: %s", curl_multi_strerror(ret));
1483 _url_con_list = eina_list_append(_url_con_list, url_con);
1484 ecore_timer_thaw(_curl_timeout);
1490 _ecore_con_event_url_free(void *data __UNUSED__,