aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/ecore/src/lib/ecore_con/ecore_con_url.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/ecore/src/lib/ecore_con/ecore_con_url.c')
-rw-r--r--libraries/ecore/src/lib/ecore_con/ecore_con_url.c1635
1 files changed, 0 insertions, 1635 deletions
diff --git a/libraries/ecore/src/lib/ecore_con/ecore_con_url.c b/libraries/ecore/src/lib/ecore_con/ecore_con_url.c
deleted file mode 100644
index 05f0678..0000000
--- a/libraries/ecore/src/lib/ecore_con/ecore_con_url.c
+++ /dev/null
@@ -1,1635 +0,0 @@
1/*
2 * For info on how to use libcurl, see:
3 * http://curl.haxx.se/libcurl/c/libcurl-tutorial.html
4 */
5
6/*
7 * FIXME: Support more CURL features...
8 */
9
10#ifdef HAVE_CONFIG_H
11# include <config.h>
12#endif
13
14#include <string.h>
15#include <errno.h>
16#include <sys/stat.h>
17#include <sys/types.h>
18#include <unistd.h>
19
20#ifdef HAVE_WS2TCPIP_H
21# include <ws2tcpip.h>
22#endif
23
24#ifdef HAVE_ESCAPE
25# include <Escape.h>
26#endif
27
28#include "Ecore.h"
29#include "ecore_private.h"
30#include "Ecore_Con.h"
31#include "ecore_con_private.h"
32
33#define CURL_MIN_TIMEOUT 100
34
35int ECORE_CON_EVENT_URL_DATA = 0;
36int ECORE_CON_EVENT_URL_COMPLETE = 0;
37int ECORE_CON_EVENT_URL_PROGRESS = 0;
38
39#ifdef HAVE_CURL
40static void _ecore_con_url_event_url_complete(Ecore_Con_Url *url_con, CURLMsg *curlmsg);
41static void _ecore_con_url_multi_remove(Ecore_Con_Url *url_con);
42static Eina_Bool _ecore_con_url_perform(Ecore_Con_Url *url_con);
43static size_t _ecore_con_url_header_cb(void *ptr, size_t size, size_t nitems, void *stream);
44static size_t _ecore_con_url_data_cb(void *buffer, size_t size, size_t nitems, void *userp);
45static int _ecore_con_url_progress_cb(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
46static size_t _ecore_con_url_read_cb(void *ptr, size_t size, size_t nitems, void *stream);
47static void _ecore_con_event_url_free(Ecore_Con_Url *url_con, void *ev);
48static Eina_Bool _ecore_con_url_timer(void *data);
49static Eina_Bool _ecore_con_url_fd_handler(void *data, Ecore_Fd_Handler *fd_handler);
50static Eina_Bool _ecore_con_url_timeout_cb(void *data);
51static void _ecore_con_url_status_get(Ecore_Con_Url *url_con);
52
53static Eina_List *_url_con_list = NULL;
54static Eina_List *_fd_hd_list = NULL;
55static CURLM *_curlm = NULL;
56static int _init_count = 0;
57static Ecore_Timer *_curl_timer = NULL;
58static Eina_Bool pipelining = EINA_FALSE;
59
60#endif
61
62/**
63 * @addtogroup Ecore_Con_Url_Group Ecore URL Connection Functions
64 *
65 * @{
66 */
67
68EAPI int
69ecore_con_url_init(void)
70{
71#ifdef HAVE_CURL
72 long ms;
73 if (++_init_count > 1) return _init_count;
74
75 ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
76 ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
77 ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
78
79 // curl_global_init() is not thread safe!
80 if (curl_global_init(CURL_GLOBAL_ALL)) return --_init_count;
81
82 _curlm = curl_multi_init();
83 if (!_curlm)
84 {
85 curl_global_cleanup();
86 return --_init_count;
87 }
88
89 curl_multi_timeout(_curlm, &ms);
90 if (ms >= CURL_MIN_TIMEOUT || ms <= 0) ms = CURL_MIN_TIMEOUT;
91
92 _curl_timer = ecore_timer_add((double)ms / 1000, _ecore_con_url_timer, NULL);
93 ecore_timer_freeze(_curl_timer);
94
95 return _init_count;
96#else
97 return 0;
98#endif
99}
100
101EAPI int
102ecore_con_url_shutdown(void)
103{
104#ifdef HAVE_CURL
105 Ecore_Con_Url *url_con;
106 Ecore_Fd_Handler *fd_handler;
107 if (_init_count == 0) return 0;
108 --_init_count;
109 if (_init_count) return _init_count;
110
111 if (_curl_timer)
112 {
113 ecore_timer_del(_curl_timer);
114 _curl_timer = NULL;
115 }
116
117 EINA_LIST_FREE(_url_con_list, url_con)
118 ecore_con_url_free(url_con);
119 EINA_LIST_FREE(_fd_hd_list, fd_handler)
120 ecore_main_fd_handler_del(fd_handler);
121
122 if (_curlm)
123 {
124 curl_multi_cleanup(_curlm);
125 _curlm = NULL;
126 }
127 curl_global_cleanup();
128 return 0;
129#endif
130 return 1;
131}
132
133EAPI void
134ecore_con_url_pipeline_set(Eina_Bool enable)
135{
136#ifdef HAVE_CURL
137 if (enable == pipelining) return;
138 curl_multi_setopt(_curlm, CURLMOPT_PIPELINING, !!enable);
139 pipelining = enable;
140#else
141 return;
142 (void)enable;
143#endif
144}
145
146EAPI Eina_Bool
147ecore_con_url_pipeline_get(void)
148{
149#ifdef HAVE_CURL
150 return pipelining;
151#endif
152 return EINA_FALSE;
153}
154
155extern Ecore_Con_Socks *_ecore_con_proxy_global;
156
157EAPI Ecore_Con_Url *
158ecore_con_url_new(const char *url)
159{
160#ifdef HAVE_CURL
161 Ecore_Con_Url *url_con;
162 CURLcode ret;
163
164 if (!_init_count)
165 return NULL;
166
167 url_con = calloc(1, sizeof(Ecore_Con_Url));
168 if (!url_con)
169 return NULL;
170
171 url_con->write_fd = -1;
172
173 url_con->curl_easy = curl_easy_init();
174 if (!url_con->curl_easy)
175 {
176 free(url_con);
177 return NULL;
178 }
179
180 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_CON_URL);
181
182 if (!ecore_con_url_url_set(url_con, url))
183 {
184 ecore_con_url_free(url_con);
185 return NULL;
186 }
187
188 // Read socks proxy
189 url_con->proxy_type = -1;
190 if (_ecore_con_proxy_global && _ecore_con_proxy_global->ip &&
191 (_ecore_con_proxy_global->version == 4 ||
192 _ecore_con_proxy_global->version == 5))
193 {
194 char proxy[256];
195 char host[256];
196
197 if (_ecore_con_proxy_global->version == 5)
198 {
199 if (_ecore_con_proxy_global->lookup)
200 snprintf(host, sizeof(host), "socks5h://%s",
201 _ecore_con_proxy_global->ip);
202 else snprintf(host, sizeof(host), "socks5://%s",
203 _ecore_con_proxy_global->ip);
204 }
205 else if (_ecore_con_proxy_global->version == 4)
206 {
207 if (_ecore_con_proxy_global->lookup)
208 snprintf(host, sizeof(host), "socks4a://%s",
209 _ecore_con_proxy_global->ip);
210 else snprintf(host, sizeof(host), "socks4://%s",
211 _ecore_con_proxy_global->ip);
212 }
213
214 if (_ecore_con_proxy_global->port > 0 &&
215 _ecore_con_proxy_global->port <= 65535)
216 snprintf(proxy, sizeof(proxy), "%s:%d", host,
217 _ecore_con_proxy_global->port);
218 else snprintf(proxy, sizeof(proxy), "%s", host);
219
220 ecore_con_url_proxy_set(url_con, proxy);
221 ecore_con_url_proxy_username_set(url_con,
222 _ecore_con_proxy_global->username);
223 }
224
225 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_ENCODING, "gzip,deflate");
226 if (ret != CURLE_OK)
227 {
228 ERR("Could not set CURLOPT_ENCODING to \"gzip,deflate\": %s",
229 curl_easy_strerror(ret));
230 ecore_con_url_free(url_con);
231 return NULL;
232 }
233
234 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEFUNCTION,
235 _ecore_con_url_data_cb);
236 curl_easy_setopt(url_con->curl_easy, CURLOPT_WRITEDATA, url_con);
237
238 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION,
239 _ecore_con_url_progress_cb);
240 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSDATA, url_con);
241 curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_FALSE);
242
243 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERFUNCTION,
244 _ecore_con_url_header_cb);
245 curl_easy_setopt(url_con->curl_easy, CURLOPT_HEADERDATA, url_con);
246
247 /*
248 * FIXME: Check that these timeouts are sensible defaults
249 * FIXME: Provide a means to change these timeouts
250 */
251 curl_easy_setopt(url_con->curl_easy, CURLOPT_CONNECTTIMEOUT, 30);
252 curl_easy_setopt(url_con->curl_easy, CURLOPT_FOLLOWLOCATION, 1);
253
254 return url_con;
255#else
256 return NULL;
257 url = NULL;
258#endif
259}
260
261EAPI Ecore_Con_Url *
262ecore_con_url_custom_new(const char *url,
263 const char *custom_request)
264{
265#ifdef HAVE_CURL
266 Ecore_Con_Url *url_con;
267 CURLcode ret;
268
269 if (!url)
270 return NULL;
271
272 if (!custom_request)
273 return NULL;
274
275 url_con = ecore_con_url_new(url);
276
277 if (!url_con)
278 return NULL;
279
280 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_CUSTOMREQUEST, custom_request);
281 if (ret != CURLE_OK)
282 {
283 ERR("Could not set a custom request string: %s",
284 curl_easy_strerror(ret));
285 ecore_con_url_free(url_con);
286 return NULL;
287 }
288
289 return url_con;
290#else
291 return NULL;
292 url = NULL;
293 custom_request = NULL;
294#endif
295}
296
297EAPI void
298ecore_con_url_free(Ecore_Con_Url *url_con)
299{
300#ifdef HAVE_CURL
301 char *s;
302
303 if (!url_con) return;
304
305 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
306 {
307 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_free");
308 return;
309 }
310
311 if (url_con->curl_easy)
312 {
313 // FIXME : How can we delete curl_easy's fds ?? (Curl do not give this info.)
314 // This cause "Failed to delete epoll fd xx!" error messages
315 curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION, NULL);
316 curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_TRUE);
317
318 if (url_con->multi)
319 {
320 _ecore_con_url_multi_remove(url_con);
321 _url_con_list = eina_list_remove(_url_con_list, url_con);
322 }
323
324 curl_easy_cleanup(url_con->curl_easy);
325 }
326 if (url_con->timer) ecore_timer_del(url_con->timer);
327
328 url_con->curl_easy = NULL;
329 url_con->timer = NULL;
330 url_con->dead = EINA_TRUE;
331 if (url_con->event_count) return;
332 ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
333
334 curl_slist_free_all(url_con->headers);
335 EINA_LIST_FREE(url_con->additional_headers, s)
336 free(s);
337 EINA_LIST_FREE(url_con->response_headers, s)
338 free(s);
339 eina_stringshare_del(url_con->url);
340 if (url_con->post_data) free(url_con->post_data);
341 free(url_con);
342#else
343 return;
344 (void)url_con;
345#endif
346}
347
348EAPI const char *
349ecore_con_url_url_get(Ecore_Con_Url *url_con)
350{
351#ifdef HAVE_CURL
352 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
353 {
354 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, __func__);
355 return NULL;
356 }
357 return url_con->url;
358#else
359 return NULL;
360 (void)url_con;
361#endif
362}
363
364EAPI int
365ecore_con_url_status_code_get(Ecore_Con_Url *url_con)
366{
367#ifdef HAVE_CURL
368 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
369 {
370 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, __func__);
371 return 0;
372 }
373
374 if (url_con->status) return url_con->status;
375 _ecore_con_url_status_get(url_con);
376 return url_con->status;
377#else
378 return -1;
379 (void)url_con;
380#endif
381}
382
383EAPI Eina_Bool
384ecore_con_url_url_set(Ecore_Con_Url *url_con, const char *url)
385{
386#ifdef HAVE_CURL
387 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
388 {
389 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_url_set");
390 return EINA_FALSE;
391 }
392
393 if (url_con->dead) return EINA_FALSE;
394 eina_stringshare_replace(&url_con->url, url);
395
396 if (url_con->url)
397 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL,
398 url_con->url);
399 else
400 curl_easy_setopt(url_con->curl_easy, CURLOPT_URL, "");
401
402 return EINA_TRUE;
403#else
404 return EINA_FALSE;
405 (void)url;
406 (void)url_con;
407#endif
408}
409
410EAPI void
411ecore_con_url_data_set(Ecore_Con_Url *url_con, void *data)
412{
413#ifdef HAVE_CURL
414 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
415 {
416 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_set");
417 return;
418 }
419
420 url_con->data = data;
421#else
422 return;
423 url_con = NULL;
424 data = NULL;
425#endif
426}
427
428EAPI void
429ecore_con_url_additional_header_add(Ecore_Con_Url *url_con, const char *key, const char *value)
430{
431#ifdef HAVE_CURL
432 char *tmp;
433
434 if (url_con->dead) return;
435 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
436 {
437 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
438 "ecore_con_url_additional_header_add");
439 return;
440 }
441
442 if (url_con->dead) return;
443 tmp = malloc(strlen(key) + strlen(value) + 3);
444 if (!tmp)
445 return;
446
447 sprintf(tmp, "%s: %s", key, value);
448 url_con->additional_headers = eina_list_append(url_con->additional_headers,
449 tmp);
450#else
451 return;
452 url_con = NULL;
453 key = NULL;
454 value = NULL;
455#endif
456}
457
458EAPI void
459ecore_con_url_additional_headers_clear(Ecore_Con_Url *url_con)
460{
461#ifdef HAVE_CURL
462 char *s;
463
464 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
465 {
466 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
467 "ecore_con_url_additional_headers_clear");
468 return;
469 }
470
471 EINA_LIST_FREE(url_con->additional_headers, s)
472 free(s);
473#else
474 return;
475 url_con = NULL;
476#endif
477}
478
479EAPI void *
480ecore_con_url_data_get(Ecore_Con_Url *url_con)
481{
482#ifdef HAVE_CURL
483 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
484 {
485 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_get");
486 return NULL;
487 }
488
489 return url_con->data;
490#else
491 return NULL;
492 url_con = NULL;
493#endif
494}
495
496EAPI void
497ecore_con_url_time(Ecore_Con_Url *url_con, Ecore_Con_Url_Time condition, double timestamp)
498{
499#ifdef HAVE_CURL
500 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
501 {
502 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_time");
503 return;
504 }
505
506 if (url_con->dead) return;
507 url_con->time_condition = condition;
508 url_con->timestamp = timestamp;
509#else
510 return;
511 (void)url_con;
512 (void)condition;
513 (void)timestamp;
514#endif
515}
516
517EAPI void
518ecore_con_url_fd_set(Ecore_Con_Url *url_con, int fd)
519{
520#ifdef HAVE_CURL
521 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
522 {
523 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_set");
524 return;
525 }
526
527 if (url_con->dead) return;
528 url_con->write_fd = fd;
529#else
530 return;
531 (void)url_con;
532 (void)fd;
533#endif
534}
535
536EAPI int
537ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
538{
539#ifdef HAVE_CURL
540 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
541 {
542 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
543 "ecore_con_url_received_bytes_get");
544 return -1;
545 }
546
547 return url_con->received;
548#else
549 return 0;
550 (void)url_con;
551#endif
552}
553
554EAPI const Eina_List *
555ecore_con_url_response_headers_get(Ecore_Con_Url *url_con)
556{
557#ifdef HAVE_CURL
558 return url_con->response_headers;
559#else
560 return NULL;
561 (void)url_con;
562#endif
563}
564
565EAPI Eina_Bool
566ecore_con_url_httpauth_set(Ecore_Con_Url *url_con, const char *username, const char *password, Eina_Bool safe)
567{
568#ifdef HAVE_CURL
569 CURLcode ret;
570
571 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
572 {
573 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
574 "ecore_con_url_httpauth_set");
575 return EINA_FALSE;
576 }
577
578 if (url_con->dead) return EINA_FALSE;
579# if LIBCURL_VERSION_NUM >= 0x071301
580 if ((username) && (password))
581 {
582 if (safe)
583 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH,
584 CURLAUTH_ANYSAFE);
585 else
586 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
587
588 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
589 if (ret != CURLE_OK)
590 {
591 ERR("Could not set username for HTTP authentication: %s",
592 curl_easy_strerror(ret));
593 return EINA_FALSE;
594 }
595
596 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
597 if (ret != CURLE_OK)
598 {
599 ERR("Could not set password for HTTP authentication: %s",
600 curl_easy_strerror(ret));
601 return EINA_FALSE;
602 }
603
604 return EINA_TRUE;
605 }
606# endif
607#else
608 return EINA_FALSE;
609 (void)url_con;
610 (void)username;
611 (void)password;
612 (void)safe;
613#endif
614
615 return EINA_FALSE;
616}
617
618#define MODE_AUTO 0
619#define MODE_GET 1
620#define MODE_POST 2
621
622static Eina_Bool
623_ecore_con_url_send(Ecore_Con_Url *url_con, int mode, const void *data, long length, const char *content_type)
624{
625#ifdef HAVE_CURL
626 Eina_List *l;
627 const char *s;
628 char tmp[512];
629
630 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
631 {
632 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_send");
633 return EINA_FALSE;
634 }
635
636 if (!url_con->url) return EINA_FALSE;
637 if (url_con->dead) return EINA_FALSE;
638
639 /* Free response headers from previous send() calls */
640 EINA_LIST_FREE(url_con->response_headers, s)
641 free((char *)s);
642 url_con->response_headers = NULL;
643 url_con->status = 0;
644
645 curl_slist_free_all(url_con->headers);
646 url_con->headers = NULL;
647
648 if ((mode == MODE_POST) || (mode == MODE_AUTO))
649 {
650 if (url_con->post_data) free(url_con->post_data);
651 url_con->post_data = NULL;
652 if ((data) && (length > 0))
653 {
654 url_con->post_data = malloc(length);
655 if (url_con->post_data)
656 {
657 memcpy(url_con->post_data, data, length);
658 if ((content_type) && (strlen(content_type) < 450))
659 {
660 snprintf(tmp, sizeof(tmp), "Content-Type: %s", content_type);
661 url_con->headers = curl_slist_append(url_con->headers, tmp);
662 }
663 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDS, url_con->post_data);
664 curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, length);
665 }
666 else
667 return EINA_FALSE;
668 }
669 else curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, 0);
670 if (mode == MODE_POST)
671 curl_easy_setopt(url_con->curl_easy, CURLOPT_POST, 1);
672 }
673
674 switch (url_con->time_condition)
675 {
676 case ECORE_CON_URL_TIME_NONE:
677 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
678 CURL_TIMECOND_NONE);
679 break;
680
681 case ECORE_CON_URL_TIME_IFMODSINCE:
682 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
683 CURL_TIMECOND_IFMODSINCE);
684 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE,
685 (long)url_con->timestamp);
686 break;
687
688 case ECORE_CON_URL_TIME_IFUNMODSINCE:
689 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMECONDITION,
690 CURL_TIMECOND_IFUNMODSINCE);
691 curl_easy_setopt(url_con->curl_easy, CURLOPT_TIMEVALUE,
692 (long)url_con->timestamp);
693 break;
694 }
695
696 /* Additional headers */
697 EINA_LIST_FOREACH(url_con->additional_headers, l, s)
698 url_con->headers = curl_slist_append(url_con->headers, s);
699
700 curl_easy_setopt(url_con->curl_easy, CURLOPT_HTTPHEADER, url_con->headers);
701
702 url_con->received = 0;
703
704 return _ecore_con_url_perform(url_con);
705#else
706 return EINA_FALSE;
707 (void)url_con;
708 (void)mode;
709 (void)data;
710 (void)length;
711 (void)content_type;
712#endif
713}
714
715EAPI Eina_Bool
716ecore_con_url_get(Ecore_Con_Url *url_con)
717{
718 return _ecore_con_url_send(url_con, MODE_GET, NULL, 0, NULL);
719}
720
721EAPI Eina_Bool
722ecore_con_url_post(Ecore_Con_Url *url_con, const void *data, long length, const char *content_type)
723{
724 return _ecore_con_url_send(url_con, MODE_POST, data, length, content_type);
725}
726
727EAPI Eina_Bool
728ecore_con_url_ftp_upload(Ecore_Con_Url *url_con, const char *filename, const char *user, const char *pass, const char *upload_dir)
729{
730#ifdef HAVE_CURL
731 char url[4096];
732 char userpwd[4096];
733 FILE *fd;
734 struct stat file_info;
735 CURLcode ret;
736
737 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
738 {
739 ECORE_MAGIC_FAIL(url_con,
740 ECORE_MAGIC_CON_URL,
741 "ecore_con_url_ftp_upload");
742 return EINA_FALSE;
743 }
744
745 if (url_con->dead) return EINA_FALSE;
746 if (!url_con->url) return EINA_FALSE;
747 if ((!filename) || (!filename[0])) return EINA_FALSE;
748
749 if (stat(filename, &file_info))
750 return EINA_FALSE;
751
752 snprintf(userpwd, sizeof(userpwd), "%s:%s", user, pass);
753 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERPWD, userpwd);
754 if (ret != CURLE_OK)
755 {
756 ERR("Could not set username and password for FTP upload: %s",
757 curl_easy_strerror(ret));
758 return EINA_FALSE;
759 }
760
761 char tmp[PATH_MAX];
762 snprintf(tmp, PATH_MAX, "%s", filename);
763
764 if (upload_dir)
765 snprintf(url, sizeof(url), "ftp://%s/%s/%s", url_con->url,
766 upload_dir, basename(tmp));
767 else
768 snprintf(url, sizeof(url), "ftp://%s/%s", url_con->url,
769 basename(tmp));
770
771 if (!ecore_con_url_url_set(url_con, url))
772 return EINA_FALSE;
773
774 curl_easy_setopt(url_con->curl_easy, CURLOPT_INFILESIZE_LARGE,
775 (curl_off_t)file_info.st_size);
776 curl_easy_setopt(url_con->curl_easy, CURLOPT_UPLOAD, 1);
777 curl_easy_setopt(url_con->curl_easy, CURLOPT_READFUNCTION,
778 _ecore_con_url_read_cb);
779
780 fd = fopen(filename, "rb");
781 if (!fd)
782 {
783 ERR("Could not open \"%s\" for FTP upload", filename);
784 return EINA_FALSE;
785 }
786 curl_easy_setopt(url_con->curl_easy, CURLOPT_READDATA, fd);
787
788 return _ecore_con_url_perform(url_con);
789#else
790 return EINA_FALSE;
791 (void)url_con;
792 (void)filename;
793 (void)user;
794 (void)pass;
795 (void)upload_dir;
796#endif
797}
798
799EAPI void
800ecore_con_url_cookies_init(Ecore_Con_Url *url_con)
801{
802#ifdef HAVE_CURL
803 if (!url_con)
804 return;
805
806 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
807 {
808 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
809 "ecore_con_url_cookies_init");
810 return;
811 }
812
813 if (url_con->dead) return;
814 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEFILE, "");
815#else
816 return;
817 (void)url_con;
818#endif
819}
820
821EAPI void
822ecore_con_url_cookies_ignore_old_session_set(Ecore_Con_Url *url_con, Eina_Bool ignore)
823{
824#ifdef HAVE_CURL
825 if (!url_con)
826 return;
827
828 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
829 {
830 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
831 "ecore_con_url_cookies_ignore_old_session_set");
832 return;
833 }
834
835 if (url_con->dead) return;
836 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIESESSION, ignore);
837#else
838 return;
839 (void)url_con;
840 (void)ignore;
841#endif
842}
843
844EAPI void
845ecore_con_url_cookies_clear(Ecore_Con_Url *url_con)
846{
847#ifdef HAVE_CURL
848 if (!url_con)
849 return;
850
851 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
852 {
853 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
854 "ecore_con_url_cookies_clear");
855 return;
856 }
857
858 if (url_con->dead) return;
859 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "ALL");
860#else
861 return;
862 (void)url_con;
863#endif
864}
865
866EAPI void
867ecore_con_url_cookies_session_clear(Ecore_Con_Url *url_con)
868{
869#ifdef HAVE_CURL
870 if (!url_con)
871 return;
872
873 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
874 {
875 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
876 "ecore_con_url_cookies_session_clear");
877 return;
878 }
879
880 if (url_con->dead) return;
881 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "SESS");
882#else
883 return;
884 (void)url_con;
885#endif
886}
887
888EAPI void
889ecore_con_url_cookies_file_add(Ecore_Con_Url *url_con, const char *const file_name)
890{
891#ifdef HAVE_CURL
892 if (!url_con)
893 return;
894
895 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
896 {
897 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
898 "ecore_con_url_cookies_file_add");
899 return;
900 }
901
902 if (url_con->dead) return;
903 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEFILE, file_name);
904#else
905 return;
906 (void)url_con;
907 (void)file_name;
908#endif
909}
910
911EAPI Eina_Bool
912ecore_con_url_cookies_jar_file_set(Ecore_Con_Url *url_con, const char *const cookiejar_file)
913{
914#ifdef HAVE_CURL
915 CURLcode ret;
916
917 if (!url_con)
918 return EINA_FALSE;
919
920 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
921 {
922 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
923 "ecore_con_url_cookies_jar_file_set");
924 return EINA_FALSE;
925 }
926
927 if (url_con->dead) return EINA_FALSE;
928 ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIEJAR,
929 cookiejar_file);
930 if (ret != CURLE_OK)
931 {
932 ERR("Setting the cookie-jar name failed: %s",
933 curl_easy_strerror(ret));
934 return EINA_FALSE;
935 }
936
937 return EINA_TRUE;
938#else
939 return EINA_FALSE;
940 (void)url_con;
941 (void)cookiejar_file;
942#endif
943}
944
945EAPI void
946ecore_con_url_cookies_jar_write(Ecore_Con_Url *url_con)
947{
948#ifdef HAVE_CURL
949 if (!url_con)
950 return;
951
952 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
953 {
954 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
955 "ecore_con_url_cookies_jar_write");
956 return;
957 }
958
959 if (url_con->dead) return;
960 curl_easy_setopt(url_con->curl_easy, CURLOPT_COOKIELIST, "FLUSH");
961#else
962 return;
963 (void)url_con;
964#endif
965}
966
967EAPI void
968ecore_con_url_verbose_set(Ecore_Con_Url *url_con, Eina_Bool verbose)
969{
970#ifdef HAVE_CURL
971 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
972 {
973 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
974 "ecore_con_url_verbose_set");
975 return;
976 }
977
978 if (!url_con->url)
979 return;
980
981 if (url_con->dead) return;
982 curl_easy_setopt(url_con->curl_easy, CURLOPT_VERBOSE, (int)verbose);
983#else
984 return;
985 (void)url_con;
986 (void)verbose;
987#endif
988}
989
990EAPI void
991ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con, Eina_Bool use_epsv)
992{
993#ifdef HAVE_CURL
994 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
995 {
996 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
997 "ecore_con_url_ftp_use_epsv_set");
998 return;
999 }
1000
1001 if (!url_con->url)
1002 return;
1003
1004 if (url_con->dead) return;
1005 curl_easy_setopt(url_con->curl_easy, CURLOPT_FTP_USE_EPSV, (int)use_epsv);
1006#else
1007 return;
1008 (void)url_con;
1009 (void)use_epsv;
1010#endif
1011}
1012
1013/**
1014 * Toggle libcurl's verify peer's certificate option.
1015 *
1016 * If @p verify is @c EINA_TRUE, libcurl will verify
1017 * the authenticity of the peer's certificate, otherwise
1018 * it will not. Default behavior of libcurl is to check
1019 * peer's certificate.
1020 *
1021 * @param url_con Ecore_Con_Url instance which will be acted upon.
1022 * @param verify Whether or not libcurl will check peer's certificate.
1023 * @since 1.1.0
1024 */
1025EAPI void
1026ecore_con_url_ssl_verify_peer_set(Ecore_Con_Url *url_con, Eina_Bool verify)
1027{
1028#ifdef HAVE_CURL
1029 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1030 {
1031 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL,
1032 "ecore_con_url_ssl_verify_peer_set");
1033 return;
1034 }
1035
1036 if (!url_con->url)
1037 return;
1038
1039 if (url_con->dead) return;
1040 curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, (int)verify);
1041#else
1042 return;
1043 (void)url_con;
1044 (void)verify;
1045#endif
1046}
1047
1048/**
1049 * Set a custom CA to trust for SSL/TLS connections.
1050 *
1051 * Specify the path of a file (in PEM format) containing one or more
1052 * CA certificate(s) to use for the validation of the server certificate.
1053 *
1054 * This function can also disable CA validation if @p ca_path is @c NULL.
1055 * However, the server certificate still needs to be valid for the connection
1056 * to succeed (i.e., the certificate must concern the server the
1057 * connection is made to).
1058 *
1059 * @param url_con Connection object that will use the custom CA.
1060 * @param ca_path Path to a CA certificate(s) file or @c NULL to disable
1061 * CA validation.
1062 *
1063 * @return @c 0 on success. When cURL is used, non-zero return values
1064 * are equal to cURL error codes.
1065 */
1066EAPI int
1067ecore_con_url_ssl_ca_set(Ecore_Con_Url *url_con, const char *ca_path)
1068{
1069 int res = -1;
1070
1071#ifdef HAVE_CURL
1072 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1073 {
1074 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_ssl_ca_set");
1075 return -1;
1076 }
1077
1078 if (!url_con->url) return -1;
1079 if (url_con->dead) return -1;
1080 if (ca_path == NULL)
1081 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, 0);
1082 else
1083 {
1084 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_SSL_VERIFYPEER, 1);
1085 if (!res)
1086 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_CAINFO, ca_path);
1087 }
1088#else
1089 return -1;
1090 (void)url_con;
1091 (void)ca_path;
1092#endif
1093
1094 return res;
1095}
1096
1097EAPI Eina_Bool
1098ecore_con_url_http_version_set(Ecore_Con_Url *url_con, Ecore_Con_Url_Http_Version version)
1099{
1100#ifdef HAVE_CURL
1101 int res = -1;
1102 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1103 {
1104 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_http_version_set");
1105 return EINA_FALSE;
1106 }
1107 if (url_con->dead) return EINA_FALSE;
1108 switch (version)
1109 {
1110 case ECORE_CON_URL_HTTP_VERSION_1_0:
1111 res = curl_easy_setopt(url_con->curl_easy,
1112 CURLOPT_HTTP_VERSION,
1113 CURL_HTTP_VERSION_1_0);
1114 break;
1115
1116 case ECORE_CON_URL_HTTP_VERSION_1_1:
1117 res = curl_easy_setopt(url_con->curl_easy,
1118 CURLOPT_HTTP_VERSION,
1119 CURL_HTTP_VERSION_1_1);
1120 break;
1121
1122 default:
1123 break;
1124 }
1125 if (res != CURLE_OK)
1126 {
1127 ERR("curl http version setting failed: %s", curl_easy_strerror(res));
1128 return EINA_FALSE;
1129 }
1130 return EINA_TRUE;
1131#else
1132 (void)url_con;
1133 (void)version;
1134 return EINA_FALSE;
1135#endif
1136}
1137
1138EAPI Eina_Bool
1139ecore_con_url_proxy_set(Ecore_Con_Url *url_con, const char *proxy)
1140{
1141#ifdef HAVE_CURL
1142 int res = -1;
1143 curl_version_info_data *vers = NULL;
1144
1145 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1146 {
1147 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_proxy_set");
1148 return EINA_FALSE;
1149 }
1150
1151 if (!url_con->url) return EINA_FALSE;
1152 if (url_con->dead) return EINA_FALSE;
1153
1154 if (!proxy) res = curl_easy_setopt(url_con->curl_easy, CURLOPT_PROXY, "");
1155 else
1156 {
1157 // before curl version 7.21.7, socks protocol:// prefix is not supported
1158 // (e.g. socks4://, socks4a://, socks5:// or socks5h://, etc.)
1159 vers = curl_version_info(CURLVERSION_NOW);
1160 if (vers->version_num < 0x71507)
1161 {
1162 url_con->proxy_type = CURLPROXY_HTTP;
1163 if (strstr(proxy, "socks4a"))
1164 url_con->proxy_type = CURLPROXY_SOCKS4A;
1165 else if (strstr(proxy, "socks4"))
1166 url_con->proxy_type = CURLPROXY_SOCKS4;
1167 else if (strstr(proxy, "socks5h"))
1168 url_con->proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
1169 else if (strstr(proxy, "socks5"))
1170 url_con->proxy_type = CURLPROXY_SOCKS5;
1171 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_PROXYTYPE,
1172 url_con->proxy_type);
1173 if (res != CURLE_OK)
1174 {
1175 ERR("curl proxy type setting failed: %s",
1176 curl_easy_strerror(res));
1177 url_con->proxy_type = -1;
1178 return EINA_FALSE;
1179 }
1180 }
1181 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_PROXY, proxy);
1182 }
1183 if (res != CURLE_OK)
1184 {
1185 ERR("curl proxy setting failed: %s", curl_easy_strerror(res));
1186 url_con->proxy_type = -1;
1187 return EINA_FALSE;
1188 }
1189 return EINA_TRUE;
1190#else
1191 return EINA_FALSE;
1192 (void)url_con;
1193 (void)proxy;
1194#endif
1195}
1196
1197EAPI void
1198ecore_con_url_timeout_set(Ecore_Con_Url *url_con, double timeout)
1199{
1200#ifdef HAVE_CURL
1201 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1202 {
1203 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_timeout_set");
1204 return;
1205 }
1206
1207 if (url_con->dead) return;
1208 if (!url_con->url || timeout < 0) return;
1209 if (url_con->timer) ecore_timer_del(url_con->timer);
1210 url_con->timer = ecore_timer_add(timeout, _ecore_con_url_timeout_cb, url_con);
1211#else
1212 return;
1213 (void)url_con;
1214 (void)timeout;
1215#endif
1216}
1217
1218EAPI Eina_Bool
1219ecore_con_url_proxy_username_set(Ecore_Con_Url *url_con, const char *username)
1220{
1221#ifdef HAVE_CURL
1222 int res = -1;
1223 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1224 {
1225 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_proxy_username_set");
1226 return EINA_FALSE;
1227 }
1228
1229 if (url_con->dead) return EINA_FALSE;
1230 if (!url_con->url) return EINA_FALSE;
1231 if ((!username) || (!username[0])) return EINA_FALSE;
1232 if (url_con->proxy_type == CURLPROXY_SOCKS4 || url_con->proxy_type == CURLPROXY_SOCKS4A)
1233 {
1234 ERR("Proxy type should be socks5 and above");
1235 return EINA_FALSE;
1236 }
1237
1238 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
1239 if (res != CURLE_OK)
1240 {
1241 ERR("curl_easy_setopt() failed: %s", curl_easy_strerror(res));
1242 return EINA_FALSE;
1243 }
1244 return EINA_TRUE;
1245#else
1246 return EINA_FALSE;
1247 (void)url_con;
1248 (void)username;
1249#endif
1250}
1251
1252EAPI Eina_Bool
1253ecore_con_url_proxy_password_set(Ecore_Con_Url *url_con, const char *password)
1254{
1255#ifdef HAVE_CURL
1256 int res = -1;
1257 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1258 {
1259 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_proxy_password_set");
1260 return EINA_FALSE;
1261 }
1262 if (!url_con->url) return EINA_FALSE;
1263 if (url_con->dead) return EINA_FALSE;
1264 if (!password) return EINA_FALSE;
1265 if (url_con->proxy_type == CURLPROXY_SOCKS4 || url_con->proxy_type == CURLPROXY_SOCKS4A)
1266 {
1267 ERR("Proxy type should be socks5 and above");
1268 return EINA_FALSE;
1269 }
1270
1271 res = curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
1272 if (res != CURLE_OK)
1273 {
1274 ERR("curl_easy_setopt() failed: %s", curl_easy_strerror(res));
1275 return EINA_FALSE;
1276 }
1277 return EINA_TRUE;
1278#else
1279 return EINA_FALSE;
1280 (void)url_con;
1281 (void)password;
1282#endif
1283}
1284
1285/**
1286 * @}
1287 */
1288
1289#ifdef HAVE_CURL
1290static void
1291_ecore_con_url_status_get(Ecore_Con_Url *url_con)
1292{
1293 long status = 0;
1294
1295 if (!url_con->curl_easy) return;
1296 if (!curl_easy_getinfo(url_con->curl_easy, CURLINFO_RESPONSE_CODE, &status))
1297 url_con->status = status;
1298 else
1299 url_con->status = 0;
1300}
1301
1302static void
1303_ecore_con_url_event_url_complete(Ecore_Con_Url *url_con, CURLMsg *curlmsg)
1304{
1305 Ecore_Con_Event_Url_Complete *e;
1306
1307 e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
1308 if (!e) return;
1309
1310 if (curlmsg && (curlmsg->data.result == CURLE_OK))
1311 {
1312 if (!url_con->status)
1313 _ecore_con_url_status_get(url_con);
1314 }
1315 else if (curlmsg)
1316 ERR("Curl message have errors: %d", curlmsg->data.result);
1317 else
1318 CRIT("THIS IS BAD.");
1319
1320 e->status = url_con->status;
1321 e->url_con = url_con;
1322 url_con->event_count++;
1323 ecore_event_add(ECORE_CON_EVENT_URL_COMPLETE, e, (Ecore_End_Cb)_ecore_con_event_url_free, url_con);
1324}
1325
1326static void
1327_ecore_con_url_multi_remove(Ecore_Con_Url *url_con)
1328{
1329 CURLMcode ret;
1330
1331 ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
1332 url_con->multi = EINA_FALSE;
1333 if (ret != CURLM_OK) ERR("curl_multi_remove_handle failed: %s", curl_multi_strerror(ret));
1334}
1335
1336static Eina_Bool
1337_ecore_con_url_timeout_cb(void *data)
1338{
1339 Ecore_Con_Url *url_con = data;
1340
1341 if (!url_con) return ECORE_CALLBACK_CANCEL;
1342 if (!url_con->curl_easy) return ECORE_CALLBACK_CANCEL;
1343
1344 _ecore_con_url_multi_remove(url_con);
1345 _url_con_list = eina_list_remove(_url_con_list, url_con);
1346
1347 curl_slist_free_all(url_con->headers);
1348 url_con->headers = NULL;
1349
1350 url_con->timer = NULL;
1351
1352 _ecore_con_url_event_url_complete(url_con, NULL);
1353 return ECORE_CALLBACK_CANCEL;
1354}
1355
1356static size_t
1357_ecore_con_url_data_cb(void *buffer, size_t size, size_t nitems, void *userp)
1358{
1359 Ecore_Con_Url *url_con;
1360 Ecore_Con_Event_Url_Data *e;
1361 size_t real_size = size * nitems;
1362
1363 url_con = (Ecore_Con_Url *)userp;
1364
1365 if (!url_con)
1366 return -1;
1367
1368 if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
1369 {
1370 ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_data_cb");
1371 return -1;
1372 }
1373
1374 url_con->received += real_size;
1375
1376 INF("reading from %s", url_con->url);
1377 if (url_con->write_fd < 0)
1378 {
1379 e =
1380 malloc(sizeof(Ecore_Con_Event_Url_Data) + sizeof(unsigned char) *
1381 (real_size - 1));
1382 if (e)
1383 {
1384 e->url_con = url_con;
1385 e->size = real_size;
1386 memcpy(e->data, buffer, real_size);
1387 url_con->event_count++;
1388 ecore_event_add(ECORE_CON_EVENT_URL_DATA, e, (Ecore_End_Cb)_ecore_con_event_url_free, url_con);
1389 }
1390 }
1391 else
1392 {
1393 ssize_t count = 0;
1394 size_t total_size = real_size;
1395 size_t offset = 0;
1396
1397 while (total_size > 0)
1398 {
1399 count = write(url_con->write_fd,
1400 (char *)buffer + offset,
1401 total_size);
1402 if (count < 0)
1403 {
1404 if (errno != EAGAIN && errno != EINTR)
1405 return -1;
1406 }
1407 else
1408 {
1409 total_size -= count;
1410 offset += count;
1411 }
1412 }
1413 }
1414
1415 return real_size;
1416}
1417
1418static size_t
1419_ecore_con_url_header_cb(void *ptr, size_t size, size_t nitems, void *stream)
1420{
1421 size_t real_size = size * nitems;
1422 Ecore_Con_Url *url_con = stream;
1423
1424 char *header = malloc(sizeof(char) * (real_size + 1));
1425 if (!header)
1426 return real_size;
1427
1428 memcpy(header, ptr, real_size);
1429 header[real_size] = '\0';
1430
1431 url_con->response_headers = eina_list_append(url_con->response_headers,
1432 header);
1433
1434 return real_size;
1435}
1436
1437static int
1438_ecore_con_url_progress_cb(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
1439{
1440 Ecore_Con_Event_Url_Progress *e;
1441 Ecore_Con_Url *url_con;
1442
1443 url_con = clientp;
1444
1445 e = malloc(sizeof(Ecore_Con_Event_Url_Progress));
1446 if (e)
1447 {
1448 e->url_con = url_con;
1449 e->down.total = dltotal;
1450 e->down.now = dlnow;
1451 e->up.total = ultotal;
1452 e->up.now = ulnow;
1453 url_con->event_count++;
1454 ecore_event_add(ECORE_CON_EVENT_URL_PROGRESS, e, (Ecore_End_Cb)_ecore_con_event_url_free, url_con);
1455 }
1456
1457 return 0;
1458}
1459
1460static size_t
1461_ecore_con_url_read_cb(void *ptr, size_t size, size_t nitems, void *stream)
1462{
1463 size_t retcode = fread(ptr, size, nitems, stream);
1464
1465 if (ferror((FILE *)stream))
1466 {
1467 fclose(stream);
1468 return CURL_READFUNC_ABORT;
1469 }
1470 else if (retcode == 0)
1471 {
1472 fclose((FILE *)stream);
1473 return 0;
1474 }
1475
1476#ifdef _WIN32
1477 INF("*** We read %Iu bytes from file", retcode);
1478#else
1479 INF("*** We read %zu bytes from file", retcode);
1480#endif
1481 return retcode;
1482}
1483
1484static void
1485_ecore_con_url_info_read(void)
1486{
1487 CURLMsg *curlmsg;
1488 int n_remaining;
1489
1490 while ((curlmsg = curl_multi_info_read(_curlm, &n_remaining)))
1491 {
1492 Eina_List *l, *ll;
1493 Ecore_Con_Url *url_con = NULL;
1494 DBG("Curl message: %d", curlmsg->msg);
1495
1496 if (curlmsg->msg == CURLMSG_DONE)
1497 {
1498 EINA_LIST_FOREACH_SAFE(_url_con_list, l, ll, url_con)
1499 {
1500 if (curlmsg->easy_handle == url_con->curl_easy)
1501 _ecore_con_url_event_url_complete(url_con, curlmsg);
1502 }
1503 }
1504 }
1505}
1506
1507static void
1508_ecore_con_url_curl_clear(void)
1509{
1510 Ecore_Fd_Handler *fdh;
1511 Ecore_Con_Url *url_con;
1512
1513 EINA_LIST_FREE(_fd_hd_list, fdh) ecore_main_fd_handler_del(fdh);
1514 EINA_LIST_FREE(_url_con_list, url_con) _ecore_con_url_multi_remove(url_con);
1515}
1516
1517static Eina_Bool
1518_ecore_con_url_fd_handler(void *data __UNUSED__, Ecore_Fd_Handler *fd_handler __UNUSED__)
1519{
1520 Ecore_Fd_Handler *fdh;
1521 EINA_LIST_FREE(_fd_hd_list, fdh) ecore_main_fd_handler_del(fdh);
1522 ecore_timer_interval_set(_curl_timer, 0.1);
1523 return ECORE_CALLBACK_CANCEL;
1524}
1525
1526static void
1527_ecore_con_url_fdset(void)
1528{
1529 CURLMcode ret;
1530 fd_set read_set, write_set, exc_set;
1531 int fd, fd_max;
1532
1533 FD_ZERO(&read_set);
1534 FD_ZERO(&write_set);
1535 FD_ZERO(&exc_set);
1536
1537 ret = curl_multi_fdset(_curlm, &read_set, &write_set, &exc_set, &fd_max);
1538 if (ret != CURLM_OK)
1539 {
1540 ERR("curl_multi_fdset failed: %s", curl_multi_strerror(ret));
1541 return;
1542 }
1543
1544 for (fd = 0; fd <= fd_max; fd++)
1545 {
1546 int flags = 0;
1547 if (FD_ISSET(fd, &read_set)) flags |= ECORE_FD_READ;
1548 if (FD_ISSET(fd, &write_set)) flags |= ECORE_FD_WRITE;
1549 if (FD_ISSET(fd, &exc_set)) flags |= ECORE_FD_ERROR;
1550 if (flags)
1551 {
1552 // FIXME: Who is owner (easy_handle) of this fd?? (Curl do not give this info.)
1553 // This cause "Failed to delete epoll fd xx!" error messages
1554 Ecore_Fd_Handler *fd_handler;
1555 fd_handler = ecore_main_fd_handler_add(fd, flags,
1556 _ecore_con_url_fd_handler,
1557 NULL, NULL, NULL);
1558 if (fd_handler)
1559 _fd_hd_list = eina_list_append(_fd_hd_list, fd_handler);
1560 }
1561 }
1562}
1563
1564static Eina_Bool
1565_ecore_con_url_timer(void *data __UNUSED__)
1566{
1567 Ecore_Fd_Handler *fdh;
1568 int still_running;
1569 CURLMcode ret;
1570
1571 EINA_LIST_FREE(_fd_hd_list, fdh) ecore_main_fd_handler_del(fdh);
1572 _ecore_con_url_info_read();
1573
1574 ret = curl_multi_perform(_curlm, &still_running);
1575 if (ret == CURLM_CALL_MULTI_PERFORM)
1576 {
1577 DBG("curl_multi_perform() again immediately");
1578 return ECORE_CALLBACK_RENEW;
1579 }
1580 else if (ret != CURLM_OK)
1581 {
1582 ERR("curl_multi_perform() failed: %s", curl_multi_strerror(ret));
1583 _ecore_con_url_curl_clear();
1584 ecore_timer_freeze(_curl_timer);
1585 }
1586
1587 if (still_running)
1588 {
1589 long ms;
1590 _ecore_con_url_fdset();
1591 curl_multi_timeout(_curlm, &ms);
1592 DBG("multiperform is still running: %d, timeout: %ld", still_running, ms);
1593 if (ms >= CURL_MIN_TIMEOUT || ms <= 0) ms = CURL_MIN_TIMEOUT;
1594 ecore_timer_interval_set(_curl_timer, (double)ms / 1000);
1595 }
1596 else
1597 {
1598 DBG("multiperform ended");
1599 _ecore_con_url_info_read();
1600 _ecore_con_url_curl_clear();
1601 ecore_timer_freeze(_curl_timer);
1602 }
1603
1604 return ECORE_CALLBACK_RENEW;
1605}
1606
1607static Eina_Bool
1608_ecore_con_url_perform(Ecore_Con_Url *url_con)
1609{
1610 CURLMcode ret;
1611
1612 ret = curl_multi_add_handle(_curlm, url_con->curl_easy);
1613 if (ret != CURLM_OK)
1614 {
1615 ERR("curl_multi_add_handle() failed: %s", curl_multi_strerror(ret));
1616 return EINA_FALSE;
1617 }
1618
1619 url_con->multi = EINA_TRUE;
1620 _url_con_list = eina_list_append(_url_con_list, url_con);
1621 ecore_timer_thaw(_curl_timer);
1622
1623 return EINA_TRUE;
1624}
1625
1626static void
1627_ecore_con_event_url_free(Ecore_Con_Url *url_con, void *ev)
1628{
1629 free(ev);
1630 url_con->event_count--;
1631 if (url_con->dead && (!url_con->event_count))
1632 ecore_con_url_free(url_con);
1633}
1634
1635#endif