1 /*
2 * Copyright (c) 2023-2025 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 "cloud_file_napi.h"
17
18 #include <memory>
19 #include <mutex>
20 #include <sys/types.h>
21
22 #include "async_work.h"
23 #include "cloud_sync_manager.h"
24 #include "dfs_error.h"
25 #include "dfsu_access_token_helper.h"
26 #include "filemgmt_libn.h"
27 #include "n_napi.h"
28 #include "utils_log.h"
29
30 namespace OHOS::FileManagement::CloudSync {
31 using namespace FileManagement::LibN;
32 using namespace std;
StartDownloadInner(const std::string & uri)33 int32_t CloudDownloadCallbackImplNapi::StartDownloadInner(const std::string &uri)
34 {
35 int64_t downloadId = 0;
36 std::lock_guard<std::mutex> lock(downloadInfoMtx_);
37 int32_t ret = CloudSyncManager::GetInstance().StartDownloadFile(uri, shared_from_this(), downloadId);
38 if (ret != E_OK) {
39 LOGE("Start batch download failed! ret = %{public}d", ret);
40 return ret;
41 }
42
43 downloadInfos_[downloadId] = std::make_shared<SingleProgressNapi>(downloadId);
44 return ret;
45 }
46
StopDownloadInner(const std::string & uri)47 int32_t CloudDownloadCallbackImplNapi::StopDownloadInner(const std::string &uri)
48 {
49 auto downloadIdList = GetDownloadIdsByUri(uri);
50 int32_t ret = E_OK;
51 int32_t resErr = E_OK;
52 LOGI("Stop Download downloadId list size: %{public}zu", downloadIdList.size());
53 for (auto taskId : downloadIdList) {
54 resErr = CloudSyncManager::GetInstance().StopDownloadFile(taskId, true);
55 if (resErr != E_OK) {
56 ret = resErr;
57 continue;
58 }
59 }
60 if (ret != E_OK) {
61 LOGE("Stop Download failed! ret = %{public}d", ret);
62 }
63 return ret;
64 }
65
Constructor(napi_env env,napi_callback_info info)66 napi_value CloudFileDownloadNapi::Constructor(napi_env env, napi_callback_info info)
67 {
68 LOGI("CloudFileDownloadNapi::Constructor begin");
69 NFuncArg funcArg(env, info);
70 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
71 LOGE("Start Number of arguments unmatched");
72 NError(JsErrCode::E_IPCSS).ThrowErr(env);
73 return nullptr;
74 }
75 auto downloadEntity = make_unique<DownloadEntity>();
76 if (!NClass::SetEntityFor<DownloadEntity>(env, funcArg.GetThisVar(), move(downloadEntity))) {
77 LOGE("Failed to set download entity.");
78 NError(JsErrCode::E_IPCSS).ThrowErr(env);
79 return nullptr;
80 }
81 LOGI("CloudFileDownloadNapi::Constructor end");
82 return funcArg.GetThisVar();
83 }
84
GetCallbackImpl(napi_env env,NFuncArg & funcArg,bool isInit)85 static std::shared_ptr<CloudDownloadCallbackImplNapi> GetCallbackImpl(napi_env env, NFuncArg &funcArg, bool isInit)
86 {
87 auto downloadEntity = NClass::GetEntityOf<DownloadEntity>(env, funcArg.GetThisVar());
88 if (!downloadEntity) {
89 LOGE("Failed to get file cache entity.");
90 return nullptr;
91 }
92
93 if (downloadEntity->callbackImpl == nullptr && isInit) {
94 downloadEntity->callbackImpl = std::make_shared<CloudDownloadCallbackImplNapi>(env);
95 }
96 return downloadEntity->callbackImpl;
97 }
98
Start(napi_env env,napi_callback_info info)99 napi_value CloudFileDownloadNapi::Start(napi_env env, napi_callback_info info)
100 {
101 LOGI("Start begin");
102 NFuncArg funcArg(env, info);
103 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
104 LOGE("Start Number of arguments unmatched");
105 NError(E_PARAMS).ThrowErr(env);
106 return nullptr;
107 }
108 auto [succUri, uri, size] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
109 if (!succUri || size == 0) {
110 LOGE("Start get uri parameter failed!");
111 NError(E_PARAMS).ThrowErr(env);
112 return nullptr;
113 }
114
115 auto callbackImpl = GetCallbackImpl(env, funcArg, true);
116 auto cbExec = [uri{string(uri.get())}, callbackImpl{callbackImpl}]() -> NError {
117 if (callbackImpl == nullptr) {
118 LOGE("Failed to get download callback");
119 return NError(E_PARAMS);
120 }
121 int32_t ret = callbackImpl->StartDownloadInner(uri);
122 if (ret != E_OK) {
123 LOGE("Start Download failed! ret = %{public}d", ret);
124 return NError(Convert2JsErrNum(ret));
125 }
126 LOGI("Start Download Success!");
127 return NError(ERRNO_NOERR);
128 };
129
130 auto cbCompl = [](napi_env env, NError err) -> NVal {
131 if (err) {
132 return {env, err.GetNapiErr(env)};
133 }
134 return NVal::CreateUndefined(env);
135 };
136
137 string procedureName = "cloudFileDownload";
138 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
139 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
140 }
141
CheckPermissions(const string & permission,bool isSystemApp)142 static int32_t CheckPermissions(const string &permission, bool isSystemApp)
143 {
144 if (!permission.empty() && !DfsuAccessTokenHelper::CheckCallerPermission(permission)) {
145 LOGE("permission denied");
146 return JsErrCode::E_PERMISSION;
147 }
148 if (isSystemApp && !DfsuAccessTokenHelper::IsSystemApp()) {
149 LOGE("caller hap is not system hap");
150 return JsErrCode::E_PERMISSION_SYS;
151 }
152 return E_OK;
153 }
154
On(napi_env env,napi_callback_info info)155 napi_value CloudFileDownloadNapi::On(napi_env env, napi_callback_info info)
156 {
157 LOGI("On begin");
158 NFuncArg funcArg(env, info);
159 if (!funcArg.InitArgs(NARG_CNT::TWO)) {
160 LOGE("On Number of arguments unmatched");
161 NError(E_PARAMS).ThrowErr(env);
162 return nullptr;
163 }
164 int32_t ret = CheckPermissions(PERM_CLOUD_SYNC, true);
165 if (ret != E_OK) {
166 LOGE("On get progress failed!");
167 NError(ret).ThrowErr(env);
168 return nullptr;
169 }
170 auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
171 if (!succProgress || std::string(progress.get()) != "progress") {
172 LOGE("On get progress failed!");
173 NError(E_PARAMS).ThrowErr(env);
174 return nullptr;
175 }
176
177 if (!NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) {
178 LOGE("Argument type mismatch");
179 NError(E_PARAMS).ThrowErr(env);
180 return nullptr;
181 }
182 auto callbackImpl = GetCallbackImpl(env, funcArg, true);
183 if (callbackImpl == nullptr) {
184 NError(E_PARAMS).ThrowErr(env);
185 return nullptr;
186 }
187 ret = callbackImpl->RegisterCallback(funcArg[NARG_POS::SECOND]);
188 if (ret != napi_ok) {
189 LOGE("On register callback fail, ret: %{public}d", ret);
190 NError(JsErrCode::E_IPCSS).ThrowErr(env);
191 return nullptr;
192 }
193
194 return NVal::CreateUndefined(env).val_;
195 }
196
Off(napi_env env,napi_callback_info info)197 napi_value CloudFileDownloadNapi::Off(napi_env env, napi_callback_info info)
198 {
199 LOGI("Off begin");
200 NFuncArg funcArg(env, info);
201 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
202 LOGE("Off Number of arguments unmatched");
203 NError(E_PARAMS).ThrowErr(env);
204 return nullptr;
205 }
206 int32_t ret = CheckPermissions(PERM_CLOUD_SYNC, true);
207 if (ret != E_OK) {
208 LOGE("Off get progress failed!");
209 NError(ret).ThrowErr(env);
210 return nullptr;
211 }
212 auto [succProgress, progress, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
213 if (!succProgress || std::string(progress.get()) != "progress") {
214 LOGE("Off get progress failed!");
215 NError(E_PARAMS).ThrowErr(env);
216 return nullptr;
217 }
218
219 napi_value callbackVel = nullptr;
220 if (funcArg.GetArgc() == (uint)NARG_CNT::TWO) {
221 if (!NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function)) {
222 LOGE("Off argument type mismatch");
223 NError(E_PARAMS).ThrowErr(env);
224 return nullptr;
225 }
226 callbackVel = funcArg[NARG_POS::SECOND];
227 }
228
229 auto callbackImpl = GetCallbackImpl(env, funcArg, false);
230 if (callbackImpl == nullptr || callbackImpl->UnregisterCallback(callbackVel) != napi_ok) {
231 LOGE("Off no callback is registered for this event type");
232 NError(JsErrCode::E_IPCSS).ThrowErr(env);
233 return nullptr;
234 }
235 return NVal::CreateUndefined(env).val_;
236 }
237
Stop(napi_env env,napi_callback_info info)238 napi_value CloudFileDownloadNapi::Stop(napi_env env, napi_callback_info info)
239 {
240 LOGI("Stop begin");
241 NFuncArg funcArg(env, info);
242 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
243 LOGE("Stop Number of arguments unmatched");
244 NError(E_PARAMS).ThrowErr(env);
245 return nullptr;
246 }
247
248 auto [succUri, uri, size] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
249 if (!succUri || size == 0) {
250 LOGE("Stop get uri parameter failed!");
251 NError(E_PARAMS).ThrowErr(env);
252 return nullptr;
253 }
254
255 auto callbackImpl = GetCallbackImpl(env, funcArg, false);
256 auto cbExec = [uri{string(uri.get())}, callbackImpl{callbackImpl}]() -> NError {
257 if (callbackImpl == nullptr) {
258 LOGE("Failed to get download callback");
259 return NError(E_PARAMS);
260 }
261 int32_t ret = callbackImpl->StopDownloadInner(uri);
262 if (ret != E_OK) {
263 LOGE("Stop Download failed! ret = %{public}d", ret);
264 return NError(Convert2JsErrNum(ret));
265 }
266 return NError(ERRNO_NOERR);
267 };
268
269 auto cbCompl = [](napi_env env, NError err) -> NVal {
270 if (err) {
271 return {env, err.GetNapiErr(env)};
272 }
273 return NVal::CreateUndefined(env);
274 };
275
276 string procedureName = "cloudFileDownload";
277 auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
278 return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbCompl).val_;
279 }
280
ToExport(std::vector<napi_property_descriptor> props)281 bool CloudFileDownloadNapi::ToExport(std::vector<napi_property_descriptor> props)
282 {
283 std::string className = GetClassName();
284 auto [succ, classValue] = NClass::DefineClass(exports_.env_, className, Constructor, std::move(props));
285 if (!succ) {
286 NError(JsErrCode::E_IPCSS).ThrowErr(exports_.env_);
287 LOGE("Failed to define GallerySync class");
288 return false;
289 }
290
291 succ = NClass::SaveClass(exports_.env_, className, classValue);
292 if (!succ) {
293 NError(JsErrCode::E_IPCSS).ThrowErr(exports_.env_);
294 LOGE("Failed to save GallerySync class");
295 return false;
296 }
297
298 return exports_.AddProp(className, classValue);
299 }
300
Export()301 bool CloudFileDownloadNapi::Export()
302 {
303 vector<napi_property_descriptor> props = {
304 NVal::DeclareNapiFunction("start", CloudFileDownloadNapi::Start),
305 NVal::DeclareNapiFunction("on", CloudFileDownloadNapi::On),
306 NVal::DeclareNapiFunction("off", CloudFileDownloadNapi::Off),
307 NVal::DeclareNapiFunction("stop", CloudFileDownloadNapi::Stop),
308 };
309 return ToExport(props);
310 }
311
SetClassName(std::string classname)312 void CloudFileDownloadNapi::SetClassName(std::string classname)
313 {
314 className_ = classname;
315 }
316
GetClassName()317 string CloudFileDownloadNapi::GetClassName()
318 {
319 return className_;
320 }
321 } // namespace OHOS::FileManagement::CloudSync