From 1604309aceb22f9baaf4e606522df402b51423a6 Mon Sep 17 00:00:00 2001 From: Albert Vaca Date: Fri, 30 Aug 2013 19:10:43 +0200 Subject: [PATCH] Added symmetric pairing A KNotification asks to accept the pairing when the other ends requests it --- daemon/daemon.cpp | 68 ++++- daemon/default_args.h | 8 + daemon/device.cpp | 236 +++++++++++++++--- daemon/device.h | 33 ++- daemon/networkpackage.h | 3 - daemon/networkpackagetypes.h | 1 + .../notifications/notificationsplugin.cpp | 1 + kcm/kcm.cpp | 103 ++++++-- kcm/kcm.h | 8 +- kcm/kcm.ui | 186 +++++++++----- libkdeconnect/devicesmodel.cpp | 6 +- 11 files changed, 499 insertions(+), 154 deletions(-) diff --git a/daemon/daemon.cpp b/daemon/daemon.cpp index 0004d53cc..98de7f266 100644 --- a/daemon/daemon.cpp +++ b/daemon/daemon.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,58 @@ Daemon::Daemon(QObject *parent, const QList&) 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&) 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("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("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); diff --git a/daemon/default_args.h b/daemon/default_args.h index 4cf0fce1d..ff1b7f7ff 100644 --- a/daemon/default_args.h +++ b/daemon/default_args.h @@ -41,11 +41,19 @@ struct default_arg { }; //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 struct default_arg { static T* get() { NULL; } }; */ + +//QByteArray-> empty qbytearray +template<> +struct default_arg { + static QByteArray get() { return QByteArray(); } +}; + //QStrings -> empty string template<> struct default_arg { diff --git a/daemon/device.cpp b/daemon/device.cpp index 827f757d0..5f6876a73 100644 --- a/daemon/device.cpp +++ b/daemon/device.cpp @@ -7,7 +7,10 @@ #include #include #include +#include +#include +#include #include #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("name", QString("unnamed")); + m_deviceName = name; + + const QByteArray& key = data.readEntry("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("deviceId"); + m_deviceName = identityPackage.get("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 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("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("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("deviceName"); - } else if (m_paired) { - //qDebug() << "package received from trusted device" << name(); - Q_EMIT receivedPackage(np); + + if (np.type() == PACKAGE_TYPE_PAIR) { + + qDebug() << "Pair package"; + + bool pair = np.get("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("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("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 { - qDebug() << "device" << name() << "not trusted, ignoring package" << np.type(); + + //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("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 diff --git a/daemon/device.h b/daemon/device.h index aaeee3d80..edcaadf62 100644 --- a/daemon/device.h +++ b/daemon/device.h @@ -25,6 +25,8 @@ #include #include #include +#include +#include #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 m_deviceLinks; QMap m_plugins; - bool m_knownIdentiy; + QTimer pairingTimer; }; Q_DECLARE_METATYPE(Device*) diff --git a/daemon/networkpackage.h b/daemon/networkpackage.h index 92c8d6918..a2db3b5cf 100644 --- a/daemon/networkpackage.h +++ b/daemon/networkpackage.h @@ -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; } diff --git a/daemon/networkpackagetypes.h b/daemon/networkpackagetypes.h index 96ccaabef..618a9bf76 100644 --- a/daemon/networkpackagetypes.h +++ b/daemon/networkpackagetypes.h @@ -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") diff --git a/daemon/plugins/notifications/notificationsplugin.cpp b/daemon/plugins/notifications/notificationsplugin.cpp index 691677a8e..4df6dc5f4 100644 --- a/daemon/plugins/notifications/notificationsplugin.cpp +++ b/daemon/plugins/notifications/notificationsplugin.cpp @@ -57,6 +57,7 @@ NotificationsPlugin::~NotificationsPlugin() bool NotificationsPlugin::receivePackage(const NetworkPackage& np) { if (np.type() != PACKAGE_TYPE_NOTIFICATION) return false; + if (np.get("request")) return false; notificationsDbusInterface->processPackage(np); diff --git a/kcm/kcm.cpp b/kcm/kcm.cpp index 8d97e0ee0..3a65cbe32 100644 --- a/kcm/kcm.cpp +++ b/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 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 pendingReply = currentDevice->setPair(b); - pendingReply.waitForFinished(); - if (pendingReply.isValid()) { - //If dbus was down, calling this would make kcm crash - 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))); - } + + 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()); } 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; diff --git a/kcm/kcm.h b/kcm/kcm.h index 2fc8fd1e8..6dcbc550c 100644 --- a/kcm/kcm.h +++ b/kcm/kcm.h @@ -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 diff --git a/kcm/kcm.ui b/kcm/kcm.ui index b9879b6ae..8b86b1630 100644 --- a/kcm/kcm.ui +++ b/kcm/kcm.ui @@ -54,79 +54,129 @@ - + 0 0 - - - - - true - - - false - - - - - - - 10 - 75 - true - - - - Device - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Trust this device - - - - - - - - 0 - 0 - - - - Send ping - - - - + + + + 0 + 0 + + + + + QLayout::SetMaximumSize + + + + + + 10 + 75 + true + + + + Device + + + + + + + + 0 + 0 + + + + (status) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + 0 + + + 0 + + + -1 + + + + + + + + 0 + 0 + + + + Request pair + + + + + + + + 0 + 0 + + + + Unpair + + + + + + + + 0 + 0 + + + + Send ping + + + + + + + + @@ -156,6 +206,12 @@
kpluginselector.h
1 + + KMessageWidget + QWidget +
kmessagewidget.h
+ 1 +
diff --git a/libkdeconnect/devicesmodel.cpp b/libkdeconnect/devicesmodel.cpp index 1fc81ec3d..ccca7957d 100644 --- a/libkdeconnect/devicesmodel.cpp +++ b/libkdeconnect/devicesmodel.cpp @@ -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; }