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