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 #include "napi_send_recv_mms.h"
16
17 #include "ability.h"
18 #include "napi_base_context.h"
19 #include "napi_mms_pdu.h"
20 #include "napi_mms_pdu_helper.h"
21 #include "sms_constants_utils.h"
22 #include "telephony_permission.h"
23
24 namespace OHOS {
25 namespace Telephony {
26 namespace {
27 const std::string SMS_PROFILE_URI = "datashare:///com.ohos.smsmmsability";
28 static const int32_t DEFAULT_REF_COUNT = 1;
29 const bool STORE_MMS_PDU_TO_FILE = false;
30 const int32_t ARGS_ONE = 1;
31 std::shared_ptr<DataShare::DataShareHelper> g_datashareHelper = nullptr;
32 constexpr static uint32_t WAIT_PDN_TOGGLE_TIME = 3000;
33 } // namespace
34 std::mutex NapiSendRecvMms::downloadCtx_;
35 std::mutex NapiSendRecvMms::countCtx_;
36 int32_t NapiSendRecvMms::reqCount_ = 0;
37 bool NapiSendRecvMms::waitFlag = false;
38
GetDataShareHelper(napi_env env,napi_callback_info info)39 std::shared_ptr<OHOS::DataShare::DataShareHelper> GetDataShareHelper(napi_env env, napi_callback_info info)
40 {
41 size_t argc = ARGS_ONE;
42 napi_value argv[ARGS_ONE] = { 0 };
43 napi_value thisVar = nullptr;
44 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
45
46 std::shared_ptr<DataShare::DataShareHelper> dataShareHelper = nullptr;
47 bool isStageMode = false;
48 napi_status status = OHOS::AbilityRuntime::IsStageContext(env, argv[0], isStageMode);
49 if (status != napi_ok || !isStageMode) {
50 auto ability = OHOS::AbilityRuntime::GetCurrentAbility(env);
51 if (ability == nullptr) {
52 TELEPHONY_LOGE("Failed to get native ability instance");
53 return nullptr;
54 }
55 auto context = ability->GetContext();
56 if (context == nullptr) {
57 TELEPHONY_LOGE("Failed to get native context instance");
58 return nullptr;
59 }
60 dataShareHelper = DataShare::DataShareHelper::Creator(context->GetToken(), SMS_PROFILE_URI);
61 } else {
62 auto context = OHOS::AbilityRuntime::GetStageModeContext(env, argv[0]);
63 if (context == nullptr) {
64 TELEPHONY_LOGE("Failed to get native stage context instance");
65 return nullptr;
66 }
67 dataShareHelper = DataShare::DataShareHelper::Creator(context->GetToken(), SMS_PROFILE_URI);
68 }
69 return dataShareHelper;
70 }
71
GetMmsPduFromFile(const std::string & fileName,std::string & mmsPdu)72 bool GetMmsPduFromFile(const std::string &fileName, std::string &mmsPdu)
73 {
74 char realPath[PATH_MAX] = { 0 };
75 if (fileName.empty() || realpath(fileName.c_str(), realPath) == nullptr) {
76 TELEPHONY_LOGE("path or realPath is nullptr");
77 return false;
78 }
79
80 FILE *pFile = fopen(realPath, "rb");
81 if (pFile == nullptr) {
82 TELEPHONY_LOGE("openFile Error");
83 return false;
84 }
85
86 (void)fseek(pFile, 0, SEEK_END);
87 long fileLen = ftell(pFile);
88 if (fileLen <= 0 || fileLen > static_cast<long>(MMS_PDU_MAX_SIZE)) {
89 (void)fclose(pFile);
90 TELEPHONY_LOGE("fileLen Over Max Error");
91 return false;
92 }
93
94 std::unique_ptr<char[]> pduBuffer = std::make_unique<char[]>(fileLen);
95 if (!pduBuffer) {
96 (void)fclose(pFile);
97 TELEPHONY_LOGE("make unique pduBuffer nullptr Error");
98 return false;
99 }
100 (void)fseek(pFile, 0, SEEK_SET);
101 int32_t totolLength = static_cast<int32_t>(fread(pduBuffer.get(), 1, MMS_PDU_MAX_SIZE, pFile));
102 TELEPHONY_LOGI("fread totolLength%{private}d", totolLength);
103
104 long i = 0;
105 while (i < fileLen) {
106 mmsPdu += pduBuffer[i];
107 i++;
108 }
109 (void)fclose(pFile);
110 return true;
111 }
112
StoreSendMmsPduToDataBase(NapiMmsPduHelper & helper)113 void StoreSendMmsPduToDataBase(NapiMmsPduHelper &helper) __attribute__((no_sanitize("cfi")))
114 {
115 std::shared_ptr<NAPIMmsPdu> mmsPduObj = std::make_shared<NAPIMmsPdu>();
116 if (mmsPduObj == nullptr) {
117 TELEPHONY_LOGE("mmsPduObj nullptr");
118 helper.NotifyAll();
119 return;
120 }
121 std::string mmsPdu;
122 if (!GetMmsPduFromFile(helper.GetPduFileName(), mmsPdu)) {
123 TELEPHONY_LOGE("get mmsPdu fail");
124 helper.NotifyAll();
125 return;
126 }
127 mmsPduObj->InsertMmsPdu(helper, mmsPdu);
128 }
129
NativeSendMms(napi_env env,void * data)130 void NativeSendMms(napi_env env, void *data)
131 {
132 auto asyncContext = static_cast<MmsContext *>(data);
133 if (asyncContext == nullptr) {
134 TELEPHONY_LOGE("asyncContext nullptr");
135 return;
136 }
137 if (!TelephonyPermission::CheckCallerIsSystemApp()) {
138 TELEPHONY_LOGE("Non-system applications use system APIs!");
139 asyncContext->errorCode = TELEPHONY_ERR_ILLEGAL_USE_OF_SYSTEM_API;
140 return;
141 }
142 if (!STORE_MMS_PDU_TO_FILE) {
143 std::string pduFileName = NapiUtil::ToUtf8(asyncContext->data);
144 if (pduFileName.empty()) {
145 asyncContext->errorCode = TELEPHONY_ERR_ARGUMENT_INVALID;
146 asyncContext->resolved = false;
147 TELEPHONY_LOGE("pduFileName empty");
148 return;
149 }
150 if (g_datashareHelper == nullptr) {
151 asyncContext->errorCode = TELEPHONY_ERR_LOCAL_PTR_NULL;
152 asyncContext->resolved = false;
153 TELEPHONY_LOGE("g_datashareHelper is nullptr");
154 return;
155 }
156 NapiMmsPduHelper helper;
157 helper.SetDataShareHelper(g_datashareHelper);
158 helper.SetPduFileName(pduFileName);
159 if (!helper.Run(StoreSendMmsPduToDataBase, helper)) {
160 TELEPHONY_LOGE("StoreMmsPdu fail");
161 asyncContext->errorCode = TELEPHONY_ERR_LOCAL_PTR_NULL;
162 asyncContext->resolved = false;
163 return;
164 }
165 asyncContext->data = NapiUtil::ToUtf16(helper.GetDbUrl());
166 }
167 asyncContext->errorCode =
168 DelayedSingleton<SmsServiceManagerClient>::GetInstance()->SendMms(asyncContext->slotId, asyncContext->mmsc,
169 asyncContext->data, asyncContext->mmsConfig.userAgent, asyncContext->mmsConfig.userAgentProfile);
170 if (asyncContext->errorCode == TELEPHONY_ERR_SUCCESS) {
171 asyncContext->resolved = true;
172 } else {
173 asyncContext->resolved = false;
174 }
175 TELEPHONY_LOGI("NativeSendMms end resolved = %{public}d", asyncContext->resolved);
176 }
177
SendMmsCallback(napi_env env,napi_status status,void * data)178 void SendMmsCallback(napi_env env, napi_status status, void *data)
179 {
180 auto context = static_cast<MmsContext *>(data);
181 if (g_datashareHelper != nullptr) {
182 g_datashareHelper->Release();
183 }
184 if (context == nullptr) {
185 TELEPHONY_LOGE("SendMmsCallback context nullptr");
186 return;
187 }
188 napi_value callbackValue = nullptr;
189 if (context->resolved) {
190 napi_get_undefined(env, &callbackValue);
191 } else {
192 JsError error = NapiUtil::ConverErrorMessageWithPermissionForJs(
193 context->errorCode, "sendMms", "ohos.permission.SEND_MESSAGES");
194 callbackValue = NapiUtil::CreateErrorMessage(env, error.errorMessage, error.errorCode);
195 }
196 NapiUtil::Handle1ValueCallback(env, context, callbackValue);
197 }
198
MatchMmsParameters(napi_env env,napi_value parameters[],size_t parameterCount)199 bool MatchMmsParameters(napi_env env, napi_value parameters[], size_t parameterCount)
200 {
201 bool typeMatch = false;
202 switch (parameterCount) {
203 case TWO_PARAMETERS: {
204 typeMatch = NapiUtil::MatchParameters(env, parameters, { napi_object, napi_object });
205 break;
206 }
207 case THREE_PARAMETERS: {
208 typeMatch = NapiUtil::MatchParameters(env, parameters, { napi_object, napi_object, napi_function });
209 break;
210 }
211 default: {
212 break;
213 }
214 }
215 if (typeMatch) {
216 return NapiUtil::MatchObjectProperty(env, parameters[1],
217 {
218 { "slotId", napi_number },
219 { "mmsc", napi_string },
220 { "data", napi_string },
221 { "mmsConfig", napi_object },
222 });
223 }
224 return false;
225 }
226
GetMmsValueLength(napi_env env,napi_value param)227 static bool GetMmsValueLength(napi_env env, napi_value param)
228 {
229 size_t len = 0;
230 napi_status status = napi_get_value_string_utf8(env, param, nullptr, 0, &len);
231 if (status != napi_ok) {
232 TELEPHONY_LOGE("Get length failed");
233 return false;
234 }
235 return (len > 0) && (len < BUFF_LENGTH);
236 }
237
GetMmsNameProperty(napi_env env,napi_value param,MmsContext & context)238 static void GetMmsNameProperty(napi_env env, napi_value param, MmsContext &context)
239 {
240 napi_value slotIdValue = NapiUtil::GetNamedProperty(env, param, "slotId");
241 if (slotIdValue != nullptr) {
242 napi_get_value_int32(env, slotIdValue, &(context.slotId));
243 }
244 napi_value mmscValue = NapiUtil::GetNamedProperty(env, param, "mmsc");
245 if (mmscValue != nullptr && GetMmsValueLength(env, mmscValue)) {
246 char strChars[NORMAL_STRING_SIZE] = { 0 };
247 size_t strLength = 0;
248 napi_get_value_string_utf8(env, mmscValue, strChars, BUFF_LENGTH, &strLength);
249 std::string str8(strChars, strLength);
250 context.mmsc = NapiUtil::ToUtf16(str8);
251 }
252 napi_value dataValue = NapiUtil::GetNamedProperty(env, param, "data");
253 if (dataValue != nullptr && GetMmsValueLength(env, dataValue)) {
254 char strChars[NORMAL_STRING_SIZE] = { 0 };
255 size_t strLength = 0;
256 napi_get_value_string_utf8(env, dataValue, strChars, BUFF_LENGTH, &strLength);
257 std::string str8(strChars, strLength);
258 context.data = NapiUtil::ToUtf16(str8);
259 }
260 napi_value configValue = NapiUtil::GetNamedProperty(env, param, "mmsConfig");
261 if (configValue != nullptr) {
262 napi_value uaValue = NapiUtil::GetNamedProperty(env, configValue, "userAgent");
263 if (uaValue != nullptr && GetMmsValueLength(env, uaValue)) {
264 char strChars[NORMAL_STRING_SIZE] = { 0 };
265 size_t strLength = 0;
266 napi_get_value_string_utf8(env, uaValue, strChars, BUFF_LENGTH, &strLength);
267 std::string str8(strChars, strLength);
268 context.mmsConfig.userAgent = NapiUtil::ToUtf16(str8);
269 }
270 napi_value uaprofValue = NapiUtil::GetNamedProperty(env, configValue, "userAgentProfile");
271 if (uaprofValue != nullptr && GetMmsValueLength(env, uaprofValue)) {
272 char strChars[NORMAL_STRING_SIZE] = { 0 };
273 size_t strLength = 0;
274 napi_get_value_string_utf8(env, uaprofValue, strChars, BUFF_LENGTH, &strLength);
275 std::string str8(strChars, strLength);
276 context.mmsConfig.userAgentProfile = NapiUtil::ToUtf16(str8);
277 }
278 }
279 }
280
SendMms(napi_env env,napi_callback_info info)281 napi_value NapiSendRecvMms::SendMms(napi_env env, napi_callback_info info)
282 {
283 size_t parameterCount = THREE_PARAMETERS;
284 napi_value parameters[THREE_PARAMETERS] = { 0 };
285 napi_value thisVar = nullptr;
286 void *data = nullptr;
287
288 napi_get_cb_info(env, info, ¶meterCount, parameters, &thisVar, &data);
289 if (!MatchMmsParameters(env, parameters, parameterCount)) {
290 TELEPHONY_LOGE("parameter matching failed.");
291 NapiUtil::ThrowParameterError(env);
292 return nullptr;
293 }
294 auto context = std::make_unique<MmsContext>().release();
295 if (context == nullptr) {
296 TELEPHONY_LOGE("MmsContext is nullptr.");
297 NapiUtil::ThrowParameterError(env);
298 return nullptr;
299 }
300 if (!STORE_MMS_PDU_TO_FILE) {
301 g_datashareHelper = GetDataShareHelper(env, info);
302 }
303 GetMmsNameProperty(env, parameters[1], *context);
304 if (parameterCount == THREE_PARAMETERS) {
305 napi_create_reference(env, parameters[PARAMETERS_INDEX_TWO], DEFAULT_REF_COUNT, &context->callbackRef);
306 }
307 napi_value result = NapiUtil::HandleAsyncWork(env, context, "SendMms", NativeSendMms, SendMmsCallback);
308 return result;
309 }
310
WriteBufferToFile(const std::unique_ptr<char[]> & buff,uint32_t len,const std::string & strPathName)311 bool WriteBufferToFile(const std::unique_ptr<char[]> &buff, uint32_t len, const std::string &strPathName)
312 {
313 if (buff == nullptr) {
314 TELEPHONY_LOGE("buff nullptr");
315 return false;
316 }
317
318 char realPath[PATH_MAX] = { 0 };
319 if (strPathName.empty() || realpath(strPathName.c_str(), realPath) == nullptr) {
320 TELEPHONY_LOGE("path or realPath is nullptr");
321 return false;
322 }
323
324 FILE *pFile = fopen(realPath, "wb");
325 if (pFile == nullptr) {
326 TELEPHONY_LOGE("openFile Error");
327 return false;
328 }
329 uint32_t fileLen = fwrite(buff.get(), len, 1, pFile);
330 (void)fclose(pFile);
331 if (fileLen > 0) {
332 TELEPHONY_LOGI("write mms buffer to file success");
333 return true;
334 } else {
335 TELEPHONY_LOGI("write mms buffer to file error");
336 return false;
337 }
338 }
339
StoreMmsPduToFile(const std::string & fileName,const std::string & mmsPdu)340 bool StoreMmsPduToFile(const std::string &fileName, const std::string &mmsPdu)
341 {
342 uint32_t len = static_cast<uint32_t>(mmsPdu.size());
343 if (len > MMS_PDU_MAX_SIZE || len == 0) {
344 TELEPHONY_LOGE("MMS pdu length invalid");
345 return false;
346 }
347
348 std::unique_ptr<char[]> resultResponse = std::make_unique<char[]>(len);
349 if (memset_s(resultResponse.get(), len, 0x00, len) != EOK) {
350 TELEPHONY_LOGE("memset_s err");
351 return false;
352 }
353 if (memcpy_s(resultResponse.get(), len, &mmsPdu[0], len) != EOK) {
354 TELEPHONY_LOGE("memcpy_s error");
355 return false;
356 }
357
358 TELEPHONY_LOGI("len:%{public}d", len);
359 if (!WriteBufferToFile(std::move(resultResponse), len, fileName)) {
360 TELEPHONY_LOGE("write to file error");
361 return false;
362 }
363 return true;
364 }
365
GetMmsPduFromDataBase(NapiMmsPduHelper & helper)366 void GetMmsPduFromDataBase(NapiMmsPduHelper &helper) __attribute__((no_sanitize("cfi")))
367 {
368 NAPIMmsPdu mmsPduObj;
369 std::string mmsPdu = mmsPduObj.GetMmsPdu(helper);
370 if (mmsPdu.empty()) {
371 TELEPHONY_LOGE("from dataBase empty");
372 return;
373 }
374
375 mmsPduObj.DeleteMmsPdu(helper);
376 if (!StoreMmsPduToFile(helper.GetStoreFileName(), mmsPdu)) {
377 TELEPHONY_LOGE("store mmsPdu fail");
378 }
379 helper.NotifyAll();
380 }
381
DownloadExceptionCase(MmsContext & context,std::shared_ptr<OHOS::DataShare::DataShareHelper> g_datashareHelper)382 static bool DownloadExceptionCase(
383 MmsContext &context, std::shared_ptr<OHOS::DataShare::DataShareHelper> g_datashareHelper)
384 {
385 if (!TelephonyPermission::CheckCallerIsSystemApp()) {
386 TELEPHONY_LOGE("Non-system applications use system APIs!");
387 context.errorCode = TELEPHONY_ERR_ILLEGAL_USE_OF_SYSTEM_API;
388 context.resolved = false;
389 return false;
390 }
391 if (g_datashareHelper == nullptr) {
392 TELEPHONY_LOGE("g_datashareHelper is nullptr");
393 context.errorCode = TELEPHONY_ERR_LOCAL_PTR_NULL;
394 context.resolved = false;
395 return false;
396 }
397 std::string fileName = NapiUtil::ToUtf8(context.data);
398 char realPath[PATH_MAX] = { 0 };
399 if (fileName.empty() || realpath(fileName.c_str(), realPath) == nullptr) {
400 TELEPHONY_LOGE("path or realPath is nullptr");
401 context.errorCode = TELEPHONY_ERR_ARGUMENT_INVALID;
402 context.resolved = false;
403 return false;
404 }
405 FILE *pFile = fopen(realPath, "wb");
406 if (pFile == nullptr) {
407 TELEPHONY_LOGE("openFile Error");
408 context.errorCode = TELEPHONY_ERR_ARGUMENT_INVALID;
409 context.resolved = false;
410 return false;
411 }
412 (void)fclose(pFile);
413 return true;
414 }
415
UpdateReqCount()416 void UpdateReqCount()
417 {
418 std::unique_lock<std::mutex> lck(NapiSendRecvMms::countCtx_);
419 NapiSendRecvMms::reqCount_++;
420 TELEPHONY_LOGI("reqCount_:%{public}d", NapiSendRecvMms::reqCount_);
421 }
422
DecreaseReqCount()423 void DecreaseReqCount()
424 {
425 NapiSendRecvMms::reqCount_--;
426 if (NapiSendRecvMms::reqCount_ > 0) {
427 NapiSendRecvMms::waitFlag = true;
428 } else {
429 NapiSendRecvMms::waitFlag = false;
430 }
431 }
432
NativeDownloadMms(napi_env env,void * data)433 void NativeDownloadMms(napi_env env, void *data)
434 {
435 auto asyncContext = static_cast<MmsContext *>(data);
436 if (asyncContext == nullptr) {
437 TELEPHONY_LOGE("asyncContext nullptr");
438 return;
439 }
440 if (!DownloadExceptionCase(*asyncContext, g_datashareHelper)) {
441 TELEPHONY_LOGE("Exception case");
442 return;
443 }
444
445 TELEPHONY_LOGI("native download mms");
446 UpdateReqCount();
447 std::unique_lock<std::mutex> lck(NapiSendRecvMms::downloadCtx_);
448 if (NapiSendRecvMms::waitFlag) {
449 TELEPHONY_LOGI("down multiple mms at once wait");
450 std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_PDN_TOGGLE_TIME));
451 }
452 std::u16string dbUrls;
453 asyncContext->errorCode =
454 DelayedSingleton<SmsServiceManagerClient>::GetInstance()->DownloadMms(asyncContext->slotId, asyncContext->mmsc,
455 dbUrls, asyncContext->mmsConfig.userAgent, asyncContext->mmsConfig.userAgentProfile);
456 TELEPHONY_LOGI("NativeDownloadMms dbUrls:%{public}s", NapiUtil::ToUtf8(dbUrls).c_str());
457 if (asyncContext->errorCode == TELEPHONY_ERR_SUCCESS) {
458 asyncContext->resolved = true;
459 if (!STORE_MMS_PDU_TO_FILE) {
460 NapiMmsPduHelper helper;
461 helper.SetDataShareHelper(g_datashareHelper);
462 helper.SetDbUrl(NapiUtil::ToUtf8(dbUrls));
463 helper.SetStoreFileName(NapiUtil::ToUtf8(asyncContext->data));
464 if (!helper.Run(GetMmsPduFromDataBase, helper)) {
465 TELEPHONY_LOGE("StoreMmsPdu fail");
466 asyncContext->errorCode = TELEPHONY_ERR_LOCAL_PTR_NULL;
467 asyncContext->resolved = false;
468 return;
469 }
470 }
471 } else {
472 asyncContext->resolved = false;
473 }
474 DecreaseReqCount();
475 TELEPHONY_LOGI("NativeDownloadMms end resolved = %{public}d", asyncContext->resolved);
476 }
477
DownloadMmsCallback(napi_env env,napi_status status,void * data)478 void DownloadMmsCallback(napi_env env, napi_status status, void *data)
479 {
480 auto context = static_cast<MmsContext *>(data);
481 if (g_datashareHelper != nullptr && !NapiSendRecvMms::waitFlag) {
482 g_datashareHelper->Release();
483 }
484 if (context == nullptr) {
485 TELEPHONY_LOGE("SendMmsCallback context nullptr");
486 return;
487 }
488 napi_value callbackValue = nullptr;
489 if (context->resolved) {
490 napi_get_undefined(env, &callbackValue);
491 } else {
492 JsError error = NapiUtil::ConverErrorMessageWithPermissionForJs(
493 context->errorCode, "downloadMms", "ohos.permission.RECEIVE_MMS");
494 callbackValue = NapiUtil::CreateErrorMessage(env, error.errorMessage, error.errorCode);
495 }
496 NapiUtil::Handle1ValueCallback(env, context, callbackValue);
497 }
498
DownloadMms(napi_env env,napi_callback_info info)499 napi_value NapiSendRecvMms::DownloadMms(napi_env env, napi_callback_info info)
500 {
501 size_t parameterCount = THREE_PARAMETERS;
502 napi_value parameters[THREE_PARAMETERS] = { 0 };
503 napi_value thisVar = nullptr;
504 void *data = nullptr;
505
506 napi_get_cb_info(env, info, ¶meterCount, parameters, &thisVar, &data);
507 if (!MatchMmsParameters(env, parameters, parameterCount)) {
508 TELEPHONY_LOGE("DownloadMms parameter matching failed.");
509 NapiUtil::ThrowParameterError(env);
510 return nullptr;
511 }
512 auto context = std::make_unique<MmsContext>().release();
513 if (context == nullptr) {
514 TELEPHONY_LOGE("DownloadMms MmsContext is nullptr.");
515 NapiUtil::ThrowParameterError(env);
516 return nullptr;
517 }
518 if (!STORE_MMS_PDU_TO_FILE) {
519 g_datashareHelper = GetDataShareHelper(env, info);
520 }
521 GetMmsNameProperty(env, parameters[1], *context);
522 if (parameterCount == THREE_PARAMETERS) {
523 napi_create_reference(env, parameters[PARAMETERS_INDEX_TWO], DEFAULT_REF_COUNT, &context->callbackRef);
524 }
525 napi_value result = NapiUtil::HandleAsyncWork(env, context, "DownloadMms", NativeDownloadMms, DownloadMmsCallback);
526 return result;
527 }
528 } // namespace Telephony
529 } // namespace OHOS
530