diff --git a/CMakeLists.txt b/CMakeLists.txt index bdb00be..cefd812 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,127 +1,128 @@ # 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 ) # 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/CMakeLists.txt b/src/CMakeLists.txt index e839af2..01a4bb2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,66 +1,67 @@ # 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 b3f2ee2..ff7fa6f 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,57 +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 ) kauth_install_helper_files(kpmcore_externalcommand org.kde.kpmcore.externalcommand root) 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 5ed64c1..356d37c 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -1,403 +1,414 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016-2018 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 .* *************************************************************************/ #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/externalcommandhelper.h" #include "util/report.h" - #include "externalcommandhelper_interface.h" -#include -#include -#include -#include +#include #include -#include #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; }; -KAuth::ExecuteJob* ExternalCommand::m_job; bool ExternalCommand::helperStarted = false; QWidget* ExternalCommand::parent; - +Auth::PolkitQt1Backend* ExternalCommand::m_authJob; /** 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(); 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"), + 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; } - // TODO KF6:Use new signal-slot syntax - connect(m_job, SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long))); - connect(m_job, &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport); - - auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), + /*Use signals from externalcommandhelper*/ + connect(m_authJob, &ExternalCommandHelper::reportProgress, this, &ExternalCommand::emitReport); + connect(m_authJob, &ExternalCommandHelper::progress, this, &ExternalCommand::emitProgress); + + 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() -{ +//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 when needed. */ +// 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(); - KAuth::Action action = KAuth::Action(QStringLiteral("org.kde.kpmcore.externalcommand.init")); - action.setHelperId(QStringLiteral("org.kde.kpmcore.externalcommand")); - action.setTimeout(10 * 24 * 3600 * 1000); // 10 days - action.setParentWidget(parent); - QVariantMap arguments; - action.setArguments(arguments); - m_job = action.execute(); - m_job->start(); + ////////////////////////////////////// + // Authorize using Polkit backend /// + //////////////////////////////////// + + // initialize KDE Polkit daemon + m_authJob->initPolkitAgent(QStringLiteral("org.kde.kpmcore.externalcommand.init"), parent); + + 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 (helper sends newData signal just before it enters event loop) + + // Wait until ExternalCommand Helper is ready (helper sends progress(int) and reportProgress(QVariantMap &) signal just before it enters event loop) QEventLoop loop; auto exitLoop = [&] () { loop.exit(); }; - auto conn = QObject::connect(m_job, &KAuth::ExecuteJob::newData, exitLoop); - QObject::connect(m_job, &KJob::finished, [=] () { if(m_job->error()) exitLoop(); } ); + auto conn1 = QObject::connect(m_authJob, &ExternalCommandHelper::progress, exitLoop); + auto conn2 = QObject::connect(m_authJob, &ExternalCommandHelper::reportProgress, exitLoop); + + //QObject::connect(m_job, &KJob::finished, [=] () { if(m_job->error()) exitLoop(); } ); + loop.exec(); - QObject::disconnect(conn); - + + QObject::disconnect(conn1); + QObject::disconnect(conn2); + + if (!isActionAuthorized || authResult == PolkitQt1::Authority::No) { + 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()); + auto interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), + QStringLiteral("/Helper"), QDBusConnection::systemBus()); interface->exit(); - } 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(); } + +#include "moc_externalcommand.cpp" diff --git a/src/util/externalcommand.h b/src/util/externalcommand.h index c36c42e..e06adfd 100644 --- a/src/util/externalcommand.h +++ b/src/util/externalcommand.h @@ -1,144 +1,143 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016-2018 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 .* *************************************************************************/ #ifndef KPMCORE_EXTERNALCOMMAND_H #define KPMCORE_EXTERNALCOMMAND_H #include "util/libpartitionmanagerexport.h" #include #include #include #include -#include #include #include #include -namespace KAuth { class ExecuteJob; } +namespace Auth { class PolkitQt1Backend; } -class KJob; -class Report; class CopySource; class CopyTarget; +class Report; + +class KJob; class QDBusInterface; struct ExternalCommandPrivate; class DBusThread : public QThread { Q_OBJECT - // We register on DBus so the helper can monitor us and terminate if we - // terminate. + // We register on DBus so the helper can monitor us and terminate if we terminate. Q_CLASSINFO("D-Bus Interface", "org.kde.kpmcore.applicationinterface") + 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 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&); public Q_SLOTS: void emitProgress(KJob*, unsigned long percent) { emit progress(percent); } + void emitReport(const QVariantMap& report) { emit reportSignal(report); } private: void setExitCode(int i); - void onReadOutput(); +// void onReadOutput(); private: std::unique_ptr d; - // KAuth - static KAuth::ExecuteJob *m_job; + // Use Polkit Authorization backend for authorizing actions + static Auth::PolkitQt1Backend *m_authJob; static bool helperStarted; static QWidget *parent; + }; #endif diff --git a/src/util/externalcommand_polkitbackend.cpp b/src/util/externalcommand_polkitbackend.cpp new file mode 100644 index 0000000..ba7c189 --- /dev/null +++ b/src/util/externalcommand_polkitbackend.cpp @@ -0,0 +1,207 @@ + /************************************************************************* + * 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 + +using namespace PolkitQt1; + +namespace Auth +{ + +PolkitEventLoop::PolkitEventLoop(QObject *parent) + : QEventLoop(parent), + m_result(Authority::No) +{ + +} + +PolkitEventLoop::~PolkitEventLoop() +{ + +} + +void PolkitEventLoop::requestQuit(const Authority::Result &result) +{ + m_result = result; + QTimer::singleShot(1000, this, PolkitQt1Backend::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() +{ + +} + +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("setWindowIdForAction")); + + callAgent << action; + callAgent << parentWindowID; + + QDBusPendingCall call = QDBusConnection::sessionBus().asyncCall(callAgent); + call.waitForFinished(); + + auto watcher = new QDBusPendingCallWatcher(call); + + connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, action, watcher](){ + + const QDBusMessage reply = watcher->reply(); + + if (reply.type() == QDBusMessage::ErrorMessage) { + qWarning() << "Could not call the Authentication Agent, Error:" << reply.errorMessage(); + } + + watcher->deleteLater(); + }); +} + +Authority::Result PolkitQt1Backend::actionStatus(const QString &action, const QByteArray &callerID) const +{ + SystemBusNameSubject subject(QString::fromUtf8(callerID)); + + auto authority = Authority::instance(); + + auto result = authority->checkAuthorizationSync(action, subject, Authority::None); + + if (authority->hasError()) { + qDebug() << "Encountered error while checking action status, Error code:" << authority->lastError() << "\n"; + qDebug() << "Error Details:" << authority->errorDetails(); + authority->clearError(); + } + + return result; +} + +QByteArray PolkitQt1Backend::callerID() const +{ + return QDBusConnection::systemBus().baseService().toUtf8(); +} + +bool PolkitQt1Backend::authorizeAction(const QString &action, const QByteArray &callerID) +{ + SystemBusNameSubject subject(QString::fromUtf8(callerID)); + + auto authority = Authority::instance(); + + PolkitEventLoop event(qobject_cast(qApp)); + + connect(authority, &Authority::checkAuthorizationFinished, &event, &PolkitEventLoop::requestQuit); + + authority->checkAuthorizationSync(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() == Authority::Yes) { + // Emit signal signalling that current action is authorized by the authority instance + emit actionAuthorized(true); + return true; + } else { + emit actionAuthorized(false); + return false; + } +} + +bool stopAction(const QString &action, const QByteArray &callerID) +{ + Q_UNUSED(action) + + SystemBusNameSubject subject(QString::fromUtf8(callerID)); + + auto authority = Authority::instance(); + + PolkitEventLoop event(qobject_cast(qApp)); + event.exec(); + + return authority->revokeTemporaryAuthorizationsSync(subject); +} + +// Dummy function for QTimer +void PolkitQt1Backend::quit() +{ + +} + +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 + +#include "moc_externalcommand_polkitbackend.cpp" diff --git a/src/util/externalcommand_polkitbackend.h b/src/util/externalcommand_polkitbackend.h new file mode 100644 index 0000000..b429aee --- /dev/null +++ b/src/util/externalcommand_polkitbackend.h @@ -0,0 +1,152 @@ +/************************************************************************* + * 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 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. + * \param callerID The Application process ID of the action + * + * \return \c true if action is successfully stopped, \c false Action is not stopped. + * + */ + bool stopAction(const QString &action, const QByteArray &callerID); + + // Dummy function for calling QTimer + static void quit(); + +public Q_SLOTS: + void authStatusChanged(); + +Q_SIGNALS: + void actionAuthorized(bool isAuthorized = false); + +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: + // Quits from the current Polkit Event loop + void requestQuit(const Authority::Result &result); + +private: + Authority::Result m_result; +}; + +} // namespace Auth + +#endif // KPMCORE_EXTERNALCOMMAND_POLKITBACKEND_H