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