1 /*
2  * Copyright (c) 2022 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 "napi/native_common.h"
17 #include "accesstoken_kit.h"
18 
19 #include "print_task.h"
20 #include "napi_print_utils.h"
21 #include "print_callback.h"
22 #include "print_log.h"
23 #include "print_manager_client.h"
24 #include "print_constant.h"
25 
26 namespace OHOS::Print {
27 
28 using namespace std;
29 using namespace Security::AccessToken;
30 
31 const std::string EVENT_BLOCK = "block";
32 const std::string EVENT_SUCCESS = "succeed";
33 const std::string EVENT_FAIL = "fail";
34 const std::string EVENT_CANCEL = "cancel";
35 
36 static const std::string SPOOLER_BUNDLE_NAME = "com.ohos.spooler";
37 static const std::string SPOOLER_PREVIEW_ABILITY_NAME = "PrintServiceExtAbility";
38 static const std::string LAUNCH_PARAMETER_JOB_ID = "jobId";
39 static const std::string LAUNCH_PARAMETER_FILE_LIST = "fileList";
40 static const std::string TOKEN_KEY = "ohos.ability.params.token";
41 static const std::string UI_EXTENSION_TYPE_NAME = "ability.want.params.uiExtensionType";
42 static const std::string PRINT_UI_EXTENSION_TYPE = "sysDialog/print";
43 static const std::string CALLER_PKG_NAME = "caller.pkgName";
44 static const std::string ABILITY_PARAMS_STREAM = "ability.params.stream";
45 static const std::string LAUNCH_PARAMETER_FILE_LIST_SIZE = "fileListSize";
46 static const int32_t MAX_FILE_LIST_SIZE = 100;
47 
PrintTask(const std::vector<std::string> & innerList,const sptr<IRemoteObject> & innerCallerToken_)48 PrintTask::PrintTask(const std::vector<std::string> &innerList, const sptr<IRemoteObject> &innerCallerToken_)
49     : taskId_("")
50 {
51     if (!innerList.empty()) {
52         if (innerList.begin()->find("fd://") == 0) {
53             PRINT_HILOGD("list type: fdlist");
54             for (auto fdPath : innerList) {
55                 pathType_ = FD_PATH;
56                 uint32_t fd = PrintUtils::GetIdFromFdPath(fdPath);
57                 fdList_.emplace_back(fd);
58             }
59         } else {
60             PRINT_HILOGD("list type: filelist");
61             fileList_.assign(innerList.begin(), innerList.end());
62             pathType_ = FILE_PATH_ABSOLUTED;
63             if (!fileList_.empty() && fileList_.begin()->find("file://") == 0) {
64                 pathType_ = FILE_PATH;
65             }
66         }
67     }
68 
69     supportEvents_[EVENT_BLOCK] = true;
70     supportEvents_[EVENT_SUCCESS] = true;
71     supportEvents_[EVENT_FAIL] = true;
72     supportEvents_[EVENT_CANCEL] = true;
73     callerToken_ = innerCallerToken_;
74 }
75 
PrintTask(const std::string & innerPrintJobName_,const sptr<IPrintCallback> & innerPrintAdapterCallback_,const std::shared_ptr<PrintAttributes> & innerPrintAttributes_,const sptr<IRemoteObject> & innerCallerToken_)76 PrintTask::PrintTask(const std::string &innerPrintJobName_, const sptr<IPrintCallback> &innerPrintAdapterCallback_,
77     const std::shared_ptr<PrintAttributes> &innerPrintAttributes_, const sptr<IRemoteObject> &innerCallerToken_)
78     : taskId_("")
79 {
80     supportEvents_[EVENT_BLOCK] = true;
81     supportEvents_[EVENT_SUCCESS] = true;
82     supportEvents_[EVENT_FAIL] = true;
83     supportEvents_[EVENT_CANCEL] = true;
84     printJobName_ = innerPrintJobName_;
85     printAdapterCallback_ = innerPrintAdapterCallback_;
86     printAttributes_ = innerPrintAttributes_;
87     callerToken_ = innerCallerToken_;
88 }
89 
~PrintTask()90 PrintTask::~PrintTask()
91 {
92     supportEvents_.clear();
93     Stop();
94 }
95 
Start(napi_env env,napi_callback_info info)96 uint32_t PrintTask::Start(napi_env env, napi_callback_info info)
97 {
98     if (fileList_.empty() && fdList_.empty()) {
99         PRINT_HILOGE("fileList and fdList are both empty");
100         return E_PRINT_INVALID_PARAMETER;
101     }
102     if (pathType_ == FILE_PATH_ABSOLUTED) {
103         for (auto file : fileList_) {
104             int32_t fd = PrintUtils::OpenFile(file);
105             if (fd >= 0) {
106                 fdList_.emplace_back(fd);
107                 continue;
108             }
109             PRINT_HILOGE("file[%{private}s] is invalid", file.c_str());
110             for (auto fd : fdList_) {
111                 close(fd);
112             }
113             fdList_.clear();
114             fileList_.clear();
115             return E_PRINT_INVALID_PARAMETER;
116         }
117     }
118 
119     PRINT_HILOGI("call client's StartPrint interface.");
120     std::shared_ptr<AdapterParam> adapterParam = std::make_shared<AdapterParam>();
121     CreateDefaultAdapterParam(adapterParam);
122     std::string jobId = PrintUtils::GetPrintJobId();
123     adapterParam->jobId = jobId;
124     taskId_ = jobId;
125     uint32_t ret = CallSpooler(env, info, adapterParam, false);
126     if (ret != E_PRINT_NONE) {
127         PRINT_HILOGE("CallSpooler failed.");
128         for (auto fd : fdList_) {
129             close(fd);
130         }
131         fdList_.clear();
132         fileList_.clear();
133         return ret;
134     }
135     return PrintManagerClient::GetInstance()->StartPrint(fileList_, fdList_, taskId_);
136 }
137 
StartPrintAdapter(napi_env env,napi_callback_info info)138 uint32_t PrintTask::StartPrintAdapter(napi_env env, napi_callback_info info)
139 {
140     if (printAdapterCallback_ != nullptr && printAttributes_ != nullptr) {
141         PRINT_HILOGI("call client's StartPrintAdapter interface.");
142         if (callerToken_ != nullptr) {
143             std::shared_ptr<AdapterParam> adapterParam = std::make_shared<AdapterParam>();
144             if (adapterParam == nullptr) {
145                 PRINT_HILOGE("create adapterParam failed.");
146                 return E_PRINT_SERVER_FAILURE;
147             }
148             adapterParam->documentName = printJobName_;
149             adapterParam->isCheckFdList = false;
150             adapterParam->printAttributes = *printAttributes_;
151             std::string jobId = PrintUtils::GetPrintJobId();
152             adapterParam->jobId = jobId;
153             taskId_ = jobId;
154             uint32_t ret = CallSpooler(env, info, adapterParam, true);
155             if (ret != E_PRINT_NONE) {
156                 PRINT_HILOGE("CallSpooler failed.");
157             }
158             return PrintManagerClient::GetInstance()->Print(
159                 printJobName_, printAdapterCallback_, *printAttributes_, taskId_, callerToken_);
160         }
161     }
162     return E_PRINT_INVALID_PARAMETER;
163 }
164 
CallSpooler(napi_env env,napi_callback_info info,const std::shared_ptr<AdapterParam> & adapterParam,bool isPrintByAdapter)165 uint32_t PrintTask::CallSpooler(
166     napi_env env, napi_callback_info info, const std::shared_ptr<AdapterParam> &adapterParam, bool isPrintByAdapter)
167 {
168     PRINT_HILOGI("enter CallSpooler.");
169     if (!CheckPermission(PERMISSION_NAME_PRINT)) {
170         PRINT_HILOGE("no permission to access print service, ErrorCode:[%{public}d]", E_PRINT_NO_PERMISSION);
171         return E_PRINT_NO_PERMISSION;
172     }
173     size_t argc = isPrintByAdapter ? NapiPrintUtils::ARGC_FOUR : NapiPrintUtils::ARGC_TWO;
174     size_t contextIndex = isPrintByAdapter ? NapiPrintUtils::INDEX_THREE : NapiPrintUtils::INDEX_ONE;
175     size_t callBackIndex = isPrintByAdapter ? NapiPrintUtils::INDEX_FOUR : NapiPrintUtils::INDEX_TWO;
176     size_t argMaxNum = isPrintByAdapter ? NapiPrintUtils::ARGC_FIVE : NapiPrintUtils::ARGC_THREE;
177     napi_value argv[NapiPrintUtils::ARGC_FOUR] = {0};
178     napi_value thisArg = nullptr;
179     void *data = nullptr;
180     napi_value result = nullptr;
181 
182     PRINT_CALL_BASE(env, napi_get_undefined(env, &result), E_PRINT_INVALID_PARAMETER);
183     PRINT_CALL_BASE(env, napi_get_cb_info(env, info, &argc, argv, &thisArg, &data), E_PRINT_INVALID_PARAMETER);
184     PRINT_HILOGI("CallSpooler params size: %{public}zu", argc);
185     if (argc < argMaxNum - 1) {
186         PRINT_HILOGE("invalid parameters.");
187         return E_PRINT_INVALID_PARAMETER;
188     }
189 
190     auto asyncContext = std::make_shared<BaseContext>();
191     asyncContext->env = env;
192     asyncContext->requestType = PrintRequestType::REQUEST_TYPE_START;
193     if (!ParseAbilityContextReq(env, argv[contextIndex], asyncContext->context, asyncContext->uiExtensionContext)) {
194         PRINT_HILOGE("invalid parameters.");
195         return E_PRINT_INVALID_PARAMETER;
196     }
197 
198     if (argc == argMaxNum) {
199         napi_valuetype valueType = napi_undefined;
200         PRINT_CALL_BASE(env, napi_typeof(env, argv[callBackIndex], &valueType), napi_undefined);
201         if (valueType == napi_function) {
202             PRINT_CALL_BASE(env, napi_create_reference(env, argv[callBackIndex], 1, &asyncContext->callback),
203                 E_PRINT_INVALID_PARAMETER);
204             PRINT_HILOGD("is a callback api");
205         }
206     } else {
207         PRINT_CALL_BASE(env, napi_create_promise(env, &asyncContext->deferred, &result), E_PRINT_INVALID_PARAMETER);
208         PRINT_HILOGD("is a promise api");
209     }
210     uint32_t ret = StartUIExtensionAbility(asyncContext, adapterParam);
211     PRINT_HILOGI("end CallSpooler");
212     return ret;
213 }
214 
ParseAbilityContextReq(napi_env env,const napi_value & obj,std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> & abilityContext,std::shared_ptr<OHOS::AbilityRuntime::UIExtensionContext> & uiExtensionContext)215 bool PrintTask::ParseAbilityContextReq(napi_env env, const napi_value &obj,
216     std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> &abilityContext,
217     std::shared_ptr<OHOS::AbilityRuntime::UIExtensionContext> &uiExtensionContext)
218 {
219     PRINT_HILOGD("begin ParseAbilityContextReq");
220     bool stageMode = false;
221     napi_status status = OHOS::AbilityRuntime::IsStageContext(env, obj, stageMode);
222     if (status != napi_ok || !stageMode) {
223         PRINT_HILOGE("it is not a stage mode");
224         return false;
225     }
226 
227     auto context = OHOS::AbilityRuntime::GetStageModeContext(env, obj);
228     if (context == nullptr) {
229         PRINT_HILOGE("get context failed");
230         return false;
231     }
232 
233     abilityContext = OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
234     if (abilityContext == nullptr) {
235         PRINT_HILOGE("get abilityContext failed");
236         uiExtensionContext =
237             OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::UIExtensionContext>(context);
238         if (uiExtensionContext == nullptr) {
239             PRINT_HILOGE("get uiExtensionContext failed");
240             return false;
241         }
242     }
243 
244     PRINT_HILOGD("end ParseAbilityContextReq");
245     return true;
246 }
247 
StartUIExtensionAbility(std::shared_ptr<BaseContext> asyncContext,const std::shared_ptr<AdapterParam> & adapterParam)248 uint32_t PrintTask::StartUIExtensionAbility(
249     std::shared_ptr<BaseContext> asyncContext, const std::shared_ptr<AdapterParam> &adapterParam)
250 {
251     PRINT_HILOGD("begin StartUIExtensionAbility");
252 
253     if (adapterParam == nullptr) {
254         PRINT_HILOGE("adapterParam is nullptr.");
255         return E_PRINT_INVALID_PARAMETER;
256     }
257     if ((adapterParam->isCheckFdList && fileList_.empty() && fdList_.empty())) {
258         PRINT_HILOGE("to be printed filelist and fdlist are empty.");
259         return E_PRINT_INVALID_PARAMETER;
260     }
261     AAFwk::Want want;
262     want.SetElementName(SPOOLER_BUNDLE_NAME, SPOOLER_PREVIEW_ABILITY_NAME);
263     want.SetParam(LAUNCH_PARAMETER_JOB_ID, adapterParam->jobId);
264     if (fileList_.size() <= MAX_FILE_LIST_SIZE) {
265         want.SetParam(LAUNCH_PARAMETER_FILE_LIST, fileList_);
266     } else {
267         PRINT_HILOGW("fileList exceeds the maximum length.");
268     }
269     want.SetParam(LAUNCH_PARAMETER_FILE_LIST_SIZE, static_cast<int>(fileList_.size()));
270     PrintUtils::BuildAdapterParam(adapterParam, want);
271     int32_t callerTokenId = static_cast<int32_t>(IPCSkeleton::GetCallingTokenID());
272     int32_t callerUid = IPCSkeleton::GetCallingUid();
273     int32_t callerPid = IPCSkeleton::GetCallingPid();
274     std::string callerPkg = PrintUtils::GetBundleNameForUid(callerUid);
275     want.SetParam(AAFwk::Want::PARAM_RESV_CALLER_TOKEN, callerTokenId);
276     want.SetParam(AAFwk::Want::PARAM_RESV_CALLER_UID, callerUid);
277     want.SetParam(AAFwk::Want::PARAM_RESV_CALLER_PID, callerPid);
278     want.SetParam(CALLER_PKG_NAME, callerPkg);
279     want.SetParam(UI_EXTENSION_TYPE_NAME, PRINT_UI_EXTENSION_TYPE);
280     want.SetParam(ABILITY_PARAMS_STREAM, fileList_);
281     want.SetFlags(AAFwk::Want::FLAG_AUTH_READ_URI_PERMISSION);
282 
283     uint32_t ret = StartUIExtensionAbility(want, asyncContext);
284     if (ret != E_PRINT_NONE) {
285         PRINT_HILOGE("StartUIExtensionAbility fail");
286     }
287     PRINT_HILOGD("end StartUIExtensionAbility");
288     return ret;
289 }
290 
StartUIExtensionAbility(OHOS::AAFwk::Want & want,std::shared_ptr<BaseContext> asyncContext)291 uint32_t PrintTask::StartUIExtensionAbility(OHOS::AAFwk::Want &want, std::shared_ptr<BaseContext> asyncContext)
292 {
293     PRINT_HILOGI("begin StartUIExtensionAbility");
294     if (asyncContext == nullptr) {
295         PRINT_HILOGE("asyncContext is nullptr");
296         return E_PRINT_INVALID_PARAMETER;
297     }
298 
299     if (asyncContext->context == nullptr && asyncContext->uiExtensionContext == nullptr) {
300         PRINT_HILOGE("asyncContext is nullptr");
301         return E_PRINT_INVALID_PARAMETER;
302     }
303 
304     auto uiContent = GetUIContent(asyncContext.get());
305     if (uiContent == nullptr) {
306         PRINT_HILOGE("UIContent is nullptr");
307         return E_PRINT_INVALID_PARAMETER;
308     }
309 
310     std::string info = uiContent->GetContentInfo();
311     auto callback = std::make_shared<PrintModalUICallback>(asyncContext);
312     if (callback == nullptr) {
313         PRINT_HILOGE("create callback failed.");
314         return E_PRINT_SERVER_FAILURE;
315     }
316     OHOS::Ace::ModalUIExtensionCallbacks extensionCallbacks = {
317         [callback](int32_t releaseCode) { callback->OnRelease(releaseCode); },
318         [callback](int32_t resultCode, const OHOS::AAFwk::Want& result) {
319             callback->OnResultForModal(resultCode, result);
320         },
321         [callback](const OHOS::AAFwk::WantParams& request) { callback->OnReceive(request); },
322         [callback](int32_t code, const std::string& name, const std::string& message) {
323             callback->OnError(code, name, message);
324         }
325     };
326 
327     OHOS::Ace::ModalUIExtensionConfig config;
328     config.isProhibitBack = true;
329     int32_t sessionId = uiContent->CreateModalUIExtension(want, extensionCallbacks, config);
330     PRINT_HILOGI("StartUIExtensionAbility sessionId %{public}d", sessionId);
331     callback->SetSessionId(sessionId);
332 
333     PRINT_HILOGI("end StartUIExtensionAbility");
334     return E_PRINT_NONE;
335 }
336 
GetUIContent(const BaseContext * asyncContext)337 OHOS::Ace::UIContent *PrintTask::GetUIContent(const BaseContext *asyncContext)
338 {
339     if (asyncContext == nullptr) {
340         PRINT_HILOGE("asyncContext is nullptr.");
341         return nullptr;
342     }
343     OHOS::Ace::UIContent *uiContent = nullptr;
344     if (asyncContext->context != nullptr) {
345         PRINT_HILOGI("get uiContext by ability context");
346         uiContent = asyncContext->context->GetUIContent();
347     } else if (asyncContext->uiExtensionContext != nullptr) {
348         PRINT_HILOGI("get uiContext by ui extension ability context");
349         uiContent = asyncContext->uiExtensionContext->GetUIContent();
350     } else {
351         PRINT_HILOGE("get uiContext failed.");
352     }
353 
354     return uiContent;
355 }
356 
CreateDefaultAdapterParam(const std::shared_ptr<AdapterParam> & adapterParam)357 void PrintTask::CreateDefaultAdapterParam(const std::shared_ptr<AdapterParam> &adapterParam)
358 {
359     adapterParam->documentName = "";
360     adapterParam->isCheckFdList = true;
361 }
362 
Stop()363 void PrintTask::Stop()
364 {
365     PrintManagerClient::GetInstance()->StopPrint(taskId_);
366     taskId_ = "";
367 }
368 
GetId() const369 const std::string &PrintTask::GetId() const
370 {
371     return taskId_;
372 }
373 
On(napi_env env,napi_callback_info info)374 napi_value PrintTask::On(napi_env env, napi_callback_info info)
375 {
376     PRINT_HILOGD("Enter ---->");
377     size_t argc = NapiPrintUtils::MAX_ARGC;
378     napi_value argv[NapiPrintUtils::MAX_ARGC] = { nullptr };
379     napi_value thisVal = nullptr;
380     void *data = nullptr;
381     PRINT_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVal, &data));
382     PRINT_ASSERT(env, argc == NapiPrintUtils::ARGC_TWO, "need 2 parameter!");
383 
384     napi_valuetype valuetype;
385     PRINT_CALL(env, napi_typeof(env, argv[0], &valuetype));
386     PRINT_ASSERT(env, valuetype == napi_string, "type is not a string");
387     std::string type = NapiPrintUtils::GetStringFromValueUtf8(env, argv[NapiPrintUtils::INDEX_ZERO]);
388     PRINT_HILOGD("type : %{public}s", type.c_str());
389 
390     valuetype = napi_undefined;
391     napi_typeof(env, argv[1], &valuetype);
392     PRINT_ASSERT(env, valuetype == napi_function, "callback is not a function");
393 
394     PrintTask *task;
395     PRINT_CALL(env, napi_unwrap(env, thisVal, reinterpret_cast<void **>(&task)));
396     if (task == nullptr || !task->IsSupportType(type)) {
397         PRINT_HILOGE("Event On type : %{public}s not support", type.c_str());
398         return nullptr;
399     }
400 
401     napi_ref callbackRef = NapiPrintUtils::CreateReference(env, argv[1]);
402     sptr<IPrintCallback> callback = new (std::nothrow) PrintCallback(env, callbackRef);
403     if (callback == nullptr) {
404         PRINT_HILOGE("create print callback object fail");
405         return nullptr;
406     }
407     int32_t ret = PrintManagerClient::GetInstance()->On(task->taskId_, type, callback);
408     if (ret != E_PRINT_NONE) {
409         PRINT_HILOGE("Failed to register event");
410         return nullptr;
411     }
412     return nullptr;
413 }
414 
Off(napi_env env,napi_callback_info info)415 napi_value PrintTask::Off(napi_env env, napi_callback_info info)
416 {
417     PRINT_HILOGD("Enter ---->");
418     auto context = std::make_shared<TaskEventContext>();
419     if (context == nullptr) {
420         PRINT_HILOGE("create context failed.");
421         return nullptr;
422     }
423     auto input =
424         [context](
425             napi_env env, size_t argc, napi_value *argv, napi_value self, napi_callback_info info) -> napi_status {
426         PRINT_ASSERT_BASE(env, argc == NapiPrintUtils::ARGC_ONE, "need 1 parameter!", napi_invalid_arg);
427         napi_valuetype valuetype;
428         PRINT_CALL_BASE(env, napi_typeof(env, argv[NapiPrintUtils::INDEX_ZERO], &valuetype), napi_invalid_arg);
429         PRINT_ASSERT_BASE(env, valuetype == napi_string, "type is not a string", napi_string_expected);
430         std::string type = NapiPrintUtils::GetStringFromValueUtf8(env, argv[0]);
431         PrintTask *task;
432         PRINT_CALL_BASE(env, napi_unwrap(env, self, reinterpret_cast<void **>(&task)), napi_invalid_arg);
433         if (task == nullptr || !task->IsSupportType(type)) {
434             PRINT_HILOGE("Event On type : %{public}s not support", type.c_str());
435             context->SetErrorIndex(E_PRINT_INVALID_PARAMETER);
436             return napi_invalid_arg;
437         }
438 
439         context->type = type;
440         context->taskId = task->taskId_;
441         PRINT_HILOGD("event type : %{public}s", context->type.c_str());
442         return napi_ok;
443     };
444     auto output = [context](napi_env env, napi_value *result) -> napi_status {
445         napi_status status = napi_get_boolean(env, context->result, result);
446         PRINT_HILOGD("context->result = %{public}d", context->result);
447         return status;
448     };
449     auto exec = [context](PrintAsyncCall::Context *ctx) {
450         int32_t ret = PrintManagerClient::GetInstance()->Off(context->taskId, context->type);
451         context->result = ret == E_PRINT_NONE;
452         if (ret != E_PRINT_NONE) {
453             PRINT_HILOGE("Failed to unregistered event");
454             context->SetErrorIndex(ret);
455         }
456     };
457     context->SetAction(std::move(input), std::move(output));
458     PrintAsyncCall asyncCall(env, info, std::dynamic_pointer_cast<PrintAsyncCall::Context>(context));
459     return asyncCall.Call(env, exec);
460 }
461 
IsSupportType(const std::string & type) const462 bool PrintTask::IsSupportType(const std::string &type) const
463 {
464     return supportEvents_.find(type) != supportEvents_.end();
465 }
466 
CheckPermission(const std::string & name)467 bool PrintTask::CheckPermission(const std::string &name)
468 {
469     AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
470     TypeATokenTypeEnum tokenType = AccessTokenKit::GetTokenTypeFlag(tokenId);
471     if (tokenType == TOKEN_INVALID) {
472         PRINT_HILOGE("invalid token id %{public}d", tokenId);
473         return false;
474     }
475     int result = AccessTokenKit::VerifyAccessToken(tokenId, name);
476     if (result != PERMISSION_GRANTED) {
477         PRINT_HILOGE("Current tokenId permission is %{public}d", result);
478     }
479     return result == PERMISSION_GRANTED;
480 }
481 } // namespace OHOS::Print
482