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 "upload/js_util.h"
17
18 #include <securec.h>
19
20 #include <regex>
21 #include <string>
22
23 #include "napi_utils.h"
24
25 namespace OHOS::Request::Upload {
26
27 static const std::map<ExceptionErrorCode, std::string> ErrorCodeToMsg{
28 { E_OK, E_OK_INFO },
29 { E_PERMISSION, E_PERMISSION_INFO },
30 { E_PARAMETER_CHECK, E_PARAMETER_CHECK_INFO },
31 { E_UNSUPPORTED, E_UNSUPPORTED_INFO },
32 { E_FILE_IO, E_FILE_IO_INFO },
33 { E_FILE_PATH, E_FILE_PATH_INFO },
34 { E_SERVICE_ERROR, E_SERVICE_ERROR_INFO },
35 { E_OTHER, E_OTHER_INFO },
36 };
37
Convert2String(napi_env env,napi_value jsString)38 std::string JSUtil::Convert2String(napi_env env, napi_value jsString)
39 {
40 size_t maxLen = JSUtil::MAX_LEN;
41 napi_status status = napi_get_value_string_utf8(env, jsString, NULL, 0, &maxLen);
42 if (status != napi_ok) {
43 GET_AND_THROW_LAST_ERROR((env));
44 maxLen = JSUtil::MAX_LEN;
45 }
46 if (maxLen == 0) {
47 return std::string();
48 }
49 char *buf = new char[maxLen + 1];
50 if (buf == nullptr) {
51 return std::string();
52 }
53 size_t len = 0;
54 status = napi_get_value_string_utf8(env, jsString, buf, maxLen + 1, &len);
55 if (status != napi_ok) {
56 GET_AND_THROW_LAST_ERROR((env));
57 }
58 buf[len] = 0;
59 std::string value(buf);
60 delete[] buf;
61 return value;
62 }
63
Convert2JSString(napi_env env,const std::string & cString)64 napi_value JSUtil::Convert2JSString(napi_env env, const std::string &cString)
65 {
66 napi_value jsValue = nullptr;
67 napi_create_string_utf8(env, cString.c_str(), cString.size(), &jsValue);
68 return jsValue;
69 }
70
Convert2JSValue(napi_env env,int32_t value)71 napi_value JSUtil::Convert2JSValue(napi_env env, int32_t value)
72 {
73 napi_value jsValue;
74 napi_status status = napi_create_int32(env, value, &jsValue);
75 if (status != napi_ok) {
76 return nullptr;
77 }
78 return jsValue;
79 }
80
Convert2JSUploadResponse(napi_env env,const Upload::UploadResponse & response)81 napi_value JSUtil::Convert2JSUploadResponse(napi_env env, const Upload::UploadResponse &response)
82 {
83 napi_value jsResponse = nullptr;
84 napi_create_object(env, &jsResponse);
85 napi_set_named_property(env, jsResponse, "code", Convert2JSValue(env, response.code));
86 napi_set_named_property(env, jsResponse, "data", Convert2JSString(env, response.data));
87 napi_set_named_property(env, jsResponse, "headers", Convert2JSString(env, response.headers));
88 return jsResponse;
89 }
90
ParseFunction(napi_env env,napi_value & object,const char * name,napi_ref & output)91 bool JSUtil::ParseFunction(napi_env env, napi_value &object, const char *name, napi_ref &output)
92 {
93 napi_value value = GetNamedProperty(env, object, name);
94 if (value == nullptr) {
95 return false;
96 }
97 napi_valuetype valueType = napi_null;
98 auto ret = napi_typeof(env, value, &valueType);
99 if ((ret != napi_ok) || (valueType != napi_function)) {
100 return false;
101 }
102 napi_create_reference(env, value, 1, &output);
103 return true;
104 }
105
ParseUploadConfig(napi_env env,napi_value jsConfig,const std::string & version)106 std::shared_ptr<UploadConfig> JSUtil::ParseUploadConfig(napi_env env, napi_value jsConfig, const std::string &version)
107 {
108 UPLOAD_HILOGD(UPLOAD_MODULE_JS_NAPI, "ParseUploadConfig in");
109 UploadConfig config;
110 config.protocolVersion = version;
111 bool ret = ToUploadOption(env, jsConfig, config);
112 if ((!ret) || (!CheckConfig(config))) {
113 return nullptr;
114 }
115 return std::make_shared<UploadConfig>(config);
116 }
117
CheckConfig(const UploadConfig & config)118 bool JSUtil::CheckConfig(const UploadConfig &config)
119 {
120 if (!CheckUrl(config.url)) {
121 return false;
122 }
123 if (config.files.empty()) {
124 return false;
125 }
126 return CheckMethod(config.method);
127 }
128
CheckUrl(const std::string & url)129 bool JSUtil::CheckUrl(const std::string &url)
130 {
131 if (url.empty()) {
132 return false;
133 }
134 return regex_match(url, std::regex("^http(s)?:\\/\\/.+"));
135 }
136
CheckMethod(const std::string & method)137 bool JSUtil::CheckMethod(const std::string &method)
138 {
139 return (method == POST || method == PUT);
140 }
141
GetNamedProperty(napi_env env,napi_value object,const std::string & propertyName)142 napi_value JSUtil::GetNamedProperty(napi_env env, napi_value object, const std::string &propertyName)
143 {
144 napi_value value = nullptr;
145 bool hasProperty = false;
146 NAPI_CALL(env, napi_has_named_property(env, object, propertyName.c_str(), &hasProperty));
147 if (!hasProperty) {
148 return value;
149 }
150 NAPI_CALL(env, napi_get_named_property(env, object, propertyName.c_str(), &value));
151 return value;
152 }
153
HasNamedProperty(napi_env env,napi_value object,const std::string & propertyName)154 bool JSUtil::HasNamedProperty(napi_env env, napi_value object, const std::string &propertyName)
155 {
156 bool hasProperty = false;
157 NAPI_CALL_BASE(env, napi_has_named_property(env, object, propertyName.c_str(), &hasProperty), false);
158 return hasProperty;
159 }
160
SetData(napi_env env,napi_value jsConfig,UploadConfig & config)161 bool JSUtil::SetData(napi_env env, napi_value jsConfig, UploadConfig &config)
162 {
163 if (!HasNamedProperty(env, jsConfig, "data")) {
164 return true;
165 }
166 napi_value data = nullptr;
167 napi_get_named_property(env, jsConfig, "data", &data);
168 if (data == nullptr) {
169 UPLOAD_HILOGE(UPLOAD_MODULE_JS_NAPI, "GetNamedProperty SetData failed");
170 return false;
171 }
172 config.data = Convert2RequestDataVector(env, data);
173 return true;
174 }
175
SetFiles(napi_env env,napi_value jsConfig,UploadConfig & config)176 bool JSUtil::SetFiles(napi_env env, napi_value jsConfig, UploadConfig &config)
177 {
178 napi_value files = GetNamedProperty(env, jsConfig, "files");
179 if (files == nullptr) {
180 UPLOAD_HILOGE(UPLOAD_MODULE_JS_NAPI, "GetNamedProperty SetFiles failed");
181 return false;
182 }
183 config.files = Convert2FileVector(env, files, config.protocolVersion);
184 return true;
185 }
186
ToUploadOption(napi_env env,napi_value jsConfig,UploadConfig & config)187 bool JSUtil::ToUploadOption(napi_env env, napi_value jsConfig, UploadConfig &config)
188 {
189 if (!SetMandatoryParam(env, jsConfig, "url", config.url)) {
190 return false;
191 }
192 if (!SetData(env, jsConfig, config)) {
193 return false;
194 }
195 if (!SetFiles(env, jsConfig, config)) {
196 return false;
197 }
198 if (!ParseHeader(env, jsConfig, config.header)) {
199 return false;
200 }
201 if (!SetOptionalParam(env, jsConfig, "method", config.method)) {
202 return false;
203 }
204 return true;
205 }
206
ParseHeader(napi_env env,napi_value configValue,std::map<std::string,std::string> & header)207 bool JSUtil::ParseHeader(napi_env env, napi_value configValue, std::map<std::string, std::string> &header)
208 {
209 if (!NapiUtils::HasNamedProperty(env, configValue, "header")) {
210 UPLOAD_HILOGE(UPLOAD_MODULE_JS_NAPI, "No header present, Reassign value");
211 header[tlsVersion] = TLS_VERSION;
212 header[cipherList] = TLS_CIPHER;
213 return true;
214 }
215 napi_value jsHeader = NapiUtils::GetNamedProperty(env, configValue, "header");
216 if (NapiUtils::GetValueType(env, jsHeader) != napi_object) {
217 return false;
218 }
219 auto names = NapiUtils::GetPropertyNames(env, jsHeader);
220 auto iter = find(names.begin(), names.end(), cipherList);
221 if (iter == names.end()) {
222 header[cipherList] = TLS_CIPHER;
223 }
224 for (iter = names.begin(); iter != names.end(); ++iter) {
225 auto value = NapiUtils::Convert2String(env, jsHeader, *iter);
226 if (!value.empty()) {
227 header[*iter] = value;
228 }
229 }
230 return true;
231 }
232
SetMandatoryParam(napi_env env,napi_value jsValue,const std::string & str,std::string & out)233 bool JSUtil::SetMandatoryParam(napi_env env, napi_value jsValue, const std::string &str, std::string &out)
234 {
235 napi_value value = GetNamedProperty(env, jsValue, str);
236 if (value == nullptr) {
237 UPLOAD_HILOGE(UPLOAD_MODULE_JS_NAPI, "SetMandatoryParam failed");
238 return false;
239 }
240 out = Convert2String(env, value);
241 return true;
242 }
243
SetOptionalParam(napi_env env,napi_value jsValue,const std::string & str,std::string & out)244 bool JSUtil::SetOptionalParam(napi_env env, napi_value jsValue, const std::string &str, std::string &out)
245 {
246 if (!HasNamedProperty(env, jsValue, str)) {
247 out = (str == "method" ? "POST" : "");
248 return true;
249 }
250 napi_value value = nullptr;
251 napi_get_named_property(env, jsValue, str.c_str(), &value);
252 if (value == nullptr) {
253 UPLOAD_HILOGE(UPLOAD_MODULE_JS_NAPI, "SetOptionalParam failed");
254 return false;
255 }
256 out = Convert2String(env, value);
257 return true;
258 }
259
Convert2FileL5(napi_env env,napi_value jsFile,Upload::File & file)260 bool JSUtil::Convert2FileL5(napi_env env, napi_value jsFile, Upload::File &file)
261 {
262 if (!SetOptionalParam(env, jsFile, "filename", file.filename)) {
263 return false;
264 }
265 if (!SetOptionalParam(env, jsFile, "name", file.name)) {
266 return false;
267 }
268 if (!SetMandatoryParam(env, jsFile, "uri", file.uri)) {
269 return false;
270 }
271 if (!SetOptionalParam(env, jsFile, "type", file.type)) {
272 return false;
273 }
274 return true;
275 }
276
Convert2FileVector(napi_env env,napi_value jsFiles,const std::string & version)277 std::vector<Upload::File> JSUtil::Convert2FileVector(napi_env env, napi_value jsFiles, const std::string &version)
278 {
279 bool isArray = false;
280 napi_is_array(env, jsFiles, &isArray);
281 NAPI_ASSERT_BASE(env, isArray, "not array", {});
282 uint32_t length = 0;
283 napi_get_array_length(env, jsFiles, &length);
284 std::vector<Upload::File> files;
285 for (uint32_t i = 0; i < length; ++i) {
286 napi_value jsFile = nullptr;
287 napi_handle_scope scope = nullptr;
288 napi_open_handle_scope(env, &scope);
289 napi_get_element(env, jsFiles, i, &jsFile);
290 if (jsFile == nullptr) {
291 continue;
292 }
293
294 Upload::File file;
295 bool ret = Convert2FileL5(env, jsFile, file);
296 if (!ret) {
297 continue;
298 }
299 files.push_back(file);
300 napi_close_handle_scope(env, scope);
301 }
302 return files;
303 }
304
Convert2RequestData(napi_env env,napi_value jsRequestData)305 Upload::RequestData JSUtil::Convert2RequestData(napi_env env, napi_value jsRequestData)
306 {
307 Upload::RequestData requestData;
308 napi_value value = nullptr;
309 napi_get_named_property(env, jsRequestData, "name", &value);
310 if (value != nullptr) {
311 requestData.name = Convert2String(env, value);
312 }
313 value = nullptr;
314 napi_get_named_property(env, jsRequestData, "value", &value);
315 if (value != nullptr) {
316 requestData.value = Convert2String(env, value);
317 }
318 return requestData;
319 }
320
Convert2RequestDataVector(napi_env env,napi_value jsRequestDatas)321 std::vector<Upload::RequestData> JSUtil::Convert2RequestDataVector(napi_env env, napi_value jsRequestDatas)
322 {
323 bool isArray = false;
324 napi_is_array(env, jsRequestDatas, &isArray);
325 NAPI_ASSERT_BASE(env, isArray, "not array", {});
326 uint32_t length = 0;
327 napi_get_array_length(env, jsRequestDatas, &length);
328 std::vector<Upload::RequestData> requestDatas;
329 for (uint32_t i = 0; i < length; ++i) {
330 napi_value requestData = nullptr;
331 napi_get_element(env, jsRequestDatas, i, &requestData);
332 if (requestData == nullptr) {
333 continue;
334 }
335 requestDatas.push_back(Convert2RequestData(env, requestData));
336 }
337 return requestDatas;
338 }
339
CreateBusinessError(napi_env env,const ExceptionErrorCode & errorCode,const std::string & errorMessage)340 napi_value JSUtil::CreateBusinessError(
341 napi_env env, const ExceptionErrorCode &errorCode, const std::string &errorMessage)
342 {
343 napi_value error = nullptr;
344 napi_value code = nullptr;
345 napi_value msg = nullptr;
346 auto iter = ErrorCodeToMsg.find(errorCode);
347 std::string strMsg = (iter != ErrorCodeToMsg.end() ? iter->second : "") + " " + errorMessage;
348 NAPI_CALL(env, napi_create_string_utf8(env, strMsg.c_str(), strMsg.length(), &msg));
349 NAPI_CALL(env, napi_create_uint32(env, errorCode, &code));
350 NAPI_CALL(env, napi_create_error(env, nullptr, msg, &error));
351 napi_set_named_property(env, error, "code", code);
352 return error;
353 }
354
355 } // namespace OHOS::Request::Upload