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