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_vcard.h"
17
18 #include <memory>
19
20 #include "ability.h"
21 #include "core_service_client.h"
22 #include "iostream"
23 #include "js_proxy.h"
24 #include "napi_parameter_util.h"
25 #include "napi_util.h"
26 #include "telephony_log_wrapper.h"
27 #include "telephony_permission.h"
28 #include "vcard_manager.h"
29
30 namespace OHOS {
31 namespace Telephony {
32 namespace {
33 const int32_t ARGS_ONE = 1;
34 constexpr int32_t NORMAL_STRING_SIZE = 101;
35 static const int32_t DEFAULT_REF_COUNT = 1;
36 const std::string CONTACT_URI = "datashare:///com.ohos.contactsdataability";
37 const std::string PERMISSION_READ_CONTACTS = "ohos.permission.READ_CONTACTS";
38 const std::string PERMISSION_WRITE_CONTACTS = "ohos.permission.WRITE_CONTACTS";
39 const std::string DEFAULT_CHARSET = "UTF-8";
40 constexpr int32_t TWO_PARAMETERS = 2;
41 constexpr int32_t THREE_PARAMETERS = 3;
42 constexpr int32_t FOUR_PARAMETERS = 4;
43 constexpr int32_t PARAMETERS_INDEX_TWO = 2;
44 constexpr int32_t PARAMETERS_INDEX_THREE = 3;
45 constexpr int32_t DB_CONNECT_MAX_WAIT_TIME = 10;
46
CreateEnumConstructor(napi_env env,napi_callback_info info)47 static napi_value CreateEnumConstructor(napi_env env, napi_callback_info info)
48 {
49 napi_value thisArg = nullptr;
50 void *data = nullptr;
51 napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
52 napi_value global = nullptr;
53 napi_get_global(env, &global);
54 return thisArg;
55 }
56
GetDataShareHelper(napi_env env,napi_callback_info info)57 std::shared_ptr<DataShare::DataShareHelper> GetDataShareHelper(napi_env env, napi_callback_info info)
58 {
59 size_t argc = ARGS_ONE;
60 napi_value argv[ARGS_ONE] = { 0 };
61 napi_value thisVar = nullptr;
62 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
63
64 std::shared_ptr<DataShare::DataShareHelper> dataShareHelper = nullptr;
65 bool isStageMode = false;
66 napi_status status = AbilityRuntime::IsStageContext(env, argv[0], isStageMode);
67 if (status != napi_ok || !isStageMode) {
68 auto ability = AbilityRuntime::GetCurrentAbility(env);
69 if (ability == nullptr) {
70 TELEPHONY_LOGE("Failed to get native ability instance");
71 return nullptr;
72 }
73 auto context = ability->GetContext();
74 if (context == nullptr) {
75 TELEPHONY_LOGE("Failed to get native context instance");
76 return nullptr;
77 }
78 dataShareHelper = DataShare::DataShareHelper::Creator(context->GetToken(), CONTACT_URI,
79 "", DB_CONNECT_MAX_WAIT_TIME);
80 } else {
81 auto context = AbilityRuntime::GetStageModeContext(env, argv[0]);
82 if (context == nullptr) {
83 TELEPHONY_LOGE("Failed to get native stage context instance");
84 return nullptr;
85 }
86 dataShareHelper = DataShare::DataShareHelper::Creator(context->GetToken(), CONTACT_URI,
87 "", DB_CONNECT_MAX_WAIT_TIME);
88 if (context->GetToken() == nullptr) {
89 TELEPHONY_LOGE("Failed to get native GetToken instance");
90 }
91 if (dataShareHelper == nullptr) {
92 TELEPHONY_LOGE("Failed to get native dataShareHelper instance");
93 }
94 }
95 return dataShareHelper;
96 }
97
MatchImportParameters(napi_env env,napi_value parameters[],size_t parameterCount,bool & hasAccountId,bool & hasCallback)98 bool MatchImportParameters(
99 napi_env env, napi_value parameters[], size_t parameterCount, bool &hasAccountId, bool &hasCallback)
100 {
101 if (parameterCount == TWO_PARAMETERS) {
102 return NapiUtil::MatchParameters(env, parameters, { napi_object, napi_string });
103 } else if (parameterCount == THREE_PARAMETERS) {
104 bool typeMatch = NapiUtil::MatchParameters(env, parameters, { napi_object, napi_string, napi_function });
105 if (typeMatch) {
106 hasCallback = true;
107 return typeMatch;
108 }
109 bool typeMatch2 = NapiUtil::MatchParameters(env, parameters, { napi_object, napi_string, napi_number });
110 if (typeMatch2) {
111 hasAccountId = true;
112 return true;
113 }
114 bool isAccountIdUndefined =
115 NapiUtil::MatchParameters(env, parameters, { napi_object, napi_string, napi_undefined });
116 if (isAccountIdUndefined) {
117 hasAccountId = false;
118 return true;
119 }
120 } else if (parameterCount == FOUR_PARAMETERS) {
121 bool typeMatch3 =
122 NapiUtil::MatchParameters(env, parameters, { napi_object, napi_string, napi_number, napi_function });
123 if (typeMatch3) {
124 hasAccountId = true;
125 hasCallback = true;
126 return true;
127 }
128 bool isAccountIdUndefined =
129 NapiUtil::MatchParameters(env, parameters, { napi_object, napi_string, napi_undefined, napi_function });
130 if (isAccountIdUndefined) {
131 hasAccountId = false;
132 hasCallback = true;
133 return true;
134 }
135 } else {
136 TELEPHONY_LOGE("Invalid parameter count");
137 }
138 return false;
139 }
140
NativeImportVCard(napi_env env,void * data)141 void NativeImportVCard(napi_env env, void *data)
142 {
143 auto asyncContext = static_cast<ImportContext *>(data);
144 if (asyncContext == nullptr) {
145 TELEPHONY_LOGE("asyncContext nullptr");
146 return;
147 }
148 int32_t errorCode = TELEPHONY_SUCCESS;
149 if (!TelephonyPermission::CheckCallerIsSystemApp()) {
150 TELEPHONY_LOGE("App is not systemapp");
151 errorCode = TELEPHONY_ERR_ILLEGAL_USE_OF_SYSTEM_API;
152 } else {
153 std::shared_ptr<DataShare::DataShareHelper> datashareHelper = asyncContext->datashareHelper;
154 std::string filePath = asyncContext->filePath;
155 if (datashareHelper == nullptr) {
156 TELEPHONY_LOGE("DatashareHelper is nullptr");
157 errorCode = TELEPHONY_ERR_PERMISSION_ERR;
158 } else {
159 errorCode = VCardManager::GetInstance().ImportLock(filePath, datashareHelper, asyncContext->accountId);
160 TELEPHONY_LOGI("Import finish errorCode : %{public}d", errorCode);
161 }
162 }
163 asyncContext->errorCode = errorCode;
164 if (errorCode == TELEPHONY_SUCCESS) {
165 asyncContext->resolved = true;
166 } else {
167 asyncContext->resolved = false;
168 }
169 }
170
ImportVCardCallback(napi_env env,napi_status status,void * data)171 void ImportVCardCallback(napi_env env, napi_status status, void *data)
172 {
173 auto context = static_cast<ImportContext *>(data);
174 if (context == nullptr) {
175 TELEPHONY_LOGE("ImportVCardCallback context nullptr");
176 return;
177 }
178 napi_value callbackValue = nullptr;
179 if (context->resolved) {
180 napi_get_undefined(env, &callbackValue);
181 } else {
182 JsError error = NapiUtil::ConverErrorMessageWithPermissionForJs(
183 context->errorCode, "importVCard", PERMISSION_READ_CONTACTS);
184 callbackValue = NapiUtil::CreateErrorMessage(env, error.errorMessage, error.errorCode);
185 }
186 NapiUtil::Handle1ValueCallback(env, context, callbackValue);
187 }
188
ImportVCard(napi_env env,napi_callback_info info)189 napi_value ImportVCard(napi_env env, napi_callback_info info)
190 {
191 size_t parameterCount = FOUR_PARAMETERS;
192 napi_value parameters[FOUR_PARAMETERS] = { 0 };
193 napi_value thisVar = nullptr;
194 void *data = nullptr;
195
196 napi_get_cb_info(env, info, ¶meterCount, parameters, &thisVar, &data);
197 bool hasAccountId = false;
198 bool hasCallback = false;
199 if (!MatchImportParameters(env, parameters, parameterCount, hasAccountId, hasCallback)) {
200 TELEPHONY_LOGE("ImportVCard parameter matching failed.");
201 NapiUtil::ThrowParameterError(env);
202 return nullptr;
203 }
204 auto context = std::make_unique<ImportContext>().release();
205 if (context == nullptr) {
206 TELEPHONY_LOGE("ImportVCard ImportContext is nullptr.");
207 NapiUtil::ThrowParameterError(env);
208 return nullptr;
209 }
210 context->datashareHelper = GetDataShareHelper(env, info);
211 context->filePath = NapiUtil::GetStringFromValue(env, parameters[1]);
212 if (hasAccountId) {
213 napi_get_value_int32(env, parameters[PARAMETERS_INDEX_TWO], &context->accountId);
214 }
215
216 if (hasCallback) {
217 if (parameterCount == FOUR_PARAMETERS) {
218 napi_create_reference(env, parameters[PARAMETERS_INDEX_THREE], DEFAULT_REF_COUNT, &context->callbackRef);
219 } else {
220 napi_create_reference(env, parameters[PARAMETERS_INDEX_TWO], DEFAULT_REF_COUNT, &context->callbackRef);
221 }
222 }
223 napi_value result = NapiUtil::HandleAsyncWork(env, context, "ImportVCard", NativeImportVCard, ImportVCardCallback);
224 return result;
225 }
226
NativeExportVCard(napi_env env,void * data)227 void NativeExportVCard(napi_env env, void *data)
228 {
229 auto asyncContext = static_cast<ExportContext *>(data);
230 if (asyncContext == nullptr) {
231 TELEPHONY_LOGE("asyncContext nullptr");
232 return;
233 }
234 int32_t errorCode = TELEPHONY_SUCCESS;
235 if (!TelephonyPermission::CheckCallerIsSystemApp()) {
236 errorCode = TELEPHONY_ERR_ILLEGAL_USE_OF_SYSTEM_API;
237 } else {
238 std::shared_ptr<DataShare::DataShareHelper> datashareHelper = asyncContext->datashareHelper;
239 if (datashareHelper == nullptr) {
240 errorCode = TELEPHONY_ERR_PERMISSION_ERR;
241 } else {
242 std::shared_ptr<DataShare::DataSharePredicates> datasharePredicates = asyncContext->predicates;
243 std::string charset = asyncContext->charset;
244 std::string filePath = "";
245 errorCode = VCardManager::GetInstance().ExportLock(
246 filePath, datashareHelper, *datasharePredicates, asyncContext->cardType, charset);
247 TELEPHONY_LOGI("Export finish errorCode : %{public}d", errorCode);
248 asyncContext->result = filePath;
249 }
250 }
251 asyncContext->errorCode = errorCode;
252 if (errorCode == TELEPHONY_SUCCESS) {
253 asyncContext->resolved = true;
254 } else {
255 asyncContext->resolved = false;
256 }
257 }
258
ExportVCardCallback(napi_env env,napi_status status,void * data)259 void ExportVCardCallback(napi_env env, napi_status status, void *data)
260 {
261 auto context = static_cast<ExportContext *>(data);
262 if (context == nullptr) {
263 TELEPHONY_LOGE("ExportVCardCallback context nullptr");
264 return;
265 }
266 napi_value callbackValue = nullptr;
267 if (context->resolved) {
268 std::string result = context->result;
269 napi_create_string_utf8(env, result.c_str(), result.size(), &callbackValue);
270 } else {
271 JsError error = NapiUtil::ConverErrorMessageWithPermissionForJs(
272 context->errorCode, "exportVCard", PERMISSION_READ_CONTACTS);
273 callbackValue = NapiUtil::CreateErrorMessage(env, error.errorMessage, error.errorCode);
274 }
275 NapiUtil::Handle2ValueCallback(env, context, callbackValue);
276 }
277
MatchExportParameters(napi_env env,napi_value parameters[],size_t parameterCount,bool & hasOption,bool & hasCallback)278 bool MatchExportParameters(
279 napi_env env, napi_value parameters[], size_t parameterCount, bool &hasOption, bool &hasCallback)
280 {
281 if (parameterCount == TWO_PARAMETERS) {
282 return NapiUtil::MatchParameters(env, parameters, { napi_object, napi_object });
283 } else if (parameterCount == THREE_PARAMETERS) {
284 bool typeMatch = NapiUtil::MatchParameters(env, parameters, { napi_object, napi_object, napi_function });
285 if (typeMatch) {
286 hasCallback = true;
287 return typeMatch;
288 }
289 bool typeMatch2 = NapiUtil::MatchParameters(env, parameters, { napi_object, napi_object, napi_object });
290 if (typeMatch2) {
291 hasOption = true;
292 return true;
293 }
294 bool isOptionUndefined =
295 NapiUtil::MatchParameters(env, parameters, { napi_object, napi_object, napi_undefined });
296 if (isOptionUndefined) {
297 hasOption = false;
298 return true;
299 }
300 } else if (parameterCount == FOUR_PARAMETERS) {
301 bool typeMatch3 =
302 NapiUtil::MatchParameters(env, parameters, { napi_object, napi_object, napi_object, napi_function });
303 if (typeMatch3) {
304 hasOption = true;
305 hasCallback = true;
306 return true;
307 }
308 bool isOptionUndefined =
309 NapiUtil::MatchParameters(env, parameters, { napi_object, napi_object, napi_undefined, napi_function });
310 if (isOptionUndefined) {
311 hasOption = false;
312 hasCallback = true;
313 return true;
314 }
315 } else {
316 TELEPHONY_LOGE("Invalid parameter count");
317 }
318 return false;
319 }
320
UnwrapDataSharePredicates(napi_env env,napi_value value)321 static DataShare::DataSharePredicates UnwrapDataSharePredicates(napi_env env, napi_value value)
322 {
323 if (value == nullptr) {
324 TELEPHONY_LOGE("value is null.");
325 return {};
326 }
327 JSProxy::JSProxy<DataShare::DataShareAbsPredicates> *jsProxy = nullptr;
328 napi_unwrap(env, value, reinterpret_cast<void **>(&jsProxy));
329 if (jsProxy == nullptr) {
330 TELEPHONY_LOGE("jsProxy is nullptr.");
331 return {};
332 }
333 std::shared_ptr<DataShare::DataShareAbsPredicates> predicates = jsProxy->GetInstance();
334 if (predicates == nullptr) {
335 TELEPHONY_LOGE("GetNativePredicates is nullptr.");
336 return {};
337 }
338 return DataShare::DataSharePredicates(predicates->GetOperationList());
339 }
340
GetDataSharePredicates(napi_env env,napi_callback_info info)341 std::shared_ptr<DataShare::DataSharePredicates> GetDataSharePredicates(napi_env env, napi_callback_info info)
342 {
343 size_t argc = TWO_PARAMETERS;
344 napi_value argv[TWO_PARAMETERS] = { 0 };
345 napi_value thisVar = nullptr;
346 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
347 DataShare::DataSharePredicates predicates = UnwrapDataSharePredicates(env, argv[1]);
348 std::shared_ptr<DataShare::DataSharePredicates> dataSharePredicates =
349 std::make_shared<DataShare::DataSharePredicates>(predicates);
350 return dataSharePredicates;
351 }
352
HandleOptionParameters(napi_env env,napi_value parameters[],size_t parameterCount,ExportContext * context)353 void HandleOptionParameters(napi_env env, napi_value parameters[], size_t parameterCount, ExportContext *context)
354 {
355 napi_value charset = NapiUtil::GetNamedProperty(env, parameters[TWO_PARAMETERS], "charset");
356 napi_valuetype charsetTemp = napi_undefined;
357 napi_typeof(env, charset, &charsetTemp);
358 bool isCharsetUndefined = (charsetTemp == napi_undefined);
359 if (charset != nullptr && !isCharsetUndefined) {
360 char strChars[NORMAL_STRING_SIZE] = { 0 };
361 size_t strLength = 0;
362 napi_get_value_string_utf8(env, charset, strChars, NORMAL_STRING_SIZE, &strLength);
363 std::string str8(strChars, strLength);
364 context->charset = str8;
365 } else {
366 context->charset = DEFAULT_CHARSET;
367 }
368 napi_value cardType = NapiUtil::GetNamedProperty(env, parameters[TWO_PARAMETERS], "cardType");
369 napi_valuetype cardTypeTemp = napi_undefined;
370 napi_typeof(env, cardType, &cardTypeTemp);
371 bool isCardTypeUndefined = (cardTypeTemp == napi_undefined);
372 if (cardType != nullptr && !isCardTypeUndefined) {
373 napi_get_value_int32(env, cardType, &context->cardType);
374 } else {
375 context->cardType = DEFAULT_CARD_TYPE;
376 }
377 }
378
ExportVCard(napi_env env,napi_callback_info info)379 napi_value ExportVCard(napi_env env, napi_callback_info info)
380 {
381 size_t parameterCount = FOUR_PARAMETERS;
382 napi_value parameters[FOUR_PARAMETERS] = { 0 };
383 napi_value thisVar = nullptr;
384 void *data = nullptr;
385 napi_get_cb_info(env, info, ¶meterCount, parameters, &thisVar, &data);
386 bool hasOption = false;
387 bool hasCallback = false;
388 if (!MatchExportParameters(env, parameters, parameterCount, hasOption, hasCallback)) {
389 NapiUtil::ThrowParameterError(env);
390 return nullptr;
391 }
392 auto context = std::make_unique<ExportContext>().release();
393 if (context == nullptr) {
394 NapiUtil::ThrowParameterError(env);
395 return nullptr;
396 }
397 context->datashareHelper = GetDataShareHelper(env, info);
398 std::shared_ptr<DataShare::DataSharePredicates> datasharePredicates = GetDataSharePredicates(env, info);
399 if (datasharePredicates == nullptr) {
400 NapiUtil::ThrowParameterError(env);
401 return nullptr;
402 } else {
403 context->predicates = datasharePredicates;
404 }
405 if (hasOption) {
406 HandleOptionParameters(env, parameters, parameterCount, context);
407 }
408 if (hasCallback) {
409 if (parameterCount == FOUR_PARAMETERS) {
410 napi_create_reference(env, parameters[PARAMETERS_INDEX_THREE], DEFAULT_REF_COUNT, &context->callbackRef);
411 } else {
412 napi_create_reference(env, parameters[PARAMETERS_INDEX_TWO], DEFAULT_REF_COUNT, &context->callbackRef);
413 }
414 }
415 napi_value result = NapiUtil::HandleAsyncWork(env, context, "ExportVCard", NativeExportVCard, ExportVCardCallback);
416 return result;
417 }
418
InitEnumVCardType(napi_env env,napi_value exports)419 napi_value InitEnumVCardType(napi_env env, napi_value exports)
420 {
421 napi_property_descriptor desc[] = {
422 DECLARE_NAPI_STATIC_PROPERTY("VERSION_21", NapiUtil::ToInt32Value(env, static_cast<int32_t>(VCARD_VERSION_21))),
423 DECLARE_NAPI_STATIC_PROPERTY("VERSION_30", NapiUtil::ToInt32Value(env, static_cast<int32_t>(VCARD_VERSION_30))),
424 DECLARE_NAPI_STATIC_PROPERTY("VERSION_40", NapiUtil::ToInt32Value(env, static_cast<int32_t>(VCARD_VERSION_40))),
425 };
426 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
427 return exports;
428 }
429
CreateEnumVCardType(napi_env env,napi_value exports)430 static napi_value CreateEnumVCardType(napi_env env, napi_value exports)
431 {
432 napi_value version_21 = nullptr;
433 napi_value version_30 = nullptr;
434 napi_value version_40 = nullptr;
435
436 napi_create_int32(env, (int32_t)VCardType::VCARD_VERSION_21, &version_21);
437 napi_create_int32(env, (int32_t)VCardType::VCARD_VERSION_30, &version_30);
438 napi_create_int32(env, (int32_t)VCardType::VCARD_VERSION_40, &version_40);
439
440 napi_property_descriptor desc[] = {
441 DECLARE_NAPI_STATIC_PROPERTY("VERSION_21", version_21),
442 DECLARE_NAPI_STATIC_PROPERTY("VERSION_30", version_30),
443 DECLARE_NAPI_STATIC_PROPERTY("VERSION_40", version_40),
444 };
445
446 napi_value result = nullptr;
447 napi_define_class(env, "VCardType", NAPI_AUTO_LENGTH, CreateEnumConstructor, nullptr, sizeof(desc) / sizeof(*desc),
448 desc, &result);
449 napi_set_named_property(env, exports, "VCardType", result);
450 return exports;
451 }
452
InitVcardInterface(napi_env env,napi_value exports)453 napi_status InitVcardInterface(napi_env env, napi_value exports)
454 {
455 napi_property_descriptor desc[] = {
456 DECLARE_NAPI_FUNCTION("importVCard", ImportVCard),
457 DECLARE_NAPI_FUNCTION("exportVCard", ExportVCard),
458 };
459 return napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
460 }
461 } // namespace
462
463 EXTERN_C_START
InitNapiVcard(napi_env env,napi_value exports)464 napi_value InitNapiVcard(napi_env env, napi_value exports)
465 {
466 NAPI_CALL(env, InitVcardInterface(env, exports));
467 CreateEnumVCardType(env, exports);
468 InitEnumVCardType(env, exports);
469 return exports;
470 }
471 EXTERN_C_END
472
473 static napi_module _vcardModule = {
474 .nm_version = 1,
475 .nm_flags = 0,
476 .nm_filename = nullptr,
477 .nm_register_func = InitNapiVcard,
478 .nm_modname = "telephony.vcard",
479 .nm_priv = ((void *)0),
480 .reserved = { 0 },
481 };
482
RegisterVCardModule(void)483 extern "C" __attribute__((constructor)) void RegisterVCardModule(void)
484 {
485 napi_module_register(&_vcardModule);
486 }
487 } // namespace Telephony
488 } // namespace OHOS
489