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