1 /*
2 * Copyright (c) 2022-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 #include "ipc_skeleton.h"
16 #include "napi_account_common.h"
17
18 #include "account_log_wrapper.h"
19 #include "ipc_skeleton.h"
20 #include "js_native_api.h"
21 #include "js_native_api_types.h"
22 #include "napi_account_error.h"
23 #include "napi/native_api.h"
24 #include "napi/native_common.h"
25 #include "napi/native_node_api.h"
26 #include "securec.h"
27 #include "tokenid_kit.h"
28
29 namespace OHOS {
30 namespace AccountJsKit {
31 namespace {
32 constexpr int32_t BUSINESS_ERROR_ARG_SIZE = 2;
33 const char BUSINESS_ERROR_CODE_NAME[] = "code";
34 const char BUSINESS_ERROR_DATA_NAME[] = "data";
35 }
36
37 using namespace AccountSA;
38
~CommonAsyncContext()39 CommonAsyncContext::~CommonAsyncContext()
40 {
41 if (env == nullptr) {
42 return;
43 }
44 if (callbackRef != nullptr) {
45 napi_delete_reference(env, callbackRef);
46 callbackRef = nullptr;
47 }
48 if (work != nullptr) {
49 napi_delete_async_work(env, work);
50 work = nullptr;
51 }
52 }
53
CreateExecEnv(napi_env env,uv_loop_s ** loop,uv_work_t ** work)54 bool CreateExecEnv(napi_env env, uv_loop_s **loop, uv_work_t **work)
55 {
56 *loop = nullptr;
57 napi_get_uv_event_loop(env, loop);
58 if (*loop == nullptr) {
59 ACCOUNT_LOGE("failed to get uv event loop");
60 return false;
61 }
62 *work = new (std::nothrow) uv_work_t;
63 if (*work == nullptr) {
64 ACCOUNT_LOGE("failed to create uv_work_t");
65 return false;
66 }
67 return true;
68 }
69
ProcessCallbackOrPromise(napi_env env,const CommonAsyncContext * asyncContext,napi_value err,napi_value data)70 void ProcessCallbackOrPromise(napi_env env, const CommonAsyncContext *asyncContext, napi_value err, napi_value data)
71 {
72 napi_value args[BUSINESS_ERROR_ARG_SIZE] = {0};
73 if (asyncContext->errCode == ERR_OK) {
74 napi_get_null(env, &args[0]);
75 args[1] = data;
76 } else {
77 napi_get_null(env, &args[1]);
78 args[0] = err;
79 }
80 if (asyncContext->deferred) {
81 if (asyncContext->errCode == ERR_OK) {
82 napi_resolve_deferred(env, asyncContext->deferred, args[1]);
83 } else {
84 napi_reject_deferred(env, asyncContext->deferred, args[0]);
85 }
86 } else {
87 NapiCallVoidFunction(env, args, BUSINESS_ERROR_ARG_SIZE, asyncContext->callbackRef);
88 }
89 }
90
ReturnCallbackOrPromise(napi_env env,const CommonAsyncContext * asyncContext,napi_value err,napi_value data)91 void ReturnCallbackOrPromise(napi_env env, const CommonAsyncContext *asyncContext, napi_value err, napi_value data)
92 {
93 napi_value args[BUSINESS_ERROR_ARG_SIZE] = {err, data};
94 if (asyncContext->errCode == ERR_OK) {
95 NAPI_CALL_RETURN_VOID(env, napi_get_null(env, &args[0]));
96 } else {
97 NAPI_CALL_RETURN_VOID(env, napi_get_null(env, &args[1]));
98 }
99 if (asyncContext->deferred != nullptr) {
100 if (asyncContext->errCode == ERR_OK) {
101 NAPI_CALL_RETURN_VOID(env, napi_resolve_deferred(env, asyncContext->deferred, args[1]));
102 } else {
103 NAPI_CALL_RETURN_VOID(env, napi_reject_deferred(env, asyncContext->deferred, args[0]));
104 }
105 return;
106 }
107 if (asyncContext->callbackRef != nullptr) {
108 NapiCallVoidFunction(env, args, BUSINESS_ERROR_ARG_SIZE, asyncContext->callbackRef);
109 }
110 }
111
GetIntProperty(napi_env env,napi_value obj,int32_t & property)112 bool GetIntProperty(napi_env env, napi_value obj, int32_t &property)
113 {
114 napi_valuetype valueType = napi_undefined;
115 NAPI_CALL_BASE(env, napi_typeof(env, obj, &valueType), false);
116 if (valueType != napi_number) {
117 return false;
118 }
119
120 NAPI_CALL_BASE(env, napi_get_value_int32(env, obj, &property), false);
121 return true;
122 }
123
GetLongIntProperty(napi_env env,napi_value obj,int64_t & property)124 bool GetLongIntProperty(napi_env env, napi_value obj, int64_t &property)
125 {
126 napi_valuetype valueType = napi_undefined;
127 NAPI_CALL_BASE(env, napi_typeof(env, obj, &valueType), false);
128 if (valueType != napi_number) {
129 return false;
130 }
131
132 NAPI_CALL_BASE(env, napi_get_value_int64(env, obj, &property), false);
133 return true;
134 }
135
GetBoolProperty(napi_env env,napi_value obj,bool & property)136 bool GetBoolProperty(napi_env env, napi_value obj, bool &property)
137 {
138 napi_valuetype valueType = napi_undefined;
139 NAPI_CALL_BASE(env, napi_typeof(env, obj, &valueType), false);
140 if (valueType != napi_boolean) {
141 return false;
142 }
143 NAPI_CALL_BASE(env, napi_get_value_bool(env, obj, &property), false);
144 return true;
145 }
146
GetStringProperty(napi_env env,napi_value obj,std::string & property)147 bool GetStringProperty(napi_env env, napi_value obj, std::string &property)
148 {
149 napi_valuetype valuetype = napi_undefined;
150 NAPI_CALL_BASE(env, napi_typeof(env, obj, &valuetype), false);
151 if (valuetype != napi_string) {
152 return false;
153 }
154
155 size_t propLen;
156 NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, obj, nullptr, 0, &propLen), false);
157 property.reserve(propLen + 1);
158 property.resize(propLen);
159 NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, obj, property.data(), propLen + 1, &propLen), false);
160 return true;
161 }
162
GetStringArrayProperty(napi_env env,napi_value obj,std::vector<std::string> & property,bool allowEmpty)163 bool GetStringArrayProperty(napi_env env, napi_value obj, std::vector<std::string> &property, bool allowEmpty)
164 {
165 bool isArray = false;
166 NAPI_CALL_BASE(env, napi_is_array(env, obj, &isArray), false);
167 if (!isArray) {
168 return false;
169 }
170 uint32_t length = 0;
171 NAPI_CALL_BASE(env, napi_get_array_length(env, obj, &length), false);
172 if (!allowEmpty && (length == 0)) {
173 return false;
174 }
175
176 for (size_t i = 0; i < length; i++) {
177 napi_value strJs = nullptr;
178 NAPI_CALL_BASE(env, napi_get_element(env, obj, i, &strJs), false);
179 std::string str;
180 if (!GetStringProperty(env, strJs, str)) {
181 return false;
182 }
183 property.emplace_back(str);
184 }
185 return true;
186 }
187
GetCallbackProperty(napi_env env,napi_value obj,napi_ref & property,int argNum)188 bool GetCallbackProperty(napi_env env, napi_value obj, napi_ref &property, int argNum)
189 {
190 napi_valuetype valueType = napi_undefined;
191 NAPI_CALL_BASE(env, napi_typeof(env, obj, &valueType), false);
192 if (valueType != napi_function) {
193 return false;
194 }
195 NAPI_CALL_BASE(env, napi_create_reference(env, obj, argNum, &property), false);
196 return true;
197 }
198
GetStringPropertyByKey(napi_env env,napi_value obj,const std::string & propertyName,std::string & property)199 bool GetStringPropertyByKey(napi_env env, napi_value obj, const std::string &propertyName, std::string &property)
200 {
201 napi_value value = nullptr;
202 NAPI_CALL_BASE(env, napi_get_named_property(env, obj, propertyName.c_str(), &value), false);
203
204 return GetStringProperty(env, value, property);
205 }
206
GetOptionalStringPropertyByKey(napi_env env,napi_value obj,const std::string & propertyName,std::string & property)207 bool GetOptionalStringPropertyByKey(napi_env env, napi_value obj, const std::string &propertyName,
208 std::string &property)
209 {
210 bool hasProp = false;
211 napi_has_named_property(env, obj, propertyName.c_str(), &hasProp);
212 if (!hasProp) {
213 return true;
214 }
215 napi_value value = nullptr;
216 NAPI_CALL_BASE(env, napi_get_named_property(env, obj, propertyName.c_str(), &value), false);
217 napi_valuetype valuetype = napi_undefined;
218 NAPI_CALL_BASE(env, napi_typeof(env, value, &valuetype), false);
219 if ((valuetype == napi_undefined) || (valuetype == napi_null)) {
220 ACCOUNT_LOGI("this key's value is undefined or null");
221 return true;
222 }
223 return GetStringProperty(env, value, property);
224 }
225
CompareOnAndOffRef(const napi_env env,napi_ref subscriberRef,napi_ref unsubscriberRef)226 bool CompareOnAndOffRef(const napi_env env, napi_ref subscriberRef, napi_ref unsubscriberRef)
227 {
228 napi_value subscriberCallback;
229 napi_get_reference_value(env, subscriberRef, &subscriberCallback);
230 napi_value unsubscriberCallback;
231 napi_get_reference_value(env, unsubscriberRef, &unsubscriberCallback);
232 bool result = false;
233 napi_strict_equals(env, subscriberCallback, unsubscriberCallback, &result);
234 return result;
235 }
236
IsSystemApp(napi_env env)237 bool IsSystemApp(napi_env env)
238 {
239 uint64_t tokenId = IPCSkeleton::GetSelfTokenID();
240 bool isSystemApp = Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(tokenId);
241 if (!isSystemApp) {
242 std::string errMsg = ConvertToJsErrMsg(ERR_JS_IS_NOT_SYSTEM_APP);
243 AccountNapiThrow(env, ERR_JS_IS_NOT_SYSTEM_APP, errMsg, true);
244 return false;
245 }
246 return true;
247 }
248
CreateStringArray(napi_env env,const std::vector<std::string> & strVec)249 napi_value CreateStringArray(napi_env env, const std::vector<std::string> &strVec)
250 {
251 napi_value result = nullptr;
252 napi_create_array(env, &result);
253 for (size_t i = 0; i < strVec.size(); ++i) {
254 napi_value value = nullptr;
255 napi_create_string_utf8(env, strVec[i].c_str(), NAPI_AUTO_LENGTH, &value);
256 napi_set_element(env, result, i, value);
257 }
258 return result;
259 }
CreateUint8Array(napi_env env,const uint8_t * srcData,size_t length)260 napi_value CreateUint8Array(napi_env env, const uint8_t *srcData, size_t length)
261 {
262 napi_value result = nullptr;
263 void* dstData = nullptr;
264 napi_value napiArr = nullptr;
265 NAPI_CALL(env, napi_create_arraybuffer(env, length, &dstData, &napiArr));
266 if ((length > 0) && (memcpy_s(dstData, length, srcData, length) != EOK)) {
267 return result;
268 }
269 NAPI_CALL(env, napi_create_typedarray(env, napi_uint8_array, length, napiArr, 0, &result));
270 return result;
271 }
272
ParseUint8TypedArray(napi_env env,napi_value value,uint8_t ** data,size_t * length)273 napi_status ParseUint8TypedArray(napi_env env, napi_value value, uint8_t **data, size_t *length)
274 {
275 *data = nullptr;
276 *length = 0;
277 bool isTypedArray = false;
278 napi_is_typedarray(env, value, &isTypedArray);
279 if (!isTypedArray) {
280 ACCOUNT_LOGE("invalid uint8 array");
281 return napi_ok;
282 }
283 napi_typedarray_type arrayType = static_cast<napi_typedarray_type>(-1); // -1 indicates invalid type
284 napi_value buffer = nullptr;
285 size_t offset = 0;
286 napi_get_typedarray_info(env, value, &arrayType, length, reinterpret_cast<void **>(data), &buffer, &offset);
287 if (arrayType != napi_uint8_array) {
288 ACCOUNT_LOGE("invalid uint8 array");
289 *data = nullptr;
290 *length = 0;
291 }
292 return napi_ok;
293 }
294
ParseUint8TypedArrayToVector(napi_env env,napi_value value,std::vector<uint8_t> & vec)295 napi_status ParseUint8TypedArrayToVector(napi_env env, napi_value value, std::vector<uint8_t> &vec)
296 {
297 uint8_t *data = nullptr;
298 size_t length = 0;
299 napi_status status = ParseUint8TypedArray(env, value, &data, &length);
300 if (status != napi_ok) {
301 ACCOUNT_LOGE("failed to ParseUint8TypedArray");
302 return status;
303 }
304 vec.assign(data, data + length);
305 return napi_ok;
306 }
307
ParseUint8TypedArrayToUint64(napi_env env,napi_value value,uint64_t & result)308 napi_status ParseUint8TypedArrayToUint64(napi_env env, napi_value value, uint64_t &result)
309 {
310 uint8_t *data = nullptr;
311 size_t length = 0;
312 napi_status status = ParseUint8TypedArray(env, value, &data, &length);
313 if (status != napi_ok) {
314 ACCOUNT_LOGE("failed to ParseUint8TypedArray");
315 return status;
316 }
317 if (data == nullptr) {
318 result = 0;
319 return napi_invalid_arg;
320 }
321 if (length != sizeof(uint64_t)) {
322 ACCOUNT_LOGE("failed to convert to uint64_t value");
323 return napi_invalid_arg;
324 }
325 result = *(reinterpret_cast<uint64_t *>(data));
326 return napi_ok;
327 }
328
ParseBusinessError(napi_env env,napi_value value,BusinessError & error)329 bool ParseBusinessError(napi_env env, napi_value value, BusinessError &error)
330 {
331 napi_valuetype valueType = napi_undefined;
332 NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), false);
333 if (valueType == napi_null || (valueType == napi_undefined)) {
334 error.code = 0;
335 return true;
336 }
337 napi_value napiCode = nullptr;
338 NAPI_CALL_BASE(env, napi_get_named_property(env, value, BUSINESS_ERROR_CODE_NAME, &napiCode), false);
339 if (napiCode == nullptr) {
340 ACCOUNT_LOGE("code is undefined");
341 return false;
342 }
343 NAPI_CALL_BASE(env, napi_get_value_int32(env, napiCode, &error.code), false);
344 bool hasData = false;
345 napi_has_named_property(env, value, BUSINESS_ERROR_DATA_NAME, &hasData);
346 if (hasData) {
347 napi_value napiData = nullptr;
348 napi_get_named_property(env, value, BUSINESS_ERROR_DATA_NAME, &napiData);
349 return GetStringProperty(env, napiData, error.data);
350 }
351 return true;
352 }
353
GetNamedJsFunction(napi_env env,napi_value object,const std::string & name,napi_ref & callback)354 bool GetNamedJsFunction(napi_env env, napi_value object, const std::string &name, napi_ref &callback)
355 {
356 napi_valuetype valueType = napi_undefined;
357 NAPI_CALL_BASE(env, napi_typeof(env, object, &valueType), false);
358 if (valueType != napi_object) {
359 ACCOUNT_LOGE("invalid object");
360 return false;
361 }
362 napi_value result = nullptr;
363 NAPI_CALL_BASE(env, napi_get_named_property(env, object, name.c_str(), &result), false);
364 return GetCallbackProperty(env, result, callback, 1);
365 }
366
NapiCallVoidFunction(napi_env env,napi_value * argv,size_t argc,napi_ref funcRef)367 void NapiCallVoidFunction(napi_env env, napi_value *argv, size_t argc, napi_ref funcRef)
368 {
369 napi_value undefined = nullptr;
370 NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
371 napi_value returnVal;
372 napi_value func = nullptr;
373 NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, funcRef, &func));
374 napi_call_function(env, undefined, func, argc, argv, &returnVal);
375 }
376
CreateAuthResult(napi_env env,const std::vector<uint8_t> & token,int32_t remainTimes,int32_t freezingTime)377 napi_value CreateAuthResult(
378 napi_env env, const std::vector<uint8_t> &token, int32_t remainTimes, int32_t freezingTime)
379 {
380 napi_value object = nullptr;
381 NAPI_CALL(env, napi_create_object(env, &object));
382 if (remainTimes >= 0) {
383 napi_value napiRemainTimes = 0;
384 NAPI_CALL(env, napi_create_uint32(env, remainTimes, &napiRemainTimes));
385 NAPI_CALL(env, napi_set_named_property(env, object, "remainTimes", napiRemainTimes));
386 }
387 if (freezingTime >= 0) {
388 napi_value napiFreezingTimes = 0;
389 NAPI_CALL(env, napi_create_uint32(env, freezingTime, &napiFreezingTimes));
390 NAPI_CALL(env, napi_set_named_property(env, object, "freezingTime", napiFreezingTimes));
391 }
392 if (token.size() > 0) {
393 napi_value napiToken = CreateUint8Array(env, token.data(), token.size());
394 NAPI_CALL(env, napi_set_named_property(env, object, "token", napiToken));
395 }
396 return object;
397 }
398
ReleaseNapiRefAsync(napi_env env,napi_ref napiRef)399 void ReleaseNapiRefAsync(napi_env env, napi_ref napiRef)
400 {
401 ReleaseNapiRefArray(env, {napiRef});
402 }
403
ReleaseNapiRefArray(napi_env env,const std::vector<napi_ref> & napiRefVec)404 void ReleaseNapiRefArray(napi_env env, const std::vector<napi_ref> &napiRefVec)
405 {
406 if (env == nullptr) {
407 ACCOUNT_LOGE("invalid env");
408 return;
409 }
410 std::unique_ptr<uv_work_t> work = std::make_unique<uv_work_t>();
411 std::unique_ptr<NapiRefArrayContext> context = std::make_unique<NapiRefArrayContext>();
412 uv_loop_s *loop = nullptr;
413 napi_get_uv_event_loop(env, &loop);
414 if ((loop == nullptr) || (work == nullptr) || (context == nullptr)) {
415 ACCOUNT_LOGE("fail to init execution environment");
416 return;
417 }
418 context->env = env;
419 context->napiRefVec = napiRefVec;
420 work->data = reinterpret_cast<void *>(context.get());
421 NAPI_CALL_RETURN_VOID(env, uv_queue_work_with_qos(loop, work.get(), [] (uv_work_t *work) {},
422 [] (uv_work_t *work, int status) {
423 if (work == nullptr) {
424 ACCOUNT_LOGE("work is nullptr");
425 return;
426 }
427 auto context = reinterpret_cast<NapiRefArrayContext *>(work->data);
428 if (context == nullptr) {
429 ACCOUNT_LOGE("context is nullptr");
430 delete work;
431 return;
432 }
433 for (auto &napiRef : context->napiRefVec) {
434 if (napiRef != nullptr) {
435 napi_delete_reference(context->env, napiRef);
436 }
437 }
438 delete context;
439 delete work;
440 }, uv_qos_default));
441 context.release();
442 work.release();
443 }
444
InitUvWorkCallbackEnv(uv_work_t * work,napi_handle_scope & scope)445 bool InitUvWorkCallbackEnv(uv_work_t *work, napi_handle_scope &scope)
446 {
447 if (work == nullptr) {
448 ACCOUNT_LOGE("work is nullptr");
449 return false;
450 }
451 if (work->data == nullptr) {
452 ACCOUNT_LOGE("data is nullptr");
453 return false;
454 }
455 CommonAsyncContext *data = reinterpret_cast<CommonAsyncContext *>(work->data);
456 napi_open_handle_scope(data->env, &scope);
457 if (scope == nullptr) {
458 ACCOUNT_LOGE("fail to open scope");
459 delete data;
460 work->data = nullptr;
461 return false;
462 }
463 return true;
464 }
465 } // namespace AccountJsKit
466 } // namespace OHOS