diff options
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.c | 440 |
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 | |||
18 | struct _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 | ||
32 | Ecore_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 | |||
38 | static Eina_Bool _ecore_file_download_url_complete_cb(void *data, int type, void *event); | ||
39 | static Eina_Bool _ecore_file_download_url_progress_cb(void *data, int type, void *event); | ||
40 | #endif | ||
41 | |||
42 | static Ecore_Event_Handler *_url_complete_handler = NULL; | ||
43 | static Ecore_Event_Handler *_url_progress_download = NULL; | ||
44 | static Eina_List *_job_list; | ||
45 | |||
46 | #endif /* BUILD_ECORE_CON */ | ||
47 | |||
48 | int | ||
49 | ecore_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 | |||
65 | void | ||
66 | ecore_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 | ||
83 | static 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 | |||
94 | static 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 | */ | ||
211 | EAPI Eina_Bool | ||
212 | ecore_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 | */ | ||
234 | EAPI Eina_Bool | ||
235 | ecore_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 | */ | ||
257 | EAPI Eina_Bool | ||
258 | ecore_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 | ||
276 | static 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 | |||
286 | static 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 | |||
307 | static 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 | |||
334 | Ecore_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 | */ | ||
400 | EAPI void | ||
401 | ecore_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 | */ | ||
427 | EAPI void | ||
428 | ecore_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 | */ | ||