• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &parameterCount, 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, &parameterCount, 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