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