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