1 /*
2  * Copyright (c) 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 "connectdfs.h"
17 
18 #include <cstring>
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <memory>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <tuple>
25 #include <unistd.h>
26 
27 #include "common_func.h"
28 #include "filemgmt_libhilog.h"
29 #include "distributed_file_daemon_manager.h"
30 
31 namespace OHOS {
32 namespace FileManagement {
33 namespace ModuleFileIO {
34 namespace fs = std::filesystem;
35 
CreateConnectDfsCBCBInfo(napi_env & env)36 ConnectDfsCB *ConnectDfs::CreateConnectDfsCBCBInfo(napi_env &env)
37 {
38     HILOGI("CreateConnectDfsCBCBInfo called");
39     auto connectDfsCB = new(std::nothrow) ConnectDfsCB;
40     if (connectDfsCB == nullptr) {
41         HILOGE("CreateConnectDfsCBCBInfo failed, connectDfsCB == nullptr");
42         return nullptr;
43     }
44     connectDfsCB->cbBase.cbInfo.env = env;
45     connectDfsCB->cbBase.asyncWork = nullptr;
46     connectDfsCB->cbBase.deferred = nullptr;
47     connectDfsCB->callbackRef = nullptr;
48     HILOGI("CreateConnectDfsCBCBInfo end");
49     return connectDfsCB;
50 }
51 
cbExec(napi_env env,void * data)52 void cbExec(napi_env env, void *data)
53 {
54     HILOGI("cbExec for connectDfs called");
55     auto connectDfsCB = static_cast<ConnectDfsCB *>(data);
56     sptr<NAPIDfsListener> dfsListeners(new (std::nothrow) NAPIDfsListener());
57     connectDfsCB->jsCallbackObject = dfsListeners;
58     connectDfsCB->jsCallbackObject->SetConnectDfsEnv(env);
59     HILOGI("connectDfsCB set env success");
60     if (connectDfsCB->dfsConnectCB.callback != nullptr) {
61         connectDfsCB->jsCallbackObject->
62             SetConnectDfsCBRef(connectDfsCB->dfsConnectCB.callback);
63         HILOGI("connectDfsCB set callback success");
64     } else {
65         connectDfsCB->jsCallbackObject->
66             SetConnectDfsPromiseRef(connectDfsCB->cbBase.deferred);
67         HILOGI("connectDfsCB set promise success");
68     }
69 
70     connectDfsCB->result = Storage::DistributedFile::DistributedFileDaemonManager::GetInstance().
71         OpenP2PConnectionEx(connectDfsCB->networkId, connectDfsCB->jsCallbackObject);
72     HILOGI(" cbExec end ret = %{public}d", connectDfsCB->result);
73 }
74 
cbCompl(napi_env env,napi_status status,void * data)75 void cbCompl(napi_env env, napi_status status, void *data)
76 {
77     HILOGI("cbCompl for connectDfs called");
78     auto connectDfsCB = static_cast<ConnectDfsCB *>(data);
79     napi_value result[NARG_CNT::TWO] = { nullptr };
80     napi_get_undefined(env, &result[NARG_POS::SECOND]);
81     if (connectDfsCB->result == ERRNO_NOERR) {
82         napi_get_undefined(env, &result[NARG_POS::FIRST]);
83         napi_resolve_deferred(env, connectDfsCB->cbBase.deferred, result[NARG_POS::SECOND]);
84     } else {
85         result[NARG_POS::FIRST] = NError(connectDfsCB->result).GetNapiErr(env);
86         napi_reject_deferred(env, connectDfsCB->cbBase.deferred, result[NARG_POS::FIRST]);
87     }
88     napi_delete_async_work(env, connectDfsCB->cbBase.asyncWork);
89     delete connectDfsCB;
90     connectDfsCB = nullptr;
91     HILOGI("cbCompl for connectDfs end");
92 }
93 
GetListenerFromOptionArg(napi_env env,const NFuncArg & funcArg)94 tuple<bool, NVal> ConnectDfs::GetListenerFromOptionArg(napi_env env, const NFuncArg &funcArg)
95 {
96     NVal op(env, funcArg[NARG_POS::SECOND]);
97     if (!op.HasProp("onStatus") || op.GetProp("onStatus").TypeIs(napi_undefined)) {
98         return { true, NVal() };
99     }
100     NVal onStatus = op.GetProp("onStatus");
101     if (!onStatus.TypeIs(napi_function)) {
102         HILOGE("Illegal dfsListeners.onStatus type");
103         return { false, NVal() };
104     }
105     return { true, onStatus };
106 }
107 
ParseJsOperand(napi_env env,NVal paramFromJsArg)108 tuple<bool, std::string> ConnectDfs::ParseJsOperand(napi_env env, NVal paramFromJsArg)
109 {
110     auto [succ, param, ignore] = paramFromJsArg.ToUTF8String();
111     if (!succ) {
112         HILOGE("parse parameter failed.");
113         return { false, "" };
114     }
115     std::string paramStr = std::string(param.get());
116     return { true, paramStr };
117 }
118 
ParseJsParam(napi_env env,NFuncArg & funcArg,ConnectDfsCB * connectDfsCB)119 int ConnectDfs::ParseJsParam(napi_env env, NFuncArg &funcArg, ConnectDfsCB *connectDfsCB)
120 {
121     if (!funcArg.InitArgs(NARG_CNT::TWO)) {
122         HILOGE("Number of arguments unmatched");
123         return E_PARAMS;
124     }
125     auto [succNetworkId, networkId] = ParseJsOperand(env, { env, funcArg[NARG_POS::FIRST] });
126     auto [succDfsListeners, dfsListeners] = GetListenerFromOptionArg(env, funcArg);
127     if (!succNetworkId || !succDfsListeners) {
128         HILOGE("The first/second argument requires string/napi_function");
129         return E_PARAMS;
130     }
131     connectDfsCB->networkId = networkId;
132     napi_create_reference(env, dfsListeners.val_, 1, &connectDfsCB->dfsConnectCB.callback);
133     return ERRNO_NOERR;
134 }
135 
Async(napi_env env,napi_callback_info info)136 napi_value ConnectDfs::Async(napi_env env, napi_callback_info info)
137 {
138     HILOGI("ConnectDfs::Async called");
139     ConnectDfsCB *connectDfsCB = CreateConnectDfsCBCBInfo(env);
140     if (connectDfsCB == nullptr) {
141         NError(E_PARAMS).ThrowErr(env);
142         return nullptr;
143     }
144     NFuncArg funcArg(env, info);
145     auto result = ParseJsParam(env, funcArg, connectDfsCB);
146     if (result != ERRNO_NOERR) {
147         NError(result).ThrowErr(env);
148         delete connectDfsCB;
149         connectDfsCB = nullptr;
150         return nullptr;
151     }
152 
153     napi_value ret = nullptr;
154     napi_status status = napi_create_promise(env, &connectDfsCB->cbBase.deferred, &ret);
155     if (status != napi_ok) {
156         HILOGE("INNER BUG. Cannot create promise for %{public}d", status);
157         delete connectDfsCB;
158         connectDfsCB = nullptr;
159         return nullptr;
160     }
161 
162     status = napi_create_async_work(env, nullptr, NVal::CreateUTF8String(env, "ResourceName").val_,
163         cbExec, cbCompl, static_cast<void *>(connectDfsCB), &connectDfsCB->cbBase.asyncWork);
164     if (status != napi_ok) {
165         HILOGE("INNER BUG. Failed to create async work for %{public}d", status);
166         delete connectDfsCB;
167         connectDfsCB = nullptr;
168         return nullptr;
169     }
170 
171     status = napi_queue_async_work(env, connectDfsCB->cbBase.asyncWork);
172     if (status != napi_ok) {
173         HILOGE("INNER BUG. Failed to queue async work for %{public}d", status);
174         delete connectDfsCB;
175         connectDfsCB = nullptr;
176         return nullptr;
177     }
178 
179     if (ret == nullptr) {
180         HILOGE("napi_async_work ret = nullptr");
181         NError(E_PARAMS).ThrowErr(env);
182         return NVal::CreateUndefined(env).val_;
183     }
184     HILOGI("ConnectDfs end");
185     return ret;
186 }
187 
CheckAndGetParameters(uv_work_t * work,napi_handle_scope * scope)188 ConnectDfsCB *CheckAndGetParameters(uv_work_t *work, napi_handle_scope *scope)
189 {
190     HILOGI("ConnectDfsCB::CheckAndGetParameters GetParam called");
191     if (work == nullptr) {
192         HILOGE("ConnectDfsCB, GetParam work is null");
193         return nullptr;
194     }
195     ConnectDfsCB *connectDfsCB = static_cast<ConnectDfsCB *>(work->data);
196     if (connectDfsCB == nullptr) {
197         HILOGE("ConnectDfsCB, GetParam connectDfsCB is null");
198         delete work;
199         work = nullptr;
200         return nullptr;
201     }
202     napi_open_handle_scope(connectDfsCB->cbBase.cbInfo.env, scope);
203     if (scope == nullptr) {
204         delete connectDfsCB;
205         connectDfsCB = nullptr;
206         delete work;
207         work = nullptr;
208         return nullptr;
209     }
210     HILOGI("ConnectDfsCB::CheckAndGetParameters GetParam end");
211     return connectDfsCB;
212 }
213 
SetConnectDfsEnv(const napi_env & env)214 void NAPIDfsListener::SetConnectDfsEnv(const napi_env &env)
215 {
216     env_ = env;
217 }
218 
SetConnectDfsCBRef(const napi_ref & ref)219 void NAPIDfsListener::SetConnectDfsCBRef(const napi_ref &ref)
220 {
221     onStatusRef_ = ref;
222 }
223 
SetConnectDfsPromiseRef(const napi_deferred & promiseDeferred)224 void NAPIDfsListener::SetConnectDfsPromiseRef(const napi_deferred &promiseDeferred)
225 {
226     promiseDeferred_ = promiseDeferred;
227 }
228 
WrapInt32(napi_env & env,int32_t num,const std::string & paramName)229 napi_value WrapInt32(napi_env &env, int32_t num, const std::string &paramName)
230 {
231     HILOGI("WrapInt32 called");
232     napi_value jsObject = nullptr;
233     napi_create_object(env, &jsObject);
234     napi_value jsValue = nullptr;
235     HILOGD("WrapInt32 called. %{public}s = %{public}d", paramName.c_str(), num);
236     napi_create_int32(env, num, &jsValue);
237     napi_set_named_property(env, jsObject, paramName.c_str(), jsValue);
238 
239     return jsObject;
240 }
241 
WrapString(napi_env & env,const std::string & param,const std::string & paramName)242 napi_value WrapString(napi_env &env, const std::string &param, const std::string &paramName)
243 {
244     HILOGI("WrapString called");
245     napi_value jsValue = nullptr;
246     HILOGD("WrapString called. %{public}s = %{public}s", paramName.c_str(), param.c_str());
247     napi_create_string_utf8(env, param.c_str(), NAPI_AUTO_LENGTH, &jsValue);
248 
249     return jsValue;
250 }
251 
UvWorkAfterOnStaus(uv_work_t * work,int status)252 void UvWorkAfterOnStaus(uv_work_t *work, int status)
253 {
254     HILOGI("UvWorkAfterOnStaus called");
255     napi_handle_scope scope = nullptr;
256     ConnectDfsCB *connectDfsCB = CheckAndGetParameters(work, &scope);
257     if (connectDfsCB == nullptr) {
258         return;
259     }
260     HILOGI("UvWorkAfterOnStaus, status = %{public}d", connectDfsCB->status);
261 
262     napi_value result[NARG_CNT::TWO] = {nullptr};
263     result[NARG_POS::FIRST] = WrapString(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->networkId.c_str(), "networkId");
264     result[NARG_POS::SECOND] = WrapInt32(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->status, "status");
265     if (connectDfsCB->cbBase.deferred == nullptr) {
266         napi_value callback = nullptr;
267         napi_value undefined = nullptr;
268         napi_get_undefined(connectDfsCB->cbBase.cbInfo.env, &undefined);
269         napi_value callResult = nullptr;
270         napi_get_reference_value(connectDfsCB->cbBase.cbInfo.env,
271             connectDfsCB->cbBase.cbInfo.callback, &callback);
272         napi_call_function(connectDfsCB->cbBase.cbInfo.env, undefined, callback, NARG_CNT::TWO, result, &callResult);
273         if (connectDfsCB->cbBase.cbInfo.callback != nullptr) {
274             napi_delete_reference(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->cbBase.cbInfo.callback);
275         }
276     } else {
277         napi_value res[NARG_CNT::TWO] = { nullptr };
278         napi_get_undefined(connectDfsCB->cbBase.cbInfo.env, &res[NARG_POS::SECOND]);
279         if (connectDfsCB->status == ERRNO_NOERR) {
280             napi_resolve_deferred(connectDfsCB->cbBase.cbInfo.env,
281                 connectDfsCB->cbBase.deferred, res[NARG_POS::SECOND]);
282         } else {
283             res[NARG_POS::FIRST] = NError(connectDfsCB->status).GetNapiErr(connectDfsCB->cbBase.cbInfo.env);
284             napi_reject_deferred(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->cbBase.deferred, res[NARG_POS::FIRST]);
285         }
286     }
287 
288     napi_close_handle_scope(connectDfsCB->cbBase.cbInfo.env, scope);
289     delete connectDfsCB;
290     connectDfsCB = nullptr;
291     delete work;
292     work = nullptr;
293     HILOGI("UvWorkAfterOnStaus end");
294 }
295 
OnStatus(const std::string & networkId,int32_t status)296 void NAPIDfsListener::OnStatus(const std::string &networkId, int32_t status)
297 {
298     HILOGI("NAPIDfsListener::OnStatus called");
299     uv_loop_s *loop = nullptr;
300 
301     napi_get_uv_event_loop(env_, &loop);
302     if (loop == nullptr) {
303         HILOGE("NAPIDfsListener::OnStatus, loop == nullptr");
304         return;
305     }
306 
307     uv_work_t *work = new (std::nothrow) uv_work_t;
308     if (work == nullptr) {
309         return;
310     }
311 
312     auto connectDfsCB = new (std::nothrow) ConnectDfsCB;
313     if (connectDfsCB == nullptr) {
314         HILOGE("NAPIDfsListener::OnStatus, connectDfsCb == nullptr");
315         delete work;
316         work = nullptr;
317         return;
318     }
319     connectDfsCB->cbBase.cbInfo.env = env_;
320     if (onStatusRef_ != nullptr) {
321         connectDfsCB->cbBase.cbInfo.callback = onStatusRef_;
322     } else {
323         connectDfsCB->cbBase.deferred = promiseDeferred_;
324     }
325     connectDfsCB->networkId = networkId;
326     connectDfsCB->status = status;
327     work->data = static_cast<void *>(connectDfsCB);
328 
329     int rev = uv_queue_work(
330         loop, work, [](uv_work_t *work) {}, UvWorkAfterOnStaus);
331     if (rev != ERRNO_NOERR) {
332         delete connectDfsCB;
333         connectDfsCB = nullptr;
334         delete work;
335         work = nullptr;
336     }
337     HILOGI("NAPIDfsListener::OnStatus end");
338 }
339 
340 } // namespace ModuleFileIO
341 } // namespace FileManagement
342 } // namespace OHOS