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_task.h"
17
18 #include <securec.h>
19 #include <sys/stat.h>
20
21 #include <chrono>
22 #include <cstring>
23 #include <filesystem>
24 #include <mutex>
25
26 #include "app_state_callback.h"
27 #include "async_call.h"
28 #include "js_initialize.h"
29 #include "legacy/request_manager.h"
30 #include "log.h"
31 #include "napi_base_context.h"
32 #include "napi_utils.h"
33 #include "request_event.h"
34 #include "request_manager.h"
35 #include "storage_acl.h"
36 #include "upload/upload_task_napiV5.h"
37
38 using namespace OHOS::StorageDaemon;
39 namespace fs = std::filesystem;
40 namespace OHOS::Request {
41 constexpr int64_t MILLISECONDS_IN_ONE_DAY = 24 * 60 * 60 * 1000;
42 std::mutex JsTask::createMutex_;
43 thread_local napi_ref JsTask::createCtor = nullptr;
44 std::mutex JsTask::requestMutex_;
45 thread_local napi_ref JsTask::requestCtor = nullptr;
46 std::mutex JsTask::requestFileMutex_;
47 thread_local napi_ref JsTask::requestFileCtor = nullptr;
48 std::mutex JsTask::getTaskCreateMutex_;
49 thread_local napi_ref JsTask::getTaskCreateCtor = nullptr;
50 std::mutex JsTask::taskMutex_;
51 std::map<std::string, JsTask *> JsTask::taskMap_;
52 bool JsTask::register_ = false;
53 std::mutex JsTask::pathMutex_;
54 std::map<std::string, int32_t> JsTask::pathMap_;
55 std::mutex JsTask::taskContextMutex_;
56 std::map<std::string, std::shared_ptr<JsTask::ContextInfo>> JsTask::taskContextMap_;
57
58 napi_property_descriptor clzDes[] = {
59 DECLARE_NAPI_FUNCTION(FUNCTION_ON, RequestEvent::On),
60 DECLARE_NAPI_FUNCTION(FUNCTION_OFF, RequestEvent::Off),
61 DECLARE_NAPI_FUNCTION(FUNCTION_START, RequestEvent::Start),
62 DECLARE_NAPI_FUNCTION(FUNCTION_PAUSE, RequestEvent::Pause),
63 DECLARE_NAPI_FUNCTION(FUNCTION_RESUME, RequestEvent::Resume),
64 DECLARE_NAPI_FUNCTION(FUNCTION_STOP, RequestEvent::Stop),
65 };
66
67 napi_property_descriptor clzDesV9[] = {
68 DECLARE_NAPI_FUNCTION(FUNCTION_ON, RequestEvent::On),
69 DECLARE_NAPI_FUNCTION(FUNCTION_OFF, RequestEvent::Off),
70 DECLARE_NAPI_FUNCTION(FUNCTION_SUSPEND, RequestEvent::Pause),
71 DECLARE_NAPI_FUNCTION(FUNCTION_GET_TASK_INFO, RequestEvent::Query),
72 DECLARE_NAPI_FUNCTION(FUNCTION_GET_TASK_MIME_TYPE, RequestEvent::QueryMimeType),
73 DECLARE_NAPI_FUNCTION(FUNCTION_DELETE, RequestEvent::Remove),
74 DECLARE_NAPI_FUNCTION(FUNCTION_RESTORE, RequestEvent::Resume),
75 DECLARE_NAPI_FUNCTION(FUNCTION_PAUSE, RequestEvent::Pause),
76 DECLARE_NAPI_FUNCTION(FUNCTION_QUERY, RequestEvent::Query),
77 DECLARE_NAPI_FUNCTION(FUNCTION_QUERY_MIME_TYPE, RequestEvent::QueryMimeType),
78 DECLARE_NAPI_FUNCTION(FUNCTION_REMOVE, RequestEvent::Remove),
79 DECLARE_NAPI_FUNCTION(FUNCTION_RESUME, RequestEvent::Resume),
80 };
81
~JsTask()82 JsTask::~JsTask()
83 {
84 REQUEST_HILOGD("~JsTask()");
85 ClearListener();
86 }
JsUpload(napi_env env,napi_callback_info info)87 napi_value JsTask::JsUpload(napi_env env, napi_callback_info info)
88 {
89 REQUEST_HILOGD("JsUpload in");
90 std::shared_ptr<Upload::UploadTaskNapiV5> proxy = std::make_shared<Upload::UploadTaskNapiV5>(env);
91 if (proxy->ParseCallback(env, info)) {
92 return proxy->JsUpload(env, info);
93 }
94 proxy->SetEnv(nullptr);
95 return JsMain(env, info, Version::API8);
96 }
97
JsDownload(napi_env env,napi_callback_info info)98 napi_value JsTask::JsDownload(napi_env env, napi_callback_info info)
99 {
100 REQUEST_HILOGD("JsDownload in");
101 if (Legacy::RequestManager::IsLegacy(env, info)) {
102 return Legacy::RequestManager::Download(env, info);
103 }
104 return JsMain(env, info, Version::API8);
105 }
106
JsRequestFile(napi_env env,napi_callback_info info)107 napi_value JsTask::JsRequestFile(napi_env env, napi_callback_info info)
108 {
109 REQUEST_HILOGD("JsRequestFile in");
110 return JsMain(env, info, Version::API9);
111 }
112
JsCreate(napi_env env,napi_callback_info info)113 napi_value JsTask::JsCreate(napi_env env, napi_callback_info info)
114 {
115 REQUEST_HILOGD("JsCreate in");
116 return JsMain(env, info, Version::API10);
117 }
118
JsMain(napi_env env,napi_callback_info info,Version version)119 napi_value JsTask::JsMain(napi_env env, napi_callback_info info, Version version)
120 {
121 auto context = std::make_shared<ContextInfo>();
122 context->withErrCode_ = version != Version::API8;
123 context->version_ = version;
124 auto input = [context](size_t argc, napi_value *argv, napi_value self) -> napi_status {
125 if (context->version_ == Version::API10) {
126 napi_create_reference(context->env_, argv[1], 1, &(context->jsConfig));
127 }
128 napi_value ctor = GetCtor(context->env_, context->version_);
129 napi_value jsTask = nullptr;
130 napi_status status = napi_new_instance(context->env_, ctor, argc, argv, &jsTask);
131 if (jsTask == nullptr || status != napi_ok) {
132 REQUEST_HILOGE("Get jsTask failed");
133 return napi_generic_failure;
134 }
135 napi_unwrap(context->env_, jsTask, reinterpret_cast<void **>(&context->task));
136 napi_create_reference(context->env_, jsTask, 1, &(context->taskRef));
137 return napi_ok;
138 };
139 auto exec = [context]() {
140 Config config = context->task->config_;
141 context->innerCode_ = CreateExec(context);
142 if (context->innerCode_ == E_SERVICE_ERROR && config.version == Version::API9
143 && config.action == Action::UPLOAD) {
144 context->withErrCode_ = false;
145 }
146 };
147 auto output = [context](napi_value *result) -> napi_status {
148 if (result == nullptr || context->innerCode_ != E_OK) {
149 return napi_generic_failure;
150 }
151 napi_status status = napi_get_reference_value(context->env_, context->taskRef, result);
152 context->task->SetTid(context->tid);
153 JsTask::AddTaskMap(std::to_string(context->tid), context->task);
154 JsTask::AddTaskContextMap(std::to_string(context->tid), context);
155 napi_value config = nullptr;
156 napi_get_reference_value(context->env_, context->jsConfig, &config);
157 JsInitialize::CreatProperties(context->env_, *result, config, context->task);
158 REQUEST_HILOGD("JsMain output");
159 return status;
160 };
161 context->SetInput(input).SetOutput(output).SetExec(exec);
162 AsyncCall asyncCall(env, info, context);
163 asyncCall.SetQosLevel(napi_qos_utility);
164 return asyncCall.Call(context, "create");
165 }
166
CreateExec(const std::shared_ptr<ContextInfo> & context)167 int32_t JsTask::CreateExec(const std::shared_ptr<ContextInfo> &context)
168 {
169 if (!RequestManager::GetInstance()->LoadRequestServer()) {
170 return E_SERVICE_ERROR;
171 }
172 if (context->task->config_.mode == Mode::FOREGROUND) {
173 RegisterForegroundResume();
174 }
175 sptr<RequestNotify> listener = new RequestNotify();
176 std::string key = "done" + context->task->GetTid();
177 context->task->AddListener(key, listener);
178 return RequestManager::GetInstance()->Create(context->task->config_, context->tid, listener);
179 }
180
GetCtor(napi_env env,Version version)181 napi_value JsTask::GetCtor(napi_env env, Version version)
182 {
183 switch (version) {
184 case Version::API8:
185 return GetCtorV8(env);
186 case Version::API9:
187 return GetCtorV9(env);
188 case Version::API10:
189 return GetCtorV10(env);
190 default:
191 break;
192 }
193 return nullptr;
194 }
195
GetCtorV10(napi_env env)196 napi_value JsTask::GetCtorV10(napi_env env)
197 {
198 REQUEST_HILOGD("GetCtorV10 in");
199 std::lock_guard<std::mutex> lock(createMutex_);
200 napi_value cons;
201 if (createCtor != nullptr) {
202 NAPI_CALL(env, napi_get_reference_value(env, createCtor, &cons));
203 return cons;
204 }
205 size_t count = sizeof(clzDes) / sizeof(napi_property_descriptor);
206 return DefineClass(env, clzDes, count, Create, &createCtor);
207 }
208
GetCtorV9(napi_env env)209 napi_value JsTask::GetCtorV9(napi_env env)
210 {
211 REQUEST_HILOGD("GetCtorV9 in");
212 std::lock_guard<std::mutex> lock(requestFileMutex_);
213 napi_value cons;
214 if (requestFileCtor != nullptr) {
215 NAPI_CALL(env, napi_get_reference_value(env, requestFileCtor, &cons));
216 return cons;
217 }
218 size_t count = sizeof(clzDesV9) / sizeof(napi_property_descriptor);
219 return DefineClass(env, clzDesV9, count, RequestFile, &requestFileCtor);
220 }
221
GetCtorV8(napi_env env)222 napi_value JsTask::GetCtorV8(napi_env env)
223 {
224 REQUEST_HILOGD("GetCtorV8 in");
225 std::lock_guard<std::mutex> lock(requestMutex_);
226 napi_value cons;
227 if (requestCtor != nullptr) {
228 NAPI_CALL(env, napi_get_reference_value(env, requestCtor, &cons));
229 return cons;
230 }
231 size_t count = sizeof(clzDesV9) / sizeof(napi_property_descriptor);
232 return DefineClass(env, clzDesV9, count, RequestFileV8, &requestCtor);
233 }
234
DefineClass(napi_env env,const napi_property_descriptor * desc,size_t count,napi_callback cb,napi_ref * ctor)235 napi_value JsTask::DefineClass(
236 napi_env env, const napi_property_descriptor *desc, size_t count, napi_callback cb, napi_ref *ctor)
237 {
238 napi_value cons = nullptr;
239 napi_status status = napi_define_class(env, "Request", NAPI_AUTO_LENGTH, cb, nullptr, count, desc, &cons);
240 if (status != napi_ok) {
241 REQUEST_HILOGE("napi_define_class failed");
242 return nullptr;
243 }
244 status = napi_create_reference(env, cons, 1, ctor);
245 if (status != napi_ok) {
246 REQUEST_HILOGE("napi_create_reference failed");
247 return nullptr;
248 }
249 return cons;
250 }
251
Create(napi_env env,napi_callback_info info)252 napi_value JsTask::Create(napi_env env, napi_callback_info info)
253 {
254 REQUEST_HILOGD("Create API10");
255 return JsInitialize::Initialize(env, info, Version::API10);
256 }
257
RequestFile(napi_env env,napi_callback_info info)258 napi_value JsTask::RequestFile(napi_env env, napi_callback_info info)
259 {
260 REQUEST_HILOGD("RequestFile API9");
261 return JsInitialize::Initialize(env, info, Version::API9);
262 }
263
RequestFileV8(napi_env env,napi_callback_info info)264 napi_value JsTask::RequestFileV8(napi_env env, napi_callback_info info)
265 {
266 REQUEST_HILOGD("Request API8");
267 return JsInitialize::Initialize(env, info, Version::API8);
268 }
269
GetTaskCtor(napi_env env)270 napi_value JsTask::GetTaskCtor(napi_env env)
271 {
272 REQUEST_HILOGD("GetTaskCtor in");
273 std::lock_guard<std::mutex> lock(getTaskCreateMutex_);
274 napi_value cons;
275 if (getTaskCreateCtor != nullptr) {
276 NAPI_CALL(env, napi_get_reference_value(env, getTaskCreateCtor, &cons));
277 return cons;
278 }
279 size_t count = sizeof(clzDes) / sizeof(napi_property_descriptor);
280 return DefineClass(env, clzDes, count, GetTaskCreate, &getTaskCreateCtor);
281 }
282
GetTaskCreate(napi_env env,napi_callback_info info)283 napi_value JsTask::GetTaskCreate(napi_env env, napi_callback_info info)
284 {
285 REQUEST_HILOGD("GetTask Create");
286 return JsInitialize::Initialize(env, info, Version::API10, false);
287 }
288
GetTask(napi_env env,napi_callback_info info)289 napi_value JsTask::GetTask(napi_env env, napi_callback_info info)
290 {
291 auto context = std::make_shared<ContextInfo>();
292 context->withErrCode_ = true;
293 context->version_ = Version::API10;
294 auto input = [context](size_t argc, napi_value *argv, napi_value self) -> napi_status {
295 if (!ParseGetTask(context->env_, argc, argv, context)) {
296 NapiUtils::ThrowError(context->env_, E_PARAMETER_CHECK, "Parse tid or token fail!", true);
297 return napi_invalid_arg;
298 }
299 napi_create_reference(context->env_, argv[0], 1, &(context->baseContext));
300 return napi_ok;
301 };
302 auto output = [context](napi_value *result) -> napi_status {
303 if (context->innerCode_ != E_OK) {
304 return napi_generic_failure;
305 }
306 if (!GetTaskOutput(context)) {
307 return napi_generic_failure;
308 }
309 napi_status res = napi_get_reference_value(context->env_, context->taskRef, result);
310 context->task->SetTid(context->tid);
311 napi_value conf = nullptr;
312 napi_get_reference_value(context->env_, context->jsConfig, &conf);
313 JsInitialize::CreatProperties(context->env_, *result, conf, context->task);
314 return res;
315 };
316 auto exec = [context]() {
317 if (!RequestManager::GetInstance()->LoadRequestServer()) {
318 context->innerCode_ = E_SERVICE_ERROR;
319 return;
320 }
321 GetTaskExecution(context);
322 };
323 context->SetInput(input).SetOutput(output).SetExec(exec);
324 AsyncCall asyncCall(env, info, context);
325 return asyncCall.Call(context, "getTask");
326 }
327
GetTaskExecution(std::shared_ptr<ContextInfo> context)328 void JsTask::GetTaskExecution(std::shared_ptr<ContextInfo> context)
329 {
330 std::string tid = std::to_string(context->tid);
331 if (taskContextMap_.find(tid) != taskContextMap_.end()) {
332 REQUEST_HILOGD("Find in taskContextMap_");
333 if (taskContextMap_[tid]->task->config_.version != Version::API10
334 || taskContextMap_[tid]->task->config_.token != context->token) {
335 context->innerCode_ = E_TASK_NOT_FOUND;
336 return;
337 }
338 context->task = taskContextMap_[tid]->task;
339 context->taskRef = taskContextMap_[tid]->taskRef;
340 context->jsConfig = taskContextMap_[tid]->jsConfig;
341 context->innerCode_ = E_OK;
342 return;
343 } else {
344 context->innerCode_ = RequestManager::GetInstance()->GetTask(tid, context->token, context->config);
345 }
346 if (context->config.version != Version::API10) {
347 context->innerCode_ = E_TASK_NOT_FOUND;
348 }
349 }
350
GetTaskOutput(std::shared_ptr<ContextInfo> context)351 bool JsTask::GetTaskOutput(std::shared_ptr<ContextInfo> context)
352 {
353 std::string tid = std::to_string(context->tid);
354 if (taskMap_.find(tid) == taskMap_.end()) {
355 napi_value config = NapiUtils::Convert2JSValue(context->env_, context->config);
356 napi_create_reference(context->env_, config, 1, &(context->jsConfig));
357 napi_value ctor = GetTaskCtor(context->env_);
358 napi_value jsTask = nullptr;
359 napi_value baseCtx = nullptr;
360 napi_get_reference_value(context->env_, context->baseContext, &baseCtx);
361 napi_value args[2] = { baseCtx, config };
362 napi_status status = napi_new_instance(context->env_, ctor, 2, args, &jsTask);
363 if (jsTask == nullptr || status != napi_ok) {
364 REQUEST_HILOGE("Get task failed");
365 return false;
366 }
367 napi_unwrap(context->env_, jsTask, reinterpret_cast<void **>(&context->task));
368 napi_create_reference(context->env_, jsTask, 1, &(context->taskRef));
369 JsTask::AddTaskMap(tid, context->task);
370 JsTask::AddTaskContextMap(tid, context);
371 }
372 return true;
373 }
374
ParseGetTask(napi_env env,size_t argc,napi_value * argv,std::shared_ptr<ContextInfo> context)375 bool JsTask::ParseGetTask(napi_env env, size_t argc, napi_value *argv, std::shared_ptr<ContextInfo> context)
376 {
377 // need at least 2 params.
378 if (argc < 2) {
379 REQUEST_HILOGE("Wrong number of arguments");
380 return false;
381 }
382 if (NapiUtils::GetValueType(env, argv[1]) != napi_string) {
383 REQUEST_HILOGE("The parameter is not of string type");
384 return false;
385 }
386 std::string tid = NapiUtils::Convert2String(env, argv[1]);
387 if (tid.empty()) {
388 REQUEST_HILOGE("tid is empty");
389 return false;
390 }
391 context->tid = std::stoi(tid);
392 // handle 3rd param TOKEN
393 if (argc == 3) {
394 if (NapiUtils::GetValueType(env, argv[2]) != napi_string) { // argv[2] is the 3rd param
395 REQUEST_HILOGE("The parameter is not of string type");
396 return false;
397 }
398 uint32_t bufferLen = TOKEN_MAX_BYTES + 2;
399 std::unique_ptr<char[]> token = std::make_unique<char[]>(bufferLen);
400 size_t len = 0;
401 napi_status status = napi_get_value_string_utf8(env, argv[2], token.get(), bufferLen, &len);
402 if (status != napi_ok) {
403 REQUEST_HILOGE("napi get value string utf8 failed");
404 memset_s(token.get(), bufferLen, 0, bufferLen);
405 return false;
406 }
407 if (len < TOKEN_MIN_BYTES || len > TOKEN_MAX_BYTES) {
408 memset_s(token.get(), bufferLen, 0, bufferLen);
409 return false;
410 }
411 context->token = NapiUtils::SHA256(token.get(), len);
412 memset_s(token.get(), bufferLen, 0, bufferLen);
413 }
414 return true;
415 }
416
Remove(napi_env env,napi_callback_info info)417 napi_value JsTask::Remove(napi_env env, napi_callback_info info)
418 {
419 struct RemoveContext : public AsyncCall::Context {
420 std::string tid;
421 bool res = false;
422 };
423
424 auto context = std::make_shared<RemoveContext>();
425 context->withErrCode_ = true;
426 context->version_ = Version::API10;
427 auto input = [context](size_t argc, napi_value *argv, napi_value self) -> napi_status {
428 context->tid = ParseTid(context->env_, argc, argv);
429 if (context->tid.empty()) {
430 NapiUtils::ThrowError(context->env_, E_PARAMETER_CHECK, "Parse tid fail!", true);
431 return napi_invalid_arg;
432 }
433 return napi_ok;
434 };
435 auto output = [context](napi_value *result) -> napi_status {
436 if (context->innerCode_ != E_OK) {
437 context->res = false;
438 return napi_generic_failure;
439 }
440 return NapiUtils::Convert2JSValue(context->env_, context->res, *result);
441 };
442 auto exec = [context]() {
443 context->innerCode_ = RequestManager::GetInstance()->Remove(context->tid, Version::API10);
444 // Removed Task can not return notify, so unref in this.
445 JsTask::ClearTaskContext(context->tid);
446 };
447 context->SetInput(std::move(input)).SetOutput(std::move(output)).SetExec(std::move(exec));
448 AsyncCall asyncCall(env, info, context);
449 return asyncCall.Call(context, "remove");
450 }
451
ParseTid(napi_env env,size_t argc,napi_value * argv)452 std::string JsTask::ParseTid(napi_env env, size_t argc, napi_value *argv)
453 {
454 if (argc < 1) {
455 REQUEST_HILOGE("Wrong number of arguments");
456 return "";
457 }
458 if (NapiUtils::GetValueType(env, argv[0]) != napi_string) {
459 REQUEST_HILOGE("The first parameter is not of string type");
460 return "";
461 }
462 return NapiUtils::Convert2String(env, argv[0]);
463 }
464
Show(napi_env env,napi_callback_info info)465 napi_value JsTask::Show(napi_env env, napi_callback_info info)
466 {
467 auto context = std::make_shared<TouchContext>();
468 auto input = [context](size_t argc, napi_value *argv, napi_value self) -> napi_status {
469 context->tid = ParseTid(context->env_, argc, argv);
470 if (context->tid.empty()) {
471 NapiUtils::ThrowError(context->env_, E_PARAMETER_CHECK, "Parse tid fail!", true);
472 return napi_invalid_arg;
473 }
474 return napi_ok;
475 };
476 return TouchInner(env, info, std::move(input), std::move(context));
477 }
478
Touch(napi_env env,napi_callback_info info)479 napi_value JsTask::Touch(napi_env env, napi_callback_info info)
480 {
481 auto context = std::make_shared<TouchContext>();
482 auto input = [context](size_t argc, napi_value *argv, napi_value self) -> napi_status {
483 bool ret = ParseTouch(context->env_, argc, argv, context);
484 if (!ret) {
485 NapiUtils::ThrowError(context->env_, E_PARAMETER_CHECK, "Parse tid or token fail!", true);
486 return napi_invalid_arg;
487 }
488 return napi_ok;
489 };
490 return TouchInner(env, info, std::move(input), std::move(context));
491 }
492
TouchInner(napi_env env,napi_callback_info info,AsyncCall::Context::InputAction input,std::shared_ptr<TouchContext> context)493 napi_value JsTask::TouchInner(napi_env env, napi_callback_info info, AsyncCall::Context::InputAction input,
494 std::shared_ptr<TouchContext> context)
495 {
496 context->withErrCode_ = true;
497 context->version_ = Version::API10;
498 auto output = [context](napi_value *result) -> napi_status {
499 if (context->innerCode_ != E_OK) {
500 return napi_generic_failure;
501 }
502 *result = NapiUtils::Convert2JSValue(context->env_, context->taskInfo);
503 return napi_ok;
504 };
505 auto exec = [context]() {
506 if (!RequestManager::GetInstance()->LoadRequestServer()) {
507 context->innerCode_ = E_SERVICE_ERROR;
508 return;
509 }
510 context->innerCode_ = RequestManager::GetInstance()->Touch(context->tid, context->token, context->taskInfo);
511 };
512 context->SetInput(std::move(input)).SetOutput(std::move(output)).SetExec(std::move(exec));
513 AsyncCall asyncCall(env, info, context);
514 return asyncCall.Call(context, "touch");
515 }
516
ParseTouch(napi_env env,size_t argc,napi_value * argv,std::shared_ptr<TouchContext> context)517 bool JsTask::ParseTouch(napi_env env, size_t argc, napi_value *argv, std::shared_ptr<TouchContext> context)
518 {
519 // 2 means least param num.
520 if (argc < 2) {
521 REQUEST_HILOGE("Wrong number of arguments");
522 return false;
523 }
524 if (NapiUtils::GetValueType(env, argv[0]) != napi_string || NapiUtils::GetValueType(env, argv[1]) != napi_string) {
525 REQUEST_HILOGE("The parameter is not of string type");
526 return false;
527 }
528 context->tid = NapiUtils::Convert2String(env, argv[0]);
529 if (context->tid.empty()) {
530 REQUEST_HILOGE("tid is empty");
531 return false;
532 }
533 uint32_t bufferLen = TOKEN_MAX_BYTES + 2;
534 char *token = new char[bufferLen];
535 size_t len = 0;
536 napi_status status = napi_get_value_string_utf8(env, argv[1], token, bufferLen, &len);
537 if (status != napi_ok) {
538 REQUEST_HILOGE("napi get value string utf8 failed");
539 memset_s(token, bufferLen, 0, bufferLen);
540 delete[] token;
541 return false;
542 }
543 if (len < TOKEN_MIN_BYTES || len > TOKEN_MAX_BYTES) {
544 memset_s(token, bufferLen, 0, bufferLen);
545 delete[] token;
546 return false;
547 }
548 context->token = NapiUtils::SHA256(token, len);
549 memset_s(token, bufferLen, 0, bufferLen);
550 delete[] token;
551 return true;
552 }
553
ParseSearch(napi_env env,size_t argc,napi_value * argv,Filter & filter)554 bool JsTask::ParseSearch(napi_env env, size_t argc, napi_value *argv, Filter &filter)
555 {
556 using namespace std::chrono;
557 filter.bundle = "*";
558 filter.before = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
559 filter.after = filter.before - MILLISECONDS_IN_ONE_DAY;
560 if (argc < 1) {
561 return true;
562 }
563 napi_valuetype valueType = NapiUtils::GetValueType(env, argv[0]);
564 if (valueType == napi_null || valueType == napi_undefined) {
565 return true;
566 }
567 if (valueType != napi_object) {
568 REQUEST_HILOGE("The parameter is not of object type");
569 return false;
570 }
571 filter.bundle = ParseBundle(env, argv[0]);
572 filter.before = ParseBefore(env, argv[0]);
573 filter.after = ParseAfter(env, argv[0], filter.before);
574 if (filter.before < filter.after) {
575 REQUEST_HILOGE("before is small than after");
576 return false;
577 }
578 filter.state = ParseState(env, argv[0]);
579 filter.action = ParseAction(env, argv[0]);
580 filter.mode = ParseMode(env, argv[0]);
581 return true;
582 }
583
ParseBundle(napi_env env,napi_value value)584 std::string JsTask::ParseBundle(napi_env env, napi_value value)
585 {
586 if (!NapiUtils::HasNamedProperty(env, value, "bundle")) {
587 return "*";
588 }
589 napi_value value1 = NapiUtils::GetNamedProperty(env, value, "bundle");
590 if (NapiUtils::GetValueType(env, value1) != napi_string) {
591 return "*";
592 }
593 return NapiUtils::Convert2String(env, value1);
594 }
595
ParseState(napi_env env,napi_value value)596 State JsTask::ParseState(napi_env env, napi_value value)
597 {
598 if (!NapiUtils::HasNamedProperty(env, value, "state")) {
599 return State::ANY;
600 }
601 napi_value value1 = NapiUtils::GetNamedProperty(env, value, "state");
602 if (NapiUtils::GetValueType(env, value1) != napi_number) {
603 return State::ANY;
604 }
605 return static_cast<State>(NapiUtils::Convert2Uint32(env, value1));
606 }
607
ParseAction(napi_env env,napi_value value)608 Action JsTask::ParseAction(napi_env env, napi_value value)
609 {
610 if (!NapiUtils::HasNamedProperty(env, value, "action")) {
611 return Action::ANY;
612 }
613 napi_value value1 = NapiUtils::GetNamedProperty(env, value, "action");
614 if (NapiUtils::GetValueType(env, value1) != napi_number) {
615 return Action::ANY;
616 }
617 return static_cast<Action>(NapiUtils::Convert2Uint32(env, value1));
618 }
619
ParseMode(napi_env env,napi_value value)620 Mode JsTask::ParseMode(napi_env env, napi_value value)
621 {
622 if (!NapiUtils::HasNamedProperty(env, value, "mode")) {
623 return Mode::ANY;
624 }
625 napi_value value1 = NapiUtils::GetNamedProperty(env, value, "mode");
626 if (NapiUtils::GetValueType(env, value1) != napi_number) {
627 return Mode::ANY;
628 }
629 return static_cast<Mode>(NapiUtils::Convert2Uint32(env, value1));
630 }
631
ParseBefore(napi_env env,napi_value value)632 int64_t JsTask::ParseBefore(napi_env env, napi_value value)
633 {
634 using namespace std::chrono;
635 int64_t now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
636 if (!NapiUtils::HasNamedProperty(env, value, "before")) {
637 return now;
638 }
639 napi_value value1 = NapiUtils::GetNamedProperty(env, value, "before");
640 if (NapiUtils::GetValueType(env, value1) != napi_number) {
641 return now;
642 }
643 int64_t ret = 0;
644 NAPI_CALL_BASE(env, napi_get_value_int64(env, value1, &ret), now);
645 return ret;
646 }
647
ParseAfter(napi_env env,napi_value value,int64_t before)648 int64_t JsTask::ParseAfter(napi_env env, napi_value value, int64_t before)
649 {
650 int64_t defaultValue = before - MILLISECONDS_IN_ONE_DAY;
651 if (!NapiUtils::HasNamedProperty(env, value, "after")) {
652 return defaultValue;
653 }
654 napi_value value1 = NapiUtils::GetNamedProperty(env, value, "after");
655 if (NapiUtils::GetValueType(env, value1) != napi_number) {
656 return defaultValue;
657 }
658 int64_t ret = 0;
659 NAPI_CALL_BASE(env, napi_get_value_int64(env, value1, &ret), defaultValue);
660 return ret;
661 }
662
Search(napi_env env,napi_callback_info info)663 napi_value JsTask::Search(napi_env env, napi_callback_info info)
664 {
665 struct SearchContext : public AsyncCall::Context {
666 Filter filter;
667 std::vector<std::string> tids;
668 };
669
670 auto context = std::make_shared<SearchContext>();
671 context->withErrCode_ = true;
672 context->version_ = Version::API10;
673 auto input = [context](size_t argc, napi_value *argv, napi_value self) -> napi_status {
674 bool ret = ParseSearch(context->env_, argc, argv, context->filter);
675 if (!ret) {
676 NapiUtils::ThrowError(context->env_, E_PARAMETER_CHECK, "Parse filter fail!", true);
677 return napi_invalid_arg;
678 }
679 return napi_ok;
680 };
681 auto output = [context](napi_value *result) -> napi_status {
682 if (context->innerCode_ != E_OK) {
683 return napi_generic_failure;
684 }
685 *result = NapiUtils::Convert2JSValue(context->env_, context->tids);
686 return napi_ok;
687 };
688 auto exec = [context]() {
689 if (!RequestManager::GetInstance()->LoadRequestServer()) {
690 context->innerCode_ = E_SERVICE_ERROR;
691 return;
692 }
693 context->innerCode_ = RequestManager::GetInstance()->Search(context->filter, context->tids);
694 };
695 context->SetInput(std::move(input)).SetOutput(std::move(output)).SetExec(std::move(exec));
696 AsyncCall asyncCall(env, info, context);
697 return asyncCall.Call(context, "search");
698 }
699
Query(napi_env env,napi_callback_info info)700 napi_value JsTask::Query(napi_env env, napi_callback_info info)
701 {
702 struct QueryContext : public AsyncCall::Context {
703 std::string tid;
704 TaskInfo taskInfo;
705 };
706
707 auto context = std::make_shared<QueryContext>();
708 context->withErrCode_ = true;
709 context->version_ = Version::API10;
710 auto input = [context](size_t argc, napi_value *argv, napi_value self) -> napi_status {
711 context->tid = ParseTid(context->env_, argc, argv);
712 if (context->tid.empty()) {
713 NapiUtils::ThrowError(context->env_, E_PARAMETER_CHECK, "Parse tid fail!", true);
714 return napi_invalid_arg;
715 }
716 return napi_ok;
717 };
718 auto output = [context](napi_value *result) -> napi_status {
719 if (context->innerCode_ != E_OK) {
720 return napi_generic_failure;
721 }
722 context->taskInfo.withSystem = true;
723 *result = NapiUtils::Convert2JSValue(context->env_, context->taskInfo);
724 return napi_ok;
725 };
726 auto exec = [context]() {
727 if (!RequestManager::GetInstance()->LoadRequestServer()) {
728 context->innerCode_ = E_SERVICE_ERROR;
729 return;
730 }
731 context->innerCode_ = RequestManager::GetInstance()->Query(context->tid, context->taskInfo);
732 };
733 context->SetInput(std::move(input)).SetOutput(std::move(output)).SetExec(std::move(exec));
734 AsyncCall asyncCall(env, info, context);
735 return asyncCall.Call(context, "query");
736 }
737
GetTid()738 std::string JsTask::GetTid()
739 {
740 return tid_;
741 }
742
SetTid(int32_t tid)743 void JsTask::SetTid(int32_t tid)
744 {
745 tid_ = std::to_string(tid);
746 }
747
GetListenerSize(const std::string & key)748 size_t JsTask::GetListenerSize(const std::string &key)
749 {
750 std::lock_guard<std::mutex> autoLock(listenerMutex_);
751 auto it = listenerMap_.find(key);
752 if (it == listenerMap_.end()) {
753 return 0;
754 }
755 REQUEST_HILOGD("listenerMap_ size %{public}zu", it->second.size());
756 return it->second.size();
757 }
758
AddTaskMap(const std::string & key,JsTask * task)759 void JsTask::AddTaskMap(const std::string &key, JsTask *task)
760 {
761 std::lock_guard<std::mutex> lockGuard(JsTask::taskMutex_);
762 JsTask::taskMap_[key] = task;
763 }
764
AddTaskContextMap(const std::string & key,std::shared_ptr<ContextInfo> context)765 void JsTask::AddTaskContextMap(const std::string &key, std::shared_ptr<ContextInfo> context)
766 {
767 std::lock_guard<std::mutex> lockGuard(JsTask::taskContextMutex_);
768 JsTask::taskContextMap_[key] = context;
769 }
770
AddListener(const std::string & key,const sptr<RequestNotify> & listener)771 void JsTask::AddListener(const std::string &key, const sptr<RequestNotify> &listener)
772 {
773 REQUEST_HILOGD("AddListener key %{public}s", key.c_str());
774 std::lock_guard<std::mutex> autoLock(listenerMutex_);
775 listenerMap_[key].push_back(listener);
776 }
777
RemoveListener(const std::string & type,const std::string & tid,napi_value callback,Version version)778 void JsTask::RemoveListener(const std::string &type, const std::string &tid, napi_value callback, Version version)
779 {
780 std::string key = type + tid;
781 std::lock_guard<std::mutex> autoLock(listenerMutex_);
782 auto it = listenerMap_.find(key);
783 if (it == listenerMap_.end()) {
784 return;
785 }
786 for (auto item = it->second.begin(); item != it->second.end(); item++) {
787 if (Equals((*item)->env_, callback, (*item)->ref_)) {
788 listenerMap_[key].erase(item);
789 break;
790 }
791 }
792 if (listenerMap_[key].empty()) {
793 RequestManager::GetInstance()->Off(type, tid, version);
794 listenerMap_.erase(key);
795 }
796 }
797
RemoveListener(const std::string & type,const std::string & tid,Version version)798 void JsTask::RemoveListener(const std::string &type, const std::string &tid, Version version)
799 {
800 {
801 std::lock_guard<std::mutex> autoLock(listenerMutex_);
802 auto it = listenerMap_.find(type + tid);
803 if (it == listenerMap_.end()) {
804 return;
805 }
806 }
807 int32_t ret = RequestManager::GetInstance()->Off(type, tid, version);
808 {
809 std::lock_guard<std::mutex> autoLock(listenerMutex_);
810 auto it = listenerMap_.find(type + tid);
811 if (it == listenerMap_.end()) {
812 return;
813 }
814 if (ret == E_OK) {
815 listenerMap_.erase(it);
816 }
817 }
818 }
819
ClearListener()820 void JsTask::ClearListener()
821 {
822 std::lock_guard<std::mutex> autoLock(listenerMutex_);
823 for (const auto &listener : listenerMap_) {
824 for (const auto &iter : listener.second) {
825 if (iter != nullptr) {
826 iter->DeleteCallbackRef();
827 }
828 }
829 }
830 listenerMap_.clear();
831 }
832
ReloadListener()833 void JsTask::ReloadListener()
834 {
835 REQUEST_HILOGD("ReloadListener in");
836 std::lock_guard<std::mutex> lockGuard(JsTask::taskMutex_);
837 for (const auto &it : taskMap_) {
838 std::string tid = it.first;
839 for (auto itListener : it.second->listenerMap_) {
840 std::string key = itListener.first;
841 if (key.find(tid) == std::string::npos) {
842 continue;
843 }
844 std::string type = key.substr(0, key.find(tid));
845 for (const auto &listener : itListener.second) {
846 RequestManager::GetInstance()->On(type, tid, listener, it.second->config_.version);
847 }
848 }
849 }
850 }
851
ClearTaskMap(const std::string & key)852 void JsTask::ClearTaskMap(const std::string &key)
853 {
854 std::lock_guard<std::mutex> lockGuard(JsTask::taskMutex_);
855 auto it = taskMap_.find(key);
856 if (it == taskMap_.end()) {
857 return;
858 }
859 taskMap_.erase(it);
860 }
861
SetDirsPermission(std::vector<std::string> & dirs)862 bool JsTask::SetDirsPermission(std::vector<std::string> &dirs)
863 {
864 if (dirs.empty()) {
865 return true;
866 }
867 std::string newPath = "/data/storage/el2/base/.ohos/.request/.certs";
868 std::vector<std::string> dirElems;
869 JsInitialize::StringSplit(newPath, '/', dirElems);
870 if (!JsInitialize::CreateDirs(dirElems)) {
871 REQUEST_HILOGE("CreateDirs Err: %{public}s", newPath.c_str());
872 return false;
873 }
874
875 for (const auto &folderPath : dirs) {
876 fs::path folder = folderPath;
877 if (!(fs::exists(folder) && fs::is_directory(folder))) {
878 return false;
879 }
880 for (const auto &entry : fs::directory_iterator(folder)) {
881 fs::path path = entry.path();
882 std::string existfilePath = folder.string() + "/" + path.filename().string();
883 std::string newfilePath = newPath + "/" + path.filename().string();
884 if (!fs::exists(newfilePath)) {
885 fs::copy(existfilePath, newfilePath);
886 }
887 if (chmod(newfilePath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
888 REQUEST_HILOGD("File add OTH access Failed.");
889 }
890 REQUEST_HILOGD("current filePath is %{public}s", newfilePath.c_str());
891 if (!JsTask::SetPathPermission(newfilePath)) {
892 REQUEST_HILOGE("Set path permission fail.");
893 return false;
894 }
895 }
896 }
897 if (!dirs.empty()) {
898 dirs.clear();
899 dirs.push_back(newPath);
900 }
901 return true;
902 }
903
SetPathPermission(const std::string & filepath)904 bool JsTask::SetPathPermission(const std::string &filepath)
905 {
906 std::string baseDir;
907 if (!CheckPathBaseDir(filepath, baseDir)) {
908 return false;
909 }
910
911 AddPathMap(filepath, baseDir);
912 for (auto it : pathMap_) {
913 if (it.second <= 0) {
914 continue;
915 }
916 if (AclSetAccess(it.first, SA_PERMISSION_X) != ACL_SUCC) {
917 REQUEST_HILOGD("AclSetAccess Parent Dir Failed: %{public}s", it.first.c_str());
918 }
919 }
920
921 std::string childDir = filepath.substr(0, filepath.rfind("/"));
922 if (AclSetAccess(childDir, SA_PERMISSION_RWX) != ACL_SUCC) {
923 REQUEST_HILOGE("AclSetAccess Child Dir Failed: %{public}s", childDir.c_str());
924 return false;
925 }
926 return true;
927 }
928
CheckPathBaseDir(const std::string & filepath,std::string & baseDir)929 bool JsTask::CheckPathBaseDir(const std::string &filepath, std::string &baseDir)
930 {
931 if (!JsInitialize::GetBaseDir(baseDir)) {
932 return false;
933 }
934
935 if (filepath.find(baseDir) != std::string::npos) {
936 return true;
937 }
938 // check baseDir replaced with el2
939 if (baseDir.find(AREA1) != std::string::npos) {
940 baseDir = baseDir.replace(baseDir.find(AREA1), AREA1.length(), AREA2);
941 if (filepath.find(baseDir) == std::string::npos) {
942 REQUEST_HILOGE("File dir not include base dir: %{public}s", baseDir.c_str());
943 return false;
944 }
945 return true;
946 }
947 // check baseDir replaced with el1
948 if (baseDir.find(AREA2) != std::string::npos) {
949 baseDir = baseDir.replace(baseDir.find(AREA2), AREA2.length(), AREA1);
950 if (filepath.find(baseDir) == std::string::npos) {
951 REQUEST_HILOGE("File dir not include base dir: %{public}s", baseDir.c_str());
952 return false;
953 }
954 return true;
955 }
956 return false;
957 }
958
AddPathMap(const std::string & filepath,const std::string & baseDir)959 void JsTask::AddPathMap(const std::string &filepath, const std::string &baseDir)
960 {
961 std::string childDir(filepath);
962 std::string parentDir;
963 while (childDir.length() > baseDir.length()) {
964 parentDir = childDir.substr(0, childDir.rfind("/"));
965 std::lock_guard<std::mutex> lockGuard(JsTask::pathMutex_);
966 auto it = pathMap_.find(parentDir);
967 if (it == pathMap_.end()) {
968 pathMap_[parentDir] = 1;
969 } else {
970 pathMap_[parentDir] += 1;
971 }
972 childDir = parentDir;
973 }
974 }
975
ResetDirAccess(const std::string & filepath)976 void JsTask::ResetDirAccess(const std::string &filepath)
977 {
978 int ret = AclSetAccess(filepath, SA_PERMISSION_CLEAN);
979 if (ret != ACL_SUCC) {
980 REQUEST_HILOGD("AclSetAccess Reset Dir Failed: %{public}s", filepath.c_str());
981 }
982 }
983
RemovePathMap(const std::string & filepath)984 void JsTask::RemovePathMap(const std::string &filepath)
985 {
986 std::string baseDir;
987 if (!CheckPathBaseDir(filepath, baseDir)) {
988 return;
989 }
990
991 if (chmod(filepath.c_str(), S_IRUSR | S_IWUSR | S_IRGRP) != 0) {
992 REQUEST_HILOGE("File remove OTH access Failed.");
993 }
994
995 std::string childDir(filepath);
996 std::string parentDir;
997 while (childDir.length() > baseDir.length()) {
998 parentDir = childDir.substr(0, childDir.rfind("/"));
999 std::lock_guard<std::mutex> lockGuard(JsTask::pathMutex_);
1000 auto it = pathMap_.find(parentDir);
1001 if (it != pathMap_.end()) {
1002 if (pathMap_[parentDir] <= 1) {
1003 pathMap_.erase(parentDir);
1004 ResetDirAccess(parentDir);
1005 } else {
1006 pathMap_[parentDir] -= 1;
1007 }
1008 }
1009 childDir = parentDir;
1010 }
1011 }
1012
RemoveDirsPermission(const std::vector<std::string> & dirs)1013 void JsTask::RemoveDirsPermission(const std::vector<std::string> &dirs)
1014 {
1015 for (const auto &folderPath : dirs) {
1016 fs::path folder = folderPath;
1017 for (const auto &entry : fs::directory_iterator(folder)) {
1018 fs::path path = entry.path();
1019 std::string filePath = folder.string() + "/" + path.filename().string();
1020 RemovePathMap(filePath);
1021 }
1022 }
1023 }
1024
ClearTaskContext(const std::string & key)1025 void JsTask::ClearTaskContext(const std::string &key)
1026 {
1027 std::lock_guard<std::mutex> lockGuard(JsTask::taskContextMutex_);
1028 auto it = taskContextMap_.find(key);
1029 if (it == taskContextMap_.end()) {
1030 REQUEST_HILOGD("Clear task context, not in ContextMap");
1031 return;
1032 }
1033 auto context = it->second;
1034 auto bodyFileNames = context->task->config_.bodyFileNames;
1035 for (auto &filePath : bodyFileNames) {
1036 NapiUtils::RemoveFile(filePath);
1037 }
1038 // Reset Acl permission
1039 for (auto &file : context->task->config_.files) {
1040 RemovePathMap(file.uri);
1041 }
1042 RemoveDirsPermission(context->task->config_.certsPath);
1043 taskContextMap_.erase(it);
1044 UnrefTaskContextMap(context);
1045 }
1046
UnrefTaskContextMap(std::shared_ptr<ContextInfo> context)1047 void JsTask::UnrefTaskContextMap(std::shared_ptr<ContextInfo> context)
1048 {
1049 ContextCallbackData *data = new ContextCallbackData();
1050 if (data == nullptr) {
1051 return;
1052 }
1053 data->context = context;
1054 UvQueue::Call(data->context->env_, static_cast<void *>(data), UvUnrefTaskContext);
1055 return;
1056 }
1057
UvUnrefTaskContext(uv_work_t * work,int status)1058 void JsTask::UvUnrefTaskContext(uv_work_t *work, int status)
1059 {
1060 ContextCallbackData *data = static_cast<ContextCallbackData *>(work->data);
1061 if (data == nullptr) {
1062 // Ensure that the `work` is not nullptr.
1063 delete work;
1064 return;
1065 }
1066 napi_handle_scope scope = nullptr;
1067 napi_open_handle_scope(data->context->env_, &scope);
1068 if (scope == nullptr) {
1069 delete data;
1070 delete work;
1071 return;
1072 }
1073 u_int32_t taskRefCount = 0;
1074 napi_reference_unref(data->context->env_, data->context->taskRef, &taskRefCount);
1075 REQUEST_HILOGD("Unref task ref, count is %{public}d", taskRefCount);
1076 if (taskRefCount == 0) {
1077 napi_delete_reference(data->context->env_, data->context->taskRef);
1078 }
1079 if (data->context->version_ == Version::API10) {
1080 u_int32_t configRefCount = 0;
1081 napi_reference_unref(data->context->env_, data->context->jsConfig, &configRefCount);
1082 REQUEST_HILOGI("Unref task config ref, count is %{public}d", configRefCount);
1083 if (configRefCount == 0) {
1084 napi_delete_reference(data->context->env_, data->context->jsConfig);
1085 }
1086 }
1087 napi_close_handle_scope(data->context->env_, scope);
1088 delete data;
1089 delete work;
1090 return;
1091 }
1092
Equals(napi_env env,napi_value value,napi_ref copy)1093 bool JsTask::Equals(napi_env env, napi_value value, napi_ref copy)
1094 {
1095 if (copy == nullptr) {
1096 return (value == nullptr);
1097 }
1098
1099 napi_value copyValue = nullptr;
1100 napi_get_reference_value(env, copy, ©Value);
1101
1102 bool isEquals = false;
1103 napi_strict_equals(env, value, copyValue, &isEquals);
1104 return isEquals;
1105 }
1106
RegisterForegroundResume()1107 void JsTask::RegisterForegroundResume()
1108 {
1109 if (register_) {
1110 return;
1111 }
1112 register_ = true;
1113 auto context = AbilityRuntime::ApplicationContext::GetInstance();
1114 if (context == nullptr) {
1115 REQUEST_HILOGE("Get ApplicationContext failed");
1116 return;
1117 }
1118 context->RegisterAbilityLifecycleCallback(std::make_shared<AppStateCallback>());
1119 REQUEST_HILOGD("Register foreground resume callback success");
1120 }
1121 } // namespace OHOS::Request