1 /*
2 * Copyright (c) 2022 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_system_storage.h"
17
18 #include <string>
19
20 #include "js_ability.h"
21 #include "js_common_utils.h"
22 #include "preferences_errno.h"
23 #include "preferences_helper.h"
24
25 using namespace OHOS::NativePreferences;
26
27 namespace OHOS {
28 namespace PreferencesJsKit {
29 struct AsyncContext {
30 std::string key;
31 std::string def;
32 std::string val;
33 std::string prefName;
34 napi_ref success;
35 napi_ref fail;
36 napi_ref complete;
37 napi_deferred deferred = nullptr;
38 int32_t output = E_ERROR;
39 napi_async_work request;
40 };
41
42 static constexpr uint32_t MAX_KEY_LENGTH = 32;
43
44 static constexpr uint32_t MAX_VALUE_LENGTH = 128;
45
46 static constexpr uint32_t FAILCOUNT = 2;
47
48 static constexpr uint32_t SUCCOUNT = 1;
49
50 static constexpr int E_KEY_EMPTY_STSTEM_STORAGE = -1006;
51
52 static constexpr int E_KEY_EXCEED_LENGTH_LIMIT_STSTEM_STORAGE = -1016;
53
54 static constexpr int E_VALUE_EXCEED_LENGTH_LIMIT_STSTEM_STORAGE = -1017;
55
56 static constexpr int E_DEFAULT_EXCEED_LENGTH_LIMIT_STSTEM_STORAGE = -1018;
57
ParseString(napi_env env,napi_value object,const char * name,const bool enable,std::string & output)58 void ParseString(napi_env env, napi_value object, const char *name, const bool enable, std::string &output)
59 {
60 napi_value value = nullptr;
61 bool exist = false;
62 napi_has_named_property(env, object, name, &exist);
63 if (exist && (napi_get_named_property(env, object, name, &value) == napi_ok)) {
64 std::string key = "";
65 int32_t ret = JSUtils::Convert2NativeValue(env, value, key);
66 NAPI_ASSERT_RETURN_VOID(env, enable || (ret == E_OK && !key.empty()), "StorageOptions is empty.");
67 output = std::move(key);
68 }
69 }
70
ParseFunction(napi_env env,napi_value object,const char * name,napi_ref & output)71 void ParseFunction(napi_env env, napi_value object, const char *name, napi_ref &output)
72 {
73 napi_value value = nullptr;
74 bool exist = false;
75 napi_has_named_property(env, object, name, &exist);
76 if (exist && (napi_get_named_property(env, object, name, &value) == napi_ok)) {
77 napi_valuetype valueType = napi_null;
78 NAPI_ASSERT_RETURN_VOID(env, value != nullptr, "value == nullptr");
79 NAPI_CALL_RETURN_VOID(env, napi_typeof(env, value, &valueType));
80 NAPI_ASSERT_RETURN_VOID(env, valueType == napi_function, "Wrong argument, function expected.");
81 NAPI_CALL_RETURN_VOID(env, napi_create_reference(env, value, 1, &output));
82 }
83 }
84
GetMessageInfo(const int & errCode)85 const std::string GetMessageInfo(const int &errCode)
86 {
87 switch (errCode) {
88 case E_KEY_EMPTY:
89 return "The key string is null or empty.";
90 case E_KEY_EXCEED_LENGTH_LIMIT:
91 return "The key string length should shorter than 32.";
92 case E_VALUE_EXCEED_LENGTH_LIMIT:
93 return "The value string length should shorter than 128.";
94 case E_DEFAULT_EXCEED_LENGTH_LIMIT:
95 return "The default string length should shorter than 128.";
96 default:
97 return "unknown err";
98 }
99 }
100
ConversionToSysStorageErrorCode(const int & errCode)101 int32_t ConversionToSysStorageErrorCode(const int &errCode)
102 {
103 switch (errCode) {
104 case E_KEY_EMPTY:
105 return E_KEY_EMPTY_STSTEM_STORAGE;
106 case E_KEY_EXCEED_LENGTH_LIMIT:
107 return E_KEY_EXCEED_LENGTH_LIMIT_STSTEM_STORAGE;
108 case E_VALUE_EXCEED_LENGTH_LIMIT:
109 return E_VALUE_EXCEED_LENGTH_LIMIT_STSTEM_STORAGE;
110 case E_DEFAULT_EXCEED_LENGTH_LIMIT:
111 return E_DEFAULT_EXCEED_LENGTH_LIMIT_STSTEM_STORAGE;
112 default:
113 return errCode;
114 }
115 }
116
Complete(napi_env env,napi_status status,void * data)117 void Complete(napi_env env, napi_status status, void *data)
118 {
119 AsyncContext *ctx = static_cast<AsyncContext *>(data);
120 if (status != napi_ok) {
121 napi_throw_type_error(env, nullptr, "Execute callback failed.");
122 return;
123 }
124 size_t len = 0;
125 if (ctx->output == E_OK && ctx->success != nullptr) {
126 napi_value successCallBack = nullptr;
127 NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, ctx->success, &successCallBack));
128 napi_value succRes[SUCCOUNT] = { 0 };
129 len = ctx->val.size();
130 NAPI_CALL_RETURN_VOID(env, napi_create_string_utf8(env, ctx->val.c_str(), len, &succRes[0]));
131 napi_value succCallbackResult = nullptr;
132 NAPI_CALL_RETURN_VOID(
133 env, napi_call_function(env, nullptr, successCallBack, SUCCOUNT, succRes, &succCallbackResult));
134 }
135
136 if (ctx->output != E_OK && ctx->fail != nullptr) {
137 napi_value failCallBack = nullptr;
138 NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, ctx->fail, &failCallBack));
139 napi_value failRes[FAILCOUNT] = { 0 };
140 std::string message = GetMessageInfo(ctx->output);
141 len = message.size();
142 NAPI_CALL_RETURN_VOID(env, napi_create_string_utf8(env, message.c_str(), len, &failRes[0]));
143 NAPI_CALL_RETURN_VOID(env, napi_create_int32(env, ConversionToSysStorageErrorCode(ctx->output), &failRes[1]));
144 napi_value failCallbackResult = nullptr;
145 NAPI_CALL_RETURN_VOID(
146 env, napi_call_function(env, nullptr, failCallBack, FAILCOUNT, failRes, &failCallbackResult));
147 }
148
149 if (ctx->complete != nullptr) {
150 napi_value completeCallBack = nullptr;
151 NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, ctx->complete, &completeCallBack));
152 napi_value completeCallbackResult = nullptr;
153 NAPI_CALL_RETURN_VOID(
154 env, napi_call_function(env, nullptr, completeCallBack, 0, nullptr, &completeCallbackResult));
155 }
156 napi_delete_async_work(env, ctx->request);
157 napi_delete_reference(env, ctx->success);
158 napi_delete_reference(env, ctx->fail);
159 napi_delete_reference(env, ctx->complete);
160 napi_value res = nullptr;
161 napi_get_undefined(env, &res);
162 napi_resolve_deferred(env, ctx->deferred, res);
163 delete ctx;
164 }
165
GetPrefName(napi_env env)166 std::string GetPrefName(napi_env env)
167 {
168 JSAbility::ContextInfo contextInfo;
169 JSAbility::GetContextInfo(env, nullptr, "", contextInfo);
170 return contextInfo.preferencesDir + "/default.xml";
171 }
172
Operate(napi_env env,napi_callback_info info,const char * resource,bool parseStrFlag,napi_async_execute_callback execute)173 napi_value Operate(napi_env env, napi_callback_info info, const char *resource, bool parseStrFlag,
174 napi_async_execute_callback execute)
175 {
176 size_t argc = 1;
177 napi_value argv[1] = { 0 };
178 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
179 NAPI_ASSERT(env, argc == 1, "Not enough arguments, expected 1.");
180 napi_valuetype valueType = napi_null;
181 NAPI_CALL(env, napi_typeof(env, argv[0], &valueType));
182 NAPI_ASSERT(env, valueType == napi_object, "Wrong argument type, object expected.");
183
184 AsyncContext *context = new (std::nothrow) AsyncContext();
185 if (context == nullptr) {
186 LOG_ERROR("Operate new failed, context is nullptr");
187 return nullptr;
188 }
189 context->prefName = GetPrefName(env);
190
191 ParseString(env, argv[0], "key", parseStrFlag, context->key);
192 ParseString(env, argv[0], "value", false, context->val);
193 ParseString(env, argv[0], "default", false, context->def);
194
195 ParseFunction(env, argv[0], "success", context->success);
196 ParseFunction(env, argv[0], "fail", context->fail);
197 ParseFunction(env, argv[0], "complete", context->complete);
198
199 napi_value ret = nullptr;
200 napi_value resourceName = nullptr;
201 napi_status status = napi_create_string_utf8(env, resource, NAPI_AUTO_LENGTH, &resourceName);
202 if (status != napi_ok) {
203 LOG_ERROR("Operate get resourceName failed, status = %{public}d", status);
204 delete context;
205 return ret;
206 }
207 status = napi_create_promise(env, &context->deferred, &ret);
208 if (status != napi_ok) {
209 LOG_ERROR("Operate create promise failed, status = %{public}d", status);
210 delete context;
211 return ret;
212 }
213 status = napi_create_async_work(env, nullptr, resourceName, execute, Complete, context, &context->request);
214 if (status != napi_ok) {
215 LOG_ERROR("Operate create asyncWork failed, status = %{public}d", status);
216 delete context;
217 return ret;
218 }
219 status = napi_queue_async_work(env, context->request);
220 if (status != napi_ok) {
221 LOG_ERROR("Operate queue asyncWork failed, status = %{public}d", status);
222 delete context;
223 }
224 return ret;
225 }
226
NapiGet(napi_env env,napi_callback_info info)227 napi_value NapiGet(napi_env env, napi_callback_info info)
228 {
229 return Operate(env, info, "get", true, [](napi_env env, void *data) {
230 AsyncContext *context = static_cast<AsyncContext *>(data);
231 if (context->key.size() > MAX_KEY_LENGTH) {
232 context->output = E_KEY_EXCEED_LENGTH_LIMIT;
233 return;
234 }
235
236 if (context->def.size() > MAX_VALUE_LENGTH) {
237 context->output = E_DEFAULT_EXCEED_LENGTH_LIMIT;
238 return;
239 }
240
241 auto pref = PreferencesHelper::GetPreferences(context->prefName, context->output);
242 context->val = pref->GetString(context->key, context->def);
243 });
244 }
245
NapiSet(napi_env env,napi_callback_info info)246 napi_value NapiSet(napi_env env, napi_callback_info info)
247 {
248 return Operate(env, info, "set", true, [](napi_env env, void *data) {
249 AsyncContext *context = static_cast<AsyncContext *>(data);
250 if (context->key.size() > MAX_KEY_LENGTH) {
251 context->output = E_KEY_EXCEED_LENGTH_LIMIT;
252 return;
253 }
254 if (context->val.size() > MAX_VALUE_LENGTH) {
255 context->output = E_VALUE_EXCEED_LENGTH_LIMIT;
256 return;
257 }
258
259 auto pref = PreferencesHelper::GetPreferences(context->prefName, context->output);
260 if (context->output != E_OK) {
261 return;
262 }
263 context->output = pref->PutString(context->key, context->val);
264 pref->FlushSync();
265 });
266 }
267
NapiDelete(napi_env env,napi_callback_info info)268 napi_value NapiDelete(napi_env env, napi_callback_info info)
269 {
270 return Operate(env, info, "delete", true, [](napi_env env, void *data) {
271 AsyncContext *context = static_cast<AsyncContext *>(data);
272 if (context->key.size() > MAX_KEY_LENGTH) {
273 context->output = E_KEY_EXCEED_LENGTH_LIMIT;
274 return;
275 }
276
277 auto pref = PreferencesHelper::GetPreferences(context->prefName, context->output);
278 if (context->output != E_OK) {
279 return;
280 }
281 context->output = pref->Delete(context->key);
282 pref->FlushSync();
283 });
284 }
285
NapiClear(napi_env env,napi_callback_info info)286 napi_value NapiClear(napi_env env, napi_callback_info info)
287 {
288 return Operate(env, info, "clear", false, [](napi_env env, void *data) {
289 AsyncContext *context = static_cast<AsyncContext *>(data);
290 auto pref = PreferencesHelper::GetPreferences(context->prefName, context->output);
291 if (context->output != E_OK) {
292 return;
293 }
294 context->output = pref->Clear();
295 pref->FlushSync();
296 });
297 }
298
InitSystemStorage(napi_env env,napi_value exports)299 napi_value InitSystemStorage(napi_env env, napi_value exports)
300 {
301 napi_property_descriptor properties[] = {
302 DECLARE_NAPI_FUNCTION("get", NapiGet),
303 DECLARE_NAPI_FUNCTION("delete", NapiDelete),
304 DECLARE_NAPI_FUNCTION("clear", NapiClear),
305 DECLARE_NAPI_FUNCTION("set", NapiSet),
306 };
307 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(properties) / sizeof(*properties), properties));
308 return exports;
309 }
310 } // namespace PreferencesJsKit
311 } // namespace OHOS