0625e228a4168668391c33a6b016ae764f55e1ee
[platform/core/api/webapi-plugins.git] / src / download / download_instance.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 #include "download/download_instance.h"
18
19 #include <functional>
20
21 #include <net_connection.h>
22
23 #include "common/filesystem/filesystem_provider.h"
24 #include "common/logger.h"
25 #include "common/picojson.h"
26 #include "common/scope_exit.h"
27 #include "common/tools.h"
28 #include "common/typeutil.h"
29
30 namespace extension {
31 namespace download {
32
33 std::vector<DownloadInstance*> DownloadInstance::instances_;
34 std::mutex DownloadInstance::instances_mutex_;
35
36 namespace {
37 // The privileges that required in Download API
38 const std::string kPrivilegeDownload = "http://tizen.org/privilege/download";
39 const std::string kDownloadManagerListenerId = "DownloadManagerListener";
40
41 }  // namespace
42
43 DownloadInstance::DownloadInstance() {
44   ScopeLogger();
45   using std::placeholders::_1;
46   using std::placeholders::_2;
47
48
49 #define REGISTER_METHOD(M) \
50     RegisterSyncHandler(#M, std::bind(&DownloadInstance::M, this, _1, _2))
51
52   REGISTER_METHOD(DownloadManagerGetMimeType);
53   REGISTER_METHOD(DownloadManagerStart);
54   REGISTER_METHOD(DownloadManagerCancel);
55   REGISTER_METHOD(DownloadManagerPause);
56   REGISTER_METHOD(DownloadManagerAbandon);
57   REGISTER_METHOD(DownloadManagerResume);
58   REGISTER_METHOD(DownloadManagerGetState);
59
60 #undef REGISTER_METHOD
61
62
63   std::lock_guard<std::mutex> lock(instances_mutex_);
64   instances_.push_back(this);
65 }
66
67 DownloadInstance::~DownloadInstance() {
68   ScopeLogger();
69   int ret;
70
71   std::lock_guard<std::mutex> lock(instances_mutex_);
72
73   for (DownloadCallbackMap::iterator it = download_callbacks.begin();
74        it != download_callbacks.end(); ++it) {
75     DownloadInfoPtr di_ptr = it->second->instance->di_map[it->second->download_id];
76     SLoggerD("~DownloadInstance() for callbackID %d Called", it->second->download_id);
77
78     if (di_ptr) {
79       ret = download_unset_state_changed_cb(di_ptr->native_download_id);
80       if (ret != DOWNLOAD_ERROR_NONE)
81         LoggerE("download_unset_state_changed_cb() is failed. (%s)", get_error_message(ret));
82
83       ret = download_unset_progress_cb(di_ptr->native_download_id);
84       if (ret != DOWNLOAD_ERROR_NONE)
85         LoggerE("download_unset_progress_cb() is failed. (%s)", get_error_message(ret));
86
87       ret = download_cancel(di_ptr->native_download_id);
88       if (ret != DOWNLOAD_ERROR_NONE)
89         LoggerE("download_cancel() is failed. (%s)", get_error_message(ret));
90
91       ret = download_destroy(di_ptr->native_download_id);
92       if (ret != DOWNLOAD_ERROR_NONE)
93         LoggerE("download_destroy() is failed. (%s)", get_error_message(ret));
94     } else {
95       LoggerD("di_ptr is nullptr");
96     }
97
98     delete (it->second);
99   }
100
101   for (auto it = instances_.begin(); it != instances_.end(); it++) {
102     if (*it == this) {
103       instances_.erase(it);
104       break;
105     }
106   }
107 }
108
109 bool DownloadInstance::CheckInstance(DownloadInstance* instance) {
110   ScopeLogger();
111   for (auto vec_instance : instances_) {
112     if (vec_instance == instance) {
113       return true;
114     }
115   }
116
117   return false;
118 }
119
120 common::PlatformResult DownloadInstance::convertError(int err, const std::string& message) {
121   char* error = nullptr;
122   if (message.empty()) {
123     error = get_error_message(err);
124   } else {
125     error = (char*)message.c_str();
126   }
127
128   switch (err) {
129     case DOWNLOAD_ERROR_INVALID_PARAMETER:
130       return common::PlatformResult(common::ErrorCode::INVALID_VALUES_ERR, error);
131     case DOWNLOAD_ERROR_OUT_OF_MEMORY:
132       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
133     case DOWNLOAD_ERROR_NETWORK_UNREACHABLE:
134       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
135     case DOWNLOAD_ERROR_CONNECTION_TIMED_OUT:
136       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
137     case DOWNLOAD_ERROR_NO_SPACE:
138       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
139     case DOWNLOAD_ERROR_PERMISSION_DENIED:
140       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
141     case DOWNLOAD_ERROR_NOT_SUPPORTED:
142       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
143     case DOWNLOAD_ERROR_INVALID_STATE:
144       return common::PlatformResult(common::ErrorCode::INVALID_VALUES_ERR, error);
145     case DOWNLOAD_ERROR_CONNECTION_FAILED:
146       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
147     case DOWNLOAD_ERROR_INVALID_URL:
148       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
149     case DOWNLOAD_ERROR_INVALID_DESTINATION:
150       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
151     case DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS:
152       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
153     case DOWNLOAD_ERROR_QUEUE_FULL:
154       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
155     case DOWNLOAD_ERROR_ALREADY_COMPLETED:
156       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
157     case DOWNLOAD_ERROR_FILE_ALREADY_EXISTS:
158       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
159     case DOWNLOAD_ERROR_CANNOT_RESUME:
160       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
161     case DOWNLOAD_ERROR_FIELD_NOT_FOUND:
162       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
163     case DOWNLOAD_ERROR_TOO_MANY_REDIRECTS:
164       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
165     case DOWNLOAD_ERROR_UNHANDLED_HTTP_CODE:
166       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
167     case DOWNLOAD_ERROR_REQUEST_TIMEOUT:
168       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
169     case DOWNLOAD_ERROR_RESPONSE_TIMEOUT:
170       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
171     case DOWNLOAD_ERROR_SYSTEM_DOWN:
172       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
173     case DOWNLOAD_ERROR_ID_NOT_FOUND:
174       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
175     case DOWNLOAD_ERROR_INVALID_NETWORK_TYPE:
176       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
177     case DOWNLOAD_ERROR_NO_DATA:
178       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
179     case DOWNLOAD_ERROR_IO_ERROR:
180       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, error);
181     default:
182       return common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, "Unknown error.");
183   }
184 }
185
186 #define CHECK_EXIST(args, name, out)                                               \
187   if (!args.contains(name)) {                                                      \
188     LogAndReportError(common::PlatformResult(common::ErrorCode::TYPE_MISMATCH_ERR, \
189                                              name " is required argument"),        \
190                       &out);                                                       \
191     return;                                                                        \
192   }
193
194 void DownloadInstance::OnStateChanged(int download_id, download_state_e state, void* user_data) {
195   ScopeLogger();
196   CallbackPtr down_cb_ptr = static_cast<CallbackPtr>(user_data);
197
198   // Prevent to call finished, cancelled or failed function more than once
199   if (DOWNLOAD_STATE_COMPLETED == down_cb_ptr->state ||
200       DOWNLOAD_STATE_CANCELED == down_cb_ptr->state ||
201       DOWNLOAD_STATE_FAILED == down_cb_ptr->state) {
202     LoggerD("Already finished job, not calling callback for %d state", down_cb_ptr->state);
203     return;
204   }
205
206   down_cb_ptr->state = state;
207   down_cb_ptr->native_download_id = download_id;
208
209   SLoggerD("State for callbackId %d changed to %d", down_cb_ptr->download_id,
210            static_cast<int>(state));
211
212   switch (state) {
213     case DOWNLOAD_STATE_NONE:
214       break;
215     case DOWNLOAD_STATE_DOWNLOADING:
216       OnStart(download_id, user_data);
217       break;
218     case DOWNLOAD_STATE_PAUSED:
219       if (!g_idle_add(OnPaused, down_cb_ptr)) {
220         LoggerE("g_idle_add failed");
221       }
222       break;
223     case DOWNLOAD_STATE_COMPLETED:
224       if (!g_idle_add(OnFinished, down_cb_ptr)) {
225         LoggerE("g_idle_add failed");
226       }
227       break;
228     case DOWNLOAD_STATE_CANCELED:
229       if (!g_idle_add(OnCanceled, down_cb_ptr)) {
230         LoggerE("g_idle_add failed");
231       }
232       break;
233     case DOWNLOAD_STATE_FAILED:
234       if (!g_idle_add(OnFailed, down_cb_ptr)) {
235         LoggerE("g_idle_add failed");
236       }
237       break;
238     default:
239       LoggerD("Unexpected download state: %d", state);
240       break;
241   }
242 }
243
244 gboolean DownloadInstance::OnProgressChanged(void* user_data) {
245   ScopeLogger();
246   CallbackPtr down_cb_ptr = static_cast<CallbackPtr>(user_data);
247   std::lock_guard<std::mutex> lock(instances_mutex_);
248   if (!CheckInstance(down_cb_ptr->instance)) {
249     return FALSE;
250   }
251
252   DownloadInfoPtr di_ptr = down_cb_ptr->instance->di_map[down_cb_ptr->download_id];
253   if (!di_ptr) {
254     LoggerW("Download handle does not exist for callback id %d", down_cb_ptr->download_id);
255     return FALSE;
256   }
257
258   picojson::value::object out;
259   out["status"] = picojson::value("progress");
260   out["downloadId"] = picojson::value(static_cast<double>(down_cb_ptr->download_id));
261   out["receivedSize"] = picojson::value(static_cast<double>(down_cb_ptr->received));
262   out["totalSize"] = picojson::value(static_cast<double>(di_ptr->file_size));
263   out["listenerId"] = picojson::value(kDownloadManagerListenerId);
264
265   LoggerD("OnProgressChanged for callbackId %d Called: Received: %llu", down_cb_ptr->download_id,
266           down_cb_ptr->received);
267
268   picojson::value v = picojson::value(out);
269   Instance::PostMessage(down_cb_ptr->instance, v.serialize().c_str());
270
271   return FALSE;
272 }
273
274 void DownloadInstance::OnStart(int download_id, void* user_data) {
275   ScopeLogger();
276   unsigned long long totalSize;
277
278   CallbackPtr down_cb_ptr = static_cast<CallbackPtr>(user_data);
279   std::lock_guard<std::mutex> lock(instances_mutex_);
280   if (!CheckInstance(down_cb_ptr->instance)) {
281     return;
282   }
283
284   SLoggerD("OnStart for callbackId %d Called", down_cb_ptr->download_id);
285
286   DownloadInfoPtr di_ptr = down_cb_ptr->instance->di_map[down_cb_ptr->download_id];
287   if (!di_ptr) {
288     LoggerW("Download handle does not exist for callback id %d", down_cb_ptr->download_id);
289     return;
290   }
291
292   download_get_content_size(download_id, &totalSize);
293
294   di_ptr->file_size = totalSize;
295 }
296
297 gboolean DownloadInstance::OnFinished(void* user_data) {
298   ScopeLogger();
299   char* fullPath = NULL;
300
301   CallbackPtr down_cb_ptr = static_cast<CallbackPtr>(user_data);
302   std::lock_guard<std::mutex> lock(instances_mutex_);
303   if (!CheckInstance(down_cb_ptr->instance)) {
304     return FALSE;
305   }
306
307   int download_id = down_cb_ptr->download_id;
308   DownloadInfoPtr di_ptr = down_cb_ptr->instance->di_map[download_id];
309   if (!di_ptr) {
310     LoggerW("Download handle does not exist for callback id %d", down_cb_ptr->download_id);
311     return FALSE;
312   }
313
314   LoggerD("OnFinished for callbackID %d Called", download_id);
315
316   picojson::value::object out;
317
318   int ret = download_get_downloaded_file_path(down_cb_ptr->native_download_id, &fullPath);
319   if (ret != DOWNLOAD_ERROR_NONE) {
320     LogAndReportError(convertError(ret), &out, ("download_get_downloaded_file_path error: %d (%s)",
321                                                 ret, get_error_message(ret)));
322   } else {
323     ret = download_unset_state_changed_cb(di_ptr->native_download_id);
324     if (ret != DOWNLOAD_ERROR_NONE) {
325       LoggerW("%s", get_error_message(ret));
326     }
327     ret = download_unset_progress_cb(di_ptr->native_download_id);
328     if (ret != DOWNLOAD_ERROR_NONE) {
329       LoggerW("%s", get_error_message(ret));
330     }
331     ret = download_destroy(di_ptr->native_download_id);
332     if (ret != DOWNLOAD_ERROR_NONE) {
333       LoggerW("%s", get_error_message(ret));
334     }
335     out["status"] = picojson::value("completed");
336     out["fullPath"] =
337         picojson::value(common::FilesystemProvider::Create().GetVirtualPath(fullPath));
338   }
339   out["downloadId"] = picojson::value(static_cast<double>(download_id));
340
341   out["listenerId"] = picojson::value(kDownloadManagerListenerId);
342   Instance::PostMessage(down_cb_ptr->instance, picojson::value(out).serialize().c_str());
343   // down_cb_ptr is freed in destructor, it prevent from crash if OnFinished state
344   // was called after OnCanceled or OnFailed
345   free(fullPath);
346
347   return FALSE;
348 }
349
350 gboolean DownloadInstance::OnPaused(void* user_data) {
351   ScopeLogger();
352   CallbackPtr down_cb_ptr = static_cast<CallbackPtr>(user_data);
353   std::lock_guard<std::mutex> lock(instances_mutex_);
354   if (!CheckInstance(down_cb_ptr->instance)) {
355     return FALSE;
356   }
357
358   int download_id = down_cb_ptr->download_id;
359   DownloadInfoPtr di_ptr = down_cb_ptr->instance->di_map[download_id];
360   if (!di_ptr) {
361     LoggerW("Download handle does not exist for callback id %d", down_cb_ptr->download_id);
362     return FALSE;
363   }
364
365   LoggerD("OnPaused for callbackID %d Called", download_id);
366
367   picojson::value::object out;
368   out["status"] = picojson::value("paused");
369   out["downloadId"] = picojson::value(static_cast<double>(download_id));
370   out["listenerId"] = picojson::value(kDownloadManagerListenerId);
371
372   Instance::PostMessage(down_cb_ptr->instance, picojson::value(out).serialize().c_str());
373   return FALSE;
374 }
375
376 gboolean DownloadInstance::OnCanceled(void* user_data) {
377   ScopeLogger();
378   CallbackPtr down_cb_ptr = static_cast<CallbackPtr>(user_data);
379   std::lock_guard<std::mutex> lock(instances_mutex_);
380   if (!CheckInstance(down_cb_ptr->instance)) {
381     return FALSE;
382   }
383
384   int download_id = down_cb_ptr->download_id;
385   DownloadInfoPtr di_ptr = down_cb_ptr->instance->di_map[download_id];
386   if (!di_ptr) {
387     LoggerW("Download handle does not exist for callback id %d", download_id);
388     return FALSE;
389   }
390
391   LoggerD("OnCanceled for callbackID %d Called", download_id);
392
393   int ret = download_unset_state_changed_cb(di_ptr->native_download_id);
394   if (ret != DOWNLOAD_ERROR_NONE) {
395     LoggerE("%s", get_error_message(ret));
396   }
397
398   ret = download_unset_progress_cb(di_ptr->native_download_id);
399   if (ret != DOWNLOAD_ERROR_NONE) {
400     LoggerE("%s", get_error_message(ret));
401   }
402
403   picojson::value::object out;
404   out["status"] = picojson::value("canceled");
405   out["downloadId"] = picojson::value(static_cast<double>(download_id));
406   out["listenerId"] = picojson::value(kDownloadManagerListenerId);
407
408   Instance::PostMessage(down_cb_ptr->instance, picojson::value(out).serialize().c_str());
409   // down_cb_ptr is freed in destructor, it prevent from crash if OnFinished state
410   // was called after OnFinished or OnFailed
411   return FALSE;
412 }
413
414 gboolean DownloadInstance::OnFailed(void* user_data) {
415   ScopeLogger();
416   download_error_e error;
417   picojson::object out;
418
419   CallbackPtr down_cb_ptr = static_cast<CallbackPtr>(user_data);
420   std::lock_guard<std::mutex> lock(instances_mutex_);
421   if (!CheckInstance(down_cb_ptr->instance)) {
422     return FALSE;
423   }
424
425   int download_id = down_cb_ptr->download_id;
426   DownloadInfoPtr di_ptr = down_cb_ptr->instance->di_map[download_id];
427   if (!di_ptr) {
428     LoggerW("Download handle does not exist for callback id %d", download_id);
429     return FALSE;
430   }
431
432   LoggerD("OnFailed for callbackID %d called", download_id);
433
434   download_get_error(down_cb_ptr->native_download_id, &error);
435   if (DOWNLOAD_ERROR_NONE != error) {
436     int http_status = 0;
437     int ret = download_get_http_status(down_cb_ptr->native_download_id, &http_status);
438     std::string error_message;
439     if (DOWNLOAD_ERROR_NONE != ret) {
440       LoggerE("Gathering HTTP status failed, default error message will be used");
441     } else {
442       LoggerD("HTTP status is: %d", http_status);
443       error_message = "Error with HTTP status: " + std::to_string(http_status);
444     }
445     LogAndReportError(convertError(error, error_message), &out,
446                       ("download_get_error error: %d (%s)", error, get_error_message(error)));
447   }
448
449   int ret = download_unset_state_changed_cb(di_ptr->native_download_id);
450   if (ret != DOWNLOAD_ERROR_NONE) {
451     LoggerE("%s", get_error_message(ret));
452   }
453
454   ret = download_unset_progress_cb(di_ptr->native_download_id);
455   if (ret != DOWNLOAD_ERROR_NONE) {
456     LoggerE("%s", get_error_message(ret));
457   }
458
459   out["downloadId"] = picojson::value(static_cast<double>(down_cb_ptr->download_id));
460   out["listenerId"] = picojson::value(kDownloadManagerListenerId);
461
462   Instance::PostMessage(down_cb_ptr->instance, picojson::value(out).serialize().c_str());
463   // down_cb_ptr is freed in destructor, it prevent from crash if OnFinished state
464   // was called after OnFinished or OnCanceled
465   return FALSE;
466 }
467
468 void DownloadInstance::progress_changed_cb(int download_id, long long unsigned received,
469                                            void* user_data) {
470   ScopeLogger();
471   CallbackPtr down_cb_ptr = static_cast<CallbackPtr>(user_data);
472   down_cb_ptr->received = received;
473   down_cb_ptr->native_download_id = download_id;
474
475   if (!g_idle_add(OnProgressChanged, down_cb_ptr)) {
476     LoggerE("g_idle_add failed");
477   }
478 }
479
480 #define CHECK_CONNECTION_ERROR(ret)                                                            \
481   switch (ret) {                                                                               \
482     case CONNECTION_ERROR_NONE:                                                                \
483       break;                                                                                   \
484     case CONNECTION_ERROR_NOT_SUPPORTED:                                                       \
485       return LogAndCreateResult(                                                               \
486           common::ErrorCode::NOT_SUPPORTED_ERR,                                                \
487           "The networkType of the given DownloadRequest is not supported",                     \
488           ("The networkType of the given DownloadRequest is not supported"));                  \
489     default:                                                                                   \
490       return LogAndCreateResult(common::ErrorCode::UNKNOWN_ERR, "Connection problem occurred", \
491                                 ("Connection problem occurred"));                              \
492   }
493
494 common::PlatformResult DownloadInstance::CheckNetworkConnection(const std::string& network_type,
495                                                                 bool& network_support,
496                                                                 bool& network_available,
497                                                                 DownloadInfoPtr di_ptr) {
498   connection_h connection = nullptr;
499   int ret = connection_create(&connection);
500   CHECK_CONNECTION_ERROR(ret)
501   SCOPE_EXIT {
502     connection_destroy(connection);
503   };
504
505   connection_type_e connection_type = CONNECTION_TYPE_DISCONNECTED;
506   ret = connection_get_type(connection, &connection_type);
507   CHECK_CONNECTION_ERROR(ret)
508
509   if (CONNECTION_TYPE_DISCONNECTED == connection_type) {
510     return LogAndCreateResult(common::ErrorCode::UNKNOWN_ERR, "Connection problem occurred",
511                               ("Connection type is disconnected"));
512   }
513
514   if ("CELLULAR" == network_type) {
515     // check if connection type is supported
516     bool cell_support = false;
517     system_info_get_platform_bool("http://tizen.org/feature/network.telephony", &cell_support);
518
519     // check if connection is available
520     connection_cellular_state_e cell_state = CONNECTION_CELLULAR_STATE_OUT_OF_SERVICE;
521     ret = connection_get_cellular_state(connection, &cell_state);
522     CHECK_CONNECTION_ERROR(ret)
523     bool cell_available = (CONNECTION_CELLULAR_STATE_CONNECTED == cell_state);
524
525     // setup download parameters using cellular network
526     network_support = cell_support;
527     network_available = cell_available;
528     di_ptr->network_type = DOWNLOAD_NETWORK_DATA_NETWORK;
529   } else if ("WIFI" == network_type) {
530     // check if connection type is supported
531     bool wifi_support = false;
532     system_info_get_platform_bool("http://tizen.org/feature/network.wifi", &wifi_support);
533
534     // check if connection is available
535     connection_wifi_state_e wifi_state = CONNECTION_WIFI_STATE_DEACTIVATED;
536     ret = connection_get_wifi_state(connection, &wifi_state);
537     CHECK_CONNECTION_ERROR(ret)
538     bool wifi_available = (CONNECTION_WIFI_STATE_CONNECTED == wifi_state);
539
540     // setup download parameters using wifi network
541     network_support = wifi_support;
542     network_available = wifi_available;
543     di_ptr->network_type = DOWNLOAD_NETWORK_WIFI;
544   } else if (("ALL" == network_type) && (CONNECTION_TYPE_DISCONNECTED != connection_type)) {
545     // setup download parameters using 'all' network
546     network_support = true;
547     network_available = true;
548     di_ptr->network_type = DOWNLOAD_NETWORK_ALL;
549   } else {
550     return LogAndCreateResult(
551         common::ErrorCode::INVALID_VALUES_ERR,
552         "The input parameter contains an invalid network type.",
553         ("The input parameter contains an invalid network type: %s", network_type.c_str()));
554   }
555   return common::PlatformResult(common::ErrorCode::NO_ERROR);
556 }
557 #undef CHECK_CONNECTION_ERROR
558
559 void DownloadInstance::DownloadManagerStart(const picojson::value& args, picojson::object& out) {
560   ScopeLogger();
561   CHECK_PRIVILEGE_ACCESS(kPrivilegeDownload, &out);
562   CHECK_EXIST(args, "downloadId", out)
563   CHECK_EXIST(args, "url", out)
564
565   int ret;
566   std::string network_type;
567
568   DownloadInfoPtr di_ptr(new DownloadInfo);
569
570   di_ptr->download_id = static_cast<int>(args.get("downloadId").get<double>());
571   di_ptr->url = args.get("url").is<std::string>() ? args.get("url").get<std::string>() : "";
572
573   if (!args.get("destination").is<picojson::null>()) {
574     if (args.get("destination").is<std::string>() &&
575         args.get("destination").get<std::string>() != "") {
576       di_ptr->destination = args.get("destination").get<std::string>();
577       di_ptr->destination = common::FilesystemProvider::Create().GetRealPath(di_ptr->destination);
578     }
579   }
580
581   CHECK_STORAGE_ACCESS(di_ptr->destination.empty()
582                            ? common::FilesystemProvider::Create().GetRealPath("downloads")
583                            : di_ptr->destination,
584                        &out);
585
586   if (!args.get("fileName").is<picojson::null>()) {
587     if (args.get("fileName").is<std::string>() && args.get("fileName").get<std::string>() != "") {
588       di_ptr->file_name = args.get("fileName").get<std::string>();
589     }
590   }
591
592   if (!args.get("networkType").is<picojson::null>()) {
593     network_type = args.get("networkType").is<std::string>()
594                        ? args.get("networkType").get<std::string>()
595                        : "ALL";
596   }
597
598   bool network_support = false;
599   bool network_available = false;
600
601   common::PlatformResult result =
602       CheckNetworkConnection(network_type, network_support, network_available, di_ptr);
603   if (!result) {
604     LogAndReportError(result, &out);
605     return;
606   }
607
608   /*
609    * There is no relevant feature for network_type == "ALL".
610    */
611   if (!network_support && ("ALL" != network_type)) {
612     LogAndReportError(common::PlatformResult(common::ErrorCode::NOT_SUPPORTED_ERR,
613                                              "The networkType of the given DownloadRequest "
614                                              "is not supported on this device."),
615                       &out,
616                       ("Requested network type (%s) is not supported.", network_type.c_str()));
617     return;
618   }
619
620   if (!network_available) {
621     LogAndReportError(common::PlatformResult(common::ErrorCode::NETWORK_ERR,
622                                              "The networkType of the given DownloadRequest "
623                                              "is currently not available on this device."),
624                       &out,
625                       ("Requested network type (%s) is not available.", network_type.c_str()));
626     return;
627   }
628
629   CallbackPtr down_cb_ptr(new DownloadCallback);
630
631   down_cb_ptr->download_id = di_ptr->download_id;
632   down_cb_ptr->instance = this;
633
634   download_callbacks[down_cb_ptr->download_id] = down_cb_ptr;
635
636   ret = download_create(&di_ptr->native_download_id);
637   if (ret != DOWNLOAD_ERROR_NONE) {
638     LogAndReportError(convertError(ret), &out,
639                       ("download_create error: %d (%s)", ret, get_error_message(ret)));
640     return;
641   }
642
643   ret = download_set_state_changed_cb(di_ptr->native_download_id, OnStateChanged,
644                                       static_cast<void*>(down_cb_ptr));
645   if (ret != DOWNLOAD_ERROR_NONE) {
646     LogAndReportError(convertError(ret), &out, ("download_set_state_changed_cb error: %d (%s)", ret,
647                                                 get_error_message(ret)));
648     return;
649   }
650
651   ret = download_set_progress_cb(di_ptr->native_download_id, progress_changed_cb,
652                                  static_cast<void*>(down_cb_ptr));
653   if (ret != DOWNLOAD_ERROR_NONE) {
654     LogAndReportError(convertError(ret), &out,
655                       ("download_set_progress_cb error: %d (%s)", ret, get_error_message(ret)));
656     return;
657   }
658
659   ret = download_set_url(di_ptr->native_download_id, di_ptr->url.c_str());
660   if (ret != DOWNLOAD_ERROR_NONE) {
661     LogAndReportError(convertError(ret), &out,
662                       ("download_set_url error: %d (%s)", ret, get_error_message(ret)));
663     return;
664   }
665
666   if (di_ptr->destination.size() != 0) {
667     ret = download_set_destination(di_ptr->native_download_id, di_ptr->destination.c_str());
668     if (ret != DOWNLOAD_ERROR_NONE) {
669       LogAndReportError(convertError(ret), &out,
670                         ("download_set_destination error: %d (%s)", ret, get_error_message(ret)));
671       return;
672     }
673   }
674
675   if (!di_ptr->file_name.empty()) {
676     ret = download_set_file_name(di_ptr->native_download_id, di_ptr->file_name.c_str());
677     if (ret != DOWNLOAD_ERROR_NONE) {
678       LogAndReportError(convertError(ret), &out,
679                         ("download_set_file_name error: %d (%s)", ret, get_error_message(ret)));
680       return;
681     }
682   }
683
684   ret = download_set_network_type(di_ptr->native_download_id, di_ptr->network_type);
685
686   if (args.get("httpHeader").is<picojson::object>()) {
687     picojson::object obj = args.get("httpHeader").get<picojson::object>();
688     for (picojson::object::const_iterator it = obj.begin(); it != obj.end(); ++it) {
689       download_add_http_header_field(di_ptr->native_download_id, it->first.c_str(),
690                                      it->second.to_str().c_str());
691     }
692   }
693
694   di_map[down_cb_ptr->download_id] = di_ptr;
695
696   ret = download_start(di_ptr->native_download_id);
697
698   if (ret == DOWNLOAD_ERROR_NONE) {
699     ReportSuccess(out);
700   } else {
701     LogAndReportError(convertError(ret), &out,
702                       ("download_start error: %d (%s)", ret, get_error_message(ret)));
703   }
704 }
705
706 void DownloadInstance::DownloadManagerCancel(const picojson::value& args, picojson::object& out) {
707   ScopeLogger();
708   CHECK_EXIST(args, "downloadId", out)
709   int downloadId, ret;
710
711   int callbackId = static_cast<int>(args.get("downloadId").get<double>());
712
713   if (!GetDownloadID(callbackId, downloadId)) {
714     LogAndReportError(
715         common::PlatformResult(common::ErrorCode::UNKNOWN_ERR,
716                                "The identifier does not match any download operation in progress"),
717         &out, ("downloadId: %d, found in WEB Api but missing in native API", callbackId));
718     return;
719   }
720
721   ret = download_cancel(downloadId);
722
723   if (ret == DOWNLOAD_ERROR_NONE) {
724     ReportSuccess(out);
725   } else {
726     LogAndReportError(convertError(ret), &out,
727                       ("download_cancel error: %d (%s)", ret, get_error_message(ret)));
728   }
729 }
730
731 void DownloadInstance::DownloadManagerPause(const picojson::value& args, picojson::object& out) {
732   ScopeLogger();
733   CHECK_EXIST(args, "downloadId", out)
734   int downloadId, ret;
735
736   int callbackId = static_cast<int>(args.get("downloadId").get<double>());
737
738   if (!GetDownloadID(callbackId, downloadId)) {
739     LogAndReportError(
740         common::PlatformResult(common::ErrorCode::UNKNOWN_ERR,
741                                "The identifier does not match any download operation in progress"),
742         &out, ("downloadId: %d, found in WEB Api but missing in native API", downloadId));
743     return;
744   }
745
746   ret = download_pause(downloadId);
747
748   if (ret == DOWNLOAD_ERROR_NONE) {
749     ReportSuccess(out);
750   } else {
751     LogAndReportError(convertError(ret), &out,
752                       ("download_pause error: %d (%s)", ret, get_error_message(ret)));
753   }
754 }
755
756 void DownloadInstance::DownloadManagerAbandon(const picojson::value& args, picojson::object& out) {
757   ScopeLogger();
758   CHECK_EXIST(args, "downloadId", out)
759   int callbackId, nativeDownloadId, ret;
760   callbackId = static_cast<int>(args.get("downloadId").get<double>());
761
762   if (!GetDownloadID(callbackId, nativeDownloadId)) {
763     LogAndReportError(
764         common::PlatformResult(common::ErrorCode::UNKNOWN_ERR,
765                                "The identifier does not match any download operation in progress"),
766         &out, ("downloadId: %d, found in WEB Api but missing in native API", callbackId));
767     return;
768   }
769
770   ret = download_destroy(nativeDownloadId);
771   if (ret == DOWNLOAD_ERROR_NONE) {
772     if (di_map.find(callbackId) != di_map.end()) {
773       di_map.find(callbackId)->second->abandoned = true;
774     }
775     ReportSuccess(out);
776   } else {
777     LogAndReportError(convertError(ret), &out,
778                       ("download_destroy error: %d (%s)", ret, get_error_message(ret)));
779   }
780 }
781
782 void DownloadInstance::DownloadManagerResume(const picojson::value& args, picojson::object& out) {
783   ScopeLogger();
784   CHECK_EXIST(args, "downloadId", out)
785   int nativeDownloadId, ret;
786
787   int callbackId = static_cast<int>(args.get("downloadId").get<double>());
788
789   if ((di_map.end() != di_map.find(callbackId)) && (di_map.find(callbackId)->second->abandoned)) {
790     LogAndReportError(
791         common::PlatformResult(common::ErrorCode::INVALID_VALUES_ERR,
792                                "The download operation with given identifier has been abandoned "
793                                "and can not be resumed"),
794         &out,
795         ("The download operation with identifier: %d has been abandoned and can not be resumed",
796          callbackId));
797     return;
798   }
799
800   if (!GetDownloadID(callbackId, nativeDownloadId)) {
801     LogAndReportError(
802         common::PlatformResult(common::ErrorCode::UNKNOWN_ERR,
803                                "The identifier does not match any download operation in progress"),
804         &out, ("downloadId: %d, found in WEB Api but missing in native API", callbackId));
805     return;
806   }
807
808   download_state_e state;
809   ret = download_get_state(nativeDownloadId, &state);
810
811   if (ret == DOWNLOAD_ERROR_NONE) {
812     switch (state) {
813       case DOWNLOAD_STATE_QUEUED:
814       case DOWNLOAD_STATE_DOWNLOADING:
815       case DOWNLOAD_STATE_COMPLETED:
816         ReportSuccess(out);
817         return;
818       default:
819         LoggerD("Download state: %d", state);
820     }
821   }
822
823   ret = download_set_state_changed_cb(nativeDownloadId, OnStateChanged,
824                                       static_cast<void*>(download_callbacks[callbackId]));
825   if (ret != DOWNLOAD_ERROR_NONE) {
826     LogAndReportError(convertError(ret), &out, ("download_set_state_changed_cb error: %d (%s)", ret,
827                                                 get_error_message(ret)));
828     return;
829   }
830
831   ret = download_set_progress_cb(nativeDownloadId, progress_changed_cb,
832                                  static_cast<void*>(download_callbacks[callbackId]));
833   if (ret != DOWNLOAD_ERROR_NONE) {
834     LogAndReportError(convertError(ret), &out,
835                       ("download_set_progress_cb error: %d (%s)", ret, get_error_message(ret)));
836     return;
837   }
838
839   ret = download_start(nativeDownloadId);
840
841   if (ret == DOWNLOAD_ERROR_NONE) {
842     ReportSuccess(out);
843   } else {
844     LogAndReportError(convertError(ret), &out,
845                       ("download_start error: %d (%s)", ret, get_error_message(ret)));
846   }
847 }
848
849 void DownloadInstance::DownloadManagerGetState(const picojson::value& args, picojson::object& out) {
850   ScopeLogger();
851   CHECK_EXIST(args, "downloadId", out)
852   int downloadId, ret;
853   std::string stateValue;
854   download_state_e state;
855
856   int callbackId = static_cast<int>(args.get("downloadId").get<double>());
857
858   if (di_map.find(callbackId)->second->abandoned) {
859     stateValue = "ABANDONED";
860     ReportSuccess(picojson::value(stateValue), out);
861     return;
862   }
863
864   if (!GetDownloadID(callbackId, downloadId)) {
865     LogAndReportError(
866         common::PlatformResult(common::ErrorCode::NOT_FOUND_ERR,
867                                "The identifier does not match any download operation in progress"),
868         &out, ("The identifier %d does not match any download operation in progress", downloadId));
869     return;
870   }
871
872   ret = download_get_state(downloadId, &state);
873
874   if (ret == DOWNLOAD_ERROR_NONE) {
875     switch (state) {
876       case DOWNLOAD_STATE_NONE:
877         break;
878       case DOWNLOAD_STATE_QUEUED:
879         stateValue = "QUEUED";
880         break;
881       case DOWNLOAD_STATE_DOWNLOADING:
882         stateValue = "DOWNLOADING";
883         break;
884       case DOWNLOAD_STATE_PAUSED:
885         stateValue = "PAUSED";
886         break;
887       case DOWNLOAD_STATE_COMPLETED:
888         stateValue = "COMPLETED";
889         break;
890       case DOWNLOAD_STATE_FAILED:
891         stateValue = "FAILED";
892         break;
893       case DOWNLOAD_STATE_CANCELED:
894         stateValue = "CANCELED";
895         break;
896       default:
897         LoggerD("Unexpected download state: %d", state);
898         break;
899     }
900
901     ReportSuccess(picojson::value(stateValue), out);
902   } else {
903     LogAndReportError(convertError(ret), &out,
904                       ("download_get_state error: %d (%s)", ret, get_error_message(ret)));
905   }
906 }
907
908 void DownloadInstance::DownloadManagerGetMimeType(const picojson::value& args,
909                                                   picojson::object& out) {
910   ScopeLogger();
911   CHECK_EXIST(args, "downloadId", out)
912
913   int downloadId, ret;
914   char* mimetype = NULL;
915
916   int callbackId = static_cast<int>(args.get("downloadId").get<double>());
917
918   if (!GetDownloadID(callbackId, downloadId)) {
919     LogAndReportError(
920         common::PlatformResult(common::ErrorCode::NOT_FOUND_ERR,
921                                "The identifier does not match any download operation in progress"),
922         &out, ("The identifier %d does not match any download operation in progress", downloadId));
923     return;
924   }
925
926   ret = download_get_mime_type(downloadId, &mimetype);
927
928   if (ret == DOWNLOAD_ERROR_NONE) {
929     ReportSuccess(picojson::value(mimetype), out);
930   } else {
931     LogAndReportError(convertError(ret), &out,
932                       ("download_get_mime_type error: %d (%s)", ret, get_error_message(ret)));
933   }
934   free(mimetype);
935 }
936
937 bool DownloadInstance::GetDownloadID(const int download_id, int& native_download_id) {
938   ScopeLogger();
939   if (di_map.find(download_id) != di_map.end()) {
940     native_download_id = di_map.find(download_id)->second->native_download_id;
941   } else {
942     return false;
943   }
944   return true;
945 }
946
947 #undef CHECK_EXIST
948
949 }  // namespace download
950 }  // namespace extension