Allow to use private DBus

This commit is contained in:
Weixuan Xiao 2019-06-18 01:21:31 +00:00
parent 8406e0de99
commit 4f4b6c309f
10 changed files with 294 additions and 5 deletions

View file

@ -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)

View file

@ -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)

View file

@ -19,9 +19,39 @@
*/
#include "dbushelper.h"
#include "core_debug.h"
#include <QRegExp>
#include <QProcess>
#include <QDebug>
#include <QUrl>
#include <QFile>
#include <QCoreApplication>
#include "kdeconnectconfig.h"
#ifdef Q_OS_MAC
#include <CoreFoundation/CFBundle.h>
#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
}

View file

@ -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();
}

View file

@ -33,6 +33,7 @@
#include <QSettings>
#include <QSslCertificate>
#include <QtCrypto>
#include <QThread>
#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

View file

@ -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();

View file

@ -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

View file

@ -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

View file

@ -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()

109
tests/testprivatedbus.cpp Normal file
View file

@ -0,0 +1,109 @@
/**
* Copyright 2019 Weixuan XIAO <veyx.shaw@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "dbushelper.h"
#include <QtTest>
#include <QDBusMessage>
#include <QDBusConnectionInterface>
/**
* 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"