Added symmetric pairing
A KNotification asks to accept the pairing when the other ends requests it
This commit is contained in:
parent
4eb5dda9c1
commit
1604309ace
11 changed files with 499 additions and 154 deletions
|
@ -29,6 +29,7 @@
|
|||
#include <QDBusConnection>
|
||||
#include <QNetworkSession>
|
||||
#include <QNetworkConfigurationManager>
|
||||
#include <QSslKey>
|
||||
|
||||
#include <KIcon>
|
||||
#include <KConfigGroup>
|
||||
|
@ -50,6 +51,58 @@ Daemon::Daemon(QObject *parent, const QList<QVariant>&)
|
|||
qDebug() << "My id:" << uuid;
|
||||
}
|
||||
|
||||
if (!config->group("myself").hasKey("privateKey")) {
|
||||
|
||||
//TODO: Generate
|
||||
|
||||
QByteArray key = QByteArray(
|
||||
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"MIICXAIBAAKBgQCnKxy6aZrABVvbxuWqMPbohH4KRDBGqyO/OwxvUD1qHpqZ9cJT\n"
|
||||
"bgttiIaXzdQny5esf6brI6Di/ssIp9awdLBlMT+eR6zR7g446tbxaCFuUiL0QIei\n"
|
||||
"izEveTDNRbson/8DPJrn8/81doTeXsuV7YbqmtUGwdZ5kiocAW92ZZukdQIDAQAB\n"
|
||||
"AoGBAI18yuLoMQdnQblBne8vZDumsDsmPaoCfc4EP2ETi/d+kaHPxTryABAkJq7j\n"
|
||||
"kjZgdi6VGIUacbjOqK/Zxrcw/H460EwOUzh97Z4t9CDtDhz6t3ddT8CfbG2TUgbx\n"
|
||||
"Vv3mSYSUDBdNBV6YY4fyLtZl6oI2V+rBaFIT48+vAK9doKlhAkEA2ZKm9dc80IjU\n"
|
||||
"c/Wwn8ij+6ALs4Mpa0dPYivgZ2QhXiX5TfMymal2dDufkOH4wIUO+8vV8CSmmTRU\n"
|
||||
"8Lv/B3pY7QJBAMSxeJtTSFwBcGRaZKRMIqeuZ/yMMT4EqqIh1DjBpujCRKApVpkO\n"
|
||||
"kVx3Yu7xyOfniXBwujiYNSL6LrWdKykEsKkCQEr2UDgbtIRU4H4jhHtI8dbcSavL\n"
|
||||
"4RVpOFymqWZ2BVke1EqbJC/1Ry687DlK4h3Sulre3BMlTZEziqB25WN6L/ECQBJv\n"
|
||||
"B3yXG4rz35KoHhJ/yCeq4rf6c4r6aPt07Cy9iWT6/+96sFD72oet8KmwI0IIowrU\n"
|
||||
"pb80FJbIl6QRrL/VXrECQBDdeCAG6J3Cwm4ozQiDQyiNd1qJqWc4co9savJxLtEU\n"
|
||||
"s5L4Qwfrexm16oCJimGmsa5q6Y0n4f5gY+MRh3n+nQo=\n"
|
||||
"-----END RSA PRIVATE KEY-----\n"
|
||||
);
|
||||
|
||||
//Test for validity
|
||||
//QSslKey privateKey(key.toAscii(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
||||
//qDebug() << "Valid private key:" << !privateKey.isNull();
|
||||
|
||||
config->group("myself").writeEntry("privateKey", key);
|
||||
|
||||
}
|
||||
|
||||
if (!config->group("myself").hasKey("publicKey")) {
|
||||
|
||||
//TODO: Generate
|
||||
|
||||
QByteArray key = QByteArray(
|
||||
"-----BEGIN PUBLIC KEY-----\n"
|
||||
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnKxy6aZrABVvbxuWqMPbohH4K\n"
|
||||
"RDBGqyO/OwxvUD1qHpqZ9cJTbgttiIaXzdQny5esf6brI6Di/ssIp9awdLBlMT+e\n"
|
||||
"R6zR7g446tbxaCFuUiL0QIeiizEveTDNRbson/8DPJrn8/81doTeXsuV7YbqmtUG\n"
|
||||
"wdZ5kiocAW92ZZukdQIDAQAB\n"
|
||||
"-----END PUBLIC KEY-----\n"
|
||||
|
||||
);
|
||||
|
||||
//Test for validity
|
||||
//QSslKey publicKey(key.toAscii(), QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
|
||||
//qDebug() << "Valid public key:" << !publicKey.isNull();
|
||||
|
||||
config->group("myself").writeEntry("publicKey", key);
|
||||
|
||||
}
|
||||
|
||||
//Debugging
|
||||
qDebug() << "Starting KdeConnect daemon";
|
||||
|
||||
|
@ -58,13 +111,10 @@ Daemon::Daemon(QObject *parent, const QList<QVariant>&)
|
|||
mLinkProviders.insert(new LoopbackLinkProvider());
|
||||
|
||||
//Read remebered paired devices
|
||||
const KConfigGroup& known = config->group("devices").group("paired");
|
||||
const KConfigGroup& known = config->group("devices");
|
||||
const QStringList& list = known.groupList();
|
||||
const QString defaultName("unnamed");
|
||||
Q_FOREACH(const QString& id, list) {
|
||||
const KConfigGroup& data = known.group(id);
|
||||
const QString& name = data.readEntry<QString>("name", defaultName);
|
||||
Device* device = new Device(id, name);
|
||||
Device* device = new Device(id);
|
||||
connect(device, SIGNAL(reachableStatusChanged()), this, SLOT(onDeviceReachableStatusChanged()));
|
||||
mDevices[id] = device;
|
||||
Q_EMIT deviceAdded(id);
|
||||
|
@ -127,16 +177,12 @@ void Daemon::onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink*
|
|||
|
||||
if (mDevices.contains(id)) {
|
||||
qDebug() << "It is a known device";
|
||||
|
||||
Device* device = mDevices[id];
|
||||
device->addLink(dl);
|
||||
|
||||
} else {
|
||||
qDebug() << "It is a new device";
|
||||
|
||||
const QString& name = identityPackage.get<QString>("deviceName");
|
||||
|
||||
Device* device = new Device(id, name, dl);
|
||||
Device* device = new Device(identityPackage, dl);
|
||||
connect(device, SIGNAL(reachableStatusChanged()), this, SLOT(onDeviceReachableStatusChanged()));
|
||||
mDevices[id] = device;
|
||||
|
||||
|
@ -157,7 +203,7 @@ void Daemon::onDeviceReachableStatusChanged()
|
|||
|
||||
if (!device->reachable()) {
|
||||
|
||||
if (!device->paired()) {
|
||||
if (!device->isPaired()) {
|
||||
qDebug() << "Destroying device";
|
||||
Q_EMIT deviceRemoved(id);
|
||||
mDevices.remove(id);
|
||||
|
|
|
@ -41,11 +41,19 @@ struct default_arg<int> {
|
|||
};
|
||||
|
||||
//Pointer types -> NULL (partial specialization)
|
||||
//NOTE: Comented because it doesn't makeno sense to send a pointer over the network, but I just left it here for reference --albertvaka
|
||||
/*template<class T*>
|
||||
struct default_arg<T*> {
|
||||
static T* get() { NULL; }
|
||||
};
|
||||
*/
|
||||
|
||||
//QByteArray-> empty qbytearray
|
||||
template<>
|
||||
struct default_arg<QByteArray> {
|
||||
static QByteArray get() { return QByteArray(); }
|
||||
};
|
||||
|
||||
//QStrings -> empty string
|
||||
template<>
|
||||
struct default_arg<QString> {
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
#include <KPluginSelector>
|
||||
#include <KServiceTypeTrader>
|
||||
#include <KPluginInfo>
|
||||
#include <KNotification>
|
||||
#include <KIcon>
|
||||
|
||||
#include <QSslKey>
|
||||
#include <QDebug>
|
||||
|
||||
#include "plugins/kdeconnectplugin.h"
|
||||
|
@ -16,36 +19,38 @@
|
|||
#include "linkproviders/linkprovider.h"
|
||||
#include "networkpackage.h"
|
||||
|
||||
Device::Device(const QString& id, const QString& name)
|
||||
Device::Device(const QString& id)
|
||||
{
|
||||
|
||||
m_deviceId = id;
|
||||
m_deviceName = name;
|
||||
m_paired = true;
|
||||
m_knownIdentiy = true;
|
||||
|
||||
reloadPlugins();
|
||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||
const KConfigGroup& data = config->group("devices").group(id);
|
||||
|
||||
const QString& name = data.readEntry<QString>("name", QString("unnamed"));
|
||||
m_deviceName = name;
|
||||
|
||||
const QByteArray& key = data.readEntry<QByteArray>("publicKey",QByteArray());
|
||||
m_publicKey = QSslKey(key, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
|
||||
|
||||
m_pairingRequested = false;
|
||||
|
||||
//Register in bus
|
||||
QDBusConnection::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAdaptors);
|
||||
|
||||
}
|
||||
|
||||
Device::Device(const QString& id, const QString& name, DeviceLink* link)
|
||||
Device::Device(const NetworkPackage& identityPackage, DeviceLink* dl)
|
||||
{
|
||||
m_deviceId = identityPackage.get<QString>("deviceId");
|
||||
m_deviceName = identityPackage.get<QString>("deviceName");
|
||||
|
||||
m_deviceId = id;
|
||||
m_deviceName = name;
|
||||
m_paired = false;
|
||||
m_knownIdentiy = true;
|
||||
addLink(dl);
|
||||
|
||||
addLink(link);
|
||||
|
||||
reloadPlugins();
|
||||
m_pairingRequested = false;
|
||||
|
||||
//Register in bus
|
||||
QDBusConnection::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAdaptors);
|
||||
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
|
@ -67,7 +72,7 @@ void Device::reloadPlugins()
|
|||
{
|
||||
QMap< QString, KdeConnectPlugin* > newPluginMap;
|
||||
|
||||
if (paired() && reachable()) { //Do not load any plugin for unpaired devices, nor useless loading them for unreachable devices
|
||||
if (isPaired() && reachable()) { //Do not load any plugin for unpaired devices, nor useless loading them for unreachable devices
|
||||
|
||||
QString path = KStandardDirs().resourceDirs("config").first()+"kdeconnect/";
|
||||
QMap<QString,QString> pluginStates = KSharedConfig::openConfig(path + id())->group("Plugins").entryMap();
|
||||
|
@ -114,21 +119,75 @@ void Device::reloadPlugins()
|
|||
|
||||
}
|
||||
|
||||
//TODO
|
||||
QSslKey myPrivateKey() {
|
||||
|
||||
void Device::setPair(bool b)
|
||||
{
|
||||
m_paired = b;
|
||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||
if (b) {
|
||||
qDebug() << name() << "paired";
|
||||
config->group("devices").group("paired").group(id()).writeEntry("name",name());
|
||||
Q_EMIT reachableStatusChanged();
|
||||
} else {
|
||||
qDebug() << name() << "unpaired";
|
||||
config->group("devices").group("paired").deleteGroup(id());
|
||||
//Do not Q_EMIT reachableStatusChanged() because we do not want it to suddenly disappear from device list
|
||||
const QString& key = config->group("myself").readEntry<QString>("privateKey",QString());
|
||||
|
||||
QSslKey privateKey(key.toAscii(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
||||
qDebug() << "Valid public key:" << !privateKey.isNull();
|
||||
|
||||
return privateKey;
|
||||
|
||||
}
|
||||
|
||||
void Device::requestPair()
|
||||
{
|
||||
if (isPaired() || m_pairingRequested) {
|
||||
Q_EMIT pairingFailed(i18n("Already paired"));
|
||||
return;
|
||||
}
|
||||
reloadPlugins();
|
||||
if (!reachable()) {
|
||||
Q_EMIT pairingFailed(i18n("Device not reachable"));
|
||||
return;
|
||||
}
|
||||
|
||||
//Send our own public key
|
||||
NetworkPackage np(PACKAGE_TYPE_PAIR);
|
||||
np.set("pair", true);
|
||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||
const QByteArray& key = config->group("myself").readEntry<QByteArray>("publicKey",QByteArray());
|
||||
np.set("publicKey",key);
|
||||
bool success = sendPackage(np);
|
||||
|
||||
if (!success) {
|
||||
Q_EMIT pairingFailed(i18n("Error contacting device"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_pairingRequested = true;
|
||||
pairingTimer.start(20 * 1000);
|
||||
connect(&pairingTimer, SIGNAL(timeout()),
|
||||
this, SLOT(pairingTimeout()));
|
||||
|
||||
}
|
||||
|
||||
void Device::unpair()
|
||||
{
|
||||
if (!isPaired()) return;
|
||||
|
||||
m_publicKey = QSslKey();
|
||||
m_pairingRequested = false;
|
||||
pairingTimer.stop();
|
||||
|
||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||
config->group("devices").deleteGroup(id());
|
||||
|
||||
if (reachable()) {
|
||||
NetworkPackage np(PACKAGE_TYPE_PAIR);
|
||||
np.set("pair", false);
|
||||
sendPackage(np);
|
||||
}
|
||||
|
||||
reloadPlugins(); //Will unload the plugins
|
||||
|
||||
}
|
||||
|
||||
void Device::pairingTimeout()
|
||||
{
|
||||
m_pairingRequested = false;
|
||||
Q_EMIT Q_EMIT pairingFailed("Timed out");
|
||||
}
|
||||
|
||||
static bool lessThan(DeviceLink* p1, DeviceLink* p2)
|
||||
|
@ -182,9 +241,10 @@ void Device::removeLink(DeviceLink* link)
|
|||
|
||||
bool Device::sendPackage(const NetworkPackage& np) const
|
||||
{
|
||||
if (!m_paired) {
|
||||
//qDebug() << "sendpackage disabled on untrusted device" << name();
|
||||
return false;
|
||||
//Maybe we could block here any package that is not an identity or a pairing package
|
||||
|
||||
if (isPaired()) {
|
||||
|
||||
}
|
||||
|
||||
Q_FOREACH(DeviceLink* dl, m_deviceLinks) {
|
||||
|
@ -198,14 +258,118 @@ bool Device::sendPackage(const NetworkPackage& np) const
|
|||
|
||||
void Device::privateReceivedPackage(const NetworkPackage& np)
|
||||
{
|
||||
if (np.type() == "kdeconnect.identity" && !m_knownIdentiy) {
|
||||
m_deviceName = np.get<QString>("deviceName");
|
||||
} else if (m_paired) {
|
||||
//qDebug() << "package received from trusted device" << name();
|
||||
Q_EMIT receivedPackage(np);
|
||||
} else {
|
||||
qDebug() << "device" << name() << "not trusted, ignoring package" << np.type();
|
||||
|
||||
if (np.type() == PACKAGE_TYPE_PAIR) {
|
||||
|
||||
qDebug() << "Pair package";
|
||||
|
||||
bool pair = np.get<bool>("pair");
|
||||
if (pair == isPaired()) {
|
||||
qDebug() << "Already" << (pair? "paired":"unpaired");
|
||||
return;
|
||||
}
|
||||
|
||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||
|
||||
if (pair) {
|
||||
|
||||
if (m_pairingRequested) { //We started pairing
|
||||
|
||||
qDebug() << "Pair answer";
|
||||
|
||||
m_pairingRequested = false;
|
||||
pairingTimer.stop();
|
||||
|
||||
//Store as trusted device
|
||||
const QByteArray& key = np.get<QByteArray>("publicKey");
|
||||
config->group("devices").group(id()).writeEntry("publicKey",key);
|
||||
config->group("devices").group(id()).writeEntry("name",name());
|
||||
m_publicKey = QSslKey(key, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
|
||||
|
||||
Q_EMIT pairingSuccesful();
|
||||
|
||||
} else {
|
||||
|
||||
qDebug() << "Pair request";
|
||||
|
||||
const QString& key = np.get<QString>("publicKey");
|
||||
m_tempPublicKey = QSslKey(key.toAscii(), QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
|
||||
|
||||
KNotification* notification = new KNotification("pingReceived"); //KNotification::Persistent
|
||||
notification->setPixmap(KIcon("dialog-information").pixmap(48, 48));
|
||||
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));
|
||||
notification->setTitle("KDE Connect");
|
||||
notification->setText(i18n("Pairing request from %1", m_deviceName));
|
||||
notification->setActions(QStringList() << i18n("Accept") << i18n("Reject"));
|
||||
connect(notification, SIGNAL(action1Activated()), this, SLOT(acceptPairing()));
|
||||
connect(notification, SIGNAL(action2Activated()), this, SLOT(rejectPairing()));
|
||||
notification->sendEvent();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
qDebug() << "Unpair request";
|
||||
if (m_pairingRequested) {
|
||||
pairingTimer.stop();
|
||||
Q_EMIT pairingFailed(i18n("Canceled by other peer"));
|
||||
}
|
||||
unpair();
|
||||
|
||||
}
|
||||
|
||||
} else if (!isPaired()) {
|
||||
|
||||
//TODO: Alert the other side that we don't trust them
|
||||
qDebug() << "device" << name() << "not paired, ignoring package" << np.type();
|
||||
|
||||
} else {
|
||||
|
||||
//Forward signal
|
||||
Q_EMIT receivedPackage(np);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Device::acceptPairing()
|
||||
{
|
||||
qDebug() << "Accepted pairing";
|
||||
|
||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||
|
||||
//Send our own public key
|
||||
NetworkPackage np(PACKAGE_TYPE_PAIR);
|
||||
np.set("pair", true);
|
||||
const QByteArray& key = config->group("myself").readEntry<QByteArray>("publicKey",QByteArray());
|
||||
np.set("publicKey",key);
|
||||
bool success = sendPackage(np);
|
||||
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Store as trusted device
|
||||
m_publicKey = m_tempPublicKey;
|
||||
config->group("devices").group(id()).writeEntry("publicKey", m_tempPublicKey.toPem());
|
||||
config->group("devices").group(id()).writeEntry("name", name());
|
||||
|
||||
reloadPlugins(); //This will load plugins
|
||||
}
|
||||
|
||||
void Device::rejectPairing()
|
||||
{
|
||||
qDebug() << "Rejected pairing";
|
||||
|
||||
NetworkPackage np(PACKAGE_TYPE_PAIR);
|
||||
np.set("pair", false);
|
||||
sendPackage(np);
|
||||
|
||||
KNotification* notification = (KNotification*)sender();
|
||||
notification->setActions(QStringList());
|
||||
notification->setText(i18n("Pairing rejected"));
|
||||
notification->update();
|
||||
|
||||
}
|
||||
|
||||
QStringList Device::availableLinks() const
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include <QDBusConnection>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QSslKey>
|
||||
#include <QTimer>
|
||||
|
||||
#include "networkpackage.h"
|
||||
|
||||
|
@ -40,11 +42,11 @@ class Device
|
|||
Q_PROPERTY(QString name READ name)
|
||||
|
||||
public:
|
||||
//Device known from KConfig, we trust it but we need to wait for a incoming devicelink to communicate
|
||||
Device(const QString& id, const QString& name);
|
||||
//Read device from KConfig, we already know it but we need to wait for a incoming devicelink to communicate
|
||||
Device(const QString& id);
|
||||
|
||||
//Device known via an incoming connection sent to us via a devicelink, we know everything but we don't trust it yet
|
||||
Device(const QString& id, const QString& name, DeviceLink* dl);
|
||||
Device(const NetworkPackage& np, DeviceLink* dl);
|
||||
|
||||
virtual ~Device();
|
||||
|
||||
|
@ -57,8 +59,8 @@ public:
|
|||
void removeLink(DeviceLink*);
|
||||
|
||||
Q_SCRIPTABLE QStringList availableLinks() const;
|
||||
Q_SCRIPTABLE bool trusted() const { return m_paired; }
|
||||
Q_SCRIPTABLE bool paired() const { return m_paired; }
|
||||
Q_SCRIPTABLE bool isPaired() const { return !m_publicKey.isNull(); }
|
||||
Q_SCRIPTABLE bool pairRequested() const { return m_pairingRequested; }
|
||||
Q_SCRIPTABLE bool reachable() const { return !m_deviceLinks.empty(); }
|
||||
Q_SCRIPTABLE bool hasPlugin(const QString& name);
|
||||
Q_SCRIPTABLE QStringList loadedPlugins();
|
||||
|
@ -69,28 +71,37 @@ Q_SIGNALS:
|
|||
public Q_SLOTS:
|
||||
virtual bool sendPackage(const NetworkPackage& np) const;
|
||||
|
||||
//Dbus operations called from kcm
|
||||
//Dbus operations
|
||||
public Q_SLOTS:
|
||||
Q_SCRIPTABLE void setPair(bool b);
|
||||
Q_SCRIPTABLE void reloadPlugins(); //From settings
|
||||
Q_SCRIPTABLE void requestPair();
|
||||
Q_SCRIPTABLE void unpair();
|
||||
Q_SCRIPTABLE void reloadPlugins(); //From kconf
|
||||
Q_SCRIPTABLE void sendPing();
|
||||
void acceptPairing();
|
||||
void rejectPairing();
|
||||
|
||||
private Q_SLOTS:
|
||||
void privateReceivedPackage(const NetworkPackage& np);
|
||||
void linkDestroyed(QObject* o = 0);
|
||||
virtual void privateReceivedPackage(const NetworkPackage& np);
|
||||
void pairingTimeout();
|
||||
|
||||
Q_SIGNALS:
|
||||
Q_SCRIPTABLE void reachableStatusChanged();
|
||||
Q_SCRIPTABLE void pluginsChanged();
|
||||
Q_SCRIPTABLE void pairingSuccesful();
|
||||
Q_SCRIPTABLE void pairingFailed(const QString& error);
|
||||
|
||||
private:
|
||||
bool m_paired;
|
||||
QString m_deviceId;
|
||||
QString m_deviceName;
|
||||
QSslKey m_publicKey;
|
||||
QSslKey m_tempPublicKey;
|
||||
bool m_pairingRequested;
|
||||
|
||||
QList<DeviceLink*> m_deviceLinks;
|
||||
QMap<QString, KdeConnectPlugin*> m_plugins;
|
||||
bool m_knownIdentiy;
|
||||
|
||||
QTimer pairingTimer;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Device*)
|
||||
|
|
|
@ -48,9 +48,6 @@ public:
|
|||
static void unserialize(const QByteArray&, NetworkPackage*);
|
||||
QByteArray serialize() const;
|
||||
|
||||
static void rsaUnserialize(const QByteArray&, NetworkPackage*, Qssl );
|
||||
QByteArray rsaSerialize() const;
|
||||
|
||||
static void createIdentityPackage(NetworkPackage*);
|
||||
|
||||
long id() const { return mId; }
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#define NETWORKPACKAGETYPES_H
|
||||
|
||||
#define PACKAGE_TYPE_IDENTITY QString("kdeconnect.identity")
|
||||
#define PACKAGE_TYPE_PAIR QString("kdeconnect.pair")
|
||||
#define PACKAGE_TYPE_PING QString("kdeconnect.ping")
|
||||
#define PACKAGE_TYPE_NOTIFICATION QString("kdeconnect.notification")
|
||||
#define PACKAGE_TYPE_BATTERY QString("kdeconnect.battery")
|
||||
|
|
|
@ -57,6 +57,7 @@ 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);
|
||||
|
||||
|
|
101
kcm/kcm.cpp
101
kcm/kcm.cpp
|
@ -59,6 +59,8 @@ KdeConnectKcm::KdeConnectKcm(QWidget *parent, const QVariantList&)
|
|||
kcmUi->deviceList->setModel(sortProxyModel);
|
||||
|
||||
kcmUi->deviceInfo->setVisible(false);
|
||||
kcmUi->progressBar->setVisible(false);
|
||||
kcmUi->messages->setVisible(false);
|
||||
|
||||
setButtons(KCModule::NoAdditionalButton);
|
||||
|
||||
|
@ -66,12 +68,12 @@ KdeConnectKcm::KdeConnectKcm(QWidget *parent, const QVariantList&)
|
|||
this, SLOT(resetSelection()));
|
||||
connect(kcmUi->deviceList, SIGNAL(pressed(QModelIndex)),
|
||||
this, SLOT(deviceSelected(QModelIndex)));
|
||||
connect(kcmUi->pair_button, SIGNAL(pressed()),
|
||||
this, SLOT(requestPair()));
|
||||
connect(kcmUi->unpair_button, SIGNAL(pressed()),
|
||||
this, SLOT(unpair()));
|
||||
connect(kcmUi->ping_button, SIGNAL(pressed()),
|
||||
this, SLOT(sendPing()));
|
||||
connect(kcmUi->trust_checkbox, SIGNAL(toggled(bool)),
|
||||
this, SLOT(trustedStateChanged(bool)));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -85,6 +87,7 @@ void KdeConnectKcm::resetSelection()
|
|||
kcmUi->deviceList->selectionModel()->setCurrentIndex(sortProxyModel->mapFromSource(currentIndex), QItemSelectionModel::ClearAndSelect);
|
||||
}
|
||||
|
||||
|
||||
void KdeConnectKcm::deviceSelected(const QModelIndex& current)
|
||||
{
|
||||
|
||||
|
@ -105,13 +108,33 @@ void KdeConnectKcm::deviceSelected(const QModelIndex& current)
|
|||
return;
|
||||
}
|
||||
|
||||
kcmUi->messages->setVisible(false);
|
||||
if (currentDevice->pairRequested()) {
|
||||
kcmUi->progressBar->setVisible(true);
|
||||
kcmUi->unpair_button->setVisible(false);
|
||||
kcmUi->pair_button->setVisible(false);
|
||||
kcmUi->ping_button->setVisible(false);
|
||||
} else {
|
||||
kcmUi->progressBar->setVisible(false);
|
||||
if (currentDevice->isPaired()) {
|
||||
kcmUi->unpair_button->setVisible(true);
|
||||
kcmUi->pair_button->setVisible(false);
|
||||
kcmUi->ping_button->setVisible(true);
|
||||
} else {
|
||||
kcmUi->unpair_button->setVisible(false);
|
||||
kcmUi->pair_button->setVisible(true);
|
||||
kcmUi->ping_button->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//FIXME: KPluginSelector has no way to remove a list of plugins and load another, so we need to destroy and recreate it each time
|
||||
delete kcmUi->pluginSelector;
|
||||
kcmUi->pluginSelector = new KPluginSelector(this);
|
||||
kcmUi->verticalLayout_2->addWidget(kcmUi->pluginSelector);
|
||||
|
||||
kcmUi->deviceName->setText(currentDevice->name());
|
||||
kcmUi->trust_checkbox->setChecked(currentDevice->paired());
|
||||
kcmUi->name_label->setText(currentDevice->name());
|
||||
kcmUi->status_label->setText(currentDevice->isPaired()? "paired" : "unpaired");
|
||||
|
||||
KService::List offers = KServiceTypeTrader::self()->query("KdeConnect/Plugin");
|
||||
QList<KPluginInfo> scriptinfos = KPluginInfo::fromServices(offers);
|
||||
|
@ -124,22 +147,58 @@ void KdeConnectKcm::deviceSelected(const QModelIndex& current)
|
|||
this, SLOT(pluginsConfigChanged()));
|
||||
}
|
||||
|
||||
void KdeConnectKcm::trustedStateChanged(bool b)
|
||||
void KdeConnectKcm::requestPair()
|
||||
{
|
||||
if (!currentDevice) return;
|
||||
QDBusPendingReply<void> pendingReply = currentDevice->setPair(b);
|
||||
pendingReply.waitForFinished();
|
||||
if (pendingReply.isValid()) {
|
||||
//If dbus was down, calling this would make kcm crash
|
||||
|
||||
kcmUi->messages->hide();
|
||||
|
||||
kcmUi->pair_button->setVisible(false);
|
||||
kcmUi->progressBar->setVisible(true);
|
||||
|
||||
connect(currentDevice,SIGNAL(pairingSuccesful()),
|
||||
this, SLOT(pairingSuccesful()));
|
||||
connect(currentDevice,SIGNAL(pairingFailed(QString)),
|
||||
this, SLOT(pairingFailed(QString)));
|
||||
|
||||
currentDevice->requestPair();
|
||||
|
||||
}
|
||||
|
||||
void KdeConnectKcm::unpair()
|
||||
{
|
||||
if (!currentDevice) return;
|
||||
|
||||
kcmUi->pair_button->setVisible(true);
|
||||
kcmUi->unpair_button->setVisible(false);
|
||||
kcmUi->progressBar->setVisible(false);
|
||||
kcmUi->ping_button->setVisible(false);
|
||||
|
||||
currentDevice->unpair();
|
||||
|
||||
kcmUi->status_label->setText("(unpaired)");
|
||||
|
||||
devicesModel->deviceStatusChanged(currentDevice->id());
|
||||
}
|
||||
|
||||
void KdeConnectKcm::pairingFailed(const QString& error)
|
||||
{
|
||||
kcmUi->messages->setText("Error trying to pair: "+error);
|
||||
kcmUi->messages->animatedShow();
|
||||
kcmUi->progressBar->setVisible(false);
|
||||
kcmUi->pair_button->setVisible(true);
|
||||
}
|
||||
|
||||
void KdeConnectKcm::pairingSuccesful()
|
||||
{
|
||||
kcmUi->progressBar->setVisible(false);
|
||||
kcmUi->unpair_button->setVisible(true);
|
||||
kcmUi->pair_button->setVisible(false);
|
||||
kcmUi->ping_button->setVisible(true);
|
||||
|
||||
kcmUi->status_label->setText("(paired)");
|
||||
|
||||
devicesModel->deviceStatusChanged(currentDevice->id());
|
||||
} else {
|
||||
//Revert checkbox
|
||||
disconnect(kcmUi->trust_checkbox, SIGNAL(toggled(bool)),
|
||||
this, SLOT(trustedStateChanged(bool)));
|
||||
kcmUi->trust_checkbox->setCheckState(b? Qt::Unchecked : Qt::Checked);
|
||||
connect(kcmUi->trust_checkbox, SIGNAL(toggled(bool)),
|
||||
this, SLOT(trustedStateChanged(bool)));
|
||||
}
|
||||
}
|
||||
|
||||
void KdeConnectKcm::pluginsConfigChanged()
|
||||
|
@ -147,8 +206,8 @@ void KdeConnectKcm::pluginsConfigChanged()
|
|||
//Store previous selection
|
||||
if (!currentDevice) return;
|
||||
|
||||
DeviceDbusInterface* auxCurrentDevice = currentDevice; //HACK to avoid infinite recursion (for some reason calling save on pluginselector emits changed)
|
||||
currentDevice = 0;
|
||||
DeviceDbusInterface* auxCurrentDevice = currentDevice;
|
||||
currentDevice = 0; //HACK to avoid infinite recursion (for some reason calling save on pluginselector emits changed)
|
||||
kcmUi->pluginSelector->save();
|
||||
currentDevice = auxCurrentDevice;
|
||||
|
||||
|
|
|
@ -56,10 +56,12 @@ private:
|
|||
|
||||
private Q_SLOTS:
|
||||
void deviceSelected(const QModelIndex& current);
|
||||
void trustedStateChanged(bool);
|
||||
void requestPair();
|
||||
void pluginsConfigChanged();
|
||||
void sendPing();
|
||||
void resetSelection();
|
||||
void pairingSuccesful();
|
||||
void pairingFailed(const QString& error);
|
||||
|
||||
private:
|
||||
Ui::KdeConnectKcmUi* kcmUi;
|
||||
|
@ -69,8 +71,8 @@ private:
|
|||
DeviceDbusInterface* currentDevice;
|
||||
QModelIndex currentIndex;
|
||||
//KSharedConfigPtr config;
|
||||
|
||||
|
||||
public slots:
|
||||
void unpair();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
84
kcm/kcm.ui
84
kcm/kcm.ui
|
@ -54,27 +54,28 @@
|
|||
</property>
|
||||
<layout class="QVBoxLayout" name="deviceInfoBorder">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="deviceInfo">
|
||||
<widget class="QWidget" name="deviceInfo" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<widget class="QWidget" name="header_2" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="header">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMaximumSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="deviceName">
|
||||
<widget class="QLabel" name="name_label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
|
@ -87,6 +88,19 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="status_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>(status)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
|
@ -101,7 +115,26 @@
|
|||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="trust_checkbox">
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pair_button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
|
@ -109,7 +142,20 @@
|
|||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Trust this device</string>
|
||||
<string>Request pair</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="unpair_button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Unpair</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -127,6 +173,10 @@
|
|||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KMessageWidget" name="messages" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KPluginSelector" name="pluginSelector" native="true">
|
||||
|
@ -156,6 +206,12 @@
|
|||
<header>kpluginselector.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>KMessageWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>kmessagewidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
@ -121,7 +121,7 @@ void DevicesModel::refreshDeviceList()
|
|||
DeviceDbusInterface* deviceDbusInterface = new DeviceDbusInterface(id,this);
|
||||
|
||||
bool onlyPaired = (m_displayFilter & StatusPaired);
|
||||
if (onlyPaired && !deviceDbusInterface->paired()) continue;
|
||||
if (onlyPaired && !deviceDbusInterface->isPaired()) continue;
|
||||
bool onlyReachable = (m_displayFilter & StatusReachable);
|
||||
if (onlyReachable && !deviceDbusInterface->reachable()) continue;
|
||||
|
||||
|
@ -154,7 +154,7 @@ QVariant DevicesModel::data(const QModelIndex &index, int role) const
|
|||
//FIXME: This function gets called lots of times, producing lots of dbus calls. Add a cache.
|
||||
switch (role) {
|
||||
case IconModelRole: {
|
||||
bool paired = device->paired();
|
||||
bool paired = device->isPaired();
|
||||
bool reachable = device->reachable();
|
||||
QString icon = reachable? (paired? "user-online" : "user-busy") : "user-offline";
|
||||
return KIcon(icon).pixmap(32, 32);
|
||||
|
@ -169,7 +169,7 @@ QVariant DevicesModel::data(const QModelIndex &index, int role) const
|
|||
int status = StatusUnknown;
|
||||
if (device->reachable()) {
|
||||
status |= StatusReachable;
|
||||
if (device->paired()) status |= StatusPaired;
|
||||
if (device->isPaired()) status |= StatusPaired;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue