1 /*
2 * Copyright (c) 2023-2024 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 <condition_variable>
17 #include <cstdint>
18 #include <functional>
19 #include <memory>
20 #include <mutex>
21 #include <optional>
22 #include <string>
23 #include <vector>
24
25 #include "curl/curl.h"
26
27 #include "base/log/log.h"
28 #include "base/network/download_manager.h"
29 #include "base/utils/macros.h"
30
31 #define ACE_CURL_EASY_SET_OPTION(handle, opt, data) \
32 do { \
33 CURLcode result = curl_easy_setopt(handle, opt, data); \
34 if (result != CURLE_OK) { \
35 return false; \
36 } \
37 } while (0)
38
39 namespace OHOS::Ace {
40
41 std::unique_ptr<DownloadManager> DownloadManager::instance_ = nullptr;
42
43 class ACE_FORCE_EXPORT DownloadManagerPrview : public DownloadManager {
44 public:
45 DownloadManagerPrview() = default;
~DownloadManagerPrview()46 ~DownloadManagerPrview()
47 {
48 if (isCurl_) {
49 curl_global_cleanup();
50 }
51 }
52
Download(const std::string & url,std::vector<uint8_t> & dataOut)53 bool Download(const std::string& url, std::vector<uint8_t>& dataOut) override
54 {
55 // when calling, it is necessary to set it to true and call curl clean up method
56 // during download manager ohos object destruction
57 isCurl_ = true;
58 if (!Initialize()) {
59 return false;
60 }
61
62 std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> handle(curl_easy_init(), &curl_easy_cleanup);
63 if (!handle) {
64 return false;
65 }
66
67 dataOut.clear();
68 std::string errorStr;
69 errorStr.reserve(CURL_ERROR_SIZE);
70
71 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_URL, url.c_str());
72 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEFUNCTION, OnWritingMemory);
73 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEDATA, &dataOut);
74 // Some servers don't like requests that are made without a user-agent field, so we provide one
75 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_USERAGENT, "libcurl-agent/1.0");
76 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_URL, url.c_str());
77 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_VERBOSE, 1L);
78 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_ERRORBUFFER, errorStr.data());
79
80 ProxyInfo proxy;
81 if (GetProxy(proxy)) {
82 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXY, proxy.host.c_str());
83 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXYPORT, proxy.port);
84 if (!proxy.exclusions.empty()) {
85 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_NOPROXY, proxy.exclusions.c_str());
86 }
87 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
88 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HTTPPROXYTUNNEL, 1L);
89 }
90
91 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYPEER, 0L);
92 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYHOST, 0L);
93
94 CURLcode result = curl_easy_perform(handle.get());
95 if (result != CURLE_OK) {
96 LOGE("Failed to download, url: %{private}s, %{public}s", url.c_str(), curl_easy_strerror(result));
97 if (!errorStr.empty()) {
98 LOGE("Failed to download reason: %{public}s", errorStr.c_str());
99 }
100 dataOut.clear();
101 return false;
102 }
103 dataOut.shrink_to_fit();
104 return true;
105 }
106
Download(const std::string & url,const std::shared_ptr<DownloadResult> & result)107 bool Download(const std::string& url, const std::shared_ptr<DownloadResult>& result) override
108 {
109 return false;
110 }
111
DownloadAsync(DownloadCallback && downloadCallback,const std::string & url,int32_t instanceId,int32_t nodeId)112 bool DownloadAsync(
113 DownloadCallback&& downloadCallback, const std::string& url, int32_t instanceId, int32_t nodeId) override
114 {
115 return false;
116 }
117
DownloadSync(DownloadCallback && downloadCallback,const std::string & url,int32_t instanceId,int32_t nodeId)118 bool DownloadSync(
119 DownloadCallback&& downloadCallback, const std::string& url, int32_t instanceId, int32_t nodeId) override
120 {
121 return false;
122 }
123
RemoveDownloadTask(const std::string & url,int32_t nodeId,bool isCancel=true)124 bool RemoveDownloadTask(const std::string& url, int32_t nodeId, bool isCancel = true) override
125 {
126 return false;
127 }
128
129 private:
130 struct ProxyInfo {
131 std::string host;
132 int32_t port = 0;
133 std::string exclusions;
134 };
135
Initialize()136 bool Initialize()
137 {
138 if (initialized_) {
139 return true;
140 }
141
142 std::lock_guard<std::mutex> lock(mutex_);
143 if (initialized_) {
144 return true;
145 }
146 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
147 LOGE("Failed to initialize 'curl'");
148 return false;
149 }
150 initialized_ = true;
151 return true;
152 }
153
OnWritingMemory(void * data,size_t size,size_t memBytes,void * userData)154 static size_t OnWritingMemory(void* data, size_t size, size_t memBytes, void* userData)
155 {
156 // size is always 1, for more details see https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
157 auto& dataOut = *static_cast<std::vector<uint8_t>*>(userData);
158 auto chunkData = static_cast<uint8_t*>(data);
159 dataOut.insert(dataOut.end(), chunkData, chunkData + memBytes);
160 return memBytes;
161 }
162
GetProxy(ProxyInfo & proxy)163 static bool GetProxy(ProxyInfo& proxy)
164 {
165 return false;
166 }
167
168 std::mutex mutex_;
169 bool initialized_ = false;
170 bool isCurl_ = false;
171 };
172
GetInstance()173 DownloadManager* DownloadManager::GetInstance()
174 {
175 static std::once_flag onceFlag;
176 std::call_once(onceFlag, []() {
177 instance_.reset(new DownloadManagerPrview());
178 });
179 return instance_.get();
180 }
181 } // namespace OHOS::Ace
182