diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 01a4bb2..ac4f23f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,67 +1,66 @@ # Copyright (C) 2008, 2012 by Volker Lanz # Copyright (C) 2015 by Teo Mrnjavac # # 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 . ############################################ add_definitions(-DTRANSLATION_DOMAIN=\"kpmcore\") include(backend/CMakeLists.txt) include(core/CMakeLists.txt) include(util/CMakeLists.txt) include(ops/CMakeLists.txt) include(jobs/CMakeLists.txt) include(fs/CMakeLists.txt) include(gui/CMakeLists.txt) set(kpmcore_SRCS ${BACKEND_SRC} ${FS_SRC} ${CORE_SRC} ${OPS_SRC} ${JOBS_SRC} ${UTIL_SRC} ${GUI_SRC} ) ki18n_wrap_ui(kpmcore_SRCS ${gui_UIFILES}) add_library(kpmcore SHARED ${kpmcore_SRCS}) target_link_libraries( kpmcore PUBLIC Qt5::Core PRIVATE ${BLKID_LIBRARIES} ${POLKITQT-1_LIBRARIES} Qt5::DBus Qt5::Gui qca-qt5 KF5::I18n KF5::CoreAddons KF5::WidgetsAddons - KF5::AuthCore ) install(TARGETS kpmcore EXPORT KPMcoreTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CORE_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/core/ COMPONENT Devel) install(FILES ${BACKEND_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/backend/ COMPONENT Devel) install(FILES ${FS_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/fs/ COMPONENT Devel) install(FILES ${JOBS_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/jobs/ COMPONENT Devel) install(FILES ${OPS_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/ops/ COMPONENT Devel) install(FILES ${UTIL_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/util/ COMPONENT Devel) install(FILES ${GUI_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/gui/ COMPONENT Devel) ############################################ add_subdirectory(plugins) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 9b15146..d43a6d1 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,60 +1,60 @@ 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::AuthCore 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} ) -kauth_install_helper_files(kpmcore_externalcommand org.kde.kpmcore.externalcommand root) +# 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 5758d12..03db53b 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -1,420 +1,408 @@ /************************************************************************* * 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; - DBusThread *m_thread; 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); } - - d->m_thread = new DBusThread; - d->m_thread->start(); - + /** 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) { 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(); } - -void DBusThread::run() -{ - if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.applicationinterface")) || - !QDBusConnection::systemBus().registerObject(QStringLiteral("/Application"), this, QDBusConnection::ExportAllSlots)) { - qWarning() << QDBusConnection::systemBus().lastError().message(); - return; - } - - QEventLoop loop; - loop.exec(); -} diff --git a/src/util/externalcommand.h b/src/util/externalcommand.h index b9c47e2..55d162c 100644 --- a/src/util/externalcommand.h +++ b/src/util/externalcommand.h @@ -1,151 +1,142 @@ /************************************************************************* * 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 .* *************************************************************************/ #ifndef KPMCORE_EXTERNALCOMMAND_H #define KPMCORE_EXTERNALCOMMAND_H #include "util/libpartitionmanagerexport.h" #include #include #include #include #include #include #include namespace Auth { class PolkitQt1Backend; } -class Report; class CopySource; class CopyTarget; +class Report; class QDBusInterface; - class KJob; struct ExternalCommandPrivate; -class DBusThread : public QThread -{ - Q_OBJECT - -private: - void run() override; -}; - /** An external command. Runs an external command as a child process. @author Volker Lanz @author Andrius Štikonas */ class LIBKPMCORE_EXPORT ExternalCommand : public QObject { Q_OBJECT // We register on DBus so the helper can monitor us and terminate if we terminate. Q_CLASSINFO("D-Bus Interface", "org.kde.kpmcore.applicationinterface") Q_DISABLE_COPY(ExternalCommand) public: explicit ExternalCommand(const QString& cmd = QString(), const QStringList& args = QStringList(), const QProcess::ProcessChannelMode processChannelMode = QProcess::MergedChannels); explicit ExternalCommand(Report& report, const QString& cmd = QString(), const QStringList& args = QStringList(), const QProcess::ProcessChannelMode processChannelMode = QProcess::MergedChannels); ~ExternalCommand(); public: bool copyBlocks(const CopySource& source, CopyTarget& target); bool writeData(Report& commandReport, const QByteArray& buffer, const QString& deviceNode, const quint64 firstByte); // same as copyBlocks but from QByteArray /**< @param cmd the command to run */ void setCommand(const QString& cmd); /**< @return the command to run */ const QString& command() const; /**< @return the arguments */ const QStringList& args() const; /**< @param s the argument to add */ void addArg(const QString& s); /**< @param args the new arguments */ void setArgs(const QStringList& args); bool write(const QByteArray& input); /**< @param input the input for the program */ bool startCopyBlocks(); bool start(int timeout = 30000); bool run(int timeout = 30000); /**< @return the exit code */ int exitCode() const; /**< @return the command output */ const QString output() const; /**< @return the command output */ const QByteArray& rawOutput() const; /**< @return pointer to the Report or nullptr */ Report* report(); void emitReport(const QVariantMap& report) { emit reportSignal(report); } /**< Dummy function for QTimer when needed. */ void quit(); // KAuth /**< start ExternalCommand Helper */ bool startHelper(); /**< stop ExternalCommand Helper */ static void stopHelper(); /**< Sets a parent widget for the authentication dialog. * @param p parent widget */ static void setParentWidget(QWidget *p) { parent = p; } Q_SIGNALS: void progress(int); void reportSignal(const QVariantMap&); void newData(); public Q_SLOTS: void emitProgress(KJob*, unsigned long percent) { emit progress(percent); } Q_SCRIPTABLE void emitNewData(int percent); Q_SCRIPTABLE void emitNewData(QString message); private: void setExitCode(int i); // void onReadOutput(); private: std::unique_ptr d; // Authorize using Polkit backend static Auth::PolkitQt1Backend *m_authJob; static bool helperStarted; static QWidget *parent; }; #endif // KPMCORE_EXTERNALCOMMAND_H diff --git a/src/util/externalcommand_polkitbackend.cpp b/src/util/externalcommand_polkitbackend.cpp index 6f5bc12..c55fcf1 100644 --- a/src/util/externalcommand_polkitbackend.cpp +++ b/src/util/externalcommand_polkitbackend.cpp @@ -1,187 +1,222 @@ /************************************************************************* * 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 #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() { } 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(QLatin1String("org.kde.polkit-kde-authentication-agent-1"), QLatin1String("/org/kde/Polkit1AuthAgent"), QLatin1String("org.kde.Polkit1AuthAgent"), - QLatin1String("setWIdForAction")); + 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); 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) { 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 09b8ade..9fe19c0 100644 --- a/src/util/externalcommand_polkitbackend.h +++ b/src/util/externalcommand_polkitbackend.h @@ -1,138 +1,156 @@ /************************************************************************* * 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: static Authority::Result m_result; }; } // namespace Auth #endif // KPMCORE_EXTERNALCOMMAND_POLKITBACKEND_H diff --git a/src/util/org.kde.kpmcore.externalcommand.service b/src/util/org.kde.kpmcore.externalcommand.service new file mode 100644 index 0000000..4f08f65 --- /dev/null +++ b/src/util/org.kde.kpmcore.externalcommand.service @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=org.kde.kpmcore.helperinterface +Exec=/usr/lib/kauth/libexec/kpmcore_externalcommand +User=root +