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 "cloud_sync_napi.h"
17 
18 #include <sys/types.h>
19 
20 #include "async_work.h"
21 #include "cloud_sync_manager.h"
22 #include "dfs_error.h"
23 #include "dfsu_access_token_helper.h"
24 #include "securec.h"
25 #include "uri.h"
26 #include "utils_log.h"
27 
28 namespace OHOS::FileManagement::CloudSync {
29 using namespace FileManagement::LibN;
30 using namespace std;
31 const int32_t PARAM0 = 0;
32 static const unsigned int READ_SIZE = 1024;
33 const string FILE_SCHEME = "file";
34 thread_local unique_ptr<ChangeListenerNapi> g_listObj = nullptr;
35 mutex CloudSyncNapi::sOnOffMutex_;
36 static mutex obsMutex_;
37 
38 class ObserverImpl : public AAFwk::DataAbilityObserverStub {
39 public:
ObserverImpl(const shared_ptr<CloudNotifyObserver> cloudNotifyObserver)40     explicit ObserverImpl(const shared_ptr<CloudNotifyObserver> cloudNotifyObserver)
41         : cloudNotifyObserver_(cloudNotifyObserver){};
42     void OnChange();
43     void OnChangeExt(const AAFwk::ChangeInfo &info);
44     static sptr<ObserverImpl> GetObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer);
45     static bool FindObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer);
46     static bool DeleteObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer);
47 
48 private:
49     struct ObserverParam {
50         sptr<ObserverImpl> obs_;
51         list<Uri> uris_;
52     };
53     shared_ptr<CloudNotifyObserver> cloudNotifyObserver_;
54     static ConcurrentMap<CloudNotifyObserver *, ObserverParam> observers_;
55 };
56 
57 ConcurrentMap<CloudNotifyObserver *, ObserverImpl::ObserverParam> ObserverImpl::observers_;
58 
OnChange()59 void ObserverImpl::OnChange() {}
60 
OnChangeExt(const AAFwk::ChangeInfo & info)61 void ObserverImpl::OnChangeExt(const AAFwk::ChangeInfo &info)
62 {
63     if (cloudNotifyObserver_ == nullptr) {
64         LOGE("cloudNotifyObserver_ is null!");
65         return;
66     }
67     cloudNotifyObserver_->OnchangeExt(info);
68 }
69 
GetObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)70 sptr<ObserverImpl> ObserverImpl::GetObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
71 {
72     lock_guard<mutex> lock(obsMutex_);
73     sptr<ObserverImpl> result = nullptr;
74     observers_.Compute(observer.get(), [&result, &uri, &observer](const auto &key, auto &value) {
75         if (value.obs_ == nullptr) {
76             value.obs_ = new (nothrow) ObserverImpl(observer);
77             value.uris_.push_back(uri);
78         } else {
79             auto it = find(value.uris_.begin(), value.uris_.end(), uri);
80             if (it == value.uris_.end()) {
81                 value.uris_.push_back(uri);
82             }
83         }
84 
85         result = value.obs_;
86         return result != nullptr;
87     });
88 
89     return result;
90 }
91 
FindObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)92 bool ObserverImpl::FindObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
93 {
94     lock_guard<mutex> lock(obsMutex_);
95     auto result = observers_.Find(observer.get());
96     if (result.first) {
97         auto it = std::find(result.second.uris_.begin(), result.second.uris_.end(), uri);
98         if (it == result.second.uris_.end()) {
99             return false;
100         }
101     }
102     return result.first;
103 }
104 
DeleteObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)105 bool ObserverImpl::DeleteObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
106 {
107     lock_guard<mutex> lock(obsMutex_);
108     return observers_.ComputeIfPresent(observer.get(), [&uri](auto &key, auto &value) {
109         value.uris_.remove_if([&uri](const auto &value) { return uri == value; });
110         return !value.uris_.empty();
111     });
112 }
113 
CloudSyncCallbackImpl(napi_env env,napi_value fun)114 CloudSyncCallbackImpl::CloudSyncCallbackImpl(napi_env env, napi_value fun) : env_(env)
115 {
116     if (fun != nullptr) {
117         napi_create_reference(env_, fun, 1, &cbOnRef_);
118     }
119 }
120 
OnComplete(UvChangeMsg * msg)121 void CloudSyncCallbackImpl::OnComplete(UvChangeMsg *msg)
122 {
123     auto cloudSyncCallback = msg->cloudSyncCallback_.lock();
124     if (cloudSyncCallback == nullptr || cloudSyncCallback->cbOnRef_ == nullptr) {
125         LOGE("cloudSyncCallback->cbOnRef_ is nullptr");
126         return;
127     }
128     auto env = cloudSyncCallback->env_;
129     auto ref = cloudSyncCallback->cbOnRef_;
130     napi_handle_scope scope = nullptr;
131     napi_open_handle_scope(env, &scope);
132     napi_value jsCallback = nullptr;
133     napi_status status = napi_get_reference_value(env, ref, &jsCallback);
134     if (status != napi_ok) {
135         LOGE("Create reference failed, status: %{public}d", status);
136         napi_close_handle_scope(env, scope);
137         return;
138     }
139     NVal obj = NVal::CreateObject(env);
140     obj.AddProp("state", NVal::CreateInt32(env, (int32_t)msg->state_).val_);
141     obj.AddProp("error", NVal::CreateInt32(env, (int32_t)msg->error_).val_);
142     napi_value retVal = nullptr;
143     napi_value global = nullptr;
144     napi_get_global(env, &global);
145     status = napi_call_function(env, global, jsCallback, ARGS_ONE, &(obj.val_), &retVal);
146     if (status != napi_ok) {
147         LOGE("napi call function failed, status: %{public}d", status);
148     }
149     napi_close_handle_scope(env, scope);
150 }
151 
OnSyncStateChanged(CloudSyncState state,ErrorType error)152 void CloudSyncCallbackImpl::OnSyncStateChanged(CloudSyncState state, ErrorType error)
153 {
154     uv_loop_s *loop = nullptr;
155     napi_get_uv_event_loop(env_, &loop);
156     if (loop == nullptr) {
157         return;
158     }
159 
160     uv_work_t *work = new (nothrow) uv_work_t;
161     if (work == nullptr) {
162         LOGE("Failed to create uv work");
163         return;
164     }
165 
166     UvChangeMsg *msg = new (std::nothrow) UvChangeMsg(shared_from_this(), state, error);
167     if (msg == nullptr) {
168         delete work;
169         return;
170     }
171 
172     work->data = reinterpret_cast<void *>(msg);
173     int ret = uv_queue_work(
174         loop, work, [](uv_work_t *work) {},
175         [](uv_work_t *work, int status) {
176             auto msg = reinterpret_cast<UvChangeMsg *>(work->data);
177             OnComplete(msg);
178             delete msg;
179             delete work;
180         });
181     if (ret != 0) {
182         LOGE("Failed to execute libuv work queue, ret: %{public}d", ret);
183         delete msg;
184         delete work;
185     }
186 }
187 
DeleteReference()188 void CloudSyncCallbackImpl::DeleteReference()
189 {
190     if (cbOnRef_ != nullptr) {
191         napi_delete_reference(env_, cbOnRef_);
192         cbOnRef_ = nullptr;
193     }
194 }
195 
OnSyncStateChanged(SyncType type,SyncPromptState state)196 void CloudSyncCallbackImpl::OnSyncStateChanged(SyncType type, SyncPromptState state)
197 {
198     return;
199 }
200 
GetBundleName(const napi_env & env,const NFuncArg & funcArg)201 string CloudSyncNapi::GetBundleName(const napi_env &env, const NFuncArg &funcArg)
202 {
203     string bundleName;
204     auto bundleEntity = NClass::GetEntityOf<BundleEntity>(env, funcArg.GetThisVar());
205     if (bundleEntity) {
206         bundleName = bundleEntity->bundleName_;
207     }
208     return bundleName;
209 }
210 
Constructor(napi_env env,napi_callback_info info)211 napi_value CloudSyncNapi::Constructor(napi_env env, napi_callback_info info)
212 {
213     NFuncArg funcArg(env, info);
214     if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
215         NError(E_PARAMS).ThrowErr(env, "Number of arguments unmatched");
216         return nullptr;
217     }
218     if (funcArg.GetArgc() == NARG_CNT::ZERO) {
219         LOGD("init without bundleName");
220         return funcArg.GetThisVar();
221     }
222     if (funcArg.GetArgc() == NARG_CNT::ONE) {
223         auto [succ, bundleName, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
224         if (!succ || bundleName.get() == string("")) {
225             LOGE("Failed to get bundle name");
226             NError(E_PARAMS).ThrowErr(env);
227             return nullptr;
228         }
229         auto bundleEntity = make_unique<BundleEntity>(bundleName.get());
230         if (!NClass::SetEntityFor<BundleEntity>(env, funcArg.GetThisVar(), move(bundleEntity))) {
231             LOGE("Failed to set file entity");
232             NError(EIO).ThrowErr(env);
233             return nullptr;
234         }
235         LOGI("init with bundleName");
236         return funcArg.GetThisVar();
237     }
238     return nullptr;
239 }
240 
OnCallback(napi_env env,napi_callback_info info)241 napi_value CloudSyncNapi::OnCallback(napi_env env, napi_callback_info info)
242 {
243     NFuncArg funcArg(env, info);
244     if (!funcArg.InitArgs(NARG_CNT::TWO)) {
245         NError(E_PARAMS).ThrowErr(env, "Number of arguments unmatched");
246         LOGE("OnCallback Number of arguments unmatched");
247         return nullptr;
248     }
249 
250     auto [succ, type, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
251     if (!(succ && (type.get() == std::string("progress")))) {
252         NError(E_PARAMS).ThrowErr(env);
253         return nullptr;
254     }
255 
256     if (!NVal(env, funcArg[(int)NARG_POS::SECOND]).TypeIs(napi_function)) {
257         LOGE("Argument type mismatch");
258         NError(E_PARAMS).ThrowErr(env);
259         return nullptr;
260     }
261 
262     if (callback_ != nullptr) {
263         LOGI("callback already exist");
264         return NVal::CreateUndefined(env).val_;
265     }
266 
267     string bundleName = GetBundleName(env, funcArg);
268     callback_ = make_shared<CloudSyncCallbackImpl>(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_);
269     int32_t ret = CloudSyncManager::GetInstance().RegisterCallback(callback_, bundleName);
270     if (ret != E_OK) {
271         LOGE("OnCallback Register error, result: %{public}d", ret);
272         NError(Convert2JsErrNum(ret)).ThrowErr(env);
273         return nullptr;
274     }
275 
276     return NVal::CreateUndefined(env).val_;
277 }
278 
OffCallback(napi_env env,napi_callback_info info)279 napi_value CloudSyncNapi::OffCallback(napi_env env, napi_callback_info info)
280 {
281     NFuncArg funcArg(env, info);
282     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
283         NError(E_PARAMS).ThrowErr(env, "Number of arguments unmatched");
284         LOGE("OffCallback Number of arguments unmatched");
285         return nullptr;
286     }
287 
288     auto [succ, type, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
289     if (!(succ && (type.get() == std::string("progress")))) {
290         NError(E_PARAMS).ThrowErr(env);
291         return nullptr;
292     }
293 
294     if (funcArg.GetArgc() == (uint)NARG_CNT::TWO && !NVal(env, funcArg[(int)NARG_POS::SECOND]).TypeIs(napi_function)) {
295         LOGE("Argument type mismatch");
296         NError(E_PARAMS).ThrowErr(env);
297         return nullptr;
298     }
299 
300     string bundleName = GetBundleName(env, funcArg);
301     int32_t ret = CloudSyncManager::GetInstance().UnRegisterCallback(bundleName);
302     if (ret != E_OK) {
303         LOGE("OffCallback UnRegister error, result: %{public}d", ret);
304         NError(Convert2JsErrNum(ret)).ThrowErr(env);
305         return nullptr;
306     }
307     if (callback_ != nullptr) {
308         /* napi delete reference */
309         callback_->DeleteReference();
310         callback_ = nullptr;
311     }
312     return NVal::CreateUndefined(env).val_;
313 }
314 
Start(napi_env env,napi_callback_info info)315 napi_value CloudSyncNapi::Start(napi_env env, napi_callback_info info)
316 {
317     NFuncArg funcArg(env, info);
318     if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
319         NError(E_PARAMS).ThrowErr(env);
320     }
321 
322     string bundleName = GetBundleName(env, funcArg);
323     auto cbExec = [bundleName]() -> NError {
324         int32_t ret = CloudSyncManager::GetInstance().StartSync(bundleName);
325         if (ret != E_OK) {
326             LOGE("Start Sync error, result: %{public}d", ret);
327             return NError(Convert2JsErrNum(ret));
328         }
329         return NError(ERRNO_NOERR);
330     };
331 
332     auto cbComplete = [](napi_env env, NError err) -> NVal {
333         if (err) {
334             return {env, err.GetNapiErr(env)};
335         }
336         return NVal::CreateUndefined(env);
337     };
338 
339     std::string procedureName = "Start";
340     auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
341     return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbComplete).val_;
342 }
343 
Stop(napi_env env,napi_callback_info info)344 napi_value CloudSyncNapi::Stop(napi_env env, napi_callback_info info)
345 {
346     NFuncArg funcArg(env, info);
347     if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
348         NError(E_PARAMS).ThrowErr(env);
349         return nullptr;
350     }
351 
352     string bundleName = GetBundleName(env, funcArg);
353     auto cbExec = [bundleName]() -> NError {
354         int32_t ret = CloudSyncManager::GetInstance().StopSync(bundleName);
355         if (ret != E_OK) {
356             LOGE("Stop Sync error, result: %{public}d", ret);
357             return NError(Convert2JsErrNum(ret));
358         }
359         return NError(ERRNO_NOERR);
360     };
361 
362     auto cbComplete = [](napi_env env, NError err) -> NVal {
363         if (err) {
364             return {env, err.GetNapiErr(env)};
365         }
366         return NVal::CreateUndefined(env);
367     };
368 
369     std::string procedureName = "Stop";
370     auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
371     return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbComplete).val_;
372 }
373 
CheckRef(napi_env env,napi_ref ref,ChangeListenerNapi & listObj,const string & uri)374 bool CloudSyncNapi::CheckRef(napi_env env, napi_ref ref, ChangeListenerNapi &listObj, const string &uri)
375 {
376     napi_value offCallback = nullptr;
377     napi_status status = napi_get_reference_value(env, ref, &offCallback);
378     if (status != napi_ok) {
379         LOGE("Create reference fail, status: %{public}d", status);
380         return false;
381     }
382     bool isSame = false;
383     shared_ptr<CloudNotifyObserver> obs;
384     string obsUri;
385     {
386         lock_guard<mutex> lock(sOnOffMutex_);
387         for (auto it = listObj.observers_.begin(); it < listObj.observers_.end(); it++) {
388             napi_value onCallback = nullptr;
389             status = napi_get_reference_value(env, (*it)->ref_, &onCallback);
390             if (status != napi_ok) {
391                 LOGE("Create reference fail, status: %{public}d", status);
392                 return false;
393             }
394             napi_strict_equals(env, offCallback, onCallback, &isSame);
395             if (isSame) {
396                 obsUri = (*it)->uri_;
397                 if (uri.compare(obsUri) != 0) {
398                     return true;
399                 }
400                 return false;
401             }
402         }
403     }
404     return true;
405 }
406 
CheckIsValidUri(Uri uri)407 static bool CheckIsValidUri(Uri uri)
408 {
409     string scheme = uri.GetScheme();
410     if (scheme != FILE_SCHEME) {
411         return false;
412     }
413     string sandboxPath = uri.GetPath();
414     char realPath[PATH_MAX + 1]{'\0'};
415     if (realpath(sandboxPath.c_str(), realPath) == nullptr) {
416         LOGE("realpath failed with %{public}d", errno);
417         return false;
418     }
419     if (strncmp(realPath, sandboxPath.c_str(), sandboxPath.size()) != 0) {
420         LOGE("sandboxPath is not equal to realPath");
421         return false;
422     }
423     if (sandboxPath.find("/data/storage/el2/cloud") != 0) {
424         LOGE("not surported uri");
425         return false;
426     }
427     return true;
428 }
429 
GetRegisterParams(napi_env env,napi_callback_info info,RegisterParams & registerParams)430 static int32_t GetRegisterParams(napi_env env, napi_callback_info info, RegisterParams &registerParams)
431 {
432     NFuncArg funcArg(env, info);
433     if (!funcArg.InitArgs(NARG_CNT::THREE)) {
434         LOGE("Arguments number mismatch");
435         return E_PARAMS;
436     }
437 
438     auto [succUri, uri, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
439     if (!succUri) {
440         LOGE("get arg uri fail");
441         return E_PARAMS;
442     }
443     registerParams.uri = string(uri.get());
444     if (!CheckIsValidUri(Uri(registerParams.uri))) {
445         LOGE("RegisterChange uri parameter format error!");
446         return E_PARAMS;
447     }
448 
449     auto [succRecursion, recursion] = NVal(env, funcArg[(int)NARG_POS::SECOND]).ToBool();
450     if (!succRecursion) {
451         LOGE("get arg recursion fail");
452         return E_PARAMS;
453     }
454     registerParams.recursion = recursion;
455 
456     if (!NVal(env, funcArg[(int)NARG_POS::THIRD]).TypeIs(napi_function)) {
457         LOGE("Argument type mismatch");
458         return E_PARAMS;
459     }
460     napi_status status =
461         napi_create_reference(env, NVal(env, funcArg[(int)NARG_POS::THIRD]).val_, 1, &registerParams.cbOnRef);
462     if (status != napi_ok) {
463         LOGE("Create reference fail, status: %{public}d", status);
464         return E_PARAMS;
465     }
466 
467     return ERR_OK;
468 }
469 
RegisterToObs(napi_env env,const RegisterParams & registerParams)470 int32_t CloudSyncNapi::RegisterToObs(napi_env env, const RegisterParams &registerParams)
471 {
472     auto observer = make_shared<CloudNotifyObserver>(*g_listObj, registerParams.uri, registerParams.cbOnRef);
473     Uri uri(registerParams.uri);
474     auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
475     if (obsMgrClient == nullptr) {
476         LOGE("get DataObsMgrClient failed");
477         return E_SA_LOAD_FAILED;
478     }
479     sptr<ObserverImpl> obs = ObserverImpl::GetObserver(uri, observer);
480     if (obs == nullptr) {
481         LOGE("new ObserverImpl failed");
482         return E_INVAL_ARG;
483     }
484     ErrCode ret = obsMgrClient->RegisterObserverExt(uri, obs, registerParams.recursion);
485     if (ret != E_OK) {
486         LOGE("ObsMgr register fail");
487         ObserverImpl::DeleteObserver(uri, observer);
488         return E_INVAL_ARG;
489     }
490     lock_guard<mutex> lock(CloudSyncNapi::sOnOffMutex_);
491     g_listObj->observers_.push_back(observer);
492     return E_OK;
493 }
494 
RegisterChange(napi_env env,napi_callback_info info)495 napi_value CloudSyncNapi::RegisterChange(napi_env env, napi_callback_info info)
496 {
497     if (!DfsuAccessTokenHelper::CheckCallerPermission(PERM_CLOUD_SYNC)) {
498         LOGE("permission denied");
499         NError(E_PERMISSION_DENIED).ThrowErr(env);
500         return nullptr;
501     }
502     if (!DfsuAccessTokenHelper::IsSystemApp()) {
503         LOGE("caller hap is not system hap");
504         NError(E_PERMISSION_SYSTEM).ThrowErr(env);
505         return nullptr;
506     }
507 
508     if (g_listObj == nullptr) {
509         g_listObj = make_unique<ChangeListenerNapi>(env);
510     }
511 
512     RegisterParams registerParams;
513     int32_t ret = GetRegisterParams(env, info, registerParams);
514     if (ret != ERR_OK) {
515         LOGE("Get Params fail");
516         NError(ret).ThrowErr(env);
517         return nullptr;
518     }
519 
520     if (CheckRef(env, registerParams.cbOnRef, *g_listObj, registerParams.uri)) {
521         ret = RegisterToObs(env, registerParams);
522         if (ret != E_OK) {
523             LOGE("Get Params fail");
524             NError(ret).ThrowErr(env);
525             return nullptr;
526         }
527     } else {
528         LOGE("Check Ref fail");
529         NError(E_PARAMS).ThrowErr(env);
530         napi_delete_reference(env, registerParams.cbOnRef);
531         return nullptr;
532     }
533     return NVal::CreateUndefined(env).val_;
534 }
535 
UnregisterFromObs(napi_env env,const string & uri)536 napi_value CloudSyncNapi::UnregisterFromObs(napi_env env, const string &uri)
537 {
538     auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
539     if (obsMgrClient == nullptr) {
540         LOGE("get DataObsMgrClient failed");
541         NError(E_SA_LOAD_FAILED).ThrowErr(env);
542         return nullptr;
543     }
544     std::vector<std::shared_ptr<CloudNotifyObserver>> offObservers;
545     {
546         lock_guard<mutex> lock(sOnOffMutex_);
547         for (auto iter = g_listObj->observers_.begin(); iter != g_listObj->observers_.end();) {
548             if (uri == (*iter)->uri_) {
549                 offObservers.push_back(*iter);
550                 vector<shared_ptr<CloudNotifyObserver>>::iterator tmp = iter;
551                 iter = g_listObj->observers_.erase(tmp);
552             } else {
553                 iter++;
554             }
555         }
556     }
557     for (auto observer : offObservers) {
558         if (!ObserverImpl::FindObserver(Uri(uri), observer)) {
559             LOGE("observer not exist");
560             NError(E_PARAMS).ThrowErr(env);
561             return nullptr;
562         }
563         sptr<ObserverImpl> obs = ObserverImpl::GetObserver(Uri(uri), observer);
564         if (obs == nullptr) {
565             LOGE("new observerimpl failed");
566             NError(E_PARAMS).ThrowErr(env);
567             return nullptr;
568         }
569         ErrCode ret = obsMgrClient->UnregisterObserverExt(Uri(uri), obs);
570         if (ret != ERR_OK) {
571             LOGE("call obs unregister fail");
572             NError(E_PARAMS).ThrowErr(env);
573             return nullptr;
574         }
575         ObserverImpl::DeleteObserver(Uri(uri), observer);
576     }
577     return NVal::CreateUndefined(env).val_;
578 }
579 
UnregisterChange(napi_env env,napi_callback_info info)580 napi_value CloudSyncNapi::UnregisterChange(napi_env env, napi_callback_info info)
581 {
582     if (!DfsuAccessTokenHelper::CheckCallerPermission(PERM_CLOUD_SYNC)) {
583         LOGE("permission denied");
584         NError(E_PERMISSION_DENIED).ThrowErr(env);
585         return nullptr;
586     }
587     if (!DfsuAccessTokenHelper::IsSystemApp()) {
588         LOGE("caller hap is not system hap");
589         NError(E_PERMISSION_SYSTEM).ThrowErr(env);
590         return nullptr;
591     }
592 
593     if (g_listObj == nullptr || g_listObj->observers_.empty()) {
594         LOGI("no obs to unregister");
595         return nullptr;
596     }
597 
598     NFuncArg funcArg(env, info);
599     if (!funcArg.InitArgs(NARG_CNT::ONE)) {
600         LOGE("params number mismatch");
601         NError(E_PARAMS).ThrowErr(env);
602         return nullptr;
603     }
604     auto [succUri, uri, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
605     if (!succUri || !CheckIsValidUri(Uri(uri.get()))) {
606         LOGE("get uri fail");
607         NError(E_PARAMS).ThrowErr(env);
608         return nullptr;
609     }
610 
611     return UnregisterFromObs(env, uri.get());
612 }
613 
SetClassName(const std::string classname)614 void CloudSyncNapi::SetClassName(const std::string classname)
615 {
616     className_ = classname;
617 }
618 
GetClassName()619 std::string CloudSyncNapi::GetClassName()
620 {
621     return className_;
622 }
623 
ToExport(std::vector<napi_property_descriptor> props)624 bool CloudSyncNapi::ToExport(std::vector<napi_property_descriptor> props)
625 {
626     std::string className = GetClassName();
627     auto [succ, classValue] =
628         NClass::DefineClass(exports_.env_, className, Constructor, std::move(props));
629     if (!succ) {
630         NError(E_GETRESULT).ThrowErr(exports_.env_);
631         LOGE("Failed to define CloudSyncNapi class");
632         return false;
633     }
634 
635     succ = NClass::SaveClass(exports_.env_, className, classValue);
636     if (!succ) {
637         NError(E_GETRESULT).ThrowErr(exports_.env_);
638         LOGE("Failed to save CloudSyncNapi class");
639         return false;
640     }
641 
642     return exports_.AddProp(className, classValue);
643 }
644 
Export()645 bool CloudSyncNapi::Export()
646 {
647     std::vector<napi_property_descriptor> props = {
648         NVal::DeclareNapiFunction("on", OnCallback),
649         NVal::DeclareNapiFunction("off", OffCallback),
650         NVal::DeclareNapiFunction("start", Start),
651         NVal::DeclareNapiFunction("stop", Stop),
652     };
653     std::string className = GetClassName();
654     auto [succ, classValue] =
655         NClass::DefineClass(exports_.env_, className, CloudSyncNapi::Constructor, std::move(props));
656     if (!succ) {
657         NError(E_GETRESULT).ThrowErr(exports_.env_);
658         LOGE("Failed to define GallerySync class");
659         return false;
660     }
661 
662     succ = NClass::SaveClass(exports_.env_, className, classValue);
663     if (!succ) {
664         NError(E_GETRESULT).ThrowErr(exports_.env_);
665         LOGE("Failed to save GallerySync class");
666         return false;
667     }
668 
669     return exports_.AddProp(className, classValue);
670 }
671 
OnChange(CloudChangeListener & listener,const napi_ref cbRef)672 void ChangeListenerNapi::OnChange(CloudChangeListener &listener, const napi_ref cbRef)
673 {
674     uv_loop_s *loop = nullptr;
675     napi_get_uv_event_loop(env_, &loop);
676     if (loop == nullptr) {
677         return;
678     }
679 
680     uv_work_t *work = new (nothrow) uv_work_t;
681     if (work == nullptr) {
682         return;
683     }
684 
685     UvChangeMsg *msg = new (std::nothrow) UvChangeMsg(env_, cbRef, listener.changeInfo, listener.strUri);
686     if (msg == nullptr) {
687         delete work;
688         return;
689     }
690     if (!listener.changeInfo.uris_.empty()) {
691         if (static_cast<NotifyType>(listener.changeInfo.changeType_) == NotifyType::NOTIFY_NONE) {
692             LOGE("changeInfo.changeType_ is other");
693             delete msg;
694             delete work;
695             return;
696         }
697         if (msg->changeInfo_.size_ > 0) {
698             msg->data_ = (uint8_t *)malloc(msg->changeInfo_.size_);
699             if (msg->data_ == nullptr) {
700                 LOGE("new msg->data failed");
701                 delete msg;
702                 delete work;
703                 return;
704             }
705             int copyRet = memcpy_s(msg->data_, msg->changeInfo_.size_, msg->changeInfo_.data_, msg->changeInfo_.size_);
706             if (copyRet != 0) {
707                 LOGE("Parcel data copy failed, err = %{public}d", copyRet);
708             }
709         }
710     }
711     work->data = reinterpret_cast<void *>(msg);
712 
713     int ret = UvQueueWork(loop, work);
714     if (ret != 0) {
715         LOGE("Failed to execute libuv work queue, ret: %{public}d", ret);
716         delete msg;
717         delete work;
718     }
719 }
720 
UvQueueWork(uv_loop_s * loop,uv_work_t * work)721 int32_t ChangeListenerNapi::UvQueueWork(uv_loop_s *loop, uv_work_t *work)
722 {
723     return uv_queue_work(
724         loop, work, [](uv_work_t *w) {},
725         [](uv_work_t *w, int s) {
726             // js thread
727             if (w == nullptr) {
728                 return;
729             }
730 
731             UvChangeMsg *msg = reinterpret_cast<UvChangeMsg *>(w->data);
732             do {
733                 if (msg == nullptr) {
734                     LOGE("UvChangeMsg is null");
735                     break;
736                 }
737                 napi_env env = msg->env_;
738                 napi_handle_scope scope = nullptr;
739                 napi_open_handle_scope(env, &scope);
740 
741                 napi_value jsCallback = nullptr;
742                 napi_status status = napi_get_reference_value(env, msg->ref_, &jsCallback);
743                 if (status != napi_ok) {
744                     napi_close_handle_scope(env, scope);
745                     LOGE("Create reference fail, status: %{public}d", status);
746                     break;
747                 }
748                 napi_value retVal = nullptr;
749                 napi_value result[ARGS_ONE];
750                 result[PARAM0] = ChangeListenerNapi::SolveOnChange(env, msg);
751                 if (result[PARAM0] == nullptr) {
752                     napi_close_handle_scope(env, scope);
753                     break;
754                 }
755                 napi_call_function(env, nullptr, jsCallback, ARGS_ONE, result, &retVal);
756                 napi_close_handle_scope(env, scope);
757                 if (status != napi_ok) {
758                     LOGE("CallJs napi_call_function fail, status: %{public}d", status);
759                     break;
760                 }
761             } while (0);
762             delete msg;
763             delete w;
764         });
765 }
766 
SetValueArray(const napi_env & env,const char * fieldStr,const std::list<Uri> listValue,napi_value & result)767 static napi_status SetValueArray(const napi_env &env, const char *fieldStr, const std::list<Uri> listValue,
768     napi_value &result)
769 {
770     napi_value value = nullptr;
771     napi_status status = napi_create_array_with_length(env, listValue.size(), &value);
772     if (status != napi_ok) {
773         LOGE("Create array error! field: %{public}s", fieldStr);
774         return status;
775     }
776     int elementIndex = 0;
777     for (auto uri : listValue) {
778         napi_value uriRet = nullptr;
779         napi_create_string_utf8(env, uri.ToString().c_str(), NAPI_AUTO_LENGTH, &uriRet);
780         status = napi_set_element(env, value, elementIndex++, uriRet);
781         if (status != napi_ok) {
782             LOGE("Set lite item failed, error: %d", status);
783         }
784     }
785     status = napi_set_named_property(env, result, fieldStr, value);
786     if (status != napi_ok) {
787         LOGE("Set array named property error! field: %{public}s", fieldStr);
788     }
789 
790     return status;
791 }
792 
SetValueInt32(const napi_env & env,const char * fieldStr,const int intValue,napi_value & result)793 static napi_status SetValueInt32(const napi_env &env, const char *fieldStr, const int intValue, napi_value &result)
794 {
795     napi_value value;
796     napi_status status = napi_create_int32(env, intValue, &value);
797     if (status != napi_ok) {
798         LOGE("Set value create int32 error! field: %{public}s", fieldStr);
799         return status;
800     }
801     status = napi_set_named_property(env, result, fieldStr, value);
802     if (status != napi_ok) {
803         LOGE("Set int32 named property error! field: %{public}s", fieldStr);
804     }
805     return status;
806 }
807 
SetIsDir(const napi_env & env,const shared_ptr<MessageParcel> parcel,napi_value & result)808 static napi_status SetIsDir(const napi_env &env, const shared_ptr<MessageParcel> parcel, napi_value &result)
809 {
810     uint32_t len = 0;
811     napi_status status = napi_invalid_arg;
812     if (!parcel->ReadUint32(len)) {
813         LOGE("Failed to read sub uri list length");
814         return status;
815     }
816     napi_value isDirArray = nullptr;
817     napi_create_array_with_length(env, len, &isDirArray);
818     int elementIndex = 0;
819     if (len > READ_SIZE) {
820         return napi_invalid_arg;
821     }
822     for (uint32_t i = 0; i < len; i++) {
823         bool isDir = parcel->ReadBool();
824         napi_value isDirRet = nullptr;
825         napi_get_boolean(env, isDir, &isDirRet);
826         napi_set_element(env, isDirArray, elementIndex++, isDirRet);
827     }
828     status = napi_set_named_property(env, result, "isDirectory", isDirArray);
829     if (status != napi_ok) {
830         LOGE("Set subUri named property error!");
831     }
832     return status;
833 }
834 
SolveOnChange(napi_env env,UvChangeMsg * msg)835 napi_value ChangeListenerNapi::SolveOnChange(napi_env env, UvChangeMsg *msg)
836 {
837     static napi_value result;
838     if (msg->changeInfo_.uris_.empty()) {
839         napi_get_undefined(env, &result);
840         return result;
841     }
842     napi_create_object(env, &result);
843     SetValueArray(env, "uris", msg->changeInfo_.uris_, result);
844     if (msg->data_ != nullptr && msg->changeInfo_.size_ > 0) {
845         shared_ptr<MessageParcel> parcel = make_shared<MessageParcel>();
846         if (parcel->ParseFrom(reinterpret_cast<uintptr_t>(msg->data_), msg->changeInfo_.size_)) {
847             napi_status status = SetIsDir(env, parcel, result);
848             if (status != napi_ok) {
849                 LOGE("Set subArray named property error! field: subUris");
850                 return nullptr;
851             }
852         }
853     }
854     SetValueInt32(env, "type", (int)msg->changeInfo_.changeType_, result);
855     return result;
856 }
857 
OnChange()858 void CloudNotifyObserver::OnChange() {}
859 
OnchangeExt(const AAFwk::ChangeInfo & changeInfo)860 void CloudNotifyObserver::OnchangeExt(const AAFwk::ChangeInfo &changeInfo)
861 {
862     CloudChangeListener listener;
863     listener.changeInfo = changeInfo;
864     listener.strUri = uri_;
865     listObj_.OnChange(listener, ref_);
866 }
867 
868 } // namespace OHOS::FileManagement::CloudSync
869