aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/ecore/src/lib/ecore_file/ecore_file_download.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/ecore/src/lib/ecore_file/ecore_file_download.c')
-rw-r--r--libraries/ecore/src/lib/ecore_file/ecore_file_download.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/libraries/ecore/src/lib/ecore_file/ecore_file_download.c b/libraries/ecore/src/lib/ecore_file/ecore_file_download.c
new file mode 100644
index 0000000..c7efc4d
--- /dev/null
+++ b/libraries/ecore/src/lib/ecore_file/ecore_file_download.c
@@ -0,0 +1,440 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdio.h>
6#include <string.h>
7
8#ifdef BUILD_ECORE_CON
9# include "Ecore_Con.h"
10#endif
11
12#include "ecore_file_private.h"
13
14#ifdef BUILD_ECORE_CON
15
16#define ECORE_MAGIC_FILE_DOWNLOAD_JOB 0xf7427cb8
17
18struct _Ecore_File_Download_Job
19{
20 ECORE_MAGIC;
21
22 Ecore_Con_Url *url_con;
23 FILE *file;
24
25 char *dst;
26
27 Ecore_File_Download_Completion_Cb completion_cb;
28 Ecore_File_Download_Progress_Cb progress_cb;
29};
30
31#ifdef HAVE_CURL
32Ecore_File_Download_Job *_ecore_file_download_curl(const char *url, const char *dst,
33 Ecore_File_Download_Completion_Cb completion_cb,
34 Ecore_File_Download_Progress_Cb progress_cb,
35 void *data,
36 Eina_Hash *headers);
37
38static Eina_Bool _ecore_file_download_url_complete_cb(void *data, int type, void *event);
39static Eina_Bool _ecore_file_download_url_progress_cb(void *data, int type, void *event);
40#endif
41
42static Ecore_Event_Handler *_url_complete_handler = NULL;
43static Ecore_Event_Handler *_url_progress_download = NULL;
44static Eina_List *_job_list;
45
46#endif /* BUILD_ECORE_CON */
47
48int
49ecore_file_download_init(void)
50{
51#ifdef BUILD_ECORE_CON
52 if (!ecore_con_url_init())
53 return 0;
54
55# ifdef HAVE_CURL
56 _url_complete_handler = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _ecore_file_download_url_complete_cb, NULL);
57 _url_progress_download = ecore_event_handler_add(ECORE_CON_EVENT_URL_PROGRESS, _ecore_file_download_url_progress_cb, NULL);
58# endif
59
60#endif /* BUILD_ECORE_CON */
61
62 return 1;
63}
64
65void
66ecore_file_download_shutdown(void)
67{
68#ifdef BUILD_ECORE_CON
69 if (_url_complete_handler)
70 ecore_event_handler_del(_url_complete_handler);
71 if (_url_progress_download)
72 ecore_event_handler_del(_url_progress_download);
73 _url_complete_handler = NULL;
74 _url_progress_download = NULL;
75 ecore_file_download_abort_all();
76
77 ecore_con_url_shutdown();
78#endif /* BUILD_ECORE_CON */
79}
80
81#ifdef BUILD_ECORE_CON
82# ifdef HAVE_CURL
83static Eina_Bool
84_ecore_file_download_headers_foreach_cb(const Eina_Hash *hash __UNUSED__, const void *key, void *data, void *fdata)
85{
86 Ecore_File_Download_Job *job = fdata;
87 ecore_con_url_additional_header_add(job->url_con, key, data);
88
89 return EINA_TRUE;
90}
91# endif
92#endif
93
94static Eina_Bool
95_ecore_file_download(const char *url,
96 const char *dst,
97 Ecore_File_Download_Completion_Cb completion_cb,
98 Ecore_File_Download_Progress_Cb progress_cb,
99 void *data,
100 Ecore_File_Download_Job **job_ret,
101 Eina_Hash *headers)
102{
103#ifdef BUILD_ECORE_CON
104 char *dir = ecore_file_dir_get(dst);
105
106 if (!ecore_file_is_dir(dir))
107 {
108 ERR("%s is not a directory", dir);
109 free(dir);
110 return EINA_FALSE;
111 }
112 free(dir);
113 if (ecore_file_exists(dst))
114 {
115 WRN("%s already exists", dst);
116 return EINA_FALSE;
117 }
118
119 if (!strncmp(url, "file://", 7))
120 {
121 /* FIXME: Maybe fork? Might take a while to copy.
122 * Check filesize? */
123 /* Just copy it */
124
125 url += 7;
126 /* skip hostname */
127 url = strchr(url, '/');
128 return ecore_file_cp(url, dst);
129 }
130# ifdef HAVE_CURL
131 else if ((!strncmp(url, "http://", 7)) || (!strncmp(url, "https://", 8)) ||
132 (!strncmp(url, "ftp://", 6)))
133 {
134 /* download */
135 Ecore_File_Download_Job *job;
136
137 job = _ecore_file_download_curl(url, dst, completion_cb, progress_cb, data, headers);
138 if(job_ret) *job_ret = job;
139 if(job)
140 return EINA_TRUE;
141 else
142 {
143 ERR("no job returned\n");
144 return EINA_FALSE;
145 }
146 return job ? EINA_TRUE : EINA_FALSE;
147 }
148# else
149 else if ((!strncmp(url, "http://", 7)) || (!strncmp(url, "https://", 8)) ||
150 (!strncmp(url, "ftp://", 6)))
151 {
152 (void)completion_cb;
153 (void)progress_cb;
154 (void)data;
155 (void)job_ret;
156 (void)headers;
157 return EINA_FALSE;
158 }
159# endif
160 else
161 {
162 return EINA_FALSE;
163 }
164#else
165 (void)url;
166 (void)dst;
167 (void)completion_cb;
168 (void)progress_cb;
169 (void)data;
170 (void)job_ret;
171 (void)headers;
172 return EINA_FALSE;
173#endif /* BUILD_ECORE_CON */
174}
175
176/**
177 * @addtogroup Ecore_File_Group Ecore_File - Files and directories convenience functions
178 *
179 * @{
180 */
181
182/**
183 * @brief Download the given url to the given destination.
184 *
185 * @param url The complete url to download.
186 * @param dst The local file to save the downloaded to.
187 * @param completion_cb A callback called on download complete.
188 * @param progress_cb A callback called during the download operation.
189 * @param data User data passed to both callbacks.
190 * @param job_ret Job used to abort the download.
191 * @return EINA_TRUE if the download start or EINA_FALSE on failure
192 *
193 * This function starts the download of the URL @p url and saves it to
194 * @p dst. @p url must provide the protocol, including 'http://',
195 * 'ftp://' or 'file://'. Ecore_File must be compiled with CURL to
196 * download using http and ftp protocols. If @p dst is ill-formed, or
197 * if it already exists, the function returns EINA_FALSE. When the
198 * download is complete, the callback @p completion_cb is called and
199 * @p data is passed to it. The @p status parameter of @p completion_cb
200 * will be filled with the status of the download (200, 404,...). The
201 * @p progress_cb is called during the download operation, each time a
202 * packet is received or when CURL wants. It can be used to display the
203 * percentage of the downloaded file. Return 0 from this callback, if provided,
204 * to continue the operation or anything else to abort the download. The only
205 * operations that can be aborted are those with protocol 'http' or 'ftp'. In
206 * that case @p job_ret can be filled. It can be used with
207 * ecore_file_download_abort() or ecore_file_download_abort_all() to
208 * respectively abort one or all download operations. This function returns
209 * EINA_TRUE if the download starts, EINA_FALSE otherwise.
210 */
211EAPI Eina_Bool
212ecore_file_download(const char *url,
213 const char *dst,
214 Ecore_File_Download_Completion_Cb completion_cb,
215 Ecore_File_Download_Progress_Cb progress_cb,
216 void *data,
217 Ecore_File_Download_Job **job_ret)
218{
219 return _ecore_file_download(url, dst, completion_cb, progress_cb, data, job_ret, NULL);
220}
221
222/**
223 * @brief Download the given url to the given destination with additional headers.
224 *
225 * @param url The complete url to download.
226 * @param dst The local file to save the downloaded to.
227 * @param completion_cb A callback called on download complete.
228 * @param progress_cb A callback called during the download operation.
229 * @param data User data passed to both callbacks.
230 * @param job_ret Job used to abort the download.
231 * @param headers pointer of header lists.
232 * @return EINA_TRUE if the download start or EINA_FALSE on failure
233 */
234EAPI Eina_Bool
235ecore_file_download_full(const char *url,
236 const char *dst,
237 Ecore_File_Download_Completion_Cb completion_cb,
238 Ecore_File_Download_Progress_Cb progress_cb,
239 void *data,
240 Ecore_File_Download_Job **job_ret,
241 Eina_Hash *headers)
242{
243 return _ecore_file_download(url, dst, completion_cb, progress_cb, data, job_ret, headers);
244}
245
246/**
247 * @brief Check if the given protocol is available.
248 *
249 * @param protocol The protocol to check.
250 * @return EINA_TRUE if protocol is handled, EINA_FALSE otherwise.
251 *
252 * This function returns EINA_TRUE if @p protocol is supported,
253 * EINA_FALSE otherwise. @p protocol can be 'http://', 'ftp://' or
254 * 'file://'. Ecore_FILE must be compiled with CURL to handle http and
255 * ftp protocols.
256 */
257EAPI Eina_Bool
258ecore_file_download_protocol_available(const char *protocol)
259{
260#ifdef BUILD_ECORE_CON
261 if (!strncmp(protocol, "file://", 7)) return EINA_TRUE;
262# ifdef HAVE_CURL
263 else if (!strncmp(protocol, "http://", 7)) return EINA_TRUE;
264 else if (!strncmp(protocol, "ftp://", 6)) return EINA_TRUE;
265# endif
266#else
267 (void)protocol;
268#endif /* BUILD_ECORE_CON */
269
270 return EINA_FALSE;
271}
272
273#ifdef BUILD_ECORE_CON
274
275# ifdef HAVE_CURL
276static int
277_ecore_file_download_url_compare_job(const void *data1, const void *data2)
278{
279 const Ecore_File_Download_Job *job = data1;
280 const Ecore_Con_Url *url = data2;
281
282 if (job->url_con == url) return 0;
283 return -1;
284}
285
286static Eina_Bool
287_ecore_file_download_url_complete_cb(void *data __UNUSED__, int type __UNUSED__, void *event)
288{
289 Ecore_Con_Event_Url_Complete *ev = event;
290 Ecore_File_Download_Job *job;
291
292 job = eina_list_search_unsorted(_job_list, _ecore_file_download_url_compare_job, ev->url_con);
293 if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB)) return ECORE_CALLBACK_PASS_ON;
294
295 fclose(job->file);
296 if (job->completion_cb)
297 job->completion_cb(ecore_con_url_data_get(job->url_con), job->dst, ev->status);
298
299 _job_list = eina_list_remove(_job_list, job);
300 free(job->dst);
301 ecore_con_url_free(job->url_con);
302 free(job);
303
304 return ECORE_CALLBACK_DONE;
305}
306
307static Eina_Bool
308_ecore_file_download_url_progress_cb(void *data __UNUSED__, int type __UNUSED__, void *event)
309{
310/* this reports the downloads progress. if we return 0, then download
311 * continues, if we return anything else, then the download stops */
312 Ecore_Con_Event_Url_Progress *ev = event;
313 Ecore_File_Download_Job *job;
314
315 job = eina_list_search_unsorted(_job_list, _ecore_file_download_url_compare_job, ev->url_con);
316 if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB)) return ECORE_CALLBACK_PASS_ON;
317
318 if (job->progress_cb)
319 if (job->progress_cb(ecore_con_url_data_get(job->url_con), job->dst,
320 (long int) ev->down.total, (long int) ev->down.now,
321 (long int) ev->up.total, (long int) ev->up.now) != 0)
322 {
323 _job_list = eina_list_remove(_job_list, job);
324 fclose(job->file);
325 free(job->dst);
326 free(job);
327
328 return ECORE_CALLBACK_PASS_ON;
329 }
330
331 return ECORE_CALLBACK_DONE;
332}
333
334Ecore_File_Download_Job *
335_ecore_file_download_curl(const char *url, const char *dst,
336 Ecore_File_Download_Completion_Cb completion_cb,
337 Ecore_File_Download_Progress_Cb progress_cb,
338 void *data,
339 Eina_Hash *headers)
340{
341 Ecore_File_Download_Job *job;
342
343 job = calloc(1, sizeof(Ecore_File_Download_Job));
344 if (!job) return NULL;
345
346 ECORE_MAGIC_SET(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB);
347
348 job->file = fopen(dst, "wb");
349 if (!job->file)
350 {
351 free(job);
352 return NULL;
353 }
354 job->url_con = ecore_con_url_new(url);
355 if (!job->url_con)
356 {
357 fclose(job->file);
358 free(job);
359 return NULL;
360 }
361
362 if (headers) eina_hash_foreach(headers, _ecore_file_download_headers_foreach_cb, job);
363 ecore_con_url_fd_set(job->url_con, fileno(job->file));
364 ecore_con_url_data_set(job->url_con, data);
365
366 job->dst = strdup(dst);
367
368 job->completion_cb = completion_cb;
369 job->progress_cb = progress_cb;
370 _job_list = eina_list_append(_job_list, job);
371
372 if (!ecore_con_url_get(job->url_con))
373 {
374 ecore_con_url_free(job->url_con);
375 _job_list = eina_list_remove(_job_list, job);
376 fclose(job->file);
377 ecore_file_remove(job->dst);
378 free(job->dst);
379 free(job);
380 return NULL;
381 }
382
383 return job;
384}
385# endif
386#endif
387
388/**
389 * @brief Abort the given download job and call the completion_cb
390 * callbck with a status of 1 (error).
391 *
392 * @param job The download job to abort.
393 *
394 * This function aborts a download operation started by
395 * ecore_file_download(). @p job is the #Ecore_File_Download_Job
396 * structure filled by ecore_file_download(). If it is @c NULL, this
397 * function does nothing. To abort all the currently downloading
398 * operations, call ecore_file_download_abort_all().
399 */
400EAPI void
401ecore_file_download_abort(Ecore_File_Download_Job *job)
402{
403 if (!job)
404 return;
405
406#ifdef BUILD_ECORE_CON
407 if (job->completion_cb)
408 job->completion_cb(ecore_con_url_data_get(job->url_con), job->dst, 1);
409# ifdef HAVE_CURL
410 ecore_con_url_free(job->url_con);
411# endif
412 _job_list = eina_list_remove(_job_list, job);
413 fclose(job->file);
414 free(job->dst);
415 free(job);
416#endif /* BUILD_ECORE_CON */
417}
418
419/**
420 * @brief Abort all downloads.
421 *
422 * This function aborts all the downloads that have been started by
423 * ecore_file_download(). It loops over the started downloads and call
424 * ecore_file_download_abort() for each of them. To abort only one
425 * specific download operation, call ecore_file_download_abort().
426 */
427EAPI void
428ecore_file_download_abort_all(void)
429{
430#ifdef BUILD_ECORE_CON
431 Ecore_File_Download_Job *job;
432
433 EINA_LIST_FREE(_job_list, job)
434 ecore_file_download_abort(job);
435#endif /* BUILD_ECORE_CON */
436}
437
438/**
439 * @}
440 */