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 <fcntl.h>
19 #include <securec.h>
20 #include <sys/stat.h>
21
22 #include <algorithm>
23 #include <cstdio>
24 #include <cstring>
25 #include <filesystem>
26 #include <fstream>
27 #include <regex>
28 #include <string>
29 #include <system_error>
30
31 #include "js_common.h"
32 #include "log.h"
33 #include "napi_utils.h"
34 #include "net_conn_client.h"
35 #include "request_manager.h"
36
37
38 static constexpr const char *PARAM_KEY_DESCRIPTION = "description";
39 static constexpr const char *PARAM_KEY_NETWORKTYPE = "networkType";
40 static constexpr const char *PARAM_KEY_FILE_PATH = "filePath";
41 static constexpr const char *PARAM_KEY_BACKGROUND = "background";
42 static constexpr uint32_t FILE_PERMISSION = 0644;
43 static constexpr uint32_t TITLE_MAXIMUM = 256;
44 static constexpr uint32_t DESCRIPTION_MAXIMUM = 1024;
45 static constexpr uint32_t URL_MAXIMUM = 2048;
46 static constexpr uint32_t PROXY_MAXIMUM = 512;
47
48 namespace OHOS::Request {
Initialize(napi_env env,napi_callback_info info,Version version,bool firstInit)49 napi_value JsInitialize::Initialize(napi_env env, napi_callback_info info, Version version, bool firstInit)
50 {
51 REQUEST_HILOGD("constructor request task!");
52 bool withErrCode = version != Version::API8;
53 napi_value self = nullptr;
54 size_t argc = NapiUtils::MAX_ARGC;
55 napi_value argv[NapiUtils::MAX_ARGC] = { nullptr };
56 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
57 int32_t number = version == Version::API8 ? NapiUtils::ONE_ARG : NapiUtils::TWO_ARG;
58 if (static_cast<int32_t>(argc) < number) {
59 NapiUtils::ThrowError(
60 env, E_PARAMETER_CHECK, "Missing mandatory parameters, invalid parameter count", withErrCode);
61 return nullptr;
62 }
63
64 Config config;
65 config.version = version;
66 config.withErrCode = withErrCode;
67 config.firstInit = firstInit;
68 std::shared_ptr<OHOS::AbilityRuntime::Context> context = nullptr;
69 ExceptionError err = InitParam(env, argv, context, config);
70 if (err.code != E_OK) {
71 REQUEST_HILOGE("err.code : %{public}d, err.errInfo : %{public}s", err.code, err.errInfo.c_str());
72 NapiUtils::ThrowError(env, err.code, err.errInfo, withErrCode);
73 return nullptr;
74 }
75 auto *task = new (std::nothrow) JsTask();
76 if (task == nullptr) {
77 REQUEST_HILOGE("Create task object failed");
78 return nullptr;
79 }
80 task->config_ = config;
81 task->isGetPermission = true;
82 RequestManager::GetInstance()->RestoreListener(JsTask::ReloadListener);
83 // `finalize` executes on the JS thread
84 auto finalize = [](napi_env env, void *data, void *hint) {
85 REQUEST_HILOGD("destructed task");
86 JsTask *task = reinterpret_cast<JsTask *>(data);
87 JsTask::ClearTaskMap(task->GetTid());
88 RequestManager::GetInstance()->RemoveAllListeners(task->GetTid());
89 delete task;
90 };
91 if (napi_wrap(env, self, task, finalize, nullptr, nullptr) != napi_ok) {
92 finalize(env, task, nullptr);
93 return nullptr;
94 }
95 return self;
96 }
97
InitParam(napi_env env,napi_value * argv,std::shared_ptr<OHOS::AbilityRuntime::Context> & context,Config & config)98 ExceptionError JsInitialize::InitParam(
99 napi_env env, napi_value *argv, std::shared_ptr<OHOS::AbilityRuntime::Context> &context, Config &config)
100 {
101 REQUEST_HILOGD("InitParam in");
102 ExceptionError err = { .code = E_OK };
103 int parametersPosition = config.version == Version::API8 ? CONFIG_PARAM_AT_FIRST : CONFIG_PARAM_AT_SECOND;
104
105 napi_status getStatus = GetContext(env, argv[0], context);
106 if (getStatus != napi_ok) {
107 REQUEST_HILOGE("Get context fail");
108 err.code = E_PARAMETER_CHECK;
109 err.errInfo = "Parameter verification failed, Get context fail";
110 return err;
111 }
112 auto applicationInfo = context->GetApplicationInfo();
113 if (applicationInfo == nullptr) {
114 err.code = E_OTHER;
115 err.errInfo = "ApplicationInfo is null";
116 return err;
117 }
118 config.bundleType = static_cast<u_int32_t>(applicationInfo->bundleType);
119 REQUEST_HILOGD("config.bundleType is %{public}d", config.bundleType);
120 if (!ParseConfig(env, argv[parametersPosition], config, err.errInfo)) {
121 err.code = E_PARAMETER_CHECK;
122 return err;
123 }
124 config.bundleName = context->GetBundleName();
125 REQUEST_HILOGD("config.bundleName is %{public}s", config.bundleName.c_str());
126 CheckFilePath(context, config, err);
127 return err;
128 }
129
GetContext(napi_env env,napi_value value,std::shared_ptr<OHOS::AbilityRuntime::Context> & context)130 napi_status JsInitialize::GetContext(
131 napi_env env, napi_value value, std::shared_ptr<OHOS::AbilityRuntime::Context> &context)
132 {
133 if (!IsStageMode(env, value)) {
134 auto ability = OHOS::AbilityRuntime::GetCurrentAbility(env);
135 if (ability == nullptr) {
136 REQUEST_HILOGE("Get current ability fail");
137 return napi_generic_failure;
138 }
139 context = ability->GetAbilityContext();
140 } else {
141 context = OHOS::AbilityRuntime::GetStageModeContext(env, value);
142 }
143 if (context == nullptr) {
144 REQUEST_HILOGE("Get Context failed, context is nullptr.");
145 return napi_generic_failure;
146 }
147 return napi_ok;
148 }
149
GetAppBaseDir(std::string & baseDir)150 bool JsInitialize::GetAppBaseDir(std::string &baseDir)
151 {
152 auto context = AbilityRuntime::Context::GetApplicationContext();
153 if (context == nullptr) {
154 REQUEST_HILOGE("AppContext is null.");
155 return false;
156 }
157 baseDir = context->GetBaseDir();
158 if (baseDir.empty()) {
159 REQUEST_HILOGE("Base dir not found.");
160 return false;
161 }
162 return true;
163 }
164
CheckFilePath(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,Config & config,ExceptionError & error)165 bool JsInitialize::CheckFilePath(
166 const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, Config &config, ExceptionError &error)
167 {
168 if (config.action == Action::DOWNLOAD) {
169 if (!CheckDownloadFile(context, config, error)) {
170 return false;
171 }
172 } else {
173 if (!CheckUploadFiles(context, config, error)) {
174 return false;
175 }
176 std::string filePath = context->GetCacheDir();
177 if (!CheckUploadBodyFiles(filePath, config, error)) {
178 return false;
179 }
180 }
181 if (!JsTask::SetDirsPermission(config.certsPath)) {
182 error.code = E_FILE_IO;
183 error.errInfo = "set files of directors permission fail";
184 return false;
185 }
186 return true;
187 }
188
CheckUploadBodyFiles(const std::string & filePath,Config & config,ExceptionError & error)189 bool JsInitialize::CheckUploadBodyFiles(const std::string &filePath, Config &config, ExceptionError &error)
190 {
191 size_t len = config.files.size();
192
193 for (size_t i = 0; i < len; i++) {
194 if (filePath.empty()) {
195 REQUEST_HILOGE("internal to cache error");
196 error.code = E_PARAMETER_CHECK;
197 error.errInfo = "Parameter verification failed, UploadBodyFiles error empty path";
198 return false;
199 }
200 auto now = std::chrono::high_resolution_clock::now();
201 auto timestamp = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
202 std::string path = filePath + "/tmp_body_" + std::to_string(i) + "_" + std::to_string(timestamp);
203 REQUEST_HILOGD("Create upload body file, %{public}s", path.c_str());
204 if (!NapiUtils::IsPathValid(path)) {
205 REQUEST_HILOGE("IsPathValid error %{public}s", path.c_str());
206 error.code = E_PARAMETER_CHECK;
207 error.errInfo = "Parameter verification failed, UploadBodyFiles error fail path";
208 return false;
209 }
210 FILE *bodyFile = fopen(path.c_str(), "w+");
211 if (bodyFile == NULL) {
212 error.code = E_FILE_IO;
213 error.errInfo = "UploadBodyFiles failed to open file errno " + std::to_string(errno);
214 return false;
215 }
216 int32_t ret = chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
217 if (ret != 0) {
218 REQUEST_HILOGE("body chmod fail: %{public}d", ret);
219 };
220
221 bool setRes = JsTask::SetPathPermission(path);
222 int32_t retClose = fclose(bodyFile);
223 if (retClose != 0) {
224 REQUEST_HILOGE("upload body fclose fail: %{public}d", ret);
225 }
226 if (!setRes) {
227 error.code = E_FILE_IO;
228 error.errInfo = "UploadBodyFiles set body path permission fail";
229 return false;
230 }
231 config.bodyFileNames.push_back(path);
232 }
233 return true;
234 }
235
CheckPathIsFile(const std::string & path,ExceptionError & error)236 bool JsInitialize::CheckPathIsFile(const std::string &path, ExceptionError &error)
237 {
238 std::error_code err;
239 if (!std::filesystem::exists(path, err)) {
240 error.code = E_FILE_IO;
241 error.errInfo = "Path not exists: " + err.message();
242 return false;
243 }
244 if (std::filesystem::is_directory(path, err)) {
245 error.code = E_FILE_IO;
246 error.errInfo = "Path not File: " + err.message();
247 return false;
248 }
249 return true;
250 }
251
GetFdDownload(const std::string & path,const Config & config,ExceptionError & error)252 bool JsInitialize::GetFdDownload(const std::string &path, const Config &config, ExceptionError &error)
253 {
254 // File is exist.
255 if (JsInitialize::FindDir(path)) {
256 if (config.firstInit && !config.overwrite) {
257 error.code = config.version == Version::API10 ? E_FILE_IO : E_FILE_PATH;
258 error.errInfo = "GetFd File exists and other error";
259 return false;
260 }
261 }
262
263 FILE *file = NULL;
264 if (config.firstInit) {
265 file = fopen(path.c_str(), "w+");
266 } else {
267 file = fopen(path.c_str(), "a+");
268 }
269
270 if (file == NULL) {
271 error.code = E_FILE_IO;
272 error.errInfo = "GetFd failed to open file errno " + std::to_string(errno);
273 return false;
274 }
275
276 int32_t ret = chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
277 if (ret != 0) {
278 REQUEST_HILOGE("download file chmod fail: %{public}d", ret);
279 };
280
281 int32_t retClose = fclose(file);
282 if (retClose != 0) {
283 REQUEST_HILOGE("download fclose fail: %{public}d", ret);
284 }
285 return true;
286 }
287
GetFdUpload(const std::string & path,const Config & config,ExceptionError & error)288 bool JsInitialize::GetFdUpload(const std::string &path, const Config &config, ExceptionError &error)
289 {
290 if (!JsInitialize::CheckPathIsFile(path, error)) {
291 error.code = config.version == Version::API10 ? E_FILE_IO : E_FILE_PATH;
292 return false;
293 }
294 FILE *file = fopen(path.c_str(), "r");
295 if (file == NULL) {
296 error.code = config.version == Version::API10 ? E_FILE_IO : E_FILE_PATH;
297 error.errInfo = "GetFd failed to open file errno " + std::to_string(errno);
298 return false;
299 }
300 REQUEST_HILOGD("upload file fopen ok");
301 int32_t ret = chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
302 if (ret != 0) {
303 REQUEST_HILOGE("upload file chmod fail: %{public}d", ret);
304 }
305 int32_t retClose = fclose(file);
306 if (retClose != 0) {
307 REQUEST_HILOGE("upload fclose fail: %{public}d", ret);
308 }
309 return true;
310 }
311
GetInternalPath(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,const Config & config,std::string & path,std::string & errInfo)312 bool JsInitialize::GetInternalPath(const std::shared_ptr<OHOS::AbilityRuntime::Context> &context,
313 const Config &config, std::string &path, std::string &errInfo)
314 {
315 std::string fileName;
316 std::string pattern = "internal://cache/";
317 size_t pos = path.find(pattern);
318 if (pos != 0) {
319 fileName = path;
320 } else {
321 fileName = path.substr(pattern.size(), path.size());
322 }
323 if (fileName.empty()) {
324 errInfo = "Parameter verification failed, GetInternalPath failed, fileName is empty";
325 return false;
326 }
327 path = context->GetCacheDir();
328 if (path.empty()) {
329 REQUEST_HILOGE("internal to cache error");
330 errInfo = "Parameter verification failed, GetInternalPath failed, cache path is empty";
331 return false;
332 }
333 path += "/" + fileName;
334 if (!NapiUtils::IsPathValid(path)) {
335 REQUEST_HILOGE("IsPathValid error %{public}s", path.c_str());
336 errInfo = "Parameter verification failed, GetInternalPath failed, filePath is not valid";
337 return false;
338 }
339 return true;
340 }
341
SetParseConfig(napi_env env,napi_value jsConfig,Config & config)342 void JsInitialize::SetParseConfig(napi_env env, napi_value jsConfig, Config &config)
343 {
344 config.overwrite = NapiUtils::Convert2Boolean(env, jsConfig, "overwrite");
345 config.metered = NapiUtils::Convert2Boolean(env, jsConfig, "metered");
346 config.gauge = NapiUtils::Convert2Boolean(env, jsConfig, "gauge");
347 config.precise = NapiUtils::Convert2Boolean(env, jsConfig, "precise");
348 config.priority = ParsePriority(env, jsConfig);
349 config.begins = ParseBegins(env, jsConfig);
350 config.ends = ParseEnds(env, jsConfig);
351 config.mode = static_cast<Mode>(NapiUtils::Convert2Uint32(env, jsConfig, "mode"));
352 config.headers = ParseMap(env, jsConfig, "headers");
353 config.extras = ParseMap(env, jsConfig, "extras");
354 if (config.mode == Mode::BACKGROUND) {
355 config.background = true;
356 }
357 }
358
ParseConfig(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)359 bool JsInitialize::ParseConfig(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
360 {
361 if (NapiUtils::GetValueType(env, jsConfig) != napi_object) {
362 errInfo = "Incorrect parameter type, Wrong config type, expected object";
363 return false;
364 }
365 if (config.version != Version::API10) {
366 return ParseConfigV9(env, jsConfig, config, errInfo);
367 }
368
369 if (!ParseAction(env, jsConfig, config.action, errInfo)) {
370 return false;
371 }
372 if (!ParseUrl(env, jsConfig, config.url, errInfo)) {
373 return false;
374 }
375 if (!ParseCertsPath(env, jsConfig, config.certsPath, errInfo)) {
376 return false;
377 }
378 if (!ParseData(env, jsConfig, config, errInfo)) {
379 return false;
380 }
381 if (!ParseIndex(env, jsConfig, config, errInfo)) {
382 return false;
383 }
384 if (!ParseProxy(env, jsConfig, config.proxy, errInfo)) {
385 return false;
386 }
387 if (!ParseTitle(env, jsConfig, config, errInfo) || !ParseToken(env, jsConfig, config, errInfo)
388 || !ParseDescription(env, jsConfig, config.description, errInfo)) {
389 return false;
390 }
391 if (!ParseSaveas(env, jsConfig, config, errInfo)) {
392 return false;
393 }
394 ParseCertificatePins(env, config.url, config.certificatePins);
395 ParseMethod(env, jsConfig, config);
396 ParseRoaming(env, jsConfig, config);
397 ParseRedirect(env, jsConfig, config.redirect);
398 ParseNetwork(env, jsConfig, config.network);
399 ParseRetry(env, jsConfig, config.retry);
400 SetParseConfig(env, jsConfig, config);
401 return true;
402 }
403
ParseRoaming(napi_env env,napi_value jsConfig,Config & config)404 void JsInitialize::ParseRoaming(napi_env env, napi_value jsConfig, Config &config)
405 {
406 if (!NapiUtils::HasNamedProperty(env, jsConfig, "roaming")) {
407 config.roaming = config.version == Version::API10;
408 } else {
409 config.roaming = NapiUtils::Convert2Boolean(env, jsConfig, "roaming");
410 }
411 }
412
ParseNetwork(napi_env env,napi_value jsConfig,Network & network)413 void JsInitialize::ParseNetwork(napi_env env, napi_value jsConfig, Network &network)
414 {
415 network = static_cast<Network>(NapiUtils::Convert2Uint32(env, jsConfig, "network"));
416 if (network != Network::ANY && network != Network::WIFI && network != Network::CELLULAR) {
417 network = Network::ANY;
418 }
419 }
420
ParseToken(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)421 bool JsInitialize::ParseToken(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
422 {
423 char *token = nullptr;
424 size_t len = 0;
425 if (!NapiUtils::HasNamedProperty(env, jsConfig, "token")) {
426 return true;
427 }
428 napi_value value = NapiUtils::GetNamedProperty(env, jsConfig, "token");
429 if (NapiUtils::GetValueType(env, value) != napi_string) {
430 return true;
431 }
432 uint32_t bufferLen = TOKEN_MAX_BYTES + 2;
433 token = new char[bufferLen];
434 napi_status status = napi_get_value_string_utf8(env, value, token, bufferLen, &len);
435 if (status != napi_ok) {
436 REQUEST_HILOGE("napi get value string utf8 failed");
437 memset_s(token, bufferLen, 0, bufferLen);
438 errInfo = "Parameter verification failed, get parameter config.token failed";
439 delete[] token;
440 return false;
441 }
442 if (len < TOKEN_MIN_BYTES || len > TOKEN_MAX_BYTES) {
443 memset_s(token, bufferLen, 0, bufferLen);
444 errInfo = "Parameter verification failed, the length of token should between 8 and 2048 bytes";
445 delete[] token;
446 return false;
447 }
448 config.token = std::string(token, len);
449 memset_s(token, bufferLen, 0, bufferLen);
450 delete[] token;
451 return true;
452 }
453
ParseIndex(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)454 bool JsInitialize::ParseIndex(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
455 {
456 config.index = NapiUtils::Convert2Uint32(env, jsConfig, "index");
457 if (config.action == Action::DOWNLOAD) {
458 config.index = 0;
459 return true;
460 }
461 if (config.files.size() <= config.index) {
462 REQUEST_HILOGE("files.size is %{public}zu, index is %{public}d", config.files.size(), config.index);
463 errInfo = "Parameter verification failed, config.index exceeds file list";
464 return false;
465 }
466 return true;
467 }
468
ParseAction(napi_env env,napi_value jsConfig,Action & action,std::string & errInfo)469 bool JsInitialize::ParseAction(napi_env env, napi_value jsConfig, Action &action, std::string &errInfo)
470 {
471 if (!NapiUtils::HasNamedProperty(env, jsConfig, "action")) {
472 REQUEST_HILOGE("ParseAction err");
473 errInfo = "Missing mandatory parameters, can not find property action";
474 return false;
475 }
476 napi_value value = NapiUtils::GetNamedProperty(env, jsConfig, "action");
477 if (NapiUtils::GetValueType(env, value) != napi_number) {
478 REQUEST_HILOGE("GetNamedProperty err");
479 errInfo = "Incorrect parameter type, action type is not of napi_number type";
480 return false;
481 }
482 action = static_cast<Action>(NapiUtils::Convert2Uint32(env, value));
483 if (action != Action::DOWNLOAD && action != Action::UPLOAD) {
484 REQUEST_HILOGE("Must be UPLOAD or DOWNLOAD");
485 errInfo = "Parameter verification failed, action must be UPLOAD or DOWNLOAD";
486 return false;
487 }
488 return true;
489 }
490
491 // Only use for Action::DOWNLOAD.
ParseSaveas(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)492 bool JsInitialize::ParseSaveas(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
493 {
494 if (config.action != Action::DOWNLOAD) {
495 config.saveas = "";
496 return true;
497 }
498 std::string temp = NapiUtils::Convert2String(env, jsConfig, "saveas");
499 StringTrim(temp);
500 if (temp.empty() || temp == "./") {
501 bool result = InterceptData("/", config.url, config.saveas);
502 if (!result) {
503 errInfo = "Parameter verification failed, config.saveas parse error";
504 }
505 return result;
506 }
507 if (temp.size() == 0 || temp[temp.size() - 1] == '/') {
508 errInfo = "Parameter verification failed, config.saveas parse error";
509 return false;
510 }
511 config.saveas = temp;
512 return true;
513 }
514
ParseBegins(napi_env env,napi_value jsConfig)515 int64_t JsInitialize::ParseBegins(napi_env env, napi_value jsConfig)
516 {
517 int64_t size = NapiUtils::Convert2Int64(env, jsConfig, "begins");
518 return size >= 0 ? size : 0;
519 }
520
ParseEnds(napi_env env,napi_value jsConfig)521 int64_t JsInitialize::ParseEnds(napi_env env, napi_value jsConfig)
522 {
523 if (!NapiUtils::HasNamedProperty(env, jsConfig, "ends")) {
524 return -1;
525 }
526
527 napi_value value = NapiUtils::GetNamedProperty(env, jsConfig, "ends");
528 if (NapiUtils::GetValueType(env, value) != napi_number) {
529 return -1;
530 }
531 return NapiUtils::Convert2Int64(env, value);
532 }
533
ParsePriority(napi_env env,napi_value jsConfig)534 uint32_t JsInitialize::ParsePriority(napi_env env, napi_value jsConfig)
535 {
536 if (!NapiUtils::HasNamedProperty(env, jsConfig, "priority")) {
537 return 0;
538 }
539 return NapiUtils::Convert2Uint32(env, jsConfig, "priority");
540 }
541
ParseDescription(napi_env env,napi_value jsConfig,std::string & description,std::string & errInfo)542 bool JsInitialize::ParseDescription(napi_env env, napi_value jsConfig, std::string &description, std::string &errInfo)
543 {
544 description = NapiUtils::Convert2String(env, jsConfig, "description");
545 if (description.size() > DESCRIPTION_MAXIMUM) {
546 errInfo = "Parameter verification failed, the length of config.description exceeds 1024";
547 return false;
548 }
549 return true;
550 }
551
ParseMap(napi_env env,napi_value jsConfig,const std::string & propertyName)552 std::map<std::string, std::string> JsInitialize::ParseMap(
553 napi_env env, napi_value jsConfig, const std::string &propertyName)
554 {
555 std::map<std::string, std::string> result;
556 napi_value jsValue = NapiUtils::GetNamedProperty(env, jsConfig, propertyName);
557 napi_valuetype jsType = NapiUtils::GetValueType(env, jsValue);
558 if (jsType == napi_undefined) {
559 return result;
560 }
561 auto names = NapiUtils::GetPropertyNames(env, jsValue);
562 for (auto iter = names.begin(); iter != names.end(); ++iter) {
563 // The value of `Header` or `extra` can be empty.
564 result[*iter] = NapiUtils::Convert2String(env, jsValue, *iter);
565 }
566 return result;
567 }
568
ParseUrl(napi_env env,napi_value jsConfig,std::string & url,std::string & errInfo)569 bool JsInitialize::ParseUrl(napi_env env, napi_value jsConfig, std::string &url, std::string &errInfo)
570 {
571 url = NapiUtils::Convert2String(env, jsConfig, "url");
572 if (url.size() > URL_MAXIMUM) {
573 REQUEST_HILOGE("The URL exceeds the maximum length of 2048");
574 errInfo = "Parameter verification failed, the length of url exceeds 2048";
575 return false;
576 }
577 if (!regex_match(url, std::regex("^http(s)?:\\/\\/.+"))) {
578 REQUEST_HILOGE("ParseUrl error");
579 errInfo = "Parameter verification failed, the url should start with http(s)://";
580 return false;
581 }
582
583 return true;
584 }
585
ParseCertsPath(napi_env env,napi_value jsConfig,std::vector<std::string> & certsPath,std::string & errInfo)586 bool JsInitialize::ParseCertsPath(
587 napi_env env, napi_value jsConfig, std::vector<std::string> &certsPath, std::string &errInfo)
588 {
589 std::string url = NapiUtils::Convert2String(env, jsConfig, "url");
590 if (url.size() > URL_MAXIMUM) {
591 REQUEST_HILOGE("The URL exceeds the maximum length of 2048");
592 errInfo = "Parameter verification failed, the length of url exceeds 2048";
593 return false;
594 }
595 if (!regex_match(url, std::regex("^http(s)?:\\/\\/.+"))) {
596 REQUEST_HILOGE("ParseUrl error");
597 errInfo = "Parameter verification failed, the url should start with http(s)://";
598 return false;
599 }
600
601 typedef std::string::const_iterator iter_t;
602
603 iter_t urlEnd = url.end();
604 iter_t protocolStart = url.cbegin();
605 iter_t protocolEnd = std::find(protocolStart, urlEnd, ':');
606 std::string protocol = std::string(protocolStart, protocolEnd);
607 if (protocol != "https") {
608 REQUEST_HILOGD("Using Http");
609 return true;
610 }
611 if (protocolEnd != urlEnd) {
612 std::string afterProtocol = &*(protocolEnd);
613 // 3 is the num of ://
614 if ((afterProtocol.length() > 3) && (afterProtocol.substr(0, 3) == "://")) {
615 // 3 means go beyound :// in protocolEnd
616 protocolEnd += 3;
617 } else {
618 protocolEnd = url.cbegin();
619 }
620 } else {
621 protocolEnd = url.cbegin();
622 }
623 iter_t hostStart = protocolEnd;
624 iter_t pathStart = std::find(hostStart, urlEnd, '/');
625 iter_t queryStart = std::find(url.cbegin(), urlEnd, '?');
626 iter_t hostEnd = std::find(protocolEnd, (pathStart != urlEnd) ? pathStart : queryStart, ':');
627 std::string hostname = std::string(hostStart, hostEnd);
628 REQUEST_HILOGD("Hostname is %{public}s", hostname.c_str());
629 NetManagerStandard::NetConnClient::GetInstance().GetTrustAnchorsForHostName(hostname, certsPath);
630 return true;
631 }
632
ParseTitle(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)633 bool JsInitialize::ParseTitle(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
634 {
635 config.title = NapiUtils::Convert2String(env, jsConfig, "title");
636 if (config.version == Version::API10 && config.title.size() > TITLE_MAXIMUM) {
637 errInfo = "Parameter verification failed, the length of config title exceeds 256";
638 return false;
639 }
640 if (config.title.empty()) {
641 config.title = config.action == Action::UPLOAD ? "upload" : "download";
642 }
643 return true;
644 }
645
ParseProxy(napi_env env,napi_value jsConfig,std::string & proxy,std::string & errInfo)646 bool JsInitialize::ParseProxy(napi_env env, napi_value jsConfig, std::string &proxy, std::string &errInfo)
647 {
648 proxy = NapiUtils::Convert2String(env, jsConfig, "proxy");
649 if (proxy.empty()) {
650 return true;
651 }
652
653 if (proxy.size() > PROXY_MAXIMUM) {
654 REQUEST_HILOGE("The proxy exceeds the maximum length of 512");
655 errInfo = "Parameter verification failed, the length of config.proxy exceeds 512";
656 return false;
657 }
658
659 if (!regex_match(proxy, std::regex("^http:\\/\\/.+:\\d{1,5}$"))) {
660 REQUEST_HILOGE("ParseProxy error");
661 errInfo = "Parameter verification failed, the format of proxy is http(s)://<address or domain>:port";
662 return false;
663 }
664 return true;
665 }
666
GetHostnameFromURL(const std::string & url)667 std::string GetHostnameFromURL(const std::string &url)
668 {
669 if (url.empty()) {
670 return "";
671 }
672 std::string delimiter = "://";
673 std::string tempUrl = url;
674 std::replace(tempUrl.begin(), tempUrl.end(), '\\', '/');
675 size_t posStart = tempUrl.find(delimiter);
676 if (posStart != std::string::npos) {
677 posStart += delimiter.length();
678 } else {
679 posStart = 0;
680 }
681 size_t notSlash = tempUrl.find_first_not_of('/', posStart);
682 if (notSlash != std::string::npos) {
683 posStart = notSlash;
684 }
685 size_t posEnd =
686 std::min({ tempUrl.find(':', posStart), tempUrl.find('/', posStart), tempUrl.find('?', posStart) });
687 if (posEnd != std::string::npos) {
688 return tempUrl.substr(posStart, posEnd - posStart);
689 }
690 return tempUrl.substr(posStart);
691 }
692
ParseCertificatePins(napi_env env,std::string & url,std::string & certificatePins)693 void JsInitialize::ParseCertificatePins(napi_env env, std::string &url, std::string &certificatePins)
694 {
695 auto hostname = GetHostnameFromURL(url);
696 if (OHOS::NetManagerStandard::NetConnClient::GetInstance().IsPinOpenMode(hostname)) {
697 REQUEST_HILOGI("Pins is openMode");
698 return;
699 }
700 auto ret = OHOS::NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, certificatePins);
701 if (ret != 0 || certificatePins.empty()) {
702 REQUEST_HILOGD("Get No pin set by hostname");
703 }
704 }
705
ParseMethod(napi_env env,napi_value jsConfig,Config & config)706 void JsInitialize::ParseMethod(napi_env env, napi_value jsConfig, Config &config)
707 {
708 if (config.version == Version::API10) {
709 config.method = config.action == Action::UPLOAD ? "PUT" : "GET";
710 } else {
711 config.method = "POST";
712 }
713 std::string method = NapiUtils::Convert2String(env, jsConfig, "method");
714 if (!method.empty()) {
715 transform(method.begin(), method.end(), method.begin(), ::toupper);
716 if (config.action == Action::UPLOAD && (method == "POST" || method == "PUT")) {
717 config.method = method;
718 }
719 if (config.action == Action::DOWNLOAD && (method == "POST" || method == "GET")) {
720 config.method = method;
721 }
722 }
723 }
724
ParseData(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)725 bool JsInitialize::ParseData(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
726 {
727 napi_value value = NapiUtils::GetNamedProperty(env, jsConfig, "data");
728 if (value == nullptr) {
729 return true;
730 }
731
732 napi_valuetype valueType = NapiUtils::GetValueType(env, value);
733 if (config.action == Action::UPLOAD && valueType == napi_object) {
734 return Convert2FormItems(env, value, config.forms, config.files, errInfo);
735 } else if (config.action == Action::DOWNLOAD && valueType == napi_string) {
736 config.data = NapiUtils::Convert2String(env, value);
737 } else {
738 REQUEST_HILOGE("data type is error");
739 errInfo = "Incorrect parameter type, the config.data parameter type is incorrect";
740 return false;
741 }
742 return true;
743 }
744
ParseName(napi_env env,napi_value jsVal,std::string & name)745 bool JsInitialize::ParseName(napi_env env, napi_value jsVal, std::string &name)
746 {
747 napi_value value = NapiUtils::GetNamedProperty(env, jsVal, "name");
748 if (NapiUtils::GetValueType(env, value) != napi_string) {
749 return false;
750 }
751 name = NapiUtils::Convert2String(env, value);
752 return true;
753 }
754
GetFormItems(napi_env env,napi_value jsVal,std::vector<FormItem> & forms,std::vector<FileSpec> & files)755 bool JsInitialize::GetFormItems(
756 napi_env env, napi_value jsVal, std::vector<FormItem> &forms, std::vector<FileSpec> &files)
757 {
758 if (!NapiUtils::HasNamedProperty(env, jsVal, "name") || !NapiUtils::HasNamedProperty(env, jsVal, "value")) {
759 return false;
760 }
761
762 std::string name;
763 if (!ParseName(env, jsVal, name)) {
764 return false;
765 }
766 napi_value value = NapiUtils::GetNamedProperty(env, jsVal, "value");
767 if (value == nullptr) {
768 REQUEST_HILOGE("Get upload value failed");
769 return false;
770 }
771 bool isArray = false;
772 napi_is_array(env, value, &isArray);
773 napi_valuetype valueType = NapiUtils::GetValueType(env, value);
774 if (valueType == napi_string) {
775 FormItem form;
776 form.name = name;
777 form.value = NapiUtils::Convert2String(env, value);
778 forms.push_back(form);
779 } else if (valueType == napi_object && !isArray) {
780 FileSpec file;
781 if (!Convert2FileSpec(env, value, name, file)) {
782 REQUEST_HILOGE("Convert2FileSpec failed");
783 return false;
784 }
785 files.push_back(file);
786 } else if (isArray) {
787 if (!Convert2FileSpecs(env, value, name, files)) {
788 return false;
789 }
790 } else {
791 REQUEST_HILOGE("value type is error");
792 return false;
793 }
794 return true;
795 }
796
Convert2FormItems(napi_env env,napi_value jsValue,std::vector<FormItem> & forms,std::vector<FileSpec> & files,std::string & errInfo)797 bool JsInitialize::Convert2FormItems(napi_env env, napi_value jsValue, std::vector<FormItem> &forms,
798 std::vector<FileSpec> &files, std::string &errInfo)
799 {
800 bool isArray = false;
801 napi_is_array(env, jsValue, &isArray);
802 NAPI_ASSERT_BASE(env, isArray, "not array", false);
803 uint32_t length = 0;
804 napi_get_array_length(env, jsValue, &length);
805 for (uint32_t i = 0; i < length; ++i) {
806 napi_value jsVal = nullptr;
807 napi_handle_scope scope = nullptr;
808 napi_open_handle_scope(env, &scope);
809 if (scope == nullptr) {
810 return false;
811 }
812 napi_get_element(env, jsValue, i, &jsVal);
813 if (jsVal == nullptr) {
814 REQUEST_HILOGE("Get element jsVal failed");
815 errInfo = "Missing mandatory parameters, Get element jsVal failed";
816 napi_close_handle_scope(env, scope);
817 return false;
818 }
819 if (!GetFormItems(env, jsVal, forms, files)) {
820 REQUEST_HILOGE("Get formItems failed");
821 errInfo = "Missing mandatory parameters, Get formItems failed";
822 napi_close_handle_scope(env, scope);
823 return false;
824 }
825 napi_close_handle_scope(env, scope);
826 }
827 if (files.empty()) {
828 errInfo = "Missing mandatory parameters, files is empty";
829 return false;
830 }
831 return true;
832 }
833
Convert2FileSpecs(napi_env env,napi_value jsValue,const std::string & name,std::vector<FileSpec> & files)834 bool JsInitialize::Convert2FileSpecs(
835 napi_env env, napi_value jsValue, const std::string &name, std::vector<FileSpec> &files)
836 {
837 REQUEST_HILOGD("Convert2FileSpecs in");
838 uint32_t length = 0;
839 napi_get_array_length(env, jsValue, &length);
840 for (uint32_t i = 0; i < length; ++i) {
841 napi_value jsVal = nullptr;
842 napi_handle_scope scope = nullptr;
843 napi_open_handle_scope(env, &scope);
844 if (scope == nullptr) {
845 return false;
846 }
847 napi_get_element(env, jsValue, i, &jsVal);
848 if (jsVal == nullptr) {
849 napi_close_handle_scope(env, scope);
850 return false;
851 }
852 FileSpec file;
853 bool ret = Convert2FileSpec(env, jsVal, name, file);
854 if (!ret) {
855 napi_close_handle_scope(env, scope);
856 return false;
857 }
858 files.push_back(file);
859 napi_close_handle_scope(env, scope);
860 }
861 return true;
862 }
863
864 // Assert `in` is trimmed.
InterceptData(const std::string & str,const std::string & in,std::string & out)865 bool JsInitialize::InterceptData(const std::string &str, const std::string &in, std::string &out)
866 {
867 std::size_t position = in.find_last_of(str);
868 // when the str at last index, will error.
869 if (position == std::string::npos || position + 1 >= in.size()) {
870 return false;
871 }
872 out = std::string(in, position + 1);
873 return true;
874 }
875
Convert2FileSpec(napi_env env,napi_value jsValue,const std::string & name,FileSpec & file)876 bool JsInitialize::Convert2FileSpec(napi_env env, napi_value jsValue, const std::string &name, FileSpec &file)
877 {
878 REQUEST_HILOGD("Convert2FileSpec in");
879 file.name = name;
880 file.uri = NapiUtils::Convert2String(env, jsValue, "path");
881 StringTrim(file.uri);
882 if (file.uri.empty()) {
883 return false;
884 }
885 file.filename = NapiUtils::Convert2String(env, jsValue, "filename");
886 file.type = NapiUtils::Convert2String(env, jsValue, "mimetype");
887 return true;
888 }
889
ParseRedirect(napi_env env,napi_value jsConfig,bool & redirect)890 void JsInitialize::ParseRedirect(napi_env env, napi_value jsConfig, bool &redirect)
891 {
892 if (!NapiUtils::HasNamedProperty(env, jsConfig, "redirect")) {
893 redirect = true;
894 } else {
895 redirect = NapiUtils::Convert2Boolean(env, jsConfig, "redirect");
896 }
897 }
898
ParseRetry(napi_env env,napi_value jsConfig,bool & retry)899 void JsInitialize::ParseRetry(napi_env env, napi_value jsConfig, bool &retry)
900 {
901 if (!NapiUtils::HasNamedProperty(env, jsConfig, "retry")) {
902 retry = true;
903 } else {
904 retry = NapiUtils::Convert2Boolean(env, jsConfig, "retry");
905 }
906 }
907
IsStageMode(napi_env env,napi_value value)908 bool JsInitialize::IsStageMode(napi_env env, napi_value value)
909 {
910 bool stageMode = true;
911 napi_status status = OHOS::AbilityRuntime::IsStageContext(env, value, stageMode);
912 if (status != napi_ok || !stageMode) {
913 return false;
914 }
915 return true;
916 }
917
ParseConfigV9(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)918 bool JsInitialize::ParseConfigV9(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
919 {
920 REQUEST_HILOGD("ParseConfigV9 in");
921 config.action = NapiUtils::GetRequestAction(env, jsConfig);
922 config.headers = ParseMap(env, jsConfig, "header");
923 if (!ParseUrl(env, jsConfig, config.url, errInfo)) {
924 errInfo = "Parse url error";
925 return false;
926 }
927 auto func = config.action == Action::UPLOAD ? ParseUploadConfig : ParseDownloadConfig;
928 if (!func(env, jsConfig, config, errInfo)) {
929 return false;
930 }
931 ParseTitle(env, jsConfig, config, errInfo);
932 return true;
933 }
934
ParseUploadConfig(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)935 bool JsInitialize::ParseUploadConfig(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
936 {
937 REQUEST_HILOGD("ParseUploadConfig in");
938 ParseMethod(env, jsConfig, config);
939 napi_value jsFiles = NapiUtils::GetNamedProperty(env, jsConfig, PARAM_KEY_FILES);
940 if (jsFiles == nullptr) {
941 errInfo = "Parse config files error";
942 return false;
943 }
944
945 config.files = NapiUtils::Convert2FileVector(env, jsFiles, "API8");
946 if (config.files.empty()) {
947 errInfo = "Parameter verification failed, Parse config files error";
948 return false;
949 }
950
951 napi_value jsData = NapiUtils::GetNamedProperty(env, jsConfig, PARAM_KEY_DATA);
952 if (jsData == nullptr) {
953 errInfo = "Parameter verification failed, Parse config data error";
954 return false;
955 }
956 config.forms = NapiUtils::Convert2RequestDataVector(env, jsData);
957
958 if (!ParseIndex(env, jsConfig, config, errInfo)) {
959 return false;
960 }
961
962 config.begins = ParseBegins(env, jsConfig);
963 config.ends = ParseEnds(env, jsConfig);
964 return true;
965 }
966
ParseDownloadConfig(napi_env env,napi_value jsConfig,Config & config,std::string & errInfo)967 bool JsInitialize::ParseDownloadConfig(napi_env env, napi_value jsConfig, Config &config, std::string &errInfo)
968 {
969 REQUEST_HILOGD("ParseDownloadConfig in");
970 config.metered = NapiUtils::Convert2Boolean(env, jsConfig, "enableMetered");
971 config.roaming = NapiUtils::Convert2Boolean(env, jsConfig, "enableRoaming");
972 config.description = NapiUtils::Convert2String(env, jsConfig, PARAM_KEY_DESCRIPTION);
973 uint32_t type = NapiUtils::Convert2Uint32(env, jsConfig, PARAM_KEY_NETWORKTYPE);
974 if (type == NETWORK_MOBILE) {
975 config.network = Network::CELLULAR;
976 } else if (type == NETWORK_WIFI) {
977 config.network = Network::WIFI;
978 } else {
979 config.network = Network::ANY;
980 }
981 config.saveas = NapiUtils::Convert2String(env, jsConfig, PARAM_KEY_FILE_PATH);
982 if (config.saveas.empty()) {
983 InterceptData("/", config.url, config.saveas);
984 }
985 config.background = NapiUtils::Convert2Boolean(env, jsConfig, PARAM_KEY_BACKGROUND);
986 config.method = "GET";
987 return true;
988 }
989
CreatProperties(napi_env env,napi_value & self,napi_value config,JsTask * task)990 void JsInitialize::CreatProperties(napi_env env, napi_value &self, napi_value config, JsTask *task)
991 {
992 if (task->config_.version == Version::API10) {
993 NapiUtils::SetStringPropertyUtf8(env, self, "tid", task->GetTid());
994 napi_set_named_property(env, self, "config", config);
995 }
996 }
997
StandardizeFileSpec(FileSpec & file)998 void JsInitialize::StandardizeFileSpec(FileSpec &file)
999 {
1000 if (file.filename.empty()) {
1001 InterceptData("/", file.uri, file.filename);
1002 }
1003 if (file.type.empty()) {
1004 InterceptData(".", file.filename, file.type);
1005 }
1006 if (file.name.empty()) {
1007 file.name = "file";
1008 }
1009 return;
1010 }
1011
CheckUserFileSpec(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,const Config & config,FileSpec & file,ExceptionError & error)1012 bool JsInitialize::CheckUserFileSpec(const std::shared_ptr<OHOS::AbilityRuntime::Context> &context,
1013 const Config &config, FileSpec &file, ExceptionError &error)
1014 {
1015 if (config.mode != Mode::FOREGROUND) {
1016 error.code = E_PARAMETER_CHECK;
1017 error.errInfo = "Parameter verification failed, user file can only for Mode::FOREGROUND";
1018 return false;
1019 }
1020 REQUEST_HILOGD("UserFile in: %{public}s", file.uri.c_str());
1021 std::shared_ptr<Uri> uri = std::make_shared<Uri>(file.uri);
1022 std::shared_ptr<AppExecFwk::DataAbilityHelper> dataAbilityHelper =
1023 AppExecFwk::DataAbilityHelper::Creator(context, uri);
1024 if (dataAbilityHelper == nullptr) {
1025 REQUEST_HILOGE("dataAbilityHelper null");
1026 error.code = E_PARAMETER_CHECK;
1027 error.errInfo = "Parameter verification failed, dataAbilityHelper null";
1028 return false;
1029 }
1030 file.fd = dataAbilityHelper->OpenFile(*uri, "r");
1031 if (file.fd < 0) {
1032 REQUEST_HILOGE("Failed to open user file: %{public}s, fd: %{public}d", file.uri.c_str(), file.fd);
1033 error.code = E_FILE_IO;
1034 error.errInfo = "Failed to open user file";
1035 return false;
1036 }
1037 StandardizeFileSpec(file);
1038 return true;
1039 }
1040
CheckUploadFiles(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,Config & config,ExceptionError & error)1041 bool JsInitialize::CheckUploadFiles(
1042 const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, Config &config, ExceptionError &error)
1043 {
1044 // need reconstruction.
1045 for (auto &file : config.files) {
1046 if (IsUserFile(file.uri)) {
1047 file.isUserFile = true;
1048 if (config.version == Version::API9) {
1049 error.code = E_PARAMETER_CHECK;
1050 error.errInfo = "Parameter verification failed, user file can only for request.agent.";
1051 return false;
1052 }
1053 if (!CheckUserFileSpec(context, config, file, error)) {
1054 return false;
1055 }
1056 StandardizeFileSpec(file);
1057 continue;
1058 }
1059
1060 if (!CheckUploadFileSpec(context, config, file, error)) {
1061 return false;
1062 }
1063 }
1064 return true;
1065 }
1066
CheckUploadFileSpec(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,Config & config,FileSpec & file,ExceptionError & error)1067 bool JsInitialize::CheckUploadFileSpec(const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, Config &config,
1068 FileSpec &file, ExceptionError &error)
1069 {
1070 file.isUserFile = false;
1071 std::string path = file.uri;
1072 if (config.version == Version::API9) {
1073 if (!GetInternalPath(context, config, path, error.errInfo)) {
1074 error.code = E_PARAMETER_CHECK;
1075 return false;
1076 }
1077 } else {
1078 std::vector<std::string> pathVec;
1079 if (!GetSandboxPath(context, config, path, pathVec, error.errInfo)) {
1080 error.code = E_PARAMETER_CHECK;
1081 return false;
1082 }
1083 }
1084 REQUEST_HILOGD("CheckUploadFileSpec path: %{public}s", path.c_str());
1085 file.uri = path;
1086 if (!GetFdUpload(path, config, error)) {
1087 return false;
1088 }
1089 if (!JsTask::SetPathPermission(file.uri)) {
1090 error.code = E_FILE_IO;
1091 error.errInfo = "set path permission fail";
1092 return false;
1093 }
1094 StandardizeFileSpec(file);
1095 return true;
1096 }
1097
CheckDownloadFile(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,Config & config,ExceptionError & error)1098 bool JsInitialize::CheckDownloadFile(
1099 const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, Config &config, ExceptionError &error)
1100 {
1101 if (config.version == Version::API9) {
1102 std::string path = config.saveas;
1103 if (config.saveas.find('/') == 0) {
1104 // API9 do not check.
1105 } else if (!GetInternalPath(context, config, path, error.errInfo)) {
1106 error.code = E_PARAMETER_CHECK;
1107 return false;
1108 }
1109 config.saveas = path;
1110 } else {
1111 if (!CheckDownloadFilePath(context, config, error.errInfo)) {
1112 error.code = E_PARAMETER_CHECK;
1113 return false;
1114 }
1115 }
1116 FileSpec file = { .uri = config.saveas, .isUserFile = false };
1117 StandardizeFileSpec(file);
1118 config.files.push_back(file);
1119 if (!GetFdDownload(file.uri, config, error)) {
1120 return false;
1121 }
1122 if (!JsTask::SetPathPermission(config.saveas)) {
1123 error.code = E_FILE_IO;
1124 error.errInfo = "set path permission fail, download";
1125 return false;
1126 }
1127 return true;
1128 }
1129
CheckDownloadFilePath(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,Config & config,std::string & errInfo)1130 bool JsInitialize::CheckDownloadFilePath(
1131 const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, Config &config, std::string &errInfo)
1132 {
1133 std::string path = config.saveas;
1134 std::vector<std::string> pathVec;
1135 if (!GetSandboxPath(context, config, path, pathVec, errInfo)) {
1136 return false;
1137 }
1138 // pop filename.
1139 pathVec.pop_back();
1140 if (!JsInitialize::CreateDirs(pathVec)) {
1141 REQUEST_HILOGE("CreateDirs Err: %{public}s", path.c_str());
1142 errInfo = "Parameter verification failed, this is fail saveas path";
1143 return false;
1144 }
1145 config.saveas = path;
1146 return true;
1147 }
1148
CreateDirs(const std::vector<std::string> & pathDirs)1149 bool JsInitialize::CreateDirs(const std::vector<std::string> &pathDirs)
1150 {
1151 std::string path;
1152 std::error_code err;
1153 for (auto elem : pathDirs) {
1154 path += "/" + elem;
1155 if (std::filesystem::exists(path, err)) {
1156 continue;
1157 }
1158 err.clear();
1159 // create_directory noexcept.
1160 if (!std::filesystem::create_directory(path, err)) {
1161 REQUEST_HILOGE("Create Dir Err: %{public}d, %{public}s", err.value(), err.message().c_str());
1162 return false;
1163 }
1164 }
1165 return true;
1166 }
1167
FindDir(const std::string & pathDir)1168 bool JsInitialize::FindDir(const std::string &pathDir)
1169 {
1170 std::error_code err;
1171 return std::filesystem::exists(pathDir, err);
1172 }
1173
IsUserFile(const std::string & path)1174 bool JsInitialize::IsUserFile(const std::string &path)
1175 {
1176 return path.find("file://docs/") == 0 || path.find("file://media/") == 0;
1177 }
1178
GetSandboxPath(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,const Config & config,std::string & path,std::vector<std::string> & pathVec,std::string & errInfo)1179 bool JsInitialize::GetSandboxPath(const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, const Config &config,
1180 std::string &path, std::vector<std::string> &pathVec, std::string &errInfo)
1181 {
1182 if (!StandardizePath(context, config, path)) {
1183 REQUEST_HILOGE("StandardizePath Err: %{public}s", path.c_str());
1184 errInfo = "Parameter verification failed, GetSandboxPath failed, StandardizePath fail";
1185 return false;
1186 };
1187 if (!WholeToNormal(path, pathVec) || pathVec.empty()) {
1188 REQUEST_HILOGE("WholeToNormal Err: %{public}s", path.c_str());
1189 errInfo = "Parameter verification failed, GetSandboxPath failed, WholeToNormal path fail";
1190 return false;
1191 };
1192 std::string baseDir;
1193 if (!CheckBelongAppBaseDir(path, baseDir)) {
1194 REQUEST_HILOGE("CheckBelongAppBaseDir Err: %{public}s", path.c_str());
1195 errInfo = "Parameter verification failed, GetSandboxPath failed, path not belong app base dir";
1196 return false;
1197 };
1198 return true;
1199 }
1200
1201 // Must not user file.
StandardizePath(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,const Config & config,std::string & path)1202 bool JsInitialize::StandardizePath(
1203 const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, const Config &config, std::string &path)
1204 {
1205 std::string WHOLE_PREFIX = "/";
1206 std::string FILE_PREFIX = "file://";
1207 std::string INTERNAL_PREFIX = "internal://";
1208 std::string CURRENT_PREFIX = "./";
1209
1210 if (path.find(WHOLE_PREFIX) == 0) {
1211 return true;
1212 }
1213 if (path.find(FILE_PREFIX) == 0) {
1214 path.erase(0, FILE_PREFIX.size());
1215 return FileToWhole(context, config, path);
1216 }
1217 if (path.find(INTERNAL_PREFIX) == 0) {
1218 path.erase(0, INTERNAL_PREFIX.size());
1219 return BaseToWhole(context, path);
1220 }
1221 if (path.find(CURRENT_PREFIX) == 0) {
1222 path.erase(0, CURRENT_PREFIX.size());
1223 return CacheToWhole(context, path);
1224 }
1225 return CacheToWhole(context, path);
1226 }
1227
1228 // BaseDir is following context.
BaseToWhole(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,std::string & path)1229 bool JsInitialize::BaseToWhole(const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, std::string &path)
1230 {
1231 std::string base = context->GetBaseDir();
1232 if (base.empty()) {
1233 REQUEST_HILOGE("GetBaseDir error.");
1234 return false;
1235 }
1236 path = base + "/" + path;
1237 return true;
1238 }
1239
CacheToWhole(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,std::string & path)1240 bool JsInitialize::CacheToWhole(const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, std::string &path)
1241 {
1242 std::string cache = context->GetCacheDir();
1243 if (cache.empty()) {
1244 REQUEST_HILOGE("GetCacheDir error.");
1245 return false;
1246 }
1247 path = cache + "/" + path;
1248 return true;
1249 }
1250
FileToWhole(const std::shared_ptr<OHOS::AbilityRuntime::Context> & context,const Config & config,std::string & path)1251 bool JsInitialize::FileToWhole(
1252 const std::shared_ptr<OHOS::AbilityRuntime::Context> &context, const Config &config, std::string &path)
1253 {
1254 std::string bundleName = path.substr(0, path.find("/"));
1255 if (bundleName != config.bundleName) {
1256 REQUEST_HILOGE("path bundleName error.");
1257 return false;
1258 }
1259 path.erase(0, bundleName.size());
1260 return true;
1261 }
1262
WholeToNormal(std::string & path,std::vector<std::string> & out)1263 bool JsInitialize::WholeToNormal(std::string &path, std::vector<std::string> &out)
1264 {
1265 std::string normalPath;
1266 std::vector<std::string> elems;
1267 StringSplit(path, '/', elems);
1268 if (!PathVecToNormal(elems, out)) {
1269 return false;
1270 }
1271 for (auto elem : out) {
1272 normalPath += "/" + elem;
1273 }
1274 path = normalPath;
1275 return true;
1276 }
1277
PathVecToNormal(const std::vector<std::string> & in,std::vector<std::string> & out)1278 bool JsInitialize::PathVecToNormal(const std::vector<std::string> &in, std::vector<std::string> &out)
1279 {
1280 for (auto elem : in) {
1281 if (elem == "..") {
1282 if (out.size() > 0) {
1283 out.pop_back();
1284 } else {
1285 return false;
1286 }
1287 } else {
1288 out.push_back(elem);
1289 }
1290 }
1291 return true;
1292 }
1293
StringSplit(const std::string & str,const char delim,std::vector<std::string> & elems)1294 void JsInitialize::StringSplit(const std::string &str, const char delim, std::vector<std::string> &elems)
1295 {
1296 std::stringstream stream(str);
1297 std::string item;
1298 while (std::getline(stream, item, delim)) {
1299 if (!item.empty()) {
1300 elems.push_back(item);
1301 }
1302 }
1303 return;
1304 }
1305
StringTrim(std::string & str)1306 void JsInitialize::StringTrim(std::string &str)
1307 {
1308 if (str.empty()) {
1309 return;
1310 }
1311 str.erase(0, str.find_first_not_of(" "));
1312 str.erase(str.find_last_not_of(" ") + 1);
1313 return;
1314 }
1315
CheckBelongAppBaseDir(const std::string & filepath,std::string & baseDir)1316 bool JsInitialize::CheckBelongAppBaseDir(const std::string &filepath, std::string &baseDir)
1317 {
1318 if (!JsInitialize::GetAppBaseDir(baseDir)) {
1319 return false;
1320 }
1321 if ((filepath.find(AREA1) == 0) || filepath.find(AREA2) == 0 || filepath.find(AREA5) == 0) {
1322 return true;
1323 } else {
1324 REQUEST_HILOGE("File dir not include base dir: %{public}s", baseDir.c_str());
1325 return false;
1326 }
1327 }
1328 } // namespace OHOS::Request
1329