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