merge with master
[platform/framework/web/wrt-plugins-tizen.git] / src / Download / DownloadManager.cpp
1 //
2 // Tizen Web Device API
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 #include <glib.h>
19
20 #include <download.h>
21
22 #include <Logger.h>
23 #include <FilesystemUtils.h>
24
25 #include "DownloadManager.h"
26
27 namespace DeviceAPI {
28 namespace Download {
29
30 static std::string _get_download_error(int err)
31 {
32         bool success = false;
33     std::string msg = "";
34
35     switch (err) {
36         case DOWNLOAD_ERROR_INVALID_PARAMETER:
37             msg = "Invalid parameter";
38             break;
39         case DOWNLOAD_ERROR_OUT_OF_MEMORY:
40             msg = "Out of memory";
41             break;
42         case DOWNLOAD_ERROR_NETWORK_UNREACHABLE:
43             msg = "Network is unreachable";
44             break;
45         case DOWNLOAD_ERROR_CONNECTION_TIMED_OUT:
46             msg = "Http session time-out";
47             break;
48         case DOWNLOAD_ERROR_NO_SPACE:
49             msg = "No space left on device";
50             break;
51         case DOWNLOAD_ERROR_FIELD_NOT_FOUND:
52             msg = "Specified field not found";
53             break;
54         case DOWNLOAD_ERROR_INVALID_STATE:
55             msg = "Invalid state";
56             break;
57         case DOWNLOAD_ERROR_CONNECTION_FAILED:
58             msg = "Connection failed";
59             break;
60         case DOWNLOAD_ERROR_INVALID_URL:
61             msg = "Invalid URL";
62             break;
63         case DOWNLOAD_ERROR_INVALID_DESTINATION:
64             msg = "Invalid destination";
65             break;
66         case DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS:
67             msg = "Full of available simultaneous downloads";
68             break;
69         case DOWNLOAD_ERROR_QUEUE_FULL:
70             msg = "Full of available downloading items from server";
71             break;
72         case DOWNLOAD_ERROR_ALREADY_COMPLETED:
73             msg = "The download is already completed";
74             break;
75         case DOWNLOAD_ERROR_FILE_ALREADY_EXISTS:
76             msg = "It is failed to rename the downloaded file";
77             break;
78         case DOWNLOAD_ERROR_CANNOT_RESUME:
79             msg = "It cannot resume";
80             break;
81         case DOWNLOAD_ERROR_TOO_MANY_REDIRECTS:
82             msg = "In case of too may redirects from http response header";
83             break;
84         case DOWNLOAD_ERROR_UNHANDLED_HTTP_CODE:
85             msg = "The download cannot handle the http status value";
86             break;
87         case DOWNLOAD_ERROR_REQUEST_TIMEOUT:
88             msg = "There are no action after client create a download id";
89             break;
90         case DOWNLOAD_ERROR_RESPONSE_TIMEOUT:
91             msg = "It does not call start API in some time although the download is created";
92             break;
93         case DOWNLOAD_ERROR_SYSTEM_DOWN:
94             msg = "There are no response from client after rebooting download daemon";
95             break;
96         case DOWNLOAD_ERROR_ID_NOT_FOUND:
97             msg = "The download id is not existed in download service module";
98             break;
99         case DOWNLOAD_ERROR_NO_DATA:
100             msg = "No data because the set API is not called";
101             break;
102         case DOWNLOAD_ERROR_IO_ERROR:
103             msg = "Internal I/O error";
104             break;
105         case DOWNLOAD_ERROR_NONE:
106             success = true;
107             break;
108         default:
109             msg = "Unknown error";
110     }
111
112     if (!success) {
113         LogError("Platform error %d <%s>", err, msg.c_str());
114     }
115
116     return msg;
117 }
118
119
120 typedef struct {
121         int downloadId;
122         download_state_e state;
123         unsigned long long received;
124         void *user_data;
125 } DOWNLOAD_EVENT_DATA_T;
126
127
128 static gboolean downloadEventCB(void *data) {
129         int ret;
130
131         DOWNLOAD_EVENT_DATA_T *fnData = static_cast<DOWNLOAD_EVENT_DATA_T*>(data);
132
133         try {
134                 long downloadId = (long)fnData->downloadId;
135
136                 DownloadManager *thisObj = (DownloadManager*)fnData->user_data;
137                 if (!thisObj) {
138                         throw UnknownException("UserData is NULL.");
139                 }
140
141                 DownloadCallback *callback = thisObj->getCallbackFromMap(downloadId);
142                 if (!callback) {
143                         throw UnknownException("Callback could not found.");
144                 }
145
146                 switch(fnData->state) {
147                         case DOWNLOAD_STATE_QUEUED:
148                         {
149                                 callback->onprogress(downloadId, 0, 0);
150                                 break;
151                         }
152                         case DOWNLOAD_STATE_PAUSED:
153                         {
154                                 callback->onpaused(downloadId);
155                                 break;
156                         }
157                         case DOWNLOAD_STATE_DOWNLOADING:
158                         {
159                                 unsigned long long totalSize = 0;
160                                 ret = download_get_content_size(downloadId, &totalSize);
161                                 if (ret != DOWNLOAD_ERROR_NONE) {
162                                         throw UnknownException(("Platform error while getting total file size. " + _get_download_error(ret)).c_str());
163                                 }
164
165                                 callback->onprogress(downloadId, fnData->received, totalSize);
166                                 break;
167                         }
168                         case DOWNLOAD_STATE_COMPLETED:
169                         {
170                                 ret = download_unset_state_changed_cb(downloadId);
171                                 if (ret != DOWNLOAD_ERROR_NONE) {
172                                         throw UnknownException(("Platform error while unsetting state changed callback. " + _get_download_error(ret)).c_str());
173                                 }
174
175                                 ret = download_unset_progress_cb(downloadId);
176                                 if (ret != DOWNLOAD_ERROR_NONE) {
177                                         throw UnknownException(("Platform error while unsetting progress callback. " + _get_download_error(ret)).c_str());
178                                 }
179
180                                 char *fullPath = NULL;
181                                 ret = download_get_downloaded_file_path(downloadId, &fullPath);
182                                 if (ret != DOWNLOAD_ERROR_NONE || !fullPath) {
183                                         throw UnknownException(("Platform error while getting downloaded full path. " + _get_download_error(ret)).c_str());
184                                 }
185
186                                 std::string virtualPath;
187                                 try {
188                                         virtualPath = DeviceAPI::Filesystem::Utils::toVirtualPath(NULL, fullPath);
189                                 } catch (...) {
190                                         LogWarning("Platform error while converting fullPath.");
191                                         virtualPath = fullPath;
192                                 }
193
194                                 callback->oncompleted(downloadId, virtualPath);
195                                 free(fullPath);
196
197                                 thisObj->removeCallbackFromMap(downloadId);
198                                 break;
199                         }
200                         case DOWNLOAD_STATE_FAILED:
201                         {
202                                 ret = download_unset_state_changed_cb(downloadId);
203                                 if (ret != DOWNLOAD_ERROR_NONE) {
204                                         throw UnknownException(("Platform error while unsetting state changed callback. " + _get_download_error(ret)).c_str());
205                                 }
206
207                                 ret = download_unset_progress_cb(downloadId);
208                                 if (ret != DOWNLOAD_ERROR_NONE) {
209                                         throw UnknownException(("Platform error while unsetting progress callback. " + _get_download_error(ret)).c_str());
210                                 }
211
212                                 int err = DOWNLOAD_ERROR_NONE;
213                                 std::string errMessage;
214                                 ret = download_get_error(downloadId, (download_error_e*)&err);
215                                 if (ret != DOWNLOAD_ERROR_NONE) {
216                                         LogWarning("Platform error while getting download error. ");
217                                 } else {
218                                         errMessage = _get_download_error(err);
219                                 }
220
221                                 UnknownException error(errMessage.c_str());
222                                 callback->onfailed(downloadId, error);
223                                 thisObj->removeCallbackFromMap(downloadId);
224                                 break;
225                         }
226                         case DOWNLOAD_STATE_CANCELED:
227                         {
228                                 ret = download_unset_state_changed_cb(downloadId);
229                                 if (ret != DOWNLOAD_ERROR_NONE) {
230                                         throw UnknownException(("Platform error while unsetting state changed callback. " + _get_download_error(ret)).c_str());
231                                 }
232
233                                 ret = download_unset_progress_cb(downloadId);
234                                 if (ret != DOWNLOAD_ERROR_NONE) {
235                                         throw UnknownException(("Platform error while unsetting progress callback. " + _get_download_error(ret)).c_str());
236                                 }
237
238                                 callback->oncanceled(downloadId);
239                                 thisObj->removeCallbackFromMap(downloadId);
240                                 break;
241                         }
242                         default:
243                                 LogWarning("State changed is ignored.");
244                                 break;
245                 }
246         } catch (const BasePlatformException &err) {
247                 LogError("download_state_changed_cb: %s", err.getMessage().c_str());
248         }
249
250         delete fnData;
251         return false;
252 }
253
254 static void download_state_changed_cb(int downloadId, download_state_e state, void *user_data)
255 {
256         LogDebug("download_state_changed_cb, downloadId=%d, state=%d", downloadId, (int)state);
257         DOWNLOAD_EVENT_DATA_T *data = new DOWNLOAD_EVENT_DATA_T;
258         data->downloadId = downloadId;
259         data->state = state;
260         data->received = 0;
261         data->user_data = user_data;
262
263         // download core f/w calls this callback function in another thread. 
264         // so we should use g_idle_add() to switch context to main thread.
265         g_idle_add(downloadEventCB, static_cast<void*>(data));
266 }
267
268 static void download_progress_cb(int downloadId, unsigned long long received, void *user_data)
269 {
270         LogDebug("download_progress_cb, downloadId=%d, received=%ld", downloadId, received);
271         DOWNLOAD_EVENT_DATA_T *data = new DOWNLOAD_EVENT_DATA_T;
272         data->downloadId = downloadId;
273         data->state = DOWNLOAD_STATE_DOWNLOADING;
274         data->received = received;
275         data->user_data = user_data;
276
277         // download core f/w calls this callback function in another thread. 
278         // so we should use g_idle_add() to switch context to main thread.
279         g_idle_add(downloadEventCB, static_cast<void*>(data));
280 }
281
282 DownloadManager::DownloadManager()
283 {
284 }
285
286 DownloadManager::~DownloadManager()
287 {
288 }
289
290 void DownloadManager::setCallbackToMap(long downloadId, DownloadCallback *callback)
291 {
292         DownloadCallback *value = mDownloadCallbacks[downloadId];
293         if (value) {
294                 delete value;
295         }
296         mDownloadCallbacks[downloadId] = callback;
297 }
298
299 DownloadCallback* DownloadManager::getCallbackFromMap(long downloadId)
300 {
301         return mDownloadCallbacks[downloadId];
302 }
303
304 void DownloadManager::removeCallbackFromMap(long downloadId) {
305         DownloadCallback *value = mDownloadCallbacks[downloadId];
306         mDownloadCallbacks.erase(downloadId);
307         if (value) {
308                 delete value;
309         }
310 }
311
312 long DownloadManager::start(DownloadRequest *request, DownloadCallback *downloadCallback)
313 {
314
315         int ret;
316         int downloadId = 0;
317
318         LogDebug("entered");
319
320         if (!request) {
321                 throw TypeMismatchException("request is NULL.");
322         }
323
324         std::string url = request->getUrl();
325         std::string destination = request->getDestination();
326         std::string fileName = request->getFileName();
327
328         LogDebug("url <%s>, destination <%s>, fileName <%s>", url.c_str(), destination.c_str(), fileName.c_str());
329
330         if (url.empty()) {
331                 throw InvalidValuesException("Invalid DownloadRequest.url.");
332         }
333
334         ret = download_create(&downloadId);
335         if (ret != DOWNLOAD_ERROR_NONE) {
336                 throw UnknownException(("Platform error while creating download. " + _get_download_error(ret)).c_str());
337         }
338
339         ret = download_set_url(downloadId, url.c_str());
340         if (ret != DOWNLOAD_ERROR_NONE) {
341                 throw UnknownException(("Platform error while setting url. " + _get_download_error(ret)).c_str());
342         }
343
344         if (!destination.empty()) {
345                 std::string fullPath;
346                 try {
347                         DeviceAPI::Filesystem::IPathPtr path = DeviceAPI::Filesystem::Utils::fromVirtualPath(NULL, destination);
348                         fullPath = path->getFullPath();
349                 } catch (...) {
350                         LogWarning("Converting virtual path is failed. [%s]", destination.c_str());
351                         fullPath = destination;
352                 }
353                 LogDebug("Converted FullPath = <%s>", fullPath.c_str());
354                 ret = download_set_destination(downloadId, fullPath.c_str());
355                 if (ret != DOWNLOAD_ERROR_NONE) {
356                         throw UnknownException(("Platform error while setting destination. " + _get_download_error(ret)).c_str());
357                 }
358         }
359
360         if (!fileName.empty()) {
361                 ret = download_set_file_name(downloadId, fileName.c_str());
362                 if (ret != DOWNLOAD_ERROR_NONE) {
363                         throw UnknownException(("Platform error while setting fileName. " + _get_download_error(ret)).c_str());
364                 }
365         }
366
367         ret = download_set_state_changed_cb(downloadId, download_state_changed_cb, this);
368         if (ret != DOWNLOAD_ERROR_NONE) {
369                 throw UnknownException(("Platform error while setting state changed callback. " + _get_download_error(ret)).c_str());
370         }
371
372         ret = download_set_progress_cb(downloadId, download_progress_cb, this);
373         if (ret != DOWNLOAD_ERROR_NONE) {
374                 throw UnknownException(("Platform error while setting progress callback. " + _get_download_error(ret)).c_str());
375         }
376
377         ret = download_start(downloadId);
378         if (ret != DOWNLOAD_ERROR_NONE) {
379                 throw UnknownException(("Platform error while starting download. " + _get_download_error(ret)).c_str());
380         }
381
382         LogDebug("downloadId: %d", downloadId);
383
384         setCallbackToMap(downloadId, downloadCallback);
385
386         return downloadId;
387 }
388
389 void DownloadManager::cancel(long downloadId)
390 {
391         int ret;
392
393         LogDebug("entered. downloadId = %d", downloadId);
394
395         ret = download_cancel(downloadId);
396         if (ret != DOWNLOAD_ERROR_NONE) {
397                 if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND) {
398                         throw NotFoundException("download id could not found.");
399                 } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
400                         throw InvalidValuesException("download id is not valid.");
401                 }
402                 throw UnknownException(("Platform error while canceling download. " + _get_download_error(ret)).c_str()); 
403         }
404 }
405
406 void DownloadManager::pause(long downloadId)
407 {
408         int ret;
409
410         LogDebug("entered. downloadId = %d", downloadId);
411
412         ret = download_pause(downloadId);
413         if (ret != DOWNLOAD_ERROR_NONE) {
414                 if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND) {
415                         throw NotFoundException("download id could not found.");
416                 } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
417                         throw InvalidValuesException("download id is not valid.");
418                 }
419                 throw UnknownException(("Platform error while pausing download. " + _get_download_error(ret)).c_str()); 
420         }
421 }
422
423 void DownloadManager::resume(long downloadId)
424 {
425         int ret;
426
427         LogDebug("entered. downloadId = %d", downloadId);
428
429         ret = download_start(downloadId);
430         if (ret != DOWNLOAD_ERROR_NONE) {
431                 if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND) {
432                         throw NotFoundException("download id could not found.");
433                 } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
434                         throw InvalidValuesException("download id is not valid.");
435                 }
436                 throw UnknownException(("Platform error while resuming download. " + _get_download_error(ret)).c_str()); 
437         }
438 }
439
440 std::string DownloadManager::getState(long downloadId)
441 {
442         int ret;
443         download_state_e state;
444         std::string result;
445
446         LogDebug("entered. downloadId = %d", downloadId);
447
448         ret = download_get_state(downloadId, &state);
449         if (ret != DOWNLOAD_ERROR_NONE) {
450                 if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND) {
451                         throw NotFoundException("download id could not found.");
452                 } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
453                         throw InvalidValuesException("download id is not valid.");
454                 }
455                 throw UnknownException(("Platform error while getting state. " + _get_download_error(ret)).c_str()); 
456         }
457
458         switch (state) {
459                 case DOWNLOAD_STATE_READY:
460                 case DOWNLOAD_STATE_QUEUED:
461                         result = TIZEN_ENUM_DOWNLOAD_STATE_QUEUED;
462                         break;
463                 case DOWNLOAD_STATE_DOWNLOADING:
464                         result = TIZEN_ENUM_DOWNLOAD_STATE_DOWNLOADING;
465                         break;
466                 case DOWNLOAD_STATE_PAUSED:
467                         result = TIZEN_ENUM_DOWNLOAD_STATE_PAUSED;
468                         break;
469                 case DOWNLOAD_STATE_COMPLETED:
470                         result = TIZEN_ENUM_DOWNLOAD_STATE_COMPLETED;
471                         break;
472                 case DOWNLOAD_STATE_FAILED:
473                         result = TIZEN_ENUM_DOWNLOAD_STATE_FAILED;
474                         break;
475                 case DOWNLOAD_STATE_CANCELED:
476                         result = TIZEN_ENUM_DOWNLOAD_STATE_CANCELED;
477                         break;
478                 default:
479                         result = "undefined";
480                         LogWarning("Unknown DownloadState was returned.");
481                         break;
482         }
483
484         return result;
485 }
486
487 DownloadRequest* DownloadManager::getDownloadRequest(long downloadId)
488 {
489         int ret;
490
491         char *url = NULL;
492         char *destination = NULL;
493         char *fileName = NULL;
494
495         LogDebug("entered. downloadId = %d", downloadId);
496
497         ret = download_get_url(downloadId, &url);
498         if (ret != DOWNLOAD_ERROR_NONE) {
499                 if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND) {
500                         throw NotFoundException("download id could not found.");
501                 } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
502                         throw InvalidValuesException("download id is not valid.");
503                 }
504                 throw UnknownException(("Platform error while getting url. " + _get_download_error(ret)).c_str());
505         }
506
507         ret = download_get_destination(downloadId, &destination);
508         if (ret != DOWNLOAD_ERROR_NONE && ret != DOWNLOAD_ERROR_NO_DATA) {
509                 if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND) {
510                         throw NotFoundException("download id could not found.");
511                 } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
512                         throw InvalidValuesException("download id is not valid.");
513                 }
514                 throw UnknownException(("Platform error while getting destination. " + _get_download_error(ret)).c_str());
515         }
516
517         ret = download_get_file_name(downloadId, &fileName);
518         if (ret != DOWNLOAD_ERROR_NONE && ret != DOWNLOAD_ERROR_NO_DATA) {
519                 if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND) {
520                         throw NotFoundException("download id could not found.");
521                 } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
522                         throw InvalidValuesException("download id is not valid.");
523                 }
524                 throw UnknownException(("Platform error while getting fileName. " + _get_download_error(ret)).c_str());
525         }
526
527         DownloadRequest *request = new DownloadRequest();
528
529         if (url) {
530                 request->setUrl(url);
531                 free(url);
532         }
533
534         if (destination) {
535                 std::string virtualPath;
536                 try {
537                         virtualPath = DeviceAPI::Filesystem::Utils::toVirtualPath(NULL, destination);
538                 } catch (...) {
539                         LogWarning("Platform error while converting destination path.");
540                         virtualPath = destination;
541                 }
542                 request->setDestination(virtualPath);
543                 free(destination);
544         }
545
546         if (fileName) {
547                 request->setFileName(fileName);
548                 free(fileName);
549         }
550
551         return request;
552 }
553
554 std::string DownloadManager::getMIMEType(long downloadId)
555 {
556         int ret;
557         char *mimeType = NULL;
558         std::string result("");
559
560         LogDebug("entered. downloadId = %d", downloadId);
561
562         ret = download_get_mime_type(downloadId, &mimeType);
563         if (ret != DOWNLOAD_ERROR_NONE) {
564                 if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND) {
565                         throw NotFoundException("download id could not found.");
566                 } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
567                         throw InvalidValuesException("download id is not valid.");
568                 } else if (ret == DOWNLOAD_ERROR_NO_DATA) {
569                         result = "";
570                 } else {
571                         throw UnknownException(("Platform error while getting MIME type. " + _get_download_error(ret)).c_str()); 
572                 }
573         } else {
574                 result = mimeType;
575         }
576
577         if (mimeType) {
578                 free(mimeType);
579         }
580
581         return result;
582 }
583
584 void DownloadManager::setListener(long downloadId, DownloadCallback *downloadCallback)
585 {
586         int ret;
587
588         LogDebug("entered. downloadId = %d", downloadId);
589
590         ret = download_set_state_changed_cb(downloadId, download_state_changed_cb, this);
591         if (ret != DOWNLOAD_ERROR_NONE) {
592                 if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND) {
593                         throw NotFoundException("download id could not found.");
594                 } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
595                         throw InvalidValuesException("download id is not valid.");
596                 }
597                 throw UnknownException(("Platform error while setting state changed callback. " + _get_download_error(ret)).c_str());
598         }
599
600         ret = download_set_progress_cb(downloadId, download_progress_cb, this);
601         if (ret != DOWNLOAD_ERROR_NONE) {
602                 if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND) {
603                         throw NotFoundException("download id could not found.");
604                 } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
605                         throw InvalidValuesException("download id is not valid.");
606                 }
607                 throw UnknownException(("Platform error while setting progress callback. " + _get_download_error(ret)).c_str());
608         }
609
610         setCallbackToMap(downloadId, downloadCallback);
611 }
612
613 } // Download
614 } // DeviceAPI