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