// Copyright (C) 2023 The Qt Company Ltd. // Copyright (C) 2019 Luxoft Sweden AB // Copyright (C) 2018 Pelagicore AG // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #if defined(Q_OS_UNIX) # include #endif #include "dbuscontextadaptor.h" #include "applicationmanager.h" #include "applicationmanager_adaptor.h" #include "packagemanager.h" #include "exception.h" #include "logging.h" #include "intentclient.h" #include "intentclientrequest.h" #include "adaptorchecks_dbus.h" //NOTE: The header for this class is autogenerated from the XML interface definition. // We are NOT using the generated cpp, but instead implement the adaptor manually. QT_USE_NAMESPACE_AM using namespace QT_PREPEND_NAMESPACE_AM(DBusAdaptorChecks); using namespace Qt::StringLiterals; ApplicationManagerAdaptor::ApplicationManagerAdaptor(QObject *parent) : QDBusAbstractAdaptor(parent) { auto am = ApplicationManager::instance(); connect(am, &ApplicationManager::countChanged, this, &ApplicationManagerAdaptor::countChanged); connect(am, &ApplicationManager::applicationAdded, this, &ApplicationManagerAdaptor::applicationAdded); connect(am, &ApplicationManager::applicationChanged, this, &ApplicationManagerAdaptor::applicationChanged); connect(am, &ApplicationManager::applicationAboutToBeRemoved, this, &ApplicationManagerAdaptor::applicationAboutToBeRemoved); connect(am, &ApplicationManager::applicationWasActivated, this, &ApplicationManagerAdaptor::applicationWasActivated); connect(am, &ApplicationManager::windowManagerCompositorReadyChanged, this, &ApplicationManagerAdaptor::windowManagerCompositorReadyChanged); // connect this signal via a lambda, since it needs a type conversion connect(am, &ApplicationManager::applicationRunStateChanged, this, [this](const QString &id, QtAM::Am::RunState runState) { emit applicationRunStateChanged(id, runState); }); } ApplicationManagerAdaptor::~ApplicationManagerAdaptor() { } int ApplicationManagerAdaptor::count() const { return ApplicationManager::instance()->count(); } bool ApplicationManagerAdaptor::dummy() const { return ApplicationManager::instance()->isDummy(); } bool ApplicationManagerAdaptor::securityChecksEnabled() const { return ApplicationManager::instance()->securityChecksEnabled(); } bool ApplicationManagerAdaptor::singleProcess() const { return ApplicationManager::instance()->isSingleProcess(); } QVariantMap ApplicationManagerAdaptor::systemProperties() const { return convertToDBusVariant(ApplicationManager::instance()->systemProperties()).toMap(); } bool ApplicationManagerAdaptor::windowManagerCompositorReady() const { return ApplicationManager::instance()->isWindowManagerCompositorReady(); } QStringList ApplicationManagerAdaptor::applicationIds() { try { checkDBusAccess(); return filterPackageListByAccess(this, ApplicationManager::instance()->applicationIds(), [](const QString &applicationId) { if (auto *app = ApplicationManager::instance()->application(applicationId)) return app->packageInfo()->id(); Q_UNREACHABLE_RETURN(QString{}); }); } catchExceptionAsDBusError({}) } uint ApplicationManagerAdaptor::applicationRunState(const QString &id) { try { checkDBusAccess(); checkApplicationAccess(id); return ApplicationManager::instance()->applicationRunState(id); } catchExceptionAsDBusError({}) } QStringList ApplicationManagerAdaptor::capabilities(const QString &id) { try { checkDBusAccess(); checkApplicationAccess(id); return ApplicationManager::instance()->capabilities(id); } catchExceptionAsDBusError({}) } bool ApplicationManagerAdaptor::debugApplication(const QString &id, const QString &debugWrapper) { return debugApplication(id, debugWrapper, QString()); } bool ApplicationManagerAdaptor::debugApplication(const QString &id, const QString &debugWrapper, const QString &documentUrl) { return debugApplication(id, debugWrapper, { }, documentUrl); } bool ApplicationManagerAdaptor::debugApplication(const QString &id, const QString &debugWrapper, const QMap &redirections, const QString &documentUrl) { try { checkDBusAccess(); checkApplicationAccess(id); QVector stdioRedirections = { -1, -1, -1 }; # if defined(Q_OS_UNIX) for (auto it = redirections.cbegin(); it != redirections.cend(); ++it) { QDBusUnixFileDescriptor dfd = it.value(); int fd = dfd.fileDescriptor(); const QString &which = it.key(); if (which == u"in") stdioRedirections[0] = ::dup(fd); else if (which == u"out") stdioRedirections[1] = ::dup(fd); else if (which == u"err") stdioRedirections[2] = ::dup(fd); } # else Q_UNUSED(redirections) # endif // defined(Q_OS_UNIX) return ApplicationManager::instance()->startApplicationInternal( id, documentUrl, QString(), debugWrapper, std::move(stdioRedirections)); } catchExceptionAsDBusError({}) } QVariantMap ApplicationManagerAdaptor::get(const QString &id) { try { checkDBusAccess(); checkApplicationAccess(id); auto map = ApplicationManager::instance()->get(id); map.remove(u"application"_s); // cannot marshall QObject * map.remove(u"applicationObject"_s); // cannot marshall QObject * return convertToDBusVariant(map).toMap(); } catchExceptionAsDBusError({}) } QString ApplicationManagerAdaptor::identifyApplication(qlonglong pid) { try { checkDBusAccess(); checkDevelopmentModeSystem(); return ApplicationManager::instance()->identifyApplication(pid); } catchExceptionAsDBusError({}) } QStringList ApplicationManagerAdaptor::identifyAllApplications(qlonglong pid) { try { checkDBusAccess(); checkDevelopmentModeSystem(); return ApplicationManager::instance()->identifyAllApplications(pid); } catchExceptionAsDBusError({}) } bool ApplicationManagerAdaptor::openUrl(const QString &url) { try { checkDBusAccess(); return ApplicationManager::instance()->openUrl(url); } catchExceptionAsDBusError({}) } bool ApplicationManagerAdaptor::startApplication(const QString &id) { return startApplication(id, QString()); } bool ApplicationManagerAdaptor::startApplication(const QString &id, const QString &documentUrl) { return startApplication(id, { }, documentUrl); } bool ApplicationManagerAdaptor::startApplication(const QString &id, const QMap &redirections, const QString &documentUrl) { try { checkDBusAccess(); checkApplicationAccess(id); QVector stdioRedirections = { -1, -1, -1 }; # if defined(Q_OS_UNIX) for (auto it = redirections.cbegin(); it != redirections.cend(); ++it) { QDBusUnixFileDescriptor dfd = it.value(); int fd = dfd.fileDescriptor(); const QString &which = it.key(); if (which == u"in") stdioRedirections[0] = dup(fd); else if (which == u"out") stdioRedirections[1] = dup(fd); else if (which == u"err") stdioRedirections[2] = dup(fd); } # else Q_UNUSED(redirections) # endif // defined(Q_OS_UNIX) return ApplicationManager::instance()->startApplicationInternal( id, documentUrl, QString(), QString(), std::move(stdioRedirections)); } catchExceptionAsDBusError({}) } void ApplicationManagerAdaptor::stopAllApplications() { stopAllApplications(false); } void ApplicationManagerAdaptor::stopAllApplications(bool forceKill) { try { checkDBusAccess(); checkDevelopmentModeSystem(); ApplicationManager::instance()->stopAllApplications(forceKill); } catchExceptionAsDBusError() } void ApplicationManagerAdaptor::stopApplication(const QString &id) { stopApplication(id, false); } void ApplicationManagerAdaptor::stopApplication(const QString &id, bool forceKill) { try { checkDBusAccess(); checkApplicationAccess(id); ApplicationManager::instance()->stopApplication(id, forceKill); } catchExceptionAsDBusError() } QString ApplicationManagerAdaptor::sendIntentRequestAs(const QString &requestingApplicationId, const QString &intentId, const QString &applicationId, const QString &jsonParameters) { try { checkDBusAccess(); if (requestingApplicationId.isEmpty()) throw Exception("requestingApplicationId cannot be empty"); if (intentId.isEmpty()) throw Exception("intentId cannot be empty"); if (applicationId == u":broadcast:") throw Exception("applicationId cannot be ':broadcast:'"); // needs to either come from or go to an accessible app (or both) try { // try "come from" first (cannot be "") checkApplicationAccess(requestingApplicationId); } catch (const Exception &) { // that didn't work, try "go to" next, but be aware that it could be "" if (applicationId.isEmpty()) throw; checkApplicationAccess(applicationId); } QVariantMap parameters; if (!jsonParameters.trimmed().isEmpty()) { QJsonParseError parseError; auto jsDoc = QJsonDocument::fromJson(jsonParameters.toUtf8(), &parseError); if (jsDoc.isNull()) throw Exception("jsonParameters is not a valid JSON document: %1").arg(parseError.errorString()); parameters = jsDoc.toVariant().toMap(); } QDBusContext *dbusContext = DBusContextAdaptor::dbusContextFor(this); dbusContext->setDelayedReply(true); auto dbusMsg = new QDBusMessage(dbusContext->message()); auto dbusName = dbusContext->connection().name(); auto icr = IntentClient::instance()->requestToSystem(requestingApplicationId, intentId, applicationId, parameters); icr->startTimeout(IntentClient::instance()->replyFromSystemTimeout()); // clang-code-model: this slot is always called (even on timeouts), so dbusMsg does not leak connect(icr, &IntentClientRequest::replyReceived, this, [icr, dbusMsg, dbusName]() { bool succeeded = icr->succeeded(); QDBusConnection conn(dbusName); if (succeeded) { auto jsonResult = QString::fromUtf8(QJsonDocument::fromVariant(icr->result()).toJson()); conn.send(dbusMsg->createReply(jsonResult)); } else { conn.send(dbusMsg->createErrorReply(QDBusError::Failed, icr->errorMessage())); } delete dbusMsg; icr->deleteLater(); }); return { }; } catchExceptionAsDBusError({}) } void ApplicationManagerAdaptor::broadcastIntentRequestAs(const QString &requestingApplicationId, const QString &intentId, const QString &jsonParameters) { try { checkDBusAccess(); if (requestingApplicationId.isEmpty()) throw Exception("requestingApplicationId cannot be empty"); if (intentId.isEmpty()) throw Exception("intentId cannot be empty"); checkApplicationAccess(requestingApplicationId); QVariantMap parameters; if (!jsonParameters.trimmed().isEmpty()) { QJsonParseError parseError; auto jsDoc = QJsonDocument::fromJson(jsonParameters.toUtf8(), &parseError); if (jsDoc.isNull()) throw Exception("jsonParameters is not a valid JSON document: %1").arg(parseError.errorString()); parameters = jsDoc.toVariant().toMap(); } IntentClient::instance()->requestToSystem(requestingApplicationId, intentId, u":broadcast:"_s, parameters); } catchExceptionAsDBusError() }