diff --git a/CMakeLists.txt b/CMakeLists.txt index cefd812..12970c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,128 +1,129 @@ # Copyright (C) 2008 by Volker Lanz # Copyright (C) 2014-2019 by Andrius Štikonas # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . project(kpmcore) cmake_minimum_required(VERSION 3.1 FATAL_ERROR) set(CMAKE_USE_RELATIVE_PATHS OFF) set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) # Dependencies set(QT_MIN_VERSION "5.10.0") set(KF5_MIN_VERSION "5.56") set(BLKID_MIN_VERSION "2.33.2") # Qca-qt5 (tested with botan and ossl backends) # Runtime # smartmontools 7.0 # Qca plugin (botan or ossl) set(VERSION_MAJOR "4") set(VERSION_MINOR "0") set(VERSION_RELEASE "0") set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}) set(SOVERSION "8") add_definitions(-D'VERSION="${VERSION}"') #" set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") find_package(PolkitQt5-1 REQUIRED) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) include(GenerateExportHeader) include(ECMSetupVersion) ecm_setup_version(${VERSION} VARIABLE_PREFIX KPMCORE VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kpmcore_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfigVersion.cmake" SOVERSION ${SOVERSION}) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core DBus Gui Widgets ) +# We do not link to KF5::AuthCore because we just have build-time dependency on it # Load the frameworks we need find_package(KF5 ${KF5_MIN_VERSION} REQUIRED Auth CoreAddons I18n WidgetsAddons ) find_package(Qca-qt5 REQUIRED) # use sane compile flags add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_NO_CAST_TO_ASCII -DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_URL_CAST_FROM_STRING -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_CAST_TO_BYTEARRAY -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_USE_FAST_OPERATOR_PLUS ) kde_enable_exceptions() if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") find_package(PkgConfig REQUIRED) pkg_check_modules(BLKID REQUIRED blkid>=${BLKID_MIN_VERSION}) endif() include_directories(${Qt5Core_INCLUDE_DIRS} ${UUID_INCLUDE_DIRS} ${BLKID_INCLUDE_DIRS} lib/ src/) add_subdirectory(src) # create a Config.cmake and a ConfigVersion.cmake file and install them set(INCLUDE_INSTALL_DIR "include/kpmcore/") set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KPMcore") configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KPMcoreConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} PATH_VARS INCLUDE_INSTALL_DIR ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KPMcoreTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KPMcoreTargets.cmake ) ki18n_install(po) set_target_properties( kpmcore PROPERTIES VERSION ${VERSION} SOVERSION ${SOVERSION} ) message(STATUS "kpmcore ${VERSION} will be built for install into ${CMAKE_INSTALL_PREFIX}") feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) enable_testing() add_subdirectory(test) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index d43a6d1..6d5072e 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,60 +1,59 @@ set(application_interface_xml org.kde.kpmcore.applicationinterface.xml) set(helper_interface_xml org.kde.kpmcore.helperinterface.xml) qt5_generate_dbus_interface( util/externalcommand.h ${application_interface_xml} OPTIONS -a ) qt5_generate_dbus_interface( util/externalcommandhelper.h ${helper_interface_xml} OPTIONS -a ) qt5_add_dbus_interface(ApplicationInterface_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${application_interface_xml} externalcommand_interface) qt5_add_dbus_interface(HelperInterface_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${helper_interface_xml} externalcommandhelper_interface) set(UTIL_SRC ${HelperInterface_SRCS} util/capacity.cpp util/externalcommand.cpp util/externalcommand_polkitbackend.cpp util/globallog.cpp util/helpers.cpp util/htmlreport.cpp util/report.cpp ) set(UTIL_LIB_HDRS util/libpartitionmanagerexport.h util/capacity.h util/externalcommand.h util/externalcommand_polkitbackend.h util/globallog.h util/helpers.h util/htmlreport.h util/report.h ) add_executable(kpmcore_externalcommand ${ApplicationInterface_SRCS} util/externalcommandhelper.cpp ) target_link_libraries(kpmcore_externalcommand qca-qt5 ${POLKITQT-1_LIBRARIES} Qt5::Core Qt5::DBus KF5::I18n ) install( TARGETS kpmcore_externalcommand DESTINATION ${KAUTH_HELPER_INSTALL_DIR} ) install( FILES util/org.kde.kpmcore.helperinterface.conf DESTINATION ${KDE_INSTALL_DBUSDIR}/system.d ) install( FILES util/org.kde.kpmcore.applicationinterface.conf DESTINATION ${KDE_INSTALL_DBUSDIR}/system.d ) install( FILES util/org.kde.kpmcore.externalcommand.service DESTINATION ${DBUS_SYSTEM_SERVICES_INSTALL_DIR} ) -# We do not link to KF5::AuthCore because we just have build-time dependency on it kauth_install_actions(org.kde.kpmcore.externalcommand util/org.kde.kpmcore.externalcommand.actions) diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index 03db53b..aab90b5 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -1,408 +1,406 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016-2018 by Andrius Štikonas * * Copyright (C) 2019 by Shubham * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "backend/corebackendmanager.h" #include "core/device.h" #include "core/copysource.h" #include "core/copytarget.h" #include "core/copytargetbytearray.h" #include "core/copysourcedevice.h" #include "core/copytargetdevice.h" #include "util/globallog.h" #include "util/externalcommand.h" #include "util/externalcommand_polkitbackend.h" #include "util/report.h" #include "externalcommandhelper_interface.h" #include #include #include #include #include #include #include #include #include #include #include struct ExternalCommandPrivate { Report *m_Report; QString m_Command; QStringList m_Args; int m_ExitCode; QByteArray m_Output; QByteArray m_Input; QProcess::ProcessChannelMode processChannelMode; }; Auth::PolkitQt1Backend* ExternalCommand::m_authJob; bool ExternalCommand::helperStarted = false; QWidget* ExternalCommand::parent; /** Creates a new ExternalCommand instance without Report. @param cmd the command to run @param args the arguments to pass to the command */ ExternalCommand::ExternalCommand(const QString& cmd, const QStringList& args, const QProcess::ProcessChannelMode processChannelMode) : d(std::make_unique()) { d->m_Report = nullptr; d->m_Command = cmd; d->m_Args = args; d->m_ExitCode = -1; d->m_Output = QByteArray(); m_authJob = new Auth::PolkitQt1Backend; if (!helperStarted) if(!startHelper()) Log(Log::Level::error) << xi18nc("@info:status", "Could not obtain administrator privileges."); d->processChannelMode = processChannelMode; } /** Creates a new ExternalCommand instance with Report. @param report the Report to write output to. @param cmd the command to run @param args the arguments to pass to the command */ ExternalCommand::ExternalCommand(Report& report, const QString& cmd, const QStringList& args, const QProcess::ProcessChannelMode processChannelMode) : d(std::make_unique()) { d->m_Report = report.newChild(); d->m_Command = cmd; d->m_Args = args; d->m_ExitCode = -1; d->m_Output = QByteArray(); d->processChannelMode = processChannelMode; } ExternalCommand::~ExternalCommand() { } /* void ExternalCommand::setup() { connect(this, qOverload(&QProcess::finished), this, &ExternalCommand::onFinished); connect(this, &ExternalCommand::readyReadStandardOutput, this, &ExternalCommand::onReadOutput); } */ /** Executes the external command. @param timeout timeout to wait for the process to start @return true on success */ bool ExternalCommand::start(int timeout) { if (command().isEmpty()) return false; if (!QDBusConnection::systemBus().isConnected()) { qWarning() << QDBusConnection::systemBus().lastError().message(); QTimer::singleShot(timeout, this, &ExternalCommand::quit); return false; } if (report()) report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" ")))); if ( qEnvironmentVariableIsSet( "KPMCORE_DEBUG" )) qDebug() << xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" "))); QString cmd = QStandardPaths::findExecutable(command()); if (cmd.isEmpty()) cmd = QStandardPaths::findExecutable(command(), { QStringLiteral("/sbin/"), QStringLiteral("/usr/sbin/"), QStringLiteral("/usr/local/sbin/") }); auto interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days bool rval = false; QDBusPendingCall pcall = interface->start(cmd, args(), d->m_Input, d->processChannelMode); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); QEventLoop loop; auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { loop.exit(); if (watcher->isError()) qWarning() << watcher->error(); else { QDBusPendingReply reply = *watcher; d->m_Output = reply.value()[QStringLiteral("output")].toByteArray(); setExitCode(reply.value()[QStringLiteral("exitCode")].toInt()); rval = reply.value()[QStringLiteral("success")].toBool(); } }; connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); loop.exec(); QTimer::singleShot(timeout, this, &ExternalCommand::quit); return rval; } bool ExternalCommand::copyBlocks(const CopySource& source, CopyTarget& target) { bool rval = true; const qint64 blockSize = 10 * 1024 * 1024; // number of bytes per block to copy if (!QDBusConnection::systemBus().isConnected()) { qWarning() << QDBusConnection::systemBus().lastError().message(); return false; } auto interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days QDBusPendingCall pcall = interface->copyblocks(source.path(), source.firstByte(), source.length(), target.path(), target.firstByte(), blockSize); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); QEventLoop loop; auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { loop.exit(); if (watcher->isError()) qWarning() << watcher->error(); else { QDBusPendingReply reply = *watcher; rval = reply.value()[QStringLiteral("success")].toBool(); CopyTargetByteArray *byteArrayTarget = dynamic_cast(&target); if (byteArrayTarget) byteArrayTarget->m_Array = reply.value()[QStringLiteral("targetByteArray")].toByteArray(); } setExitCode(!rval); }; connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); loop.exec(); return rval; } bool ExternalCommand::writeData(Report& commandReport, const QByteArray& buffer, const QString& deviceNode, const quint64 firstByte) { d->m_Report = commandReport.newChild(); if (report()) report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" ")))); bool rval = true; if (!QDBusConnection::systemBus().isConnected()) { qWarning() << QDBusConnection::systemBus().lastError().message(); return false; } auto interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days QDBusPendingCall pcall = interface->writeData(buffer, deviceNode, firstByte); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); QEventLoop loop; auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { loop.exit(); if (watcher->isError()) qWarning() << watcher->error(); else { QDBusPendingReply reply = *watcher; rval = reply.argumentAt<0>(); } setExitCode(!rval); }; connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); loop.exec(); return rval; } bool ExternalCommand::write(const QByteArray& input) { if ( qEnvironmentVariableIsSet( "KPMCORE_DEBUG" )) qDebug() << "Command input:" << QString::fromLocal8Bit(input); d->m_Input = input; return true; } /** Runs the command. @param timeout timeout to use for waiting when starting and when waiting for the process to finish @return true on success */ bool ExternalCommand::run(int timeout) { return start(timeout) /* && exitStatus() == 0*/; } //void ExternalCommand::onReadOutput() //{ // const QByteArray s = readAllStandardOutput(); // // if(m_Output.length() > 10*1024*1024) { // prevent memory overflow for badly corrupted file systems // if (report()) // report()->line() << xi18nc("@info:status", "(Command is printing too much output)"); // return; // } // // m_Output += s; // // if (report()) // *report() << QString::fromLocal8Bit(s); //} void ExternalCommand::setCommand(const QString& cmd) { d->m_Command = cmd; } const QString& ExternalCommand::command() const { return d->m_Command; } const QStringList& ExternalCommand::args() const { return d->m_Args; } void ExternalCommand::addArg(const QString& s) { d->m_Args << s; } void ExternalCommand::setArgs(const QStringList& args) { d->m_Args = args; } int ExternalCommand::exitCode() const { return d->m_ExitCode; } const QString ExternalCommand::output() const { return QString::fromLocal8Bit(d->m_Output); } const QByteArray& ExternalCommand::rawOutput() const { return d->m_Output; } Report* ExternalCommand::report() { return d->m_Report; } void ExternalCommand::setExitCode(int i) { d->m_ExitCode = i; } /**< Dummy function for QTimer */ void ExternalCommand::quit() { } bool ExternalCommand::startHelper() { if (!QDBusConnection::systemBus().isConnected()) { qWarning() << QDBusConnection::systemBus().lastError().message(); return false; } QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); if (iface.isValid()) { exit(0); } /** Authorize using Polkit backend **/ // initialize KDE Polkit daemon m_authJob->initPolkitAgent(QStringLiteral("org.kde.kpmcore.externalcommand.init"), parent); // initialize and kick start the helper application m_authJob->initHelper(QStringLiteral("org.kde.kpmcore.helperinterface")); m_authJob->startHelper(QStringLiteral("org.kde.kpmcore.externalcommand.init"), QStringLiteral("org.kde.kpmcore.helperinterface")); bool isActionAuthorized = m_authJob->authorizeAction(QStringLiteral("org.kde.kpmcore.externalcommand.init"), m_authJob->callerID()); - auto authResult = m_authJob->actionStatus(QStringLiteral("org.kde.kpmcore.externalcommand.init"), m_authJob->callerID()); - // Wait until ExternalCommand Helper is ready and sends signal(Connect to newData signal) QEventLoop loop; auto exitLoop = [&] () { loop.exit(); }; ExternalCommand cmd; auto conn = QObject::connect(&cmd, &ExternalCommand::newData, exitLoop); loop.exec(); QObject::disconnect(conn); - if (!isActionAuthorized || authResult == PolkitQt1::Authority::No || authResult == PolkitQt1::Authority::Unknown) { + if (!isActionAuthorized) { qDebug() << "Unable to obtain Administrative privileges, the action can not be executed!!"; } helperStarted = true; return true; } void ExternalCommand::stopHelper() { auto interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus()); interface->exit(); } void ExternalCommand::emitNewData(int percent) { Q_UNUSED(percent) emit newData(); } void ExternalCommand::emitNewData(QString message) { Q_UNUSED(message) emit newData(); } diff --git a/src/util/externalcommand_polkitbackend.cpp b/src/util/externalcommand_polkitbackend.cpp index c55fcf1..c6186ff 100644 --- a/src/util/externalcommand_polkitbackend.cpp +++ b/src/util/externalcommand_polkitbackend.cpp @@ -1,222 +1,226 @@ /************************************************************************* * Copyright (C) 2019 by Shubham * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "util/externalcommand_polkitbackend.h" #include #include #include #include #include #include #include #include #include using namespace PolkitQt1; Authority::Result Auth::PolkitEventLoop::m_result = Authority::No; namespace Auth { PolkitEventLoop::PolkitEventLoop(QObject *parent) : QEventLoop(qobject_cast(parent)) { } PolkitEventLoop::~PolkitEventLoop() { } +void PolkitEventLoop::requestQuit(const Authority::Result &result) +{ + m_result = result; + quit(); +} + Authority::Result PolkitEventLoop::result() const { return m_result; } PolkitQt1Backend::PolkitQt1Backend() : m_flyingActions(false) { // Connect various useful Polkit signals connect(Authority::instance(), &Authority::configChanged, this, &Auth::PolkitQt1Backend::authStatusChanged); connect(Authority::instance(), &Authority::consoleKitDBChanged, this, &Auth::PolkitQt1Backend::authStatusChanged); m_flyingActions = true; } PolkitQt1Backend::~PolkitQt1Backend() { } bool PolkitQt1Backend::initHelper(const QString &helperName) { if (!QDBusConnection::systemBus().isConnected() || !QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots) || !QDBusConnection::systemBus().registerService(helperName)) { qWarning() << "Helper initialization failed!!" << QDBusConnection::systemBus().lastError().message(); return false; } return true; } void PolkitQt1Backend::startHelper(const QString &action, const QString &helperID, int timeout /*10 days*/) { QDBusMessage message = QDBusMessage::createMethodCall(helperID, QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("performAction")); const QByteArray caller = QDBusConnection::systemBus().baseService().toUtf8(); QList args; args << action << caller; message.setArguments(args); QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(message, timeout); auto watcher = new QDBusPendingCallWatcher(pendingCall); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, action, watcher]() { watcher->deleteLater(); const QDBusMessage reply = watcher->reply(); if (reply.type() == QDBusMessage::ErrorMessage) { qWarning() << "Failed to contact the helper, Error message:" << reply.errorMessage(); } }); } void PolkitQt1Backend::initPolkitAgent(const QString &action, QWidget *parent /*= nullptr*/) const { if (!parent) { qWarning() << "Parent widget does not exists, can not proceed further"; return; } // Check if we are running terminal session or GUI session if (!qApp) { qWarning() << "We are running a TTY (Terminal) session"; qDebug() << "Can not proceed further since we do not support Text based Polkit Authentication Agent"; return; } // Get the dialog parent window Id quint64 parentWindowID = parent->effectiveWinId(); // Make a call to the KDE polkit Authentication Agent asking for it's services QDBusMessage callAgent = QDBusMessage::createMethodCall(QStringLiteral("org.kde.polkit-kde-authentication-agent-1"), QStringLiteral("/org/kde/Polkit1AuthAgent"), QStringLiteral("org.kde.Polkit1AuthAgent"), QStringLiteral("setWIdForAction")); callAgent << action; callAgent << parentWindowID; QDBusPendingCall call = QDBusConnection::sessionBus().asyncCall(callAgent); auto watcher = new QDBusPendingCallWatcher(call); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, action, watcher](){ watcher->deleteLater(); const QDBusMessage reply = watcher->reply(); if (reply.type() == QDBusMessage::ErrorMessage) { qWarning() << "Could not call the Authentication Agent, Error:" << reply.errorMessage(); } }); } Authority::Result PolkitQt1Backend::actionStatus(const QString &action, const QByteArray &callerID) const { SystemBusNameSubject subject(QString::fromUtf8(callerID)); auto authority = Authority::instance(); PolkitEventLoop::m_result = authority->checkAuthorizationSync(action, subject, Authority::AllowUserInteraction); if (authority->hasError()) { qDebug() << "Encountered error while checking action status, Error code:" << authority->lastError() << "\n"; qDebug() << "Error Details:" << authority->errorDetails(); authority->clearError(); } return PolkitEventLoop::m_result; } QByteArray PolkitQt1Backend::callerID() const { return QDBusConnection::systemBus().baseService().toUtf8(); } bool PolkitQt1Backend::authorizeAction(const QString &action, const QByteArray &callerID) { - // Set m_result here, otherwise there will be wrong log message displayed inside externalcommand.cpp line 383 - SystemBusNameSubject subject(QString::fromUtf8(callerID)); auto authority = Authority::instance(); PolkitEventLoop event; event.processEvents(); - connect(authority, &Authority::checkAuthorizationFinished, &event, &PolkitEventLoop::quit); + connect(authority, &Authority::checkAuthorizationFinished, &event, &PolkitEventLoop::requestQuit); authority->checkAuthorization(action, subject, Authority::AllowUserInteraction); event.exec(); if (authority->hasError()) { qWarning() << "Encountered error while checking authorization, Error code:" << authority->lastError() << "\n"; qDebug() << "Error details:" << authority->errorDetails(); // Clear all the errors from the buffer so that hasError() does not give previous error as a result when called later authority->clearError(); } - if (/*event.result() PolkitEventLoop::m_result*/ actionStatus(action, callerID) == Authority::Yes) { + if (event.result() /*PolkitEventLoop::m_result*/ == Authority::Yes) { return true; } else { return false; } } bool PolkitQt1Backend::revokeAuthorization(const QString &action, const QByteArray &callerID) { Q_UNUSED(action) SystemBusNameSubject subject(QString::fromUtf8(callerID)); auto authority = Authority::instance(); return authority->revokeTemporaryAuthorizationsSync(subject); } void PolkitQt1Backend::authStatusChanged() { for (auto it = m_cachedResults.begin(); it != m_cachedResults.end(); ++it) { const QString action = it.key(); QByteArray pid = QDBusConnection::systemBus().baseService().toUtf8(); if (it.value() != actionStatus(action, pid)) { *it = actionStatus(action, pid); } } // Force updating known actions Authority::instance()->enumerateActions(); m_flyingActions = true; } } // namespace Auth diff --git a/src/util/externalcommand_polkitbackend.h b/src/util/externalcommand_polkitbackend.h index 9fe19c0..a8a1066 100644 --- a/src/util/externalcommand_polkitbackend.h +++ b/src/util/externalcommand_polkitbackend.h @@ -1,156 +1,159 @@ /************************************************************************* * Copyright (C) 2019 by Shubham * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #ifndef KPMCORE_EXTERNALCOMMAND_POLKITBACKEND_H #define KPMCORE_EXTERNALCOMMAND_POLKITBACKEND_H #include #include #include #include #include #include using namespace PolkitQt1; namespace Auth { /** A Polkit Qt backend class for authorizing actions. This class is used to authorize various actions if they ask for privileged execution. It starts by verifying the action under consideration if it is the one it is saying and authorizes it based on the credentials provided. @author Shubham **/ class PolkitQt1Backend : public QObject { Q_OBJECT Q_DISABLE_COPY(PolkitQt1Backend) public: /** * \brief Constructor of PolkitQt1Backend class */ PolkitQt1Backend(); /** * \brief Destructor of PolkitQt1Backend class */ ~PolkitQt1Backend(); /** * \brief A function to check for the action's current status. * * \param name Helper service name * * \return true if helper is initialized, otherwise false. */ bool initHelper(const QString &helperName); /** * \brief A function to check for the action's current status. * * \param action Action name * \param helperID Helper service name * */ void startHelper(const QString &action, const QString &helperID, int timeout = 10 * 24 * 3600 * 1000 /*10 days*/); /** * \brief Initializes the KDE Polkit Authentication Agent. * * \param action Action in question * \param parent Parent widget * */ void initPolkitAgent(const QString &action, QWidget *parent = nullptr) const; /** * \brief A function to check for the action's current status. * * \param action Action in question * \param calledID The Application process ID of the action * * \return the result of action status ie. If action is Authorized or not. */ Authority::Result actionStatus(const QString &action, const QByteArray &callerID) const; /** * \brief Function to get the current Application process ID * * \return Application process ID of the action */ QByteArray callerID() const; /** * \brief Tries to authorize to the \p action in question. * * \param action Action in question. * \param callerID The Application process ID of the action * * \return \c true if authority authorizes the action successfully, \c false Action is not authorized. * */ bool authorizeAction(const QString &action, const QByteArray &callerID); /** * \brief Stops the running \p action from executing. * * \param action Action in question. * */ bool revokeAuthorization(const QString &action, const QByteArray &callerID); public Q_SLOTS: void authStatusChanged(); private: QHash m_cachedResults; bool m_flyingActions; // Already running actions }; /** A Polkit event loop class. This class is used to implement a polkit event loop and has the capability of returning the current authorization result of the action in que. @author Shubham **/ class PolkitEventLoop : public QEventLoop { Q_OBJECT public: PolkitEventLoop(QObject *parent = nullptr); ~PolkitEventLoop(); Authority::Result result() const; +public Q_SLOTS: + void requestQuit(const PolkitQt1::Authority::Result &result); + public: static Authority::Result m_result; }; } // namespace Auth #endif // KPMCORE_EXTERNALCOMMAND_POLKITBACKEND_H