1/*
2 * Copyright (c) 2021 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 "native_parameters_js.h"
16#include "securec.h"
17
18static constexpr int MAX_LENGTH = 128;
19static constexpr int ARGC_NUMBER = 2;
20static constexpr int ARGC_THREE_NUMBER = 3;
21static constexpr int BUF_LENGTH = 256;
22
23using StorageAsyncContext = struct StorageAsyncContext {
24    napi_env env = nullptr;
25    napi_async_work work = nullptr;
26
27    char key[BUF_LENGTH] = { 0 };
28    size_t keyLen = 0;
29    char value[BUF_LENGTH] = { 0 };
30    size_t valueLen = 0;
31    napi_deferred deferred = nullptr;
32    napi_ref callbackRef = nullptr;
33
34    int status = -1;
35    std::string getValue;
36};
37
38using StorageAsyncContextPtr = StorageAsyncContext *;
39
40static void SetCallbackWork(napi_env env, StorageAsyncContextPtr asyncContext)
41{
42    napi_value resource = nullptr;
43    napi_create_string_utf8(env, "JSStartupSet", NAPI_AUTO_LENGTH, &resource);
44    napi_create_async_work(
45        env, nullptr, resource,
46        [](napi_env env, void *data) {
47            StorageAsyncContext *asyncContext = reinterpret_cast<StorageAsyncContext *>(data);
48            asyncContext->status = SetParameter(asyncContext->key, asyncContext->value);
49            PARAM_JS_LOGV("JSApp set::asyncContext-> status = %d, asyncContext->key = %s, asyncContext->value = %s.",
50                asyncContext->status, asyncContext->key, asyncContext->value);
51        },
52        [](napi_env env, napi_status status, void *data) {
53            StorageAsyncContext *asyncContext = reinterpret_cast<StorageAsyncContext *>(data);
54            napi_value result[ARGC_NUMBER] = { 0 };
55            if (asyncContext->status == 0) {
56                napi_get_undefined(env, &result[0]);
57                napi_get_undefined(env, &result[1]);
58            } else {
59                napi_value value = nullptr;
60                napi_create_object(env, &result[0]);
61                napi_create_int32(env, asyncContext->status, &value);
62                napi_set_named_property(env, result[0], "code", value);
63                napi_get_undefined(env, &result[1]);
64            }
65
66            if (asyncContext->deferred) {
67                if (asyncContext->status == 0) {
68                    napi_resolve_deferred(env, asyncContext->deferred, result[1]);
69                } else {
70                    napi_reject_deferred(env, asyncContext->deferred, result[0]);
71                }
72            } else {
73                napi_value callback = nullptr;
74                napi_value callResult = nullptr;
75                napi_get_reference_value(env, asyncContext->callbackRef, &callback);
76                napi_call_function(env, nullptr, callback, ARGC_NUMBER, result, &callResult);
77                napi_delete_reference(env, asyncContext->callbackRef);
78            }
79            napi_delete_async_work(env, asyncContext->work);
80            delete asyncContext;
81        },
82        reinterpret_cast<void *>(asyncContext), &asyncContext->work);
83    napi_queue_async_work(env, asyncContext->work);
84}
85
86static napi_value Set(napi_env env, napi_callback_info info)
87{
88    size_t argc = ARGC_THREE_NUMBER;
89    napi_value argv[ARGC_THREE_NUMBER] = { nullptr };
90    napi_value thisVar = nullptr;
91    void *data = nullptr;
92    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
93    NAPI_ASSERT(env, argc >= ARGC_NUMBER, "requires 2 parameter");
94    StorageAsyncContextPtr asyncContext = new StorageAsyncContext();
95    asyncContext->env = env;
96    for (size_t i = 0; i < argc; i++) {
97        napi_valuetype valueType = napi_null;
98        napi_typeof(env, argv[i], &valueType);
99
100        if (i == 0 && valueType == napi_string) {
101            napi_get_value_string_utf8(env, argv[i], asyncContext->key,
102                BUF_LENGTH - 1, &asyncContext->keyLen);
103        } else if (i == 1 && valueType == napi_string) {
104            napi_get_value_string_utf8(env, argv[i], asyncContext->value,
105                BUF_LENGTH - 1, &asyncContext->valueLen);
106        } else if (i == ARGC_NUMBER && valueType == napi_function) {
107            napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
108        } else {
109            delete asyncContext;
110            NAPI_ASSERT(env, false, "type mismatch");
111        }
112    }
113
114    napi_value result = nullptr;
115    if (asyncContext->callbackRef == nullptr) {
116        napi_create_promise(env, &asyncContext->deferred, &result);
117    } else {
118        napi_get_undefined(env, &result);
119    }
120
121    SetCallbackWork(env, asyncContext);
122    return result;
123}
124
125static napi_value SetSync(napi_env env, napi_callback_info info)
126{
127    size_t argc = ARGC_NUMBER;
128    napi_value args[ARGC_NUMBER] = { nullptr };
129    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
130    NAPI_ASSERT(env, argc == ARGC_NUMBER, "Wrong number of arguments");
131    napi_valuetype valuetype0 = napi_null;
132    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
133    napi_valuetype valuetype1 = napi_null;
134    NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
135    NAPI_ASSERT(env, valuetype0 == napi_string && valuetype1 == napi_string, "Wrong argument type. string expected.");
136
137    char keyBuf[BUF_LENGTH] = { 0 };
138    size_t keySize = 0;
139    NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], keyBuf, BUF_LENGTH - 1, &keySize));
140    if (keySize >= MAX_LENGTH) {
141        return nullptr;
142    }
143
144    char valueBuf[BUF_LENGTH] = { 0 };
145    size_t valueSize = 0;
146    NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], valueBuf, BUF_LENGTH - 1, &valueSize));
147    if (valueSize >= MAX_LENGTH) {
148        return nullptr;
149    }
150
151    std::string keyStr = keyBuf;
152    std::string valueStr = valueBuf;
153    int setResult = SetParameter(keyStr.c_str(), valueStr.c_str());
154    PARAM_JS_LOGV("JSApp SetSync::setResult = %d, input keyBuf = %s.", setResult, keyBuf);
155
156    napi_value napiValue = nullptr;
157    if (setResult != 0) { // set failed
158        std::stringstream ss;
159        ss << "set: " << keyStr << " failed, error code: " << setResult;
160        napi_throw_error((env), nullptr, ss.str().c_str());
161    } else {
162        napi_get_undefined(env, &napiValue);
163    }
164    return napiValue;
165}
166
167static napi_value GetSync(napi_env env, napi_callback_info info)
168{
169    size_t argc = ARGC_NUMBER;
170    napi_value args[ARGC_NUMBER] = { nullptr };
171    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
172    NAPI_ASSERT(env, argc == 1 || argc == ARGC_NUMBER, "Wrong number of arguments");
173    napi_valuetype valuetype0 = napi_null;
174    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
175    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");
176
177    napi_valuetype valuetype1 = napi_null;
178    if (argc == ARGC_NUMBER) {
179        NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
180        NAPI_ASSERT(env, (valuetype1 == napi_string) || (valuetype1 == napi_undefined),
181            "Wrong argument type. string expected.");
182    }
183
184    char keyBuf[BUF_LENGTH] = { 0 };
185    size_t keySize = 0;
186    NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], keyBuf, BUF_LENGTH - 1, &keySize));
187    if (keySize >= MAX_LENGTH) {
188        return nullptr;
189    }
190
191    std::string keyStr = keyBuf;
192    std::string valueStr = "";
193    std::string getValue = "";
194    if (argc == ARGC_NUMBER) {
195        char valueBuf[BUF_LENGTH] = { 0 };
196        size_t valueSize = 0;
197        if (valuetype1 == napi_undefined) {
198            valueStr = "";
199        } else {
200            NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], valueBuf, BUF_LENGTH - 1, &valueSize));
201            if (valueSize >= MAX_LENGTH) {
202                return nullptr;
203            }
204            valueStr = valueBuf;
205        }
206    }
207    int ret = OHOS::system::GetStringParameter(keyStr, getValue, valueStr);
208    PARAM_JS_LOGV("JSApp GetSync::getValue = %s, input keyStr = %s.", getValue.c_str(), keyBuf);
209
210    napi_value napiValue = nullptr;
211    if (ret == 0) {
212        NAPI_CALL(env, napi_create_string_utf8(env, getValue.c_str(), strlen(getValue.c_str()), &napiValue));
213    }
214    return napiValue;
215}
216
217static void GetCallbackWork(napi_env env, StorageAsyncContextPtr asyncContext)
218{
219    napi_value resource = nullptr;
220    napi_create_string_utf8(env, "JSStartupGet", NAPI_AUTO_LENGTH, &resource);
221    napi_create_async_work(
222        env, nullptr, resource,
223        [](napi_env env, void *data) {
224            StorageAsyncContext *asyncContext = reinterpret_cast<StorageAsyncContext *>(data);
225            asyncContext->status =
226                OHOS::system::GetStringParameter(asyncContext->key, asyncContext->getValue, asyncContext->value);
227            PARAM_JS_LOGV("JSApp get status = %d, asyncContext->getValue = %s, asyncContext->key = %s, value = %s.",
228                asyncContext->status, asyncContext->getValue.c_str(), asyncContext->key, asyncContext->value);
229        },
230        [](napi_env env, napi_status status, void *data) {
231            StorageAsyncContext *asyncContext = reinterpret_cast<StorageAsyncContext *>(data);
232            napi_value result[ARGC_NUMBER] = { 0 };
233            if (asyncContext->status == 0) {
234                napi_get_undefined(env, &result[0]);
235                napi_create_string_utf8(env, asyncContext->getValue.c_str(), strlen(asyncContext->getValue.c_str()),
236                    &result[1]);
237            } else {
238                napi_value message = nullptr;
239                napi_create_object(env, &result[0]);
240                napi_create_int32(env, asyncContext->status, &message);
241                napi_set_named_property(env, result[0], "code", message);
242                napi_get_undefined(env, &result[1]);
243            }
244
245            if (asyncContext->deferred) {
246                if (asyncContext->status == 0) {
247                    napi_resolve_deferred(env, asyncContext->deferred, result[1]);
248                } else {
249                    napi_reject_deferred(env, asyncContext->deferred, result[0]);
250                }
251            } else {
252                napi_value callback = nullptr;
253                napi_value callResult = nullptr;
254                napi_get_reference_value(env, asyncContext->callbackRef, &callback);
255                napi_call_function(env, nullptr, callback, ARGC_NUMBER, result, &callResult);
256                napi_delete_reference(env, asyncContext->callbackRef);
257            }
258            napi_delete_async_work(env, asyncContext->work);
259            delete asyncContext;
260        },
261        reinterpret_cast<void *>(asyncContext), &asyncContext->work);
262    napi_queue_async_work(env, asyncContext->work);
263}
264
265static napi_value Get(napi_env env, napi_callback_info info)
266{
267    size_t argc = ARGC_THREE_NUMBER;
268    napi_value argv[ARGC_THREE_NUMBER] = { nullptr };
269    napi_value thisVar = nullptr;
270    void *data = nullptr;
271    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
272    NAPI_ASSERT(env, argc >= 1, "requires 1 parameter");
273    StorageAsyncContextPtr asyncContext = new StorageAsyncContext();
274    asyncContext->env = env;
275    for (size_t i = 0; i < argc; i++) {
276        napi_valuetype valueType = napi_null;
277        napi_typeof(env, argv[i], &valueType);
278
279        if (i == 0 && valueType == napi_string) {
280            napi_get_value_string_utf8(env, argv[i], asyncContext->key,
281                BUF_LENGTH - 1, &asyncContext->keyLen);
282        } else if (i == 1 && valueType == napi_string) {
283            napi_get_value_string_utf8(env, argv[i], asyncContext->value,
284                BUF_LENGTH - 1, &asyncContext->valueLen);
285        } else if (i == 1 && valueType == napi_undefined) {
286            strcpy_s(asyncContext->value, BUF_LENGTH, "\0");
287        } else if (i == 1 && valueType == napi_function) {
288            napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
289            break;
290        } else if (i == ARGC_NUMBER && valueType == napi_function) {
291            napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
292        } else {
293            delete asyncContext;
294            NAPI_ASSERT(env, false, "type mismatch");
295        }
296    }
297
298    napi_value result = nullptr;
299    if (asyncContext->callbackRef == nullptr) {
300        napi_create_promise(env, &asyncContext->deferred, &result);
301    } else {
302        napi_get_undefined(env, &result);
303    }
304
305    GetCallbackWork(env, asyncContext);
306    return result;
307}
308
309EXTERN_C_START
310/*
311 * Module init
312 */
313static napi_value Init(napi_env env, napi_value exports)
314{
315    /*
316     * Attribute definition
317     */
318    napi_property_descriptor desc[] = {
319        DECLARE_NAPI_FUNCTION("set", Set),
320        DECLARE_NAPI_FUNCTION("setSync", SetSync),
321        DECLARE_NAPI_FUNCTION("get", Get),
322        DECLARE_NAPI_FUNCTION("getSync", GetSync),
323        DECLARE_NAPI_FUNCTION("wait", ParamWait),
324        DECLARE_NAPI_FUNCTION("getWatcher", GetWatcher)
325    };
326    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(napi_property_descriptor), desc));
327    return RegisterWatcher(env, exports);
328}
329EXTERN_C_END
330
331/*
332 * Module definition
333 */
334static napi_module _module = {
335    .nm_version = 1,
336    .nm_flags = 0,
337    .nm_filename = NULL,
338    .nm_register_func = Init,
339    .nm_modname = "systemparameter",
340    .nm_priv = ((void *)0),
341    .reserved = { 0 }
342};
343
344/*
345 * Module registration function
346 */
347extern "C" __attribute__((constructor)) void RegisterModule(void)
348{
349    napi_module_register(&_module);
350}
351