Improved package dispatch to the different plugins

Before this patch, all plugins had to discard received packages that were
not for themselves. This could be a security problem (a plugin could sniff
other plugin's packages) and also adds some complexity and processing.

This patch makes the device instance aware of what services are required
by the different plugins and when a package is received the corresponding
plugins will get the package.

These services will be listed on the plugin's desktop file, so the user
can decide whether to enable a plugin.

Note that this only works for receiving, not sending.

REVIEW: 113210
This commit is contained in:
Aleix Pol Gonzalez 2013-10-29 17:29:31 +01:00 committed by Albert Vaca
parent 63488781b9
commit 5e151d185e
22 changed files with 68 additions and 65 deletions

View file

@ -1,12 +1,10 @@
#include "device.h"
#include <KSharedPtr>
#include <KSharedConfig>
#include <KConfigGroup>
#include <KStandardDirs>
#include <KPluginSelector>
#include <KServiceTypeTrader>
#include <KPluginInfo>
#include <KNotification>
#include <KIcon>
@ -66,7 +64,8 @@ QStringList Device::loadedPlugins() const
void Device::reloadPlugins()
{
QMap< QString, KdeConnectPlugin* > newPluginMap;
QMap<QString, KdeConnectPlugin*> newPluginMap;
QMultiMap<QString, KdeConnectPlugin*> newPluginsByInterface;
if (isPaired() && isReachable()) { //Do not load any plugin for unpaired devices, nor useless loading them for unreachable devices
@ -83,18 +82,19 @@ void Device::reloadPlugins()
: loader->getPluginInfo(pluginName).isPluginEnabledByDefault());
if (isPluginEnabled) {
KdeConnectPlugin* reusedPluginInstance = m_plugins.take(pluginName);
if (reusedPluginInstance) {
//Already loaded, reuse it
newPluginMap[pluginName] = reusedPluginInstance;
KdeConnectPlugin* plugin = m_plugins.take(pluginName);
QStringList interfaces;
if (plugin) {
interfaces = m_pluginsByinterface.keys(plugin);
} else {
KdeConnectPlugin* plugin = loader->instantiatePluginForDevice(pluginName, this);
connect(this, SIGNAL(receivedPackage(NetworkPackage)),
plugin, SLOT(receivePackage(NetworkPackage)));
newPluginMap[pluginName] = plugin;
PluginData data = loader->instantiatePluginForDevice(pluginName, this);
plugin = data.plugin;
interfaces = data.interfaces;
}
foreach(const QString& interface, interfaces) {
newPluginsByInterface.insert(interface, plugin);
}
newPluginMap[pluginName] = plugin;
}
}
}
@ -103,6 +103,7 @@ void Device::reloadPlugins()
//them anymore, otherwise they would have been moved to the newPluginMap)
qDeleteAll(m_plugins);
m_plugins = newPluginMap;
m_pluginsByinterface = newPluginsByInterface;
Q_FOREACH(KdeConnectPlugin* plugin, m_plugins) {
plugin->connected();
@ -346,16 +347,14 @@ void Device::privateReceivedPackage(const NetworkPackage& np)
}
} else if (!isPaired()) {
} else if (isPaired()) {
QList<KdeConnectPlugin*> plugins = m_pluginsByinterface.values(np.type());
foreach(KdeConnectPlugin* plugin, plugins) {
plugin->receivePackage(np);
}
} else {
//TODO: Notify the other side that we don't trust them
qDebug() << "device" << name() << "not paired, ignoring package" << np.type();
} else {
//Forward package
Q_EMIT receivedPackage(np);
}
}
@ -415,7 +414,7 @@ QStringList Device::availableLinks() const
void Device::sendPing()
{
NetworkPackage np("kdeconnect.ping");
NetworkPackage np(PACKAGE_TYPE_PING);
bool success = sendPackage(np);
qDebug() << "sendPing:" << success;
}

View file

@ -82,10 +82,6 @@ public:
Q_SCRIPTABLE QStringList loadedPlugins() const;
Q_SCRIPTABLE bool hasPlugin(const QString& name) const;
Q_SIGNALS:
///notifies about a @p np package that has just been received from the device
void receivedPackage(const NetworkPackage& np) const;
public Q_SLOTS:
///sends a @p np package to the device
virtual bool sendPackage(NetworkPackage& np);
@ -121,6 +117,7 @@ private:
QList<DeviceLink*> m_deviceLinks;
QMap<QString, KdeConnectPlugin*> m_plugins;
QMultiMap<QString, KdeConnectPlugin*> m_pluginsByinterface;
QTimer pairingTimer;

View file

@ -24,7 +24,6 @@
#define PACKAGE_TYPE_IDENTITY QLatin1String("kdeconnect.identity")
#define PACKAGE_TYPE_PAIR QLatin1String("kdeconnect.pair")
#define PACKAGE_TYPE_ENCRYPTED QLatin1String("kdeconnect.encrypted")
#define PACKAGE_TYPE_TELEPHONY QLatin1String("kdeconnect.telephony")
#define PACKAGE_TYPE_PING QLatin1String("kdeconnect.ping")
#endif // NETWORKPACKAGETYPES_H

View file

@ -58,9 +58,6 @@ BatteryPlugin::~BatteryPlugin()
bool BatteryPlugin::receivePackage(const NetworkPackage& np)
{
if (np.type() != PACKAGE_TYPE_BATTERY) return false;
bool isCharging = np.get<bool>("isCharging");
int currentCharge = np.get<int>("currentCharge");

View file

@ -37,3 +37,5 @@ Comment[ru]=Показывать значок батареи устройств
Comment[sv]=Visa telefonens batteri intill datorbatteriet
Comment[uk]=Показ даних щодо рівня заряду акумулятора на телефоні поряд з даними щодо рівня заряду акумулятора компютера
Comment[x-test]=xxShow your phone battery next to your computer batteryxx
X-KdeConnect-SupportedPackageType=kdeconnect.battery

View file

@ -29,8 +29,8 @@ K_EXPORT_PLUGIN( KdeConnectPluginFactory("kdeconnect_clipboard", "kdeconnect_cli
ClipboardPlugin::ClipboardPlugin(QObject *parent, const QVariantList &args)
: KdeConnectPlugin(parent, args)
, clipboard(QApplication::clipboard())
, ignore_next_clipboard_change(false)
, clipboard(QApplication::clipboard())
{
connect(clipboard, SIGNAL(changed(QClipboard::Mode)), this, SLOT(clipboardChanged(QClipboard::Mode)));
}
@ -51,10 +51,7 @@ void ClipboardPlugin::clipboardChanged(QClipboard::Mode mode)
bool ClipboardPlugin::receivePackage(const NetworkPackage& np)
{
if (np.type() == PACKAGE_TYPE_CLIPBOARD) {
ignore_next_clipboard_change = true;
clipboard->setText(np.get<QString>("content"));
return true;
}
return false;
ignore_next_clipboard_change = true;
clipboard->setText(np.get<QString>("content"));
return true;
}

View file

@ -37,3 +37,5 @@ Comment[ru]=Общий буфер обмена для всех устройст
Comment[sv]=Dela klippbordet mellan apparater
Comment[uk]=Спільне використання буфера обміну даними на пристроях
Comment[x-test]=xxShare the clipboard between devicesxx
X-KdeConnect-SupportedPackageType=kdeconnect.clipboard

View file

@ -15,3 +15,7 @@ Name[sk]=Plugin KDEConnect
Name[sv]=KDE anslutningsinsticksprogram
Name[uk]=Додаток KDEConnect
Name[x-test]=xxKDEConnect Pluginxx
# mandatory, list of all the package types supported
[PropertyDef::X-KdeConnect-SupportedPackageType]
Type=QStringList

View file

@ -34,3 +34,5 @@ Comment[ru]=Дистанционное управление музыкой и в
Comment[sv]=Fjärrstyr musik och videor
Comment[uk]=Віддалене керування відтворенням музики та відео
Comment[x-test]=xxRemote control your music and videosxx
X-KdeConnect-SupportedPackageType=kdeconnect.mpris

View file

@ -137,11 +137,6 @@ void MprisControlPlugin::removePlayer(const QString& ifaceName)
bool MprisControlPlugin::receivePackage (const NetworkPackage& np)
{
if (np.type() != PACKAGE_TYPE_MPRIS) {
return false;
}
if (np.has("playerList")) {
return false; //Whoever sent this is an mpris client and not an mpris control!
}

View file

@ -35,3 +35,5 @@ Comment[ru]=Показывать телефонные уведомления в
Comment[sv]=Visa telefonunderrättelser i KDE och håll dem synkroniserade
Comment[uk]=Показ сповіщень з телефону у KDE та підтримання синхронізації даних сповіщень
Comment[x-test]=xxShow phone notifications in KDE and keep them in syncxx
X-KdeConnect-SupportedPackageType=kdeconnect.notifications

View file

@ -56,7 +56,6 @@ NotificationsPlugin::~NotificationsPlugin()
bool NotificationsPlugin::receivePackage(const NetworkPackage& np)
{
if (np.type() != PACKAGE_TYPE_NOTIFICATION) return false;
if (np.get<bool>("request")) return false;
notificationsDbusInterface->processPackage(np);

View file

@ -33,3 +33,5 @@ Comment[ru]=Приостанавливать музыку/видео во вре
Comment[sv]=Pausa musik eller videor under ett telefonsamtal
Comment[uk]=Призупинка відтворення музики і відео на час телефонних дзвінків
Comment[x-test]=xxPause music/videos during a phone callxx
X-KdeConnect-SupportedPackageType=kdeconnect.telephony

View file

@ -38,11 +38,6 @@ PauseMusicPlugin::PauseMusicPlugin(QObject* parent, const QVariantList& args)
bool PauseMusicPlugin::receivePackage(const NetworkPackage& np)
{
if (np.type() != PACKAGE_TYPE_TELEPHONY) {
return false;
}
switch(pauseWhen) {
case PauseWhenRinging:
if (np.get<QString>("event") != "ringing" && np.get<QString>("event") != "talking") {
@ -54,6 +49,8 @@ bool PauseMusicPlugin::receivePackage(const NetworkPackage& np)
return true;
}
break;
case NeverPause:
return true;
}
bool pauseConditionFulfilled = !np.get<bool>("isCancel");
@ -76,7 +73,7 @@ bool PauseMusicPlugin::receivePackage(const NetworkPackage& np)
} else {
Q_FOREACH (const QString& iface, pausedSources) {
QDBusInterface mprisInterface(iface, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player");
//Calling play does not work in spotify
//Calling play does not work for Spotify
//mprisInterface->call(QDBus::Block,"Play");
//Workaround: Using playpause instead (checking first if it is already playing)
QString status = mprisInterface.property("PlaybackStatus").toString();

View file

@ -39,3 +39,5 @@ Comment[sk]=Poslať a prijať pingy
Comment[sv]=Skicka och ta emot ping
Comment[uk]=Надсилання і отримання сигналів підтримання звязку
Comment[x-test]=xxSend and receive pingsxx
X-KdeConnect-SupportedPackageType=kdeconnect.ping

View file

@ -42,9 +42,6 @@ PingPlugin::~PingPlugin()
bool PingPlugin::receivePackage(const NetworkPackage& np)
{
if (np.type() != PACKAGE_TYPE_PING) return false;
KNotification* notification = new KNotification("pingReceived"); //KNotification::Persistent
notification->setPixmap(KIcon("dialog-ok").pixmap(48, 48));
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));

View file

@ -59,30 +59,33 @@ KPluginInfo PluginLoader::getPluginInfo(const QString& name) const
return KPluginInfo(service);
}
KdeConnectPlugin* PluginLoader::instantiatePluginForDevice(const QString& name, Device* device) const
PluginData PluginLoader::instantiatePluginForDevice(const QString& name, Device* device) const
{
PluginData ret;
KService::Ptr service = plugins[name];
if (!service) {
qDebug() << "Plugin unknown" << name;
return NULL;
return ret;
}
KPluginFactory *factory = KPluginLoader(service->library()).factory();
if (!factory) {
qDebug() << "KPluginFactory could not load the plugin:" << service->library();
return NULL;
return ret;
}
ret.interfaces = service->property("X-KdeConnect-SupportedPackageType", QVariant::StringList).toStringList();
QVariant deviceVariant = QVariant::fromValue<Device*>(device);
//FIXME: create<KdeConnectPlugin> return NULL
QObject *plugin = factory->create<QObject>(device, QVariantList() << deviceVariant);
if (!plugin) {
ret.plugin = (KdeConnectPlugin*) factory->create<QObject>(device, QVariantList() << deviceVariant);
if (!ret.plugin) {
qDebug() << "Error loading plugin";
return NULL;
return ret;
}
qDebug() << "Loaded plugin:" << service->name();
return (KdeConnectPlugin*)plugin;
return ret;
}

View file

@ -32,6 +32,13 @@
class Device;
class KdeConnectPlugin;
struct PluginData
{
PluginData() : plugin(0) {}
KdeConnectPlugin* plugin;
QStringList interfaces;
};
class PluginLoader
{
@ -40,7 +47,7 @@ public:
QStringList getPluginList() const;
KPluginInfo getPluginInfo(const QString& name) const;
KdeConnectPlugin* instantiatePluginForDevice(const QString& name, Device* device) const;
PluginData instantiatePluginForDevice(const QString& name, Device* device) const;
private:
PluginLoader();

View file

@ -29,3 +29,5 @@ Comment[ru]=Получать общие файлы и адреса URL с тел
Comment[sv]=Ta emot filer och webbadresser att dela från din telefon
Comment[uk]=Отримання файлів та адрес, наданих у спільне користування з вашого телефону
Comment[x-test]=xxReceive files and URLs shared from your phonexx
X-KdeConnect-SupportedPackageType=kdeconnect.share

View file

@ -66,7 +66,6 @@ bool ShareReceiverPlugin::receivePackage(const NetworkPackage& np)
}
*/
if (np.type() != PACKAGE_TYPE_SHARE) return false;
qDebug() << "File transfer";
if (np.hasPayload()) {

View file

@ -80,9 +80,6 @@ KNotification* TelephonyPlugin::createNotification(const NetworkPackage& np)
bool TelephonyPlugin::receivePackage(const NetworkPackage& np)
{
if (np.type() != PACKAGE_TYPE_TELEPHONY) return false;
if (np.get<bool>("isCancel")) {
//It would be awesome to remove the old notification from the system tray here, but there is no way to do it :(

View file

@ -27,6 +27,8 @@
#include <KStatusNotifierItem>
#define PACKAGE_TYPE_TELEPHONY QLatin1String("kdeconnect.telephony")
class TelephonyPlugin
: public KdeConnectPlugin
{