1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "base/network/download_manager.h"
17
18 #include <memory>
19 #include <mutex>
20
21 #include "curl/curl.h"
22
23 #include "base/log/log.h"
24 #include "base/utils/singleton.h"
25 #include "base/utils/utils.h"
26
27 #define ACE_CURL_EASY_SET_OPTION(handle, opt, data) \
28 do { \
29 CURLcode result = curl_easy_setopt(handle, opt, data); \
30 if (result != CURLE_OK) { \
31 LOGE("Failed to set option: %{public}s, %{public}s", #opt, curl_easy_strerror(result)); \
32 return false; \
33 } \
34 } while (0)
35
36 namespace OHOS::Ace {
37 namespace {
38
39 class DownloadManagerImpl final : public DownloadManager, public Singleton<DownloadManagerImpl> {
40 DECLARE_SINGLETON(DownloadManagerImpl);
41 ACE_DISALLOW_MOVE(DownloadManagerImpl);
42
43 public:
Download(const std::string & url,std::vector<uint8_t> & dataOut)44 bool Download(const std::string& url, std::vector<uint8_t>& dataOut) override
45 {
46 if (!Initialize()) {
47 return false;
48 }
49
50 std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> handle(curl_easy_init(), &curl_easy_cleanup);
51 if (!handle) {
52 LOGE("Failed to create download task");
53 return false;
54 }
55
56 dataOut.clear();
57 std::string errorStr;
58 errorStr.reserve(CURL_ERROR_SIZE);
59
60 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_URL, url.c_str());
61 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEFUNCTION, OnWritingMemory);
62 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEDATA, &dataOut);
63 // Some servers don't like requests that are made without a user-agent field, so we provide one
64 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_USERAGENT, "libcurl-agent/1.0");
65 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_URL, url.c_str());
66 #if !defined(PREVIEW)
67 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_CAINFO, "/etc/ssl/certs/cacert.pem");
68 #endif
69 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_VERBOSE, 1L);
70 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_ERRORBUFFER, errorStr.data());
71
72 #ifdef IOS_PLATFORM
73 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYPEER, 0L);
74 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYHOST, 0L);
75 #endif
76
77 CURLcode result = curl_easy_perform(handle.get());
78 if (result != CURLE_OK) {
79 LOGE("Failed to download, url: %{private}s, %{public}s", url.c_str(), curl_easy_strerror(result));
80 if (!errorStr.empty()) {
81 LOGE("Failed to download reason: %{public}s", errorStr.c_str());
82 }
83 dataOut.clear();
84 return false;
85 }
86 dataOut.shrink_to_fit();
87 return true;
88 }
89
90 private:
OnWritingMemory(void * data,size_t size,size_t memBytes,void * userData)91 static size_t OnWritingMemory(void* data, size_t size, size_t memBytes, void* userData)
92 {
93 // size is always 1, for more details see https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
94 auto& dataOut = *static_cast<std::vector<uint8_t>*>(userData);
95 auto chunkData = static_cast<uint8_t*>(data);
96 dataOut.insert(dataOut.end(), chunkData, chunkData + memBytes);
97 return memBytes;
98 }
99
Initialize()100 bool Initialize()
101 {
102 if (initialized_) {
103 return true;
104 }
105
106 std::lock_guard<std::mutex> lock(mutex_);
107 if (initialized_) {
108 return true;
109 }
110 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
111 LOGE("Failed to initialize 'curl'");
112 return false;
113 }
114 initialized_ = true;
115 return true;
116 }
117
118 std::mutex mutex_;
119 bool initialized_ = false;
120 };
121
122 DownloadManagerImpl::DownloadManagerImpl() = default;
123
~DownloadManagerImpl()124 DownloadManagerImpl::~DownloadManagerImpl()
125 {
126 curl_global_cleanup();
127 }
128
129 }
130
GetInstance()131 DownloadManager& DownloadManager::GetInstance()
132 {
133 return Singleton<DownloadManagerImpl>::GetInstance();
134 }
135
136 } // namespace OHOS::Ace
137