svn update: 48958 (latest:48959)
[framework/uifw/ecore.git] / src / lib / ecore_file / ecore_file_download.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4
5 #ifdef HAVE_CONFIG_H
6 # include <config.h>
7 #endif
8
9 #include <stdio.h>
10 #include <string.h>
11
12 #ifdef BUILD_ECORE_CON
13 # include "Ecore_Con.h"
14 #endif
15
16 #include "ecore_file_private.h"
17
18 #ifdef BUILD_ECORE_CON
19
20 #define ECORE_MAGIC_FILE_DOWNLOAD_JOB   0xf7427cb8
21
22 struct _Ecore_File_Download_Job
23 {
24    ECORE_MAGIC;
25
26    Ecore_Con_Url        *url_con;
27    FILE                 *file;
28
29    char                 *dst;
30
31    void (*completion_cb)(void *data, const char *file, int status);
32
33    int  (*progress_cb)  (void *data, const char *file,
34                          long int dltotal, long int dlnow,
35                          long int ultotal, long int ulnow);
36 };
37
38 #ifdef HAVE_CURL
39 Ecore_File_Download_Job *_ecore_file_download_curl(const char *url, const char *dst,
40                                                    void (*completion_cb)(void *data, const char *file, int status),
41                                                    int (*progress_cb)(void *data, const char *file, long int dltotal, long int dlnow, long int ultotal, long int ulnow),
42                                                    void *data);
43
44 static int _ecore_file_download_url_complete_cb(void *data, int type, void *event);
45 static int _ecore_file_download_url_progress_cb(void *data, int type, void *event);
46 #endif
47
48 static Ecore_Event_Handler      *_url_complete_handler = NULL;
49 static Ecore_Event_Handler      *_url_progress_download = NULL;
50 static Eina_List                *_job_list;
51
52 #endif /* BUILD_ECORE_CON */
53
54 int
55 ecore_file_download_init(void)
56 {
57 #ifdef BUILD_ECORE_CON
58   if (!ecore_con_url_init())
59     return 0;
60
61 # ifdef HAVE_CURL
62   _url_complete_handler = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _ecore_file_download_url_complete_cb, NULL);
63   _url_progress_download = ecore_event_handler_add(ECORE_CON_EVENT_URL_PROGRESS, _ecore_file_download_url_progress_cb, NULL);
64 # endif
65
66 #endif /* BUILD_ECORE_CON */
67
68    return 1;
69 }
70
71 void
72 ecore_file_download_shutdown(void)
73 {
74 #ifdef BUILD_ECORE_CON
75   if (_url_complete_handler)
76     ecore_event_handler_del(_url_complete_handler);
77   if (_url_progress_download)
78     ecore_event_handler_del(_url_progress_download);
79   _url_complete_handler = NULL;
80   _url_progress_download = NULL;
81   ecore_file_download_abort_all();
82
83   ecore_con_url_shutdown();
84 #endif /* BUILD_ECORE_CON */
85 }
86
87 EAPI void
88 ecore_file_download_abort_all(void)
89 {
90 #ifdef BUILD_ECORE_CON
91    Ecore_File_Download_Job *job;
92
93    EINA_LIST_FREE(_job_list, job)
94              ecore_file_download_abort(job);
95 #endif /* BUILD_ECORE_CON */
96 }
97
98 /**
99  * Download @p url to the given @p dst
100  * @param  url The complete url to download
101  * @param  dst The local file to save the downloaded to
102  * @param  completion_cb A callback called on download complete
103  * @param  progress_cb A callback called during the download operation
104  * @param  data User data passed to both callbacks
105  * @param  job_ret If the protocol in use is http or ftp, this parameter will be
106  * filled with the job. Then you can use ecore_file_download_abort() to cancel it.
107  * 
108  * @return 1 if the download start or 0 on failure
109  *
110  * You must provide the full url, including 'http://', 'ftp://' or 'file://'.\n
111  * If @p dst already exist it will not be overwritten and the function will fail.\n
112  * Ecore must be compiled with CURL to download using http and ftp protocols.\n
113  * The @p status param in the @p completion_cb() will be 0 if the download goes well or
114  * 1 in case of failure.
115  */
116 EAPI int
117 ecore_file_download(const char *url, const char *dst,
118                     void (*completion_cb)(void *data, const char *file, int status),
119                     int (*progress_cb)(void *data, const char *file, long int dltotal, long int dlnow, long int ultotal, long int ulnow),
120                     void *data, Ecore_File_Download_Job **job_ret)
121 {
122 #ifdef BUILD_ECORE_CON
123    char *dir = ecore_file_dir_get(dst);
124
125    if (!ecore_file_is_dir(dir))
126      {
127         free(dir);
128         return 0;
129      }
130    free(dir);
131    if (ecore_file_exists(dst)) return 0;
132
133    /* FIXME: Add handlers for http and ftp! */
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)) ||
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);
153         if(job_ret) *job_ret = job;
154         return job != NULL;
155      }
156 # endif
157    else
158      {
159         return 0;
160      }
161 #else
162    completion_cb = NULL;
163    progress_cb = NULL;
164    data = NULL;
165    return 0;
166 #endif /* BUILD_ECORE_CON */
167 }
168
169 /**
170  * Check if the given protocol is available
171  * @param  protocol The protocol to check
172  * @return 1 if protocol is handled or 0 if not
173  *
174  * @p protocol can be 'http://', 'ftp://' or 'file://'.\n
175  * Ecore must be compiled with CURL to handle http and ftp protocols.
176  */
177 EAPI int
178 ecore_file_download_protocol_available(const char *protocol)
179 {
180 #ifdef BUILD_ECORE_CON
181    if (!strncmp(protocol, "file://", 7)) return 1;
182 # ifdef HAVE_CURL
183    else if (!strncmp(protocol, "http://", 7)) return 1;
184    else if (!strncmp(protocol, "ftp://", 6)) return 1;
185 # endif
186 #endif /* BUILD_ECORE_CON */
187
188    return 0;
189 }
190
191 #ifdef BUILD_ECORE_CON
192
193 # ifdef HAVE_CURL
194 static int
195 _ecore_file_download_url_compare_job(const void *data1, const void *data2)
196 {
197    const Ecore_File_Download_Job        *job = data1;
198    const Ecore_Con_Url                  *url = data2;
199
200    if (job->url_con == url) return 0;
201    return -1;
202 }
203
204 static int
205 _ecore_file_download_url_complete_cb(void *data __UNUSED__, int type __UNUSED__, void *event)
206 {
207    Ecore_Con_Event_Url_Complete *ev = event;
208    Ecore_File_Download_Job      *job;
209
210    job = eina_list_search_unsorted(_job_list, _ecore_file_download_url_compare_job, ev->url_con);
211    if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB)) return 1;
212
213
214    if (job->completion_cb)
215      job->completion_cb(ecore_con_url_data_get(job->url_con), job->dst, !ev->status);
216
217    _job_list = eina_list_remove(_job_list, job);
218    fclose(job->file);
219    free(job->dst);
220    free(job);
221
222    return 0;
223 }
224
225 static int
226 _ecore_file_download_url_progress_cb(void *data __UNUSED__, int type __UNUSED__, void *event)
227 {
228 /* this reports the downloads progress. if we return 0, then download
229  * continues, if we return anything else, then the download stops */
230    Ecore_Con_Event_Url_Progress *ev = event;
231    Ecore_File_Download_Job      *job;
232
233    job = eina_list_search_unsorted(_job_list, _ecore_file_download_url_compare_job, ev->url_con);
234    if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB)) return 1;
235
236    if (job->progress_cb)
237      if (job->progress_cb(ecore_con_url_data_get(job->url_con), job->dst,
238                           (long int) ev->down.total, (long int) ev->down.now,
239                           (long int) ev->up.total, (long int) ev->up.now) != 0)
240        {
241           _job_list = eina_list_remove(_job_list, job);
242           fclose(job->file);
243           free(job->dst);
244           free(job);
245
246           return 1;
247        }
248
249    return 0;
250 }
251
252 Ecore_File_Download_Job *
253 _ecore_file_download_curl(const char *url, const char *dst,
254                           void (*completion_cb)(void *data, const char *file,
255                                                 int status),
256                           int (*progress_cb)(void *data, const char *file,
257                                              long int dltotal, long int dlnow,
258                                              long int ultotal, long int ulnow),
259                           void *data)
260 {
261    Ecore_File_Download_Job *job;
262
263    job = calloc(1, sizeof(Ecore_File_Download_Job));
264    if (!job) return NULL;
265
266    ECORE_MAGIC_SET(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB);
267
268    job->file = fopen(dst, "wb");
269    if (!job->file)
270      {
271         free(job);
272         return NULL;
273      }
274    job->url_con = ecore_con_url_new(url);
275    if (!job->url_con)
276      {
277         fclose(job->file);
278         free(job);
279         return NULL;
280      }
281
282    ecore_con_url_fd_set(job->url_con, fileno(job->file));
283    ecore_con_url_data_set(job->url_con, data);
284
285    job->dst = strdup(dst);
286
287    job->completion_cb = completion_cb;
288    job->progress_cb = progress_cb;
289    _job_list = eina_list_append(_job_list, job);
290
291    ecore_con_url_send(job->url_con, NULL, 0, NULL);
292
293    return job;
294 }
295 # endif
296 #endif
297
298 /**
299  * Abort the given download job
300  * @param  job The download job to abort
301  */
302
303 EAPI void
304 ecore_file_download_abort(Ecore_File_Download_Job *job)
305 {
306 #ifdef BUILD_ECORE_CON
307 # ifdef HAVE_CURL
308    ecore_con_url_destroy(job->url_con);
309 # endif
310    _job_list = eina_list_remove(_job_list, job);
311    fclose(job->file);
312    free(job->dst);
313    free(job);
314 #endif /* BUILD_ECORE_CON */
315 }