1 /*
2  * Copyright (c) 2023 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 "napi_domain_auth_callback.h"
17 
18 #include <uv.h>
19 #include "account_error_no.h"
20 #include "account_log_wrapper.h"
21 #include "napi/native_api.h"
22 #include "napi/native_node_api.h"
23 #include "napi_account_common.h"
24 #include "napi_account_error.h"
25 
26 namespace OHOS {
27 namespace AccountJsKit {
28 namespace {
29 const size_t ARGS_SIZE_TWO = 2;
30 }
31 using namespace AccountSA;
32 
NapiDomainAuthCallback(const std::shared_ptr<DomainAccountCallback> & callback)33 NapiDomainAuthCallback::NapiDomainAuthCallback(const std::shared_ptr<DomainAccountCallback> &callback)
34     : callback_(callback)
35 {}
36 
~NapiDomainAuthCallback()37 NapiDomainAuthCallback::~NapiDomainAuthCallback()
38 {}
39 
GetDomainAuthCallback()40 std::shared_ptr<DomainAccountCallback> NapiDomainAuthCallback::GetDomainAuthCallback()
41 {
42     return callback_;
43 }
44 
Init(napi_env env,napi_value exports)45 napi_value NapiDomainAuthCallback::Init(napi_env env, napi_value exports)
46 {
47     napi_property_descriptor properties[] = {
48         DECLARE_NAPI_FUNCTION("onResult", JsOnResult),
49     };
50     const std::string className = "DomainAuthCallback";
51     napi_value constructor = nullptr;
52     NAPI_CALL(env, napi_define_class(env, className.c_str(), className.length(), JsConstructor, nullptr,
53         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor));
54     NAPI_ASSERT(env, constructor != nullptr, "define js class DomainAuthCallback failed");
55     napi_status status = napi_set_named_property(env, exports, className.c_str(), constructor);
56     NAPI_ASSERT(env, status == napi_ok, "set property DomainAuthCallback to exports failed");
57     napi_value global = nullptr;
58     status = napi_get_global(env, &global);
59     NAPI_ASSERT(env, status == napi_ok, "get napi global failed");
60     status = napi_set_named_property(env, global, className.c_str(), constructor);
61     NAPI_ASSERT(env, status == napi_ok, "set constructor to global failed");
62     return exports;
63 }
64 
ParseDoaminAuthResult(napi_env env,napi_value value,DomainAuthResult & authResult)65 static bool ParseDoaminAuthResult(napi_env env, napi_value value, DomainAuthResult &authResult)
66 {
67     bool hasProp = false;
68     napi_has_named_property(env, value, "token", &hasProp);
69     if (hasProp) {
70         napi_value napiToken = nullptr;
71         napi_get_named_property(env, value, "token", &napiToken);
72         if (ParseUint8TypedArrayToVector(env, napiToken, authResult.token) != napi_ok) {
73             ACCOUNT_LOGE("failed to parse token");
74             return false;
75         }
76     }
77     hasProp = false;
78     napi_has_named_property(env, value, "remainTimes", &hasProp);
79     if (hasProp) {
80         napi_value napiRemainTimes = nullptr;
81         napi_get_named_property(env, value, "remainTimes", &napiRemainTimes);
82         if (!GetIntProperty(env, napiRemainTimes, authResult.authStatusInfo.remainingTimes)) {
83             ACCOUNT_LOGE("failed to parse remainTimes");
84             return false;
85         }
86     } else {
87         authResult.authStatusInfo.remainingTimes = -1;
88     }
89     hasProp = false;
90     napi_has_named_property(env, value, "freezingTime", &hasProp);
91     if (hasProp) {
92         napi_value napiFreezingTime = nullptr;
93         napi_get_named_property(env, value, "freezingTime", &napiFreezingTime);
94         if (!GetIntProperty(env, napiFreezingTime, authResult.authStatusInfo.freezingTime)) {
95             ACCOUNT_LOGE("failed to parse freezingTime");
96             return false;
97         }
98     } else {
99         authResult.authStatusInfo.freezingTime = -1;
100     }
101     return true;
102 }
103 
ParseContextForOnResult(napi_env env,napi_callback_info cbInfo,CallbackParam * param)104 static bool ParseContextForOnResult(napi_env env, napi_callback_info cbInfo, CallbackParam *param)
105 {
106     size_t argc = ARGS_SIZE_TWO;
107     napi_value argv[ARGS_SIZE_TWO] = {0};
108     napi_value thisVar = nullptr;
109     napi_get_cb_info(env, cbInfo, &argc, argv, &thisVar, nullptr);
110     if (argc != ARGS_SIZE_TWO) {
111         ACCOUNT_LOGE("the number of parameter should be two");
112         return false;
113     }
114     NapiDomainAuthCallback *napiCallback = nullptr;
115     napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiCallback));
116     if (napiCallback == nullptr) {
117         ACCOUNT_LOGE("the napi callback is nullptr");
118         return false;
119     }
120     param->callback = napiCallback->GetDomainAuthCallback();
121     NAPI_CALL_BASE(env, napi_get_value_int32(env, argv[0], &(param->errCode)), false);
122     return ParseDoaminAuthResult(env, argv[1], param->authResult);
123 }
124 
JsOnResult(napi_env env,napi_callback_info cbInfo)125 napi_value NapiDomainAuthCallback::JsOnResult(napi_env env, napi_callback_info cbInfo)
126 {
127     auto *param = new (std::nothrow) CallbackParam(env);
128     if (param == nullptr) {
129         ACCOUNT_LOGE("insufficient memory for param!");
130         return nullptr;
131     }
132     std::unique_ptr<CallbackParam> paramPtr(param);
133     if (!ParseContextForOnResult(env, cbInfo, param)) {
134         std::string errMsg = "fail to parse onResult parameters";
135         AccountNapiThrow(env, ERR_JS_PARAMETER_ERROR, errMsg, true);
136         return nullptr;
137     }
138     napi_value resourceName = nullptr;
139     NAPI_CALL(env, napi_create_string_latin1(env, "JsOnResult", NAPI_AUTO_LENGTH, &resourceName));
140     NAPI_CALL(env, napi_create_async_work(env, nullptr, resourceName,
141         [](napi_env env, void *data) {
142             if (data == nullptr) {
143                 ACCOUNT_LOGE("data is nullptr");
144                 return;
145             }
146             CallbackParam *param = reinterpret_cast<CallbackParam *>(data);
147             if (param->callback == nullptr) {
148                 ACCOUNT_LOGE("callback is nullptr");
149                 return;
150             }
151             Parcel parcel;
152             if (!param->authResult.Marshalling(parcel)) {
153                 ACCOUNT_LOGE("authResult Marshalling failed");
154                 return;
155             }
156             param->callback->OnResult(param->errCode, parcel);
157         },
158         [](napi_env env, napi_status status, void *data) {
159             delete reinterpret_cast<CallbackParam *>(data);
160         },
161         reinterpret_cast<void *>(param), &param->work));
162     NAPI_CALL(env, napi_queue_async_work_with_qos(env, param->work, napi_qos_user_initiated));
163     paramPtr.release();
164     return nullptr;
165 }
166 
JsConstructor(napi_env env,napi_callback_info info)167 napi_value NapiDomainAuthCallback::JsConstructor(napi_env env, napi_callback_info info)
168 {
169     napi_value thisVar = nullptr;
170     size_t argc = 1;
171     napi_value argv[1] = { nullptr };
172     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
173     NAPI_ASSERT(env, status == napi_ok, "napi get callback info failed");
174     return thisVar;
175 }
176 
NapiDomainAccountCallback(napi_env env,std::shared_ptr<JsDomainAccountAuthCallback> & callback)177 NapiDomainAccountCallback::NapiDomainAccountCallback(napi_env env,
178     std::shared_ptr<JsDomainAccountAuthCallback> &callback) : env_(env), callback_(callback)
179 {}
180 
~NapiDomainAccountCallback()181 NapiDomainAccountCallback::~NapiDomainAccountCallback()
182 {}
183 
DomainAuthResultWork(uv_work_t * work,int status)184 static void DomainAuthResultWork(uv_work_t *work, int status)
185 {
186     std::unique_ptr<uv_work_t> workPtr(work);
187     napi_handle_scope scope = nullptr;
188     if (!InitUvWorkCallbackEnv(work, scope)) {
189         return;
190     }
191     std::unique_ptr<DomainAccountAuthCallbackParam> param(
192         reinterpret_cast<DomainAccountAuthCallbackParam *>(work->data));
193     napi_value argv[ARGS_SIZE_TWO] = {nullptr};
194     napi_create_int32(param->env, param->errCode, &argv[0]);
195     argv[1] = CreateAuthResult(param->env, param->authResult.token,
196         param->authResult.authStatusInfo.remainingTimes, param->authResult.authStatusInfo.freezingTime);
197     NapiCallVoidFunction(param->env, argv, ARGS_SIZE_TWO, param->callback->onResult);
198     napi_close_handle_scope(param->env, scope);
199 }
200 
OnResult(const int32_t errCode,Parcel & parcel)201 void NapiDomainAccountCallback::OnResult(const int32_t errCode, Parcel &parcel)
202 {
203     std::lock_guard<std::mutex> lock(mutex_);
204     if (callback_->onResultCalled) {
205         ACCOUNT_LOGE("call twice is not allowed");
206         return;
207     }
208     callback_->onResultCalled = true;
209     std::unique_ptr<uv_work_t> work = std::make_unique<uv_work_t>();
210     std::unique_ptr<DomainAccountAuthCallbackParam> param =
211         std::make_unique<DomainAccountAuthCallbackParam>(env_);
212     uv_loop_s *loop = nullptr;
213     NAPI_CALL_RETURN_VOID(env_, napi_get_uv_event_loop(env_, &loop));
214     if (loop == nullptr || work == nullptr || param == nullptr) {
215         ACCOUNT_LOGE("fail for nullptr");
216         return;
217     }
218     param->errCode = errCode;
219     std::shared_ptr<DomainAuthResult> authResult(DomainAuthResult::Unmarshalling(parcel));
220     if (authResult == nullptr) {
221         ACCOUNT_LOGE("authResult is nullptr");
222         return;
223     }
224     param->authResult = (*authResult);
225     param->callback = callback_;
226     work->data = reinterpret_cast<void *>(param.get());
227     ErrCode ret = uv_queue_work_with_qos(
228         loop, work.get(), [] (uv_work_t *work) {}, DomainAuthResultWork, uv_qos_user_initiated);
229     if (ret != ERR_OK) {
230         ACCOUNT_LOGE("fail to queue work");
231         return;
232     }
233     work.release();
234     param.release();
235 }
236 }  // namespace AccountJsKit
237 }  // namespace OHOS
238