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, &parameterCount, 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, &parameterCount, 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