1 /*
2  * Copyright (c) 2021-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 <optional>
17 #include "remove.h"
18 #include "ans_inner_errors.h"
19 
20 namespace OHOS {
21 namespace NotificationNapi {
22 const int REMOVE_MIN_PARA = 2;
23 const int REMOVE_OR_BUNDLE_MAX_PARA = 3;
24 
25 const int REMOVE_ALL_MAX_PARA = 2;
26 
27 const int REMOVE_BY_BUNDLE_AND_KEY_MIN_PARA = 3;
28 const int REMOVE_BY_BUNDLE_AND_KEY_MAX_PARA = 4;
29 
30 const int REMOVE_GROUP_BY_BUNDLE_MIN_PARA = 2;
31 const int REMOVE_GROUP_BY_BUNDLE_MAX_PARA = 3;
32 
ParseRemoveReason(const napi_env & env,const napi_value & value,RemoveParams & params)33 bool ParseRemoveReason(const napi_env &env, const napi_value &value, RemoveParams &params)
34 {
35     napi_valuetype valueType = napi_undefined;
36     NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), false);
37     if (valueType != napi_number) {
38         ANS_LOGW("RemoveReason valueType unexpected.");
39         return false;
40     }
41     int32_t removeReason = 0;
42     napi_get_value_int32(env, value, &removeReason);
43     if (!Common::IsValidRemoveReason(removeReason)) {
44         ANS_LOGW("RemoveReason value unexpected.");
45         return false;
46     }
47     params.removeReason = removeReason;
48     return true;
49 }
50 
ParseCallbackFunc(const napi_env & env,const napi_value & value,RemoveParams & params)51 bool ParseCallbackFunc(const napi_env &env, const napi_value &value,
52     RemoveParams &params)
53 {
54     napi_valuetype valueType = napi_undefined;
55     NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), false);
56     if (valueType != napi_function) {
57         ANS_LOGW("Callback is not function excute promise.");
58         return true;
59     }
60     napi_create_reference(env, value, 1, &params.callback);
61     return true;
62 }
63 
ParseHashcodeTypeParams(const napi_env & env,napi_value * argv,size_t argc,napi_valuetype valueType,RemoveParams & params)64 bool ParseHashcodeTypeParams(
65     const napi_env &env, napi_value* argv, size_t argc, napi_valuetype valueType, RemoveParams &params)
66 {
67     // argv[0]: hashCode
68     bool isArray = false;
69     napi_is_array(env, argv[PARAM0], &isArray);
70     if (isArray) {
71         std::vector<std::string> hashcodes;
72         auto retValue = Common::GetHashCodes(env, argv[PARAM0], hashcodes);
73         if (retValue == nullptr) {
74             ANS_LOGW("GetHashCodes failed.");
75             return false;
76         }
77         params.hashcodes = hashcodes;
78     } else if (valueType == napi_string) {
79         size_t strLen = 0;
80         char str[STR_MAX_SIZE] = {0};
81         NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, argv[PARAM0], str, STR_MAX_SIZE - 1, &strLen), false);
82         params.hashcode = str;
83     } else if (valueType == napi_number) {
84         int64_t number = 0;
85         NAPI_CALL_BASE(env, napi_get_value_int64(env, argv[PARAM0], &number), false);
86         params.hashcode = std::to_string(number);
87     } else {
88         bool result = false;
89         NAPI_CALL_BASE(env, napi_get_value_bool(env, argv[PARAM0], &result), false);
90         params.hashcode = std::to_string(result);
91     }
92     // argv[1]:removeReason
93     if (!ParseRemoveReason(env, argv[PARAM1], params)) {
94         return false;
95     }
96     // argv[2]:callback
97     if (argc >= REMOVE_OR_BUNDLE_MAX_PARA) {
98         if (!ParseCallbackFunc(env, argv[PARAM2], params)) {
99             return false;
100         }
101     }
102     return true;
103 }
104 
ParseBundleOptionTypeParams(const napi_env & env,napi_value * argv,size_t argc,RemoveParams & params)105 bool ParseBundleOptionTypeParams(const napi_env &env, napi_value* argv, size_t argc, RemoveParams &params)
106 {
107     if (argc < REMOVE_BY_BUNDLE_AND_KEY_MIN_PARA) {
108         ANS_LOGW("Wrong number of arguments.");
109         return false;
110     }
111     BundleAndKeyInfo bundleInfo {};
112     // argv[0]: BundleOption  argv[1]: NotificationKey
113     if (Common::GetBundleOption(env, argv[PARAM0], bundleInfo.option) == nullptr ||
114         Common::GetNotificationKey(env, argv[PARAM1], bundleInfo.key) == nullptr) {
115         ANS_LOGE("GetBundleOption failed.");
116         return false;
117     }
118     params.bundleAndKeyInfo = bundleInfo;
119     // argv[2]:removeReason
120     if (!ParseRemoveReason(env, argv[PARAM2], params)) {
121         return false;
122     }
123     // argv[3]:callback
124     if (argc >= REMOVE_BY_BUNDLE_AND_KEY_MAX_PARA) {
125         if (!ParseCallbackFunc(env, argv[PARAM3], params)) {
126             return false;
127         }
128     }
129     return true;
130 }
131 
ParseParameters(const napi_env & env,const napi_callback_info & info,RemoveParams & params)132 bool ParseParameters(const napi_env &env, const napi_callback_info &info, RemoveParams &params)
133 {
134     ANS_LOGD("enter");
135     size_t argc = REMOVE_BY_BUNDLE_AND_KEY_MAX_PARA;
136     napi_value argv[REMOVE_BY_BUNDLE_AND_KEY_MAX_PARA] = {nullptr};
137     napi_value thisVar = nullptr;
138     NAPI_CALL_BASE(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL), false);
139     if (argc < REMOVE_MIN_PARA) {
140         ANS_LOGW("Wrong number of arguments.");
141         Common::NapiThrow(env, ERROR_PARAM_INVALID, MANDATORY_PARAMETER_ARE_LEFT_UNSPECIFIED);
142         return false;
143     }
144     bool isArray = false;
145     napi_is_array(env, argv[PARAM0], &isArray);
146     napi_valuetype valueType = napi_undefined;
147     NAPI_CALL_BASE(env, napi_typeof(env, argv[PARAM0], &valueType), false);
148     if ((valueType != napi_string) && (valueType != napi_object) &&
149         (valueType != napi_number) && (valueType != napi_boolean) && !isArray) {
150         ANS_LOGW("Wrong argument type. String or object expected.");
151         std::string msg = "Incorrect parameter types.The type of param must be object or string.";
152         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
153         return false;
154     }
155     if ((valueType == napi_string) || (valueType == napi_number) || (valueType == napi_boolean) || isArray) {
156         return ParseHashcodeTypeParams(env, argv, argc, valueType, params);
157     }
158     return ParseBundleOptionTypeParams(env, argv, argc, params);
159 }
160 
ParseParametersByRemoveAll(const napi_env & env,const napi_callback_info & info,RemoveParams & params)161 napi_value ParseParametersByRemoveAll(const napi_env &env, const napi_callback_info &info, RemoveParams &params)
162 {
163     ANS_LOGD("enter");
164 
165     size_t argc = REMOVE_ALL_MAX_PARA;
166     napi_value argv[REMOVE_ALL_MAX_PARA] = {nullptr};
167     napi_value thisVar = nullptr;
168     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
169 
170     if (argc == 0) {
171         return Common::NapiGetNull(env);
172     }
173 
174     // argv[0]: bundle / userId / callback
175     napi_valuetype valuetype = napi_undefined;
176     NAPI_CALL(env, napi_typeof(env, argv[PARAM0], &valuetype));
177     if ((valuetype != napi_object) && (valuetype != napi_number) && (valuetype != napi_function)) {
178         ANS_LOGW("Wrong argument type. Function or object expected. Excute promise.");
179         return Common::NapiGetNull(env);
180     }
181     if (valuetype == napi_object) {
182         BundleAndKeyInfo bundleandKeyInfo {};
183         auto retValue = Common::GetBundleOption(env, argv[PARAM0], bundleandKeyInfo.option);
184         if (retValue == nullptr) {
185             ANS_LOGW("GetBundleOption failed.");
186             Common::NapiThrow(env, ERROR_PARAM_INVALID, PARAMETER_VERIFICATION_FAILED);
187             return nullptr;
188         }
189         params.bundleAndKeyInfo = bundleandKeyInfo;
190     } else if (valuetype == napi_number) {
191         NAPI_CALL(env, napi_get_value_int32(env, argv[PARAM0], &params.userId));
192         params.hasUserId = true;
193     } else {
194         napi_create_reference(env, argv[PARAM0], 1, &params.callback);
195     }
196 
197     // argv[1]:callback
198     if (argc >= REMOVE_ALL_MAX_PARA) {
199         NAPI_CALL(env, napi_typeof(env, argv[PARAM1], &valuetype));
200         if (valuetype != napi_function) {
201             ANS_LOGW("Callback is not function excute promise.");
202             return Common::NapiGetNull(env);
203         }
204         napi_create_reference(env, argv[PARAM1], 1, &params.callback);
205     }
206 
207     return Common::NapiGetNull(env);
208 }
209 
ParseParameters(const napi_env & env,const napi_callback_info & info,RemoveParamsGroupByBundle & params)210 napi_value ParseParameters(
211     const napi_env &env, const napi_callback_info &info, RemoveParamsGroupByBundle &params)
212 {
213     ANS_LOGD("enter");
214 
215     size_t argc = REMOVE_GROUP_BY_BUNDLE_MAX_PARA;
216     napi_value argv[REMOVE_GROUP_BY_BUNDLE_MAX_PARA] = {nullptr};
217     napi_value thisVar = nullptr;
218     napi_valuetype valuetype = napi_undefined;
219     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
220     if (argc < REMOVE_GROUP_BY_BUNDLE_MIN_PARA) {
221         ANS_LOGW("Error number of arguments.");
222         Common::NapiThrow(env, ERROR_PARAM_INVALID, MANDATORY_PARAMETER_ARE_LEFT_UNSPECIFIED);
223         return nullptr;
224     }
225 
226     // argv[0]: bundle
227     NAPI_CALL(env, napi_typeof(env, argv[PARAM0], &valuetype));
228     if (valuetype != napi_object) {
229         ANS_LOGW("Valuetype is not object.");
230         std::string msg = "Incorrect parameter types.The type of param must be object.";
231         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
232         return nullptr;
233     }
234     auto retValue = Common::GetBundleOption(env, argv[PARAM0], params.option);
235     if (retValue == nullptr) {
236         ANS_LOGE("GetBundleOption failed.");
237         Common::NapiThrow(env, ERROR_PARAM_INVALID, PARAMETER_VERIFICATION_FAILED);
238         return nullptr;
239     }
240 
241     // argv[1]: groupName: string
242     NAPI_CALL(env, napi_typeof(env, argv[PARAM1], &valuetype));
243     if (valuetype != napi_string && valuetype != napi_number && valuetype != napi_boolean) {
244         ANS_LOGW("Error argument type. String number boolean anticipate.");
245         std::string msg = "Incorrect parameter types.The type of param must be string or number or boolean.";
246         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
247         return nullptr;
248     }
249     if (valuetype == napi_string) {
250         char str[STR_MAX_SIZE] = {0};
251         size_t strLen = 0;
252         NAPI_CALL(env, napi_get_value_string_utf8(env, argv[PARAM1], str, STR_MAX_SIZE - 1, &strLen));
253         params.groupName = str;
254     } else if (valuetype == napi_number) {
255         ANS_LOGD("valuetype is number.");
256         int64_t number = 0;
257         NAPI_CALL(env, napi_get_value_int64(env, argv[PARAM1], &number));
258         params.groupName = std::to_string(number);
259     } else {
260         ANS_LOGD("valuetype is other types.");
261         bool result = false;
262         NAPI_CALL(env, napi_get_value_bool(env, argv[PARAM1], &result));
263         params.groupName = std::to_string(result);
264     }
265     // argv[2]:callback
266     if (argc >= REMOVE_GROUP_BY_BUNDLE_MAX_PARA) {
267         NAPI_CALL(env, napi_typeof(env, argv[PARAM2], &valuetype));
268         if (valuetype != napi_function) {
269             ANS_LOGW("Callback is not function.");
270             return Common::NapiGetNull(env);
271         }
272         napi_create_reference(env, argv[PARAM2], 1, &params.callback);
273     }
274     return Common::NapiGetNull(env);
275 }
276 
RemoveExecuteCallback(napi_env env,void * data)277 void RemoveExecuteCallback(napi_env env, void *data)
278 {
279     ANS_LOGI("Remove napi_create_async_work start");
280     if (!data) {
281         ANS_LOGE("Invalid async callback data");
282         return;
283     }
284     auto removeInfo = static_cast<AsyncCallbackInfoRemove *>(data);
285     if (removeInfo) {
286         if (removeInfo->params.hashcode.has_value()) {
287             removeInfo->info.errorCode = NotificationHelper::RemoveNotification(removeInfo->params.hashcode.value(),
288                 removeInfo->params.removeReason);
289         } else if (removeInfo->params.bundleAndKeyInfo.has_value()) {
290             auto &infos = removeInfo->params.bundleAndKeyInfo.value();
291             removeInfo->info.errorCode = NotificationHelper::RemoveNotification(infos.option,
292                 infos.key.id, infos.key.label, removeInfo->params.removeReason);
293         }
294     }
295 }
296 
RemoveCompleteCallback(napi_env env,napi_status status,void * data)297 void RemoveCompleteCallback(napi_env env, napi_status status, void *data)
298 {
299     ANS_LOGI("Remove napi_create_async_work end");
300     if (!data) {
301         ANS_LOGE("Invalid async callback data");
302         return;
303     }
304     auto removeInfo = static_cast<AsyncCallbackInfoRemove *>(data);
305     if (removeInfo) {
306         Common::ReturnCallbackPromise(env, removeInfo->info, Common::NapiGetNull(env));
307         if (removeInfo->info.callback != nullptr) {
308             napi_delete_reference(env, removeInfo->info.callback);
309         }
310         napi_delete_async_work(env, removeInfo->asyncWork);
311         delete removeInfo;
312         removeInfo = nullptr;
313     }
314 }
315 
Remove(napi_env env,napi_callback_info info)316 napi_value Remove(napi_env env, napi_callback_info info)
317 {
318     ANS_LOGD("enter");
319     RemoveParams params {};
320     if (!ParseParameters(env, info, params)) {
321         return Common::NapiGetUndefined(env);
322     }
323     auto removeInfo = new (std::nothrow) AsyncCallbackInfoRemove {.env = env, .asyncWork = nullptr, .params = params};
324     if (!removeInfo) {
325         ANS_LOGD("RemoveInfo is nullptr.");
326         return Common::JSParaError(env, params.callback);
327     }
328     napi_value promise = nullptr;
329     Common::PaddingCallbackPromiseInfo(env, params.callback, removeInfo->info, promise);
330 
331     napi_value resourceName = nullptr;
332     napi_create_string_latin1(env, "remove", NAPI_AUTO_LENGTH, &resourceName);
333     // Asynchronous function call
334     napi_create_async_work(env, nullptr, resourceName, RemoveExecuteCallback, RemoveCompleteCallback,
335         (void *)removeInfo, &removeInfo->asyncWork);
336     NAPI_CALL(env, napi_queue_async_work_with_qos(env, removeInfo->asyncWork, napi_qos_user_initiated));
337     if (removeInfo->info.isCallback) {
338         return Common::NapiGetNull(env);
339     } else {
340         return promise;
341     }
342 }
343 
RemoveAll(napi_env env,napi_callback_info info)344 napi_value RemoveAll(napi_env env, napi_callback_info info)
345 {
346     ANS_LOGD("enter");
347 
348     RemoveParams params {};
349     if (ParseParametersByRemoveAll(env, info, params) == nullptr) {
350         return Common::NapiGetUndefined(env);
351     }
352 
353     AsyncCallbackInfoRemove *asynccallbackinfo =
354         new (std::nothrow) AsyncCallbackInfoRemove {.env = env, .asyncWork = nullptr, .params = params};
355     if (!asynccallbackinfo) {
356         ANS_LOGD("Asynccallbackinfo is nullptr.");
357         return Common::JSParaError(env, params.callback);
358     }
359     napi_value promise = nullptr;
360     Common::PaddingCallbackPromiseInfo(env, params.callback, asynccallbackinfo->info, promise);
361 
362     ANS_LOGD("Create removeAll string.");
363     napi_value resourceName = nullptr;
364     napi_create_string_latin1(env, "removeAll", NAPI_AUTO_LENGTH, &resourceName);
365     // Asynchronous function call
366     napi_create_async_work(env,
367         nullptr,
368         resourceName,
369         [](napi_env env, void *data) {
370             ANS_LOGI("RemoveAll napi_create_async_work start");
371             AsyncCallbackInfoRemove *asynccallbackinfo = static_cast<AsyncCallbackInfoRemove *>(data);
372             if (asynccallbackinfo) {
373                 if (asynccallbackinfo->params.bundleAndKeyInfo.has_value()) {
374                     auto &infos = asynccallbackinfo->params.bundleAndKeyInfo.value();
375                     asynccallbackinfo->info.errorCode = NotificationHelper::RemoveAllNotifications(infos.option);
376                 } else if (asynccallbackinfo->params.hasUserId) {
377                     ANS_LOGD("hasUserId is true.");
378                     asynccallbackinfo->info.errorCode = NotificationHelper::RemoveNotifications(
379                         asynccallbackinfo->params.userId);
380                 } else {
381                     asynccallbackinfo->info.errorCode = NotificationHelper::RemoveNotifications();
382                 }
383             }
384         },
385         [](napi_env env, napi_status status, void *data) {
386             ANS_LOGI("RemoveAll napi_create_async_work end");
387             AsyncCallbackInfoRemove *asynccallbackinfo = static_cast<AsyncCallbackInfoRemove *>(data);
388             if (asynccallbackinfo) {
389                 Common::ReturnCallbackPromise(env, asynccallbackinfo->info, Common::NapiGetNull(env));
390                 if (asynccallbackinfo->info.callback != nullptr) {
391                     ANS_LOGD("Delete removeAll callback reference.");
392                     napi_delete_reference(env, asynccallbackinfo->info.callback);
393                 }
394                 napi_delete_async_work(env, asynccallbackinfo->asyncWork);
395                 delete asynccallbackinfo;
396                 asynccallbackinfo = nullptr;
397             }
398             ANS_LOGD("RemoveAll work complete end.");
399         },
400         (void *)asynccallbackinfo,
401         &asynccallbackinfo->asyncWork);
402 
403     napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated);
404 
405     if (asynccallbackinfo->info.isCallback) {
406         ANS_LOGD("removeAll callback is nullptr.");
407         return Common::NapiGetNull(env);
408     } else {
409         return promise;
410     }
411 }
412 
AsyncCompleteCallbackRemoveGroupByBundle(napi_env env,napi_status status,void * data)413 void AsyncCompleteCallbackRemoveGroupByBundle(napi_env env, napi_status status, void *data)
414 {
415     ANS_LOGD("enter");
416     if (!data) {
417         ANS_LOGE("Invalid async callback data");
418         return;
419     }
420     AsyncCallbackInfoRemoveGroupByBundle *asynccallbackinfo = static_cast<AsyncCallbackInfoRemoveGroupByBundle *>(data);
421     if (asynccallbackinfo) {
422         Common::ReturnCallbackPromise(env, asynccallbackinfo->info, Common::NapiGetNull(env));
423         if (asynccallbackinfo->info.callback != nullptr) {
424             napi_delete_reference(env, asynccallbackinfo->info.callback);
425         }
426         napi_delete_async_work(env, asynccallbackinfo->asyncWork);
427         delete asynccallbackinfo;
428         asynccallbackinfo = nullptr;
429     }
430 }
431 
RemoveGroupByBundle(napi_env env,napi_callback_info info)432 napi_value RemoveGroupByBundle(napi_env env, napi_callback_info info)
433 {
434     ANS_LOGD("enter");
435 
436     RemoveParamsGroupByBundle params {};
437     if (ParseParameters(env, info, params) == nullptr) {
438         return Common::NapiGetUndefined(env);
439     }
440 
441     AsyncCallbackInfoRemoveGroupByBundle *asynccallbackinfo =
442         new (std::nothrow) AsyncCallbackInfoRemoveGroupByBundle {.env = env, .asyncWork = nullptr, .params = params};
443     if (!asynccallbackinfo) {
444         ANS_LOGD("Create asynccallbackinfo failed.");
445         return Common::JSParaError(env, params.callback);
446     }
447     napi_value promise = nullptr;
448     Common::PaddingCallbackPromiseInfo(env, params.callback, asynccallbackinfo->info, promise);
449 
450     ANS_LOGD("Create removeGroupByBundle string.");
451     napi_value resourceName = nullptr;
452     napi_create_string_latin1(env, "removeGroupByBundle", NAPI_AUTO_LENGTH, &resourceName);
453     // Asynchronous function call
454     napi_create_async_work(env,
455         nullptr,
456         resourceName,
457         [](napi_env env, void *data) {
458             ANS_LOGI("RemoveGroupByBundle napi_create_async_work start");
459             AsyncCallbackInfoRemoveGroupByBundle *asynccallbackinfo =
460                 static_cast<AsyncCallbackInfoRemoveGroupByBundle *>(data);
461             if (asynccallbackinfo) {
462                 ANS_LOGI("option.bundle = %{public}s, option.uid = %{public}d, groupName = %{public}s",
463                     asynccallbackinfo->params.option.GetBundleName().c_str(),
464                     asynccallbackinfo->params.option.GetUid(),
465                     asynccallbackinfo->params.groupName.c_str());
466                 asynccallbackinfo->info.errorCode = NotificationHelper::RemoveGroupByBundle(
467                     asynccallbackinfo->params.option, asynccallbackinfo->params.groupName);
468             }
469         },
470         AsyncCompleteCallbackRemoveGroupByBundle,
471         (void *)asynccallbackinfo,
472         &asynccallbackinfo->asyncWork);
473 
474     napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated);
475 
476     if (asynccallbackinfo->info.isCallback) {
477         ANS_LOGD("removeGroupByBundle callback is nullptr.");
478         return Common::NapiGetNull(env);
479     } else {
480         return promise;
481     }
482 }
483 }  // namespace NotificationNapi
484 }  // namespace OHOS