From 4f4b6c309f3742d3c220e71074e0955aeb2623c8 Mon Sep 17 00:00:00 2001 From: Weixuan Xiao Date: Tue, 18 Jun 2019 01:21:31 +0000 Subject: [PATCH] Allow to use private DBus --- CMakeLists.txt | 4 ++ core/CMakeLists.txt | 5 +- core/dbushelper.cpp | 111 +++++++++++++++++++++++++++++++++++++- core/dbushelper.h.in | 4 ++ core/kdeconnectconfig.cpp | 48 +++++++++++++++++ core/kdeconnectconfig.h | 7 +++ daemon/kdeconnectd.cpp | 6 ++- indicator/main.cpp | 2 +- tests/CMakeLists.txt | 3 ++ tests/testprivatedbus.cpp | 109 +++++++++++++++++++++++++++++++++++++ 10 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 tests/testprivatedbus.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 45e085fe1..09ed1a85e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,10 @@ set_package_properties(KF5Kirigami2 PROPERTIES TYPE RUNTIME ) +option(PRIVATE_DBUS_ENABLED "Use private dbus session for kdeconnect" OFF) +if(PRIVATE_DBUS_ENABLED OR APPLE) + add_compile_definitions(USE_PRIVATE_DBUS) +endif() add_subdirectory(core) if(NOT SAILFISHOS) add_subdirectory(kcm) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 5cc95fa64..db2167937 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,6 +1,9 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdeconnect-core\") -set(KDECONNECT_PRIVATE_DBUS_ADDR unix:path=/tmp/kdeconnect-dbus) +set(KDECONNECT_PRIVATE_DBUS_ADDR unix:tmpdir=/tmp) +if(WIN32) + set(KDECONNECT_PRIVATE_DBUS_ADDR tcp:host=localhost,port=0) +endif() set(KDECONNECT_PRIVATE_DBUS_NAME DBusKDEConnectOnly) configure_file(dbushelper.h.in ${CMAKE_CURRENT_BINARY_DIR}/dbushelper.h) diff --git a/core/dbushelper.cpp b/core/dbushelper.cpp index 20383deec..6fe6c958e 100644 --- a/core/dbushelper.cpp +++ b/core/dbushelper.cpp @@ -19,9 +19,39 @@ */ #include "dbushelper.h" +#include "core_debug.h" + +#include +#include +#include +#include +#include +#include + +#include "kdeconnectconfig.h" + +#ifdef Q_OS_MAC +#include +#endif namespace DbusHelper { +#ifdef USE_PRIVATE_DBUS +class DBusInstancePrivate +{ +public: + DBusInstancePrivate(); + ~DBusInstancePrivate(); + + void launchDBusDaemon(); + void closeDBusDaemon(); +private: + QProcess *m_dbusProcess; +}; + +static DBusInstancePrivate dbusInstance; +#endif + void filterNonExportableCharacters(QString& s) { static QRegExp regexp(QStringLiteral("[^A-Za-z0-9_]"), Qt::CaseSensitive, QRegExp::Wildcard); @@ -30,11 +60,88 @@ void filterNonExportableCharacters(QString& s) QDBusConnection sessionBus() { -#ifdef Q_OS_MAC - return QDBusConnection::connectToBus(QStringLiteral(KDECONNECT_PRIVATE_DBUS_ADDR), QStringLiteral(KDECONNECT_PRIVATE_DBUS_NAME)); +#ifdef USE_PRIVATE_DBUS + return QDBusConnection::connectToBus(KdeConnectConfig::instance()->privateDBusAddress(), + QStringLiteral(KDECONNECT_PRIVATE_DBUS_NAME)); #else return QDBusConnection::sessionBus(); #endif } +#ifdef USE_PRIVATE_DBUS +void launchDBusDaemon() +{ + dbusInstance.launchDBusDaemon(); +} + +void closeDBusDaemon() +{ + dbusInstance.closeDBusDaemon(); +} + +void DBusInstancePrivate::launchDBusDaemon() +{ + // Kill old dbus daemon + if (m_dbusProcess != nullptr) closeDBusDaemon(); + + // Start dbus daemon + m_dbusProcess = new QProcess(); + #ifdef Q_OS_MAC + // On macOS, assuming the executable is in Contents/MacOS + CFURLRef url = (CFURLRef)CFAutorelease((CFURLRef)CFBundleCopyBundleURL(CFBundleGetMainBundle())); + QString basePath = QUrl::fromCFURL(url).path(); + QString kdeconnectDBusExecutable = basePath + QStringLiteral("Contents/MacOS/dbus-daemon"), + kdeconnectDBusConfiguration = basePath + QStringLiteral("Contents/Resources/dbus-1/session.conf"); + qCDebug(KDECONNECT_CORE) << "App package path: " << basePath; + + m_dbusProcess->setProgram(kdeconnectDBusExecutable); + m_dbusProcess->setArguments({QStringLiteral("--print-address"), + QStringLiteral("--nofork"), + QStringLiteral("--config-file=") + kdeconnectDBusConfiguration, + QStringLiteral("--address=") + QStringLiteral(KDECONNECT_PRIVATE_DBUS_ADDR)}); + m_dbusProcess->setWorkingDirectory(basePath); + #elif defined(Q_OS_WIN) + // On Windows + m_dbusProcess->setProgram(QStringLiteral("dbus-daemon.exe")); + m_dbusProcess->setArguments({QStringLiteral("--session"), + QStringLiteral("--print-address"), + QStringLiteral("--nofork"), + QStringLiteral("--address=") + QStringLiteral(KDECONNECT_PRIVATE_DBUS_ADDR)}); + #else + // On Linux or other unix-like system + m_dbusProcess->setProgram(QStringLiteral("dbus-daemon")); + m_dbusProcess->setArguments({QStringLiteral("--session"), + QStringLiteral("--print-address"), + QStringLiteral("--nofork"), + QStringLiteral("--address=") + QStringLiteral(KDECONNECT_PRIVATE_DBUS_ADDR)}); + #endif + m_dbusProcess->setStandardOutputFile(KdeConnectConfig::instance()->privateDBusAddressPath()); + m_dbusProcess->setStandardErrorFile(QProcess::nullDevice()); + m_dbusProcess->start(); +} + +void DBusInstancePrivate::closeDBusDaemon() +{ + if (m_dbusProcess != nullptr) + { + m_dbusProcess->terminate(); + m_dbusProcess->waitForFinished(); + delete m_dbusProcess; + m_dbusProcess = nullptr; + + QFile privateDBusAddressFile(KdeConnectConfig::instance()->privateDBusAddressPath()); + + if (privateDBusAddressFile.exists()) privateDBusAddressFile.resize(0); + } +} + +DBusInstancePrivate::DBusInstancePrivate() + :m_dbusProcess(nullptr){} + +DBusInstancePrivate::~DBusInstancePrivate() +{ + closeDBusDaemon(); +} +#endif + } diff --git a/core/dbushelper.h.in b/core/dbushelper.h.in index 9b8775fbd..ae0701768 100644 --- a/core/dbushelper.h.in +++ b/core/dbushelper.h.in @@ -31,6 +31,10 @@ namespace DbusHelper { void KDECONNECTCORE_EXPORT filterNonExportableCharacters(QString& s); +#ifdef USE_PRIVATE_DBUS + void KDECONNECTCORE_EXPORT launchDBusDaemon(); + void KDECONNECTCORE_EXPORT closeDBusDaemon(); +#endif QDBusConnection KDECONNECTCORE_EXPORT sessionBus(); } diff --git a/core/kdeconnectconfig.cpp b/core/kdeconnectconfig.cpp index 79cde1791..84385323d 100644 --- a/core/kdeconnectconfig.cpp +++ b/core/kdeconnectconfig.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include "core_debug.h" #include "dbushelper.h" @@ -52,6 +53,9 @@ struct KdeConnectConfigPrivate { QSettings* m_config; QSettings* m_trustedDevices; +#ifdef USE_PRIVATE_DBUS + QString m_privateDBusAddress; // Private DBus Address cache +#endif }; KdeConnectConfig* KdeConnectConfig::instance() @@ -327,3 +331,47 @@ void KdeConnectConfig::generateCertificate(const QString& certPath) Daemon::instance()->reportError(QStringLiteral("KDE Connect"), i18n("Could not store certificate file: %1", certPath)); } } + +#ifdef USE_PRIVATE_DBUS +QString KdeConnectConfig::privateDBusAddressPath() +{ + return baseConfigDir().absoluteFilePath(QStringLiteral("private_dbus_address")); +} + +QString KdeConnectConfig::privateDBusAddress() +{ + if (d->m_privateDBusAddress.length() != 0) return d->m_privateDBusAddress; + + QString dbusAddressPath = privateDBusAddressPath(); + QFile dbusAddressFile(dbusAddressPath); + + if (!dbusAddressFile.open(QFile::ReadOnly | QFile::Text)) { + qCCritical(KDECONNECT_CORE) << "Private DBus enabled but error read private dbus address conf"; + exit(1); + } + + QTextStream in(&dbusAddressFile); + + qCDebug(KDECONNECT_CORE) << "Waiting for private dbus"; + + int retry = 0; + QString addr = in.readLine(); + while(addr.length() == 0 && retry < 5) { + qCDebug(KDECONNECT_CORE) << "Retry reading private DBus address after 3s"; + QThread::sleep(3); + retry ++; + addr = in.readLine(); // Read until first not empty line + } + + if (addr.length() == 0) { + qCCritical(KDECONNECT_CORE) << "Private DBus enabled but read private dbus address failed"; + exit(1); + } + + qCDebug(KDECONNECT_CORE) << "Private dbus address: " << addr; + + d->m_privateDBusAddress = addr; + + return addr; +} +#endif diff --git a/core/kdeconnectconfig.h b/core/kdeconnectconfig.h index 003215d4b..fad9b6235 100644 --- a/core/kdeconnectconfig.h +++ b/core/kdeconnectconfig.h @@ -70,6 +70,13 @@ public: QDir deviceConfigDir(const QString& deviceId); QDir pluginConfigDir(const QString& deviceId, const QString& pluginName); //Used by KdeConnectPluginConfig +#ifdef USE_PRIVATE_DBUS + /* + * Get private DBus Address when use private DBus + */ + QString privateDBusAddressPath(); + QString privateDBusAddress(); +#endif private: KdeConnectConfig(); diff --git a/daemon/kdeconnectd.cpp b/daemon/kdeconnectd.cpp index 43ba66189..91b8be3a4 100644 --- a/daemon/kdeconnectd.cpp +++ b/daemon/kdeconnectd.cpp @@ -111,6 +111,10 @@ int main(int argc, char* argv[]) KAboutData::setApplicationData(aboutData); app.setQuitOnLastWindowClosed(false); +#ifdef USE_PRIVATE_DBUS + DbusHelper::launchDBusDaemon(); +#endif + QCommandLineParser parser; QCommandLineOption replaceOption({QStringLiteral("replace")}, i18n("Replace an existing instance")); parser.addOption(replaceOption); @@ -126,7 +130,7 @@ int main(int argc, char* argv[]) DbusHelper::sessionBus().call(message); //deliberately block until it's done, so we register the name after the app quits } -#ifndef Q_OS_MAC +#ifndef USE_PRIVATE_DBUS KDBusService dbusService(KDBusService::Unique); #endif diff --git a/indicator/main.cpp b/indicator/main.cpp index b95f15d0d..8905a75cf 100644 --- a/indicator/main.cpp +++ b/indicator/main.cpp @@ -67,7 +67,7 @@ int main(int argc, char** argv) kdeconnectdProcess.start(basePath + QStringLiteral("Contents/MacOS/kdeconnectd")); // Start kdeconnectd #endif -#ifndef Q_OS_MAC +#ifndef USE_PRIVATE_DBUS KDBusService dbusService(KDBusService::Unique); #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cf3852231..8d8096b58 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -41,3 +41,6 @@ ecm_add_test(testnotificationlistener.cpp if(SMSAPP_ENABLED) ecm_add_test(testsmshelper.cpp LINK_LIBRARIES ${kdeconnect_sms_libraries}) endif() +if(PRIVATE_DBUS_ENABLED) + ecm_add_test(testprivatedbus.cpp LINK_LIBRARIES ${kdeconnect_libraries}) +endif() diff --git a/tests/testprivatedbus.cpp b/tests/testprivatedbus.cpp new file mode 100644 index 000000000..2834b80e9 --- /dev/null +++ b/tests/testprivatedbus.cpp @@ -0,0 +1,109 @@ +/** + * Copyright 2019 Weixuan XIAO + * + * 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 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 "dbushelper.h" + +#include +#include +#include + +/** + * This class tests the working of private dbus in kdeconnect + */ +class PrivateDBusTest : public QObject +{ + Q_OBJECT + +public: + PrivateDBusTest() + { + DbusHelper::launchDBusDaemon(); + } + + ~PrivateDBusTest() + { + DbusHelper::closeDBusDaemon(); + } + +private Q_SLOTS: + void testConnectionWithPrivateDBus(); + void testServiceRegistrationWithPrivateDBus(); + void testMethodCallWithPrivateDBus(); +}; + +/** + * Open private DBus normally and get connection info + */ +void PrivateDBusTest::testConnectionWithPrivateDBus() +{ + QDBusConnection conn = DbusHelper::sessionBus(); + + QVERIFY2(conn.isConnected(), "Connection not established"); + QVERIFY2(conn.name() == QStringLiteral(KDECONNECT_PRIVATE_DBUS_NAME), + "DBus Connection is not the right one"); +} + +/** + * Open private DBus connection normally and register a service + */ +void PrivateDBusTest::testServiceRegistrationWithPrivateDBus() +{ + QDBusConnection conn = DbusHelper::sessionBus(); + QVERIFY2(conn.isConnected(), "DBus not connected"); + + QDBusConnectionInterface *bus = conn.interface(); + QVERIFY2(bus != nullptr, "Failed to get DBus interface"); + + QVERIFY2(bus->registerService(QStringLiteral("privatedbus.test")) == QDBusConnectionInterface::ServiceRegistered, + "Failed to register DBus Serice"); + + bus->unregisterService(QStringLiteral("privatedbus.test")); +} + +/** + * Open private DBus connection normally, call a method and get its reply + */ +void PrivateDBusTest::testMethodCallWithPrivateDBus() +{ + QDBusConnection conn = DbusHelper::sessionBus(); + QVERIFY2(conn.isConnected(), "DBus not connected"); + + /* + dbus-send --session \ + --dest=org.freedesktop.DBus \ + --type=method_call \ + --print-reply \ + /org/freedesktop/DBus \ + org.freedesktop.DBus.ListNames + */ + QDBusMessage msg = conn.call( + QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.DBus"), // Service + QStringLiteral("/org/freedesktop/DBus"), // Path + QStringLiteral("org.freedesktop.DBus"), // Interface + QStringLiteral("ListNames") // Method + ) + ); + + QVERIFY2(msg.type() == QDBusMessage::ReplyMessage, "Failed calling method on private DBus"); +} + +QTEST_MAIN(PrivateDBusTest); +#include "testprivatedbus.moc"