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