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