1 /*
2 * Copyright (C) 2023 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 "js_initialize.h"
17
18 #include <securec.h>
19 #include <sys/stat.h>
20
21 #include <algorithm>
22 #include <cstring>
23 #include <filesystem>
24 #include <fstream>
25 #include <regex>
26
27 #include "js_common.h"
28 #include "log.h"
29 #include "napi_utils.h"
30 #include "net_conn_client.h"
31 #include "request_manager.h"
32
33 static constexpr const char *PARAM_KEY_DESCRIPTION = "description";
34 static constexpr const char *PARAM_KEY_NETWORKTYPE = "networkType";
35 static constexpr const char *PARAM_KEY_FILE_PATH = "filePath";
36 static constexpr const char *PARAM_KEY_BACKGROUND = "background";
37 static constexpr uint32_t FILE_PERMISSION = 0644;
38 static constexpr uint32_t TITLE_MAXIMUM = 256;
39 static constexpr uint32_t DESCRIPTION_MAXIMUM = 1024;
40 static constexpr uint32_t URL_MAXIMUM = 2048;
41 namespace OHOS::Request {
Initialize(napi_env env,napi_callback_info info,Version version,bool firstInit)42 napi_value JsInitialize::Initialize(napi_env env, napi_callback_info info, Version version, bool firstInit)
43 {
44 REQUEST_HILOGD("constructor request task!");
45 bool withErrCode = version != Version::API8;
46 napi_value self = nullptr;
47 size_t argc = NapiUtils::MAX_ARGC;
48 napi_value argv[NapiUtils::MAX_ARGC] = { nullptr };
49 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
50 int32_t number = version == Version::API8 ? NapiUtils::ONE_ARG : NapiUtils::TWO_ARG;
51 if (static_cast<int32_t>(argc) < number) {
52 NapiUtils::ThrowError(env, E_PARAMETER_CHECK, "invalid parameter count", withErrCode);
53 return nullptr;
54 }
55
56 Config config;
57 config.version = version;
58 config.withErrCode = withErrCode;
59 config.firstInit = firstInit;
60 std::shared_ptr<OHOS::AbilityRuntime::Context> context = nullptr;
61 ExceptionError err = InitParam(env, argv, context, config);
62 if (err.code != E_OK) {
63 REQUEST_HILOGE("err.code : %{public}d, err.errInfo : %{public}s", err.code, err.errInfo.c_str());
64 NapiUtils::ThrowError(env, err.code, err.errInfo, withErrCode);
65 return nullptr;
66 }
67 auto *task = new (std::nothrow) JsTask();
68 if (task == nullptr) {
69 REQUEST_HILOGE("Create task object failed");
70 return nullptr;
71 }
72 task->config_ = config;
73 RequestManager::GetInstance()->RestoreListener(JsTask::ReloadListener);
74 auto finalize = [](napi_env env, void *data, void *hint) {
75 REQUEST_HILOGD("destructed task");
76 JsTask *task = reinterpret_cast<JsTask *>(data);
77 JsTask::ClearTaskMap(task->GetTid());
78 delete task;
79 };
80 if (napi_wrap(env, self, task, finalize, nullptr, nullptr) != napi_ok) {
81 finalize(env, task, nullptr);
82 return nullptr;
83 }
84 return self;
85 }
86
InitParam(napi_env env,napi_value * argv,std::shared_ptr<OHOS::AbilityRuntime::Context> & context,Config & config)87 ExceptionError JsInitialize::InitParam(
88 napi_env env, napi_value *argv, std::shared_ptr<OHOS::AbilityRuntime::Context> &context, Config &config)
89 {
90 REQUEST_HILOGD("InitParam in");
91 ExceptionError err = { .code = E_OK };
92 int parametersPosition = config.version == Version::API8 ? CONFIG_PARAM_AT_FIRST : CONFIG_PARAM_AT_SECOND;
93
94 napi_status getStatus = GetContext(env, argv[0], context);
95 if (getStatus != napi_ok) {
96 REQUEST_HILOGE("Get context fail");
97 return { .code = E_PARAMETER_CHECK, .errInfo = "Get context fail" };
98 }
99
100 if (context->GetApplicationInfo() == nullptr) {
101 return { .code = E_OTHER, .errInfo = "ApplicationInfo is null" };
102 }
103 if (!ParseConfig(env, argv[parametersPosition], config, err.errInfo)) {
104 err.code = E_PARAMETER_CHECK;
105 return err;
106 }
107 config.bundleName = context->GetBundleName();
108 REQUEST_HILOGD("config.bundleName is %{public}s", config.bundleName.c_str());
109 return CheckFilePath(context, config);
110 }
111
GetContext(napi_env env,napi_value value,std::shared_ptr<OHOS::AbilityRuntime::Context> & context)112 napi_status JsInitialize::GetContext(
113 napi_env env, napi_value value, std::shared_ptr<OHOS::AbilityRuntime::Context> &context)
114 {
115 if (!IsStageMode(env, value)) {
116 auto ability = OHOS::AbilityRuntime::GetCurrentAbility(env);
117 if (ability == nullptr) {
118 REQUEST_HILOGE("Get current ability fail");
119 return napi_generic_failure;
120 }
121 context = ability->GetAbilityContext();
122 } else {
123 context = OHOS::AbilityRuntime::GetStageModeContext(env, value);
124 }
125 if (context == nullptr) {
126 REQUEST_HILOGE("Get Context failed, context is nullptr.");
127 return napi_generic_failure;
128 }
129 return napi_ok;
130 }
131
GetBaseDir(std::string & baseDir)132 bool JsInitialize::GetBaseDir(std::string &baseDir)
133 {
134 auto context = AbilityRuntime::Context::GetApplicationContext();
135 if (context == nullptr) {
136 REQUEST_HILOGE("AppContext is null.");
137 return false;
138 }
139 baseDir = context->GetBaseDir();
140 if (baseDir.empty()) {
141 REQUEST_HILOGE("Base dir not found.");
142 return false;
143 }
144 return true;
145 }
146
CheckFilePath(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,Config & config)147 ExceptionError JsInitialize::CheckFilePath(
148 const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, Config &config)
149 {
150 ExceptionError err = { .code = E_OK };
151 if (config.action == Action::DOWNLOAD) {
152 FileSpec file = { .uri = config.saveas };
153 config.files.push_back(file);
154 }
155
156 for (auto &file : config.files) {
157 std::string path;
158 if (!GetInternalPath(file.uri, context, config, path)) {
159 return { .code = E_PARAMETER_CHECK, .errInfo = "this is fail path" };
160 }
161 file.uri = path;
162 if (file.filename.empty()) {
163 InterceptData("/", file.uri, file.filename);
164 }
165 if (file.type.empty()) {
166 InterceptData(".", file.filename, file.type);
167 }
168 if (file.name.empty()) {
169 file.name = "file";
170 }
171 if (!JsTask::SetPathPermission(file.uri)) {
172 return { .code = E_FILE_IO, .errInfo = "set path permission fail" };
173 }
174 err = GetFD(path, config, file.fd);
175 if (err.code != E_OK) {
176 return err;
177 }
178 }
179
180 if (!JsTask::SetDirsPermission(config.certsPath)) {
181 return { .code = E_FILE_IO, .errInfo = "set files of directors permission fail" };
182 }
183
184 if (config.action == Action::UPLOAD) {
185 std::string filePath = context->GetCacheDir();
186 err = CheckUploadBodyFiles(config, filePath);
187 }
188 return err;
189 }
190
CheckUploadBodyFiles(Config & config,const std::string & filePath)191 ExceptionError JsInitialize::CheckUploadBodyFiles(Config &config, const std::string &filePath)
192 {
193 ExceptionError error = { .code = E_OK };
194 size_t len = config.files.size();
195
196 for (size_t i = 0; i < len; i++) {
197 if (filePath.empty()) {
198 REQUEST_HILOGE("internal to cache error");
199 return { .code = E_PARAMETER_CHECK, .errInfo = "IsPathValid error empty path" };
200 }
201 auto now = std::chrono::high_resolution_clock::now();
202 auto timestamp = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
203 std::string fileName = filePath + "/tmp_body_" + std::to_string(i) + "_" + std::to_string(timestamp);
204 REQUEST_HILOGD("Create upload body file, %{public}s", fileName.c_str());
205 if (!NapiUtils::IsPathValid(fileName)) {
206 REQUEST_HILOGE("IsPathValid error %{public}s", fileName.c_str());
207 return { .code = E_PARAMETER_CHECK, .errInfo = "IsPathValid error fail path" };
208 }
209
210 int32_t bodyFd = open(fileName.c_str(), O_TRUNC | O_RDWR);
211 if (bodyFd < 0) {
212 bodyFd = open(fileName.c_str(), O_CREAT | O_RDWR, FILE_PERMISSION);
213 if (bodyFd < 0) {
214 return { .code = E_FILE_IO, .errInfo = "Failed to open file errno " + std::to_string(errno) };
215 }
216 }
217
218 if (bodyFd >= 0) {
219 chmod(fileName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | S_IWOTH);
220 }
221
222 config.bodyFds.push_back(bodyFd);
223 config.bodyFileNames.push_back(fileName);
224 }
225 return error;
226 }
227
GetFD(const std::string & path,const Config & config,int32_t & fd)228 ExceptionError JsInitialize::GetFD(const std::string &path, const Config &config, int32_t &fd)
229 {
230 ExceptionError error = { .code = E_OK };
231 fd = config.action == Action::UPLOAD ? open(path.c_str(), O_RDONLY) : open(path.c_str(), O_TRUNC | O_RDWR);
232 if (fd >= 0) {
233 REQUEST_HILOGD("File already exists");
234 if (config.action == Action::UPLOAD) {
235 chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
236 return error;
237 } else {
238 chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | S_IWOTH);
239 }
240
241 if (config.version == Version::API10 && config.overwrite) {
242 return error;
243 }
244 if (!config.firstInit) {
245 REQUEST_HILOGD("Task config is not firstInit");
246 return error;
247 }
248 ExceptionErrorCode code = config.version == Version::API10 ? E_FILE_IO : E_FILE_PATH;
249 return { .code = code, .errInfo = "Download File already exists" };
250 } else {
251 if (config.action == Action::UPLOAD) {
252 ExceptionErrorCode code = config.version == Version::API10 ? E_FILE_IO : E_FILE_PATH;
253 return { .code = code, .errInfo = "Failed to open file errno " + std::to_string(errno) };
254 }
255 fd = open(path.c_str(), O_CREAT | O_RDWR, FILE_PERMISSION);
256 if (fd < 0) {
257 return { .code = E_FILE_IO, .errInfo = "Failed to open file errno " + std::to_string(errno) };
258 }
259 chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | S_IWOTH);
260 }
261 return error;
262 }
263
GetInternalPath(const std::string & fileUri,const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,Config & config,std::string & filePath)264 bool JsInitialize::GetInternalPath(const std::string &fileUri,
265 const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, Config &config, std::string &filePath)
266 {
267 if (config.action == Action::DOWNLOAD && config.version != Version::API10 && fileUri.find('/') == 0) {
268 filePath = fileUri;
269 return true;
270 }
271 std::string fileName;
272 std::string pattern = config.version == Version::API10 ? "./" : "internal://cache/";
273 size_t pos = fileUri.find(pattern);
274 if (pos != 0) {
275 fileName = fileUri;
276 } else {
277 fileName = fileUri.substr(pattern.size(), fileUri.size());
278 }
279 if (fileName.empty()) {
280 return false;
281 }
282 filePath = context->GetCacheDir();
283 if (filePath.empty()) {
284 REQUEST_HILOGE("internal to cache error");
285 return false;
286 }
287 filePath += "/" + fileName;
288 if (!NapiUtils::IsPathValid(filePath)) {
289 REQUEST_HILOGE("IsPathValid error %{public}s", filePath.c_str());
290 return false;
291 }
292 return true;
293 }
294
SetParseConfig(napi_env env,napi_value jsConfig,Config & config)295 void JsInitialize::SetParseConfig(napi_env env, napi_value jsConfig, Config &config)
296 {
297 config.overwrite = NapiUtils::Convert2Boolean(env, jsConfig, "overwrite");
298 config.metered = NapiUtils::Convert2Boolean(env, jsConfig, "metered");
299 config.gauge = NapiUtils::Convert2Boolean(env, jsConfig, "gauge");
300 config.precise = NapiUtils::Convert2Boolean(env, jsConfig, "precise");
301 config.priority = ParsePriority(env, jsConfig);
302 config.begins = ParseBegins(env, jsConfig);
303 config.ends = ParseEnds(env, jsConfig);
304 config.mode = static_cast<Mode>(NapiUtils::Convert2Uint32(env, jsConfig, "mode"));
305 config.headers = ParseMap(env, jsConfig, "headers");
306 config.extras = ParseMap(env, jsConfig, "extras");
307 if (config.mode == Mode::BACKGROUND) {
308 config.background = true;
309 }
310 }
311
ParseConfig(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)312 bool JsInitialize::ParseConfig(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
313 {
314 if (NapiUtils::GetValueType(env, jsConfig) != napi_object) {
315 errInfo = "Wrong conf type, expected object";
316 return false;
317 }
318 if (config.version != Version::API10) {
319 return ParseConfigV9(env, jsConfig, config, errInfo);
320 }
321
322 if (!ParseAction(env, jsConfig, config.action)) {
323 errInfo = "parse action error";
324 return false;
325 }
326 if (!ParseUrl(env, jsConfig, config.url)) {
327 errInfo = "parse url error";
328 return false;
329 }
330 if (!ParseCertsPath(env, jsConfig, config.certsPath)) {
331 errInfo = "parse certs path error";
332 return false;
333 }
334 if (!ParseData(env, jsConfig, config)) {
335 errInfo = "parse data error";
336 return false;
337 }
338 if (!ParseIndex(env, jsConfig, config)) {
339 errInfo = "Index exceeds file list";
340 return false;
341 }
342 if (!ParseTitle(env, jsConfig, config) || !ParseToken(env, jsConfig, config)
343 || !ParseDescription(env, jsConfig, config.description)) {
344 errInfo = "Exceeding maximum length";
345 return false;
346 }
347 ParseMethod(env, jsConfig, config);
348 ParseSaveas(env, jsConfig, config);
349 ParseRoaming(env, jsConfig, config);
350 ParseRedirect(env, jsConfig, config.redirect);
351 ParseNetwork(env, jsConfig, config.network);
352 ParseRetry(env, jsConfig, config.retry);
353 SetParseConfig(env, jsConfig, config);
354 return true;
355 }
356
ParseRoaming(napi_env env,napi_value jsConfig,Config & config)357 void JsInitialize::ParseRoaming(napi_env env, napi_value jsConfig, Config &config)
358 {
359 if (!NapiUtils::HasNamedProperty(env, jsConfig, "roaming")) {
360 config.roaming = config.version == Version::API10;
361 } else {
362 config.roaming = NapiUtils::Convert2Boolean(env, jsConfig, "roaming");
363 }
364 }
365
ParseNetwork(napi_env env,napi_value jsConfig,Network & network)366 void JsInitialize::ParseNetwork(napi_env env, napi_value jsConfig, Network &network)
367 {
368 network = static_cast<Network>(NapiUtils::Convert2Uint32(env, jsConfig, "network"));
369 if (network != Network::ANY && network != Network::WIFI && network != Network::CELLULAR) {
370 network = Network::ANY;
371 }
372 }
373
ParseToken(napi_env env,napi_value jsConfig,Config & config)374 bool JsInitialize::ParseToken(napi_env env, napi_value jsConfig, Config &config)
375 {
376 char *token = nullptr;
377 size_t len = 0;
378 if (!NapiUtils::HasNamedProperty(env, jsConfig, "token")) {
379 return true;
380 }
381 napi_value value = NapiUtils::GetNamedProperty(env, jsConfig, "token");
382 if (NapiUtils::GetValueType(env, value) != napi_string) {
383 return true;
384 }
385 uint32_t bufferLen = TOKEN_MAX_BYTES + 2;
386 token = new char[bufferLen];
387 napi_status status = napi_get_value_string_utf8(env, value, token, bufferLen, &len);
388 if (status != napi_ok) {
389 REQUEST_HILOGE("napi get value string utf8 failed");
390 memset_s(token, bufferLen, 0, bufferLen);
391 delete[] token;
392 return false;
393 }
394 if (len < TOKEN_MIN_BYTES || len > TOKEN_MAX_BYTES) {
395 memset_s(token, bufferLen, 0, bufferLen);
396 delete[] token;
397 return false;
398 }
399 config.token = NapiUtils::SHA256(token, len);
400 memset_s(token, bufferLen, 0, bufferLen);
401 delete[] token;
402 return true;
403 }
404
ParseIndex(napi_env env,napi_value jsConfig,Config & config)405 bool JsInitialize::ParseIndex(napi_env env, napi_value jsConfig, Config &config)
406 {
407 config.index = NapiUtils::Convert2Uint32(env, jsConfig, "index");
408 if (config.action == Action::DOWNLOAD) {
409 config.index = 0;
410 return true;
411 }
412 if (config.files.size() <= config.index) {
413 REQUEST_HILOGE("files.size is %{public}zu, index is %{public}d", config.files.size(), config.index);
414 return false;
415 }
416 return true;
417 }
418
ParseAction(napi_env env,napi_value jsConfig,Action & action)419 bool JsInitialize::ParseAction(napi_env env, napi_value jsConfig, Action &action)
420 {
421 if (!NapiUtils::HasNamedProperty(env, jsConfig, "action")) {
422 REQUEST_HILOGE("ParseAction err");
423 return false;
424 }
425 napi_value value = NapiUtils::GetNamedProperty(env, jsConfig, "action");
426 if (NapiUtils::GetValueType(env, value) != napi_number) {
427 REQUEST_HILOGE("GetNamedProperty err");
428 return false;
429 }
430 action = static_cast<Action>(NapiUtils::Convert2Uint32(env, value));
431 if (action != Action::DOWNLOAD && action != Action::UPLOAD) {
432 REQUEST_HILOGE("Must be UPLOAD or DOWNLOAD");
433 return false;
434 }
435 return true;
436 }
437
ParseSaveas(napi_env env,napi_value jsConfig,Config & config)438 void JsInitialize::ParseSaveas(napi_env env, napi_value jsConfig, Config &config)
439 {
440 config.saveas = NapiUtils::Convert2String(env, jsConfig, "saveas");
441 if (config.saveas.empty() || config.saveas == "./") {
442 InterceptData("/", config.url, config.saveas);
443 }
444 }
445
ParseBegins(napi_env env,napi_value jsConfig)446 int64_t JsInitialize::ParseBegins(napi_env env, napi_value jsConfig)
447 {
448 int64_t size = NapiUtils::Convert2Int64(env, jsConfig, "begins");
449 return size >= 0 ? size : 0;
450 }
451
ParseEnds(napi_env env,napi_value jsConfig)452 int64_t JsInitialize::ParseEnds(napi_env env, napi_value jsConfig)
453 {
454 if (!NapiUtils::HasNamedProperty(env, jsConfig, "ends")) {
455 return -1;
456 }
457 return NapiUtils::Convert2Int64(env, jsConfig, "ends");
458 }
459
ParsePriority(napi_env env,napi_value jsConfig)460 uint32_t JsInitialize::ParsePriority(napi_env env, napi_value jsConfig)
461 {
462 if (!NapiUtils::HasNamedProperty(env, jsConfig, "priority")) {
463 return 0;
464 }
465 return NapiUtils::Convert2Uint32(env, jsConfig, "priority");
466 }
467
ParseDescription(napi_env env,napi_value jsConfig,std::string & description)468 bool JsInitialize::ParseDescription(napi_env env, napi_value jsConfig, std::string &description)
469 {
470 description = NapiUtils::Convert2String(env, jsConfig, "description");
471 if (description.size() > DESCRIPTION_MAXIMUM) {
472 return false;
473 }
474 return true;
475 }
476
ParseMap(napi_env env,napi_value jsConfig,const std::string & propertyName)477 std::map<std::string, std::string> JsInitialize::ParseMap(
478 napi_env env, napi_value jsConfig, const std::string &propertyName)
479 {
480 std::map<std::string, std::string> result;
481 napi_value jsValue = NapiUtils::GetNamedProperty(env, jsConfig, propertyName);
482 if (jsValue == nullptr) {
483 return result;
484 }
485 auto names = NapiUtils::GetPropertyNames(env, jsValue);
486 for (auto iter = names.begin(); iter != names.end(); ++iter) {
487 // The value of `Header` or `extra` can be empty.
488 result[*iter] = NapiUtils::Convert2String(env, jsValue, *iter);
489 }
490 return result;
491 }
492
ParseUrl(napi_env env,napi_value jsConfig,std::string & url)493 bool JsInitialize::ParseUrl(napi_env env, napi_value jsConfig, std::string &url)
494 {
495 url = NapiUtils::Convert2String(env, jsConfig, "url");
496 if (url.size() > URL_MAXIMUM) {
497 REQUEST_HILOGE("The URL exceeds the maximum length of 2048");
498 return false;
499 }
500 if (!regex_match(url, std::regex("^http(s)?:\\/\\/.+"))) {
501 REQUEST_HILOGE("ParseUrl error");
502 return false;
503 }
504
505 return true;
506 }
507
ParseCertsPath(napi_env env,napi_value jsConfig,std::vector<std::string> & certsPath)508 bool JsInitialize::ParseCertsPath(napi_env env, napi_value jsConfig, std::vector<std::string> &certsPath)
509 {
510 std::string url = NapiUtils::Convert2String(env, jsConfig, "url");
511 if (url.size() > URL_MAXIMUM) {
512 REQUEST_HILOGE("The URL exceeds the maximum length of 2048");
513 return false;
514 }
515 if (!regex_match(url, std::regex("^http(s)?:\\/\\/.+"))) {
516 REQUEST_HILOGE("ParseUrl error");
517 return false;
518 }
519
520 typedef std::string::const_iterator iter_t;
521
522 iter_t urlEnd = url.end();
523 iter_t protocolStart = url.cbegin();
524 iter_t protocolEnd = std::find(protocolStart, urlEnd, ':');
525 std::string protocol = std::string(protocolStart, protocolEnd);
526 if (protocol != "https") {
527 REQUEST_HILOGD("Using Http");
528 return true;
529 }
530 if (protocolEnd != urlEnd) {
531 std::string afterProtocol = &*(protocolEnd);
532 // 3 is the num of ://
533 if ((afterProtocol.length() > 3) && (afterProtocol.substr(0, 3) == "://")) {
534 // 3 means go beyound :// in protocolEnd
535 protocolEnd += 3;
536 } else {
537 protocolEnd = url.cbegin();
538 }
539 } else {
540 protocolEnd = url.cbegin();
541 }
542 iter_t hostStart = protocolEnd;
543 iter_t pathStart = std::find(hostStart, urlEnd, '/');
544 iter_t queryStart = std::find(url.cbegin(), urlEnd, '?');
545 iter_t hostEnd = std::find(protocolEnd, (pathStart != urlEnd) ? pathStart : queryStart, ':');
546 std::string hostname = std::string(hostStart, hostEnd);
547 REQUEST_HILOGD("Hostname is %{public}s", hostname.c_str());
548 NetManagerStandard::NetConnClient::GetInstance().GetTrustAnchorsForHostName(hostname, certsPath);
549
550 return true;
551 }
552
ParseTitle(napi_env env,napi_value jsConfig,Config & config)553 bool JsInitialize::ParseTitle(napi_env env, napi_value jsConfig, Config &config)
554 {
555 config.title = NapiUtils::Convert2String(env, jsConfig, "title");
556 if (config.version == Version::API10 && config.title.size() > TITLE_MAXIMUM) {
557 return false;
558 }
559 if (config.title.empty()) {
560 config.title = config.action == Action::UPLOAD ? "upload" : "download";
561 }
562 return true;
563 }
564
ParseMethod(napi_env env,napi_value jsConfig,Config & config)565 void JsInitialize::ParseMethod(napi_env env, napi_value jsConfig, Config &config)
566 {
567 if (config.version == Version::API10) {
568 config.method = config.action == Action::UPLOAD ? "PUT" : "GET";
569 } else {
570 config.method = "POST";
571 }
572 std::string method = NapiUtils::Convert2String(env, jsConfig, "method");
573 if (!method.empty()) {
574 transform(method.begin(), method.end(), method.begin(), ::toupper);
575 if (config.action == Action::UPLOAD && (method == "POST" || method == "PUT")) {
576 config.method = method;
577 }
578 if (config.action == Action::DOWNLOAD && (method == "POST" || method == "GET")) {
579 config.method = method;
580 }
581 }
582 }
583
ParseData(napi_env env,napi_value jsConfig,Config & config)584 bool JsInitialize::ParseData(napi_env env, napi_value jsConfig, Config &config)
585 {
586 napi_value value = NapiUtils::GetNamedProperty(env, jsConfig, "data");
587 if (value == nullptr) {
588 return true;
589 }
590
591 napi_valuetype valueType = NapiUtils::GetValueType(env, value);
592 if (config.action == Action::UPLOAD && valueType == napi_object) {
593 return Convert2FormItems(env, value, config.forms, config.files);
594 } else if (config.action == Action::DOWNLOAD && valueType == napi_string) {
595 config.data = NapiUtils::Convert2String(env, value);
596 } else {
597 REQUEST_HILOGE("data type is error");
598 return false;
599 }
600 return true;
601 }
602
ParseName(napi_env env,napi_value jsVal,std::string & name)603 bool JsInitialize::ParseName(napi_env env, napi_value jsVal, std::string &name)
604 {
605 napi_value value = NapiUtils::GetNamedProperty(env, jsVal, "name");
606 if (NapiUtils::GetValueType(env, value) != napi_string) {
607 return false;
608 }
609 name = NapiUtils::Convert2String(env, value);
610 return true;
611 }
612
GetFormItems(napi_env env,napi_value jsVal,std::vector<FormItem> & forms,std::vector<FileSpec> & files)613 bool JsInitialize::GetFormItems(
614 napi_env env, napi_value jsVal, std::vector<FormItem> &forms, std::vector<FileSpec> &files)
615 {
616 if (!NapiUtils::HasNamedProperty(env, jsVal, "name") || !NapiUtils::HasNamedProperty(env, jsVal, "value")) {
617 return false;
618 }
619
620 std::string name;
621 if (!ParseName(env, jsVal, name)) {
622 return false;
623 }
624 napi_value value = NapiUtils::GetNamedProperty(env, jsVal, "value");
625 if (value == nullptr) {
626 REQUEST_HILOGE("Get upload value failed");
627 return false;
628 }
629 bool isArray = false;
630 napi_is_array(env, value, &isArray);
631 napi_valuetype valueType = NapiUtils::GetValueType(env, value);
632 if (valueType == napi_string) {
633 FormItem form;
634 form.name = name;
635 form.value = NapiUtils::Convert2String(env, value);
636 forms.push_back(form);
637 } else if (valueType == napi_object && !isArray) {
638 FileSpec file;
639 if (!Convert2FileSpec(env, value, name, file)) {
640 REQUEST_HILOGE("Convert2FileSpec failed");
641 return false;
642 }
643 files.push_back(file);
644 } else if (isArray) {
645 if (!Convert2FileSpecs(env, value, name, files)) {
646 return false;
647 }
648 } else {
649 REQUEST_HILOGE("value type is error");
650 return false;
651 }
652 return true;
653 }
654
Convert2FormItems(napi_env env,napi_value jsValue,std::vector<FormItem> & forms,std::vector<FileSpec> & files)655 bool JsInitialize::Convert2FormItems(
656 napi_env env, napi_value jsValue, std::vector<FormItem> &forms, std::vector<FileSpec> &files)
657 {
658 bool isArray = false;
659 napi_is_array(env, jsValue, &isArray);
660 NAPI_ASSERT_BASE(env, isArray, "not array", false);
661 uint32_t length = 0;
662 napi_get_array_length(env, jsValue, &length);
663 for (uint32_t i = 0; i < length; ++i) {
664 napi_value jsVal = nullptr;
665 napi_handle_scope scope = nullptr;
666 napi_open_handle_scope(env, &scope);
667 napi_get_element(env, jsValue, i, &jsVal);
668 if (jsVal == nullptr) {
669 REQUEST_HILOGE("Get element jsVal failed");
670 return false;
671 }
672 if (!GetFormItems(env, jsVal, forms, files)) {
673 REQUEST_HILOGE("Get formItems failed");
674 return false;
675 }
676 napi_close_handle_scope(env, scope);
677 }
678 if (files.empty()) {
679 return false;
680 }
681 return true;
682 }
683
Convert2FileSpecs(napi_env env,napi_value jsValue,const std::string & name,std::vector<FileSpec> & files)684 bool JsInitialize::Convert2FileSpecs(
685 napi_env env, napi_value jsValue, const std::string &name, std::vector<FileSpec> &files)
686 {
687 REQUEST_HILOGD("Convert2FileSpecs in");
688 uint32_t length = 0;
689 napi_get_array_length(env, jsValue, &length);
690 for (uint32_t i = 0; i < length; ++i) {
691 napi_value jsVal = nullptr;
692 napi_handle_scope scope = nullptr;
693 napi_open_handle_scope(env, &scope);
694 napi_get_element(env, jsValue, i, &jsVal);
695 if (jsVal == nullptr) {
696 return false;
697 }
698 FileSpec file;
699 bool ret = Convert2FileSpec(env, jsVal, name, file);
700 if (!ret) {
701 return false;
702 }
703 files.push_back(file);
704 napi_close_handle_scope(env, scope);
705 }
706 return true;
707 }
708
InterceptData(const std::string & str,const std::string & in,std::string & out)709 void JsInitialize::InterceptData(const std::string &str, const std::string &in, std::string &out)
710 {
711 std::string tmpStr = std::string(in, 0, in.find_last_not_of(' ') + 1);
712 std::size_t position = tmpStr.find_last_of(str);
713 if (position == std::string::npos || position >= tmpStr.size() - 1) {
714 return;
715 }
716 out = std::string(tmpStr, position + 1);
717 }
718
Convert2FileSpec(napi_env env,napi_value jsValue,const std::string & name,FileSpec & file)719 bool JsInitialize::Convert2FileSpec(napi_env env, napi_value jsValue, const std::string &name, FileSpec &file)
720 {
721 REQUEST_HILOGD("Convert2FileSpec in");
722 file.name = name;
723 file.uri = NapiUtils::Convert2String(env, jsValue, "path");
724 if (file.uri.empty()) {
725 return false;
726 }
727 file.filename = NapiUtils::Convert2String(env, jsValue, "filename");
728 file.type = NapiUtils::Convert2String(env, jsValue, "mimetype");
729 return true;
730 }
731
ParseRedirect(napi_env env,napi_value jsConfig,bool & redirect)732 void JsInitialize::ParseRedirect(napi_env env, napi_value jsConfig, bool &redirect)
733 {
734 if (!NapiUtils::HasNamedProperty(env, jsConfig, "redirect")) {
735 redirect = true;
736 } else {
737 redirect = NapiUtils::Convert2Boolean(env, jsConfig, "redirect");
738 }
739 }
740
ParseRetry(napi_env env,napi_value jsConfig,bool & retry)741 void JsInitialize::ParseRetry(napi_env env, napi_value jsConfig, bool &retry)
742 {
743 if (!NapiUtils::HasNamedProperty(env, jsConfig, "retry")) {
744 retry = true;
745 } else {
746 retry = NapiUtils::Convert2Boolean(env, jsConfig, "retry");
747 }
748 }
749
IsStageMode(napi_env env,napi_value value)750 bool JsInitialize::IsStageMode(napi_env env, napi_value value)
751 {
752 bool stageMode = true;
753 napi_status status = OHOS::AbilityRuntime::IsStageContext(env, value, stageMode);
754 if (status != napi_ok || !stageMode) {
755 return false;
756 }
757 return stageMode;
758 }
759
ParseConfigV9(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)760 bool JsInitialize::ParseConfigV9(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
761 {
762 REQUEST_HILOGD("ParseConfigV9 in");
763 config.action = NapiUtils::GetRequestAction(env, jsConfig);
764 config.headers = ParseMap(env, jsConfig, "header");
765 if (!ParseUrl(env, jsConfig, config.url)) {
766 errInfo = "Parse url error";
767 return false;
768 }
769 auto func = config.action == Action::UPLOAD ? ParseUploadConfig : ParseDownloadConfig;
770 if (!func(env, jsConfig, config, errInfo)) {
771 return false;
772 }
773 ParseTitle(env, jsConfig, config);
774 return true;
775 }
776
ParseUploadConfig(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)777 bool JsInitialize::ParseUploadConfig(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
778 {
779 REQUEST_HILOGD("ParseUploadConfig in");
780 ParseMethod(env, jsConfig, config);
781 napi_value jsFiles = NapiUtils::GetNamedProperty(env, jsConfig, PARAM_KEY_FILES);
782 if (jsFiles == nullptr) {
783 errInfo = "Parse config files error";
784 return false;
785 }
786
787 config.files = NapiUtils::Convert2FileVector(env, jsFiles, "API8");
788 if (config.files.empty()) {
789 errInfo = "Parse config files error";
790 return false;
791 }
792
793 napi_value jsData = NapiUtils::GetNamedProperty(env, jsConfig, PARAM_KEY_DATA);
794 if (jsData == nullptr) {
795 errInfo = "Parse config data error";
796 return false;
797 }
798 config.forms = NapiUtils::Convert2RequestDataVector(env, jsData);
799
800 if (!ParseIndex(env, jsConfig, config)) {
801 errInfo = "Index exceeds file list";
802 return false;
803 }
804
805 config.begins = ParseBegins(env, jsConfig);
806 config.ends = ParseEnds(env, jsConfig);
807 return true;
808 }
809
ParseDownloadConfig(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)810 bool JsInitialize::ParseDownloadConfig(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
811 {
812 REQUEST_HILOGD("ParseDownloadConfig in");
813 config.metered = NapiUtils::Convert2Boolean(env, jsConfig, "enableMetered");
814 config.roaming = NapiUtils::Convert2Boolean(env, jsConfig, "enableRoaming");
815 config.description = NapiUtils::Convert2String(env, jsConfig, PARAM_KEY_DESCRIPTION);
816 uint32_t type = NapiUtils::Convert2Uint32(env, jsConfig, PARAM_KEY_NETWORKTYPE);
817 if (type == NETWORK_MOBILE) {
818 config.network = Network::CELLULAR;
819 } else if (type == NETWORK_WIFI) {
820 config.network = Network::WIFI;
821 } else {
822 config.network = Network::ANY;
823 }
824 config.saveas = NapiUtils::Convert2String(env, jsConfig, PARAM_KEY_FILE_PATH);
825 if (config.saveas.empty()) {
826 InterceptData("/", config.url, config.saveas);
827 }
828 config.background = NapiUtils::Convert2Boolean(env, jsConfig, PARAM_KEY_BACKGROUND);
829 config.method = "GET";
830 return true;
831 }
832
CreatProperties(napi_env env,napi_value & self,napi_value config,JsTask * task)833 void JsInitialize::CreatProperties(napi_env env, napi_value &self, napi_value config, JsTask *task)
834 {
835 if (task->config_.version == Version::API10) {
836 NapiUtils::SetStringPropertyUtf8(env, self, "tid", task->GetTid());
837 napi_set_named_property(env, self, "config", config);
838 }
839 }
840
CreateDirs(const std::vector<std::string> & pathDirs)841 bool JsInitialize::CreateDirs(const std::vector<std::string> &pathDirs)
842 {
843 std::string path;
844 for (auto elem : pathDirs) {
845 path += "/" + elem;
846 std::error_code err;
847 if (std::filesystem::exists(path, err)) {
848 continue;
849 }
850 err.clear();
851 // create_directory noexcept.
852 if (!std::filesystem::create_directory(path, err)) {
853 REQUEST_HILOGE("Create Dir Err: %{public}d, %{public}s", err.value(), err.message().c_str());
854 return false;
855 }
856 }
857 return true;
858 }
859
StringSplit(const std::string & str,const char delim,std::vector<std::string> & elems)860 void JsInitialize::StringSplit(const std::string &str, const char delim, std::vector<std::string> &elems)
861 {
862 std::stringstream stream(str);
863 std::string item;
864 while (std::getline(stream, item, delim)) {
865 if (!item.empty()) {
866 elems.push_back(item);
867 }
868 }
869 return;
870 }
871 } // namespace OHOS::Request
872