Refactor PairingHandler

We now have a single PairingHandler for all types of links.

The implementation now is more aligned with the one for Android.
This commit is contained in:
Albert Vaca Cintora 2023-06-01 02:25:37 +02:00
parent 85cc644024
commit 064ddfa3fe
35 changed files with 368 additions and 895 deletions

View file

@ -21,12 +21,12 @@ Kirigami.ScrollablePage {
iconName:"network-disconnect" iconName:"network-disconnect"
onTriggered: root.currentDevice.unpair() onTriggered: root.currentDevice.unpair()
text: i18nd("kdeconnect-app", "Unpair") text: i18nd("kdeconnect-app", "Unpair")
visible: root.currentDevice.isTrusted visible: root.currentDevice.isPaired
}, },
Kirigami.Action { Kirigami.Action {
iconName:"hands-free" iconName:"hands-free"
text: i18nd("kdeconnect-app", "Send Ping") text: i18nd("kdeconnect-app", "Send Ping")
visible: root.currentDevice.isTrusted && root.currentDevice.isReachable visible: root.currentDevice.isPaired && root.currentDevice.isReachable
onTriggered: { onTriggered: {
root.currentDevice.pluginCall("ping", "sendPing"); root.currentDevice.pluginCall("ping", "sendPing");
} }
@ -34,7 +34,7 @@ Kirigami.ScrollablePage {
Kirigami.Action { Kirigami.Action {
iconName: "settings-configure" iconName: "settings-configure"
text: i18n("Plugin Settings") text: i18n("Plugin Settings")
visible: root.currentDevice.isTrusted && root.currentDevice.isReachable visible: root.currentDevice.isPaired && root.currentDevice.isReachable
onTriggered: { onTriggered: {
pageStack.push( pageStack.push(
Qt.resolvedUrl("PluginSettings.qml"), Qt.resolvedUrl("PluginSettings.qml"),
@ -116,17 +116,17 @@ Kirigami.ScrollablePage {
Kirigami.PlaceholderMessage { Kirigami.PlaceholderMessage {
text: i18nd("kdeconnect-app", "This device is not paired") text: i18nd("kdeconnect-app", "This device is not paired")
anchors.centerIn: parent anchors.centerIn: parent
visible: root.currentDevice && root.currentDevice.isReachable && !root.currentDevice.isTrusted && !root.currentDevice.hasPairingRequests visible: root.currentDevice && root.currentDevice.isReachable && !root.currentDevice.isPaired && !root.currentDevice.isPairRequestedByPeer
helpfulAction: Kirigami.Action { helpfulAction: Kirigami.Action {
text: i18nd("kdeconnect-app", "Pair") text: i18nd("kdeconnect-app", "Pair")
icon.name:"network-connect" icon.name:"network-connect"
onTriggered: root.currentDevice.requestPair() onTriggered: root.currentDevice.requestPairing()
} }
} }
Kirigami.PlaceholderMessage { Kirigami.PlaceholderMessage {
text: i18n("Pair requested") text: i18n("Pair requested")
visible: root.currentDevice && root.currentDevice.hasPairingRequests visible: root.currentDevice && root.currentDevice.isPairRequestedByPeer
anchors.centerIn: parent anchors.centerIn: parent
spacing: Kirigami.Units.largeSpacing spacing: Kirigami.Units.largeSpacing
RowLayout { RowLayout {
@ -139,7 +139,7 @@ Kirigami.ScrollablePage {
QQC2.Button { QQC2.Button {
text: i18nd("kdeconnect-app", "Reject") text: i18nd("kdeconnect-app", "Reject")
icon.name:"dialog-cancel" icon.name:"dialog-cancel"
onClicked: root.currentDevice.rejectPairing() onClicked: root.currentDevice.cancelPairing()
} }
} }
} }

View file

@ -93,7 +93,6 @@ int main(int argc, char **argv)
if (parser.isSet(QStringLiteral("a"))) { if (parser.isSet(QStringLiteral("a"))) {
available = true; available = true;
} else { } else {
blockOnReply(iface.acquireDiscoveryMode(id));
QThread::sleep(2); QThread::sleep(2);
} }
const QStringList devices = blockOnReply<QStringList>(iface.devices(available, available)); const QStringList devices = blockOnReply<QStringList>(iface.devices(available, available));
@ -115,12 +114,12 @@ int main(int argc, char **argv)
DeviceDbusInterface deviceIface(id); DeviceDbusInterface deviceIface(id);
QString statusInfo; QString statusInfo;
const bool isReachable = deviceIface.isReachable(); const bool isReachable = deviceIface.isReachable();
const bool isTrusted = deviceIface.isTrusted(); const bool isPaired = deviceIface.isPaired();
if (isReachable && isTrusted) { if (isReachable && isPaired) {
statusInfo = i18n("(paired and reachable)"); statusInfo = i18n("(paired and reachable)");
} else if (isReachable) { } else if (isReachable) {
statusInfo = i18n("(reachable)"); statusInfo = i18n("(reachable)");
} else if (isTrusted) { } else if (isPaired) {
statusInfo = i18n("(paired)"); statusInfo = i18n("(paired)");
} }
QTextStream(stdout) << "- " << deviceIface.name() << ": " << deviceIface.id() << ' ' << statusInfo << Qt::endl; QTextStream(stdout) << "- " << deviceIface.name() << ": " << deviceIface.id() << ' ' << statusInfo << Qt::endl;
@ -132,15 +131,14 @@ int main(int argc, char **argv)
QTextStream(stderr) << i18n("No devices found") << Qt::endl; QTextStream(stderr) << i18n("No devices found") << Qt::endl;
} }
blockOnReply(iface.releaseDiscoveryMode(id));
} else if (parser.isSet(QStringLiteral("shell-device-autocompletion"))) { } else if (parser.isSet(QStringLiteral("shell-device-autocompletion"))) {
// Outputs a list of reachable devices in zsh autocomplete format, with the name as description // Outputs a list of reachable devices in zsh autocomplete format, with the name as description
const QStringList devices = blockOnReply<QStringList>(iface.devices(true, false)); const QStringList devices = blockOnReply<QStringList>(iface.devices(true, false));
for (const QString &id : devices) { for (const QString &id : devices) {
DeviceDbusInterface deviceIface(id); DeviceDbusInterface deviceIface(id);
QString statusInfo; QString statusInfo;
const bool isTrusted = deviceIface.isTrusted(); const bool isPaired = deviceIface.isPaired();
if (isTrusted) { if (isPaired) {
statusInfo = i18n("(paired)"); statusInfo = i18n("(paired)");
} else { } else {
statusInfo = i18n("(unpaired)"); statusInfo = i18n("(unpaired)");
@ -239,7 +237,6 @@ int main(int argc, char **argv)
// Device doesn't exist, go into discovery mode and wait up to 30 seconds for the device to appear // Device doesn't exist, go into discovery mode and wait up to 30 seconds for the device to appear
QEventLoop wait; QEventLoop wait;
QTextStream(stderr) << i18n("waiting for device...") << Qt::endl; QTextStream(stderr) << i18n("waiting for device...") << Qt::endl;
blockOnReply(iface.acquireDiscoveryMode(id));
QObject::connect(&iface, &DaemonDbusInterface::deviceAdded, &iface, [&](const QString &deviceAddedId) { QObject::connect(&iface, &DaemonDbusInterface::deviceAdded, &iface, [&](const QString &deviceAddedId) {
if (device == deviceAddedId) { if (device == deviceAddedId) {
@ -253,16 +250,15 @@ int main(int argc, char **argv)
if (!dev.isReachable()) { if (!dev.isReachable()) {
QTextStream(stderr) << i18n("Device not found") << Qt::endl; QTextStream(stderr) << i18n("Device not found") << Qt::endl;
} else if (blockOnReply<bool>(dev.isTrusted())) { } else if (blockOnReply<bool>(dev.isPaired())) {
QTextStream(stderr) << i18n("Already paired") << Qt::endl; QTextStream(stderr) << i18n("Already paired") << Qt::endl;
} else { } else {
QTextStream(stderr) << i18n("Pair requested") << Qt::endl; QTextStream(stderr) << i18n("Pair requested") << Qt::endl;
blockOnReply(dev.requestPair()); blockOnReply(dev.requestPairing());
} }
blockOnReply(iface.releaseDiscoveryMode(id));
} else if (parser.isSet(QStringLiteral("unpair"))) { } else if (parser.isSet(QStringLiteral("unpair"))) {
DeviceDbusInterface dev(device); DeviceDbusInterface dev(device);
if (!dev.isTrusted()) { if (!dev.isPaired()) {
QTextStream(stderr) << i18n("Already not paired") << Qt::endl; QTextStream(stderr) << i18n("Already not paired") << Qt::endl;
} else { } else {
QTextStream(stderr) << i18n("Unpaired") << Qt::endl; QTextStream(stderr) << i18n("Unpaired") << Qt::endl;

View file

@ -7,7 +7,6 @@ set(backends_kdeconnect_SRCS
backends/bluetooth/connectionmultiplexer.cpp backends/bluetooth/connectionmultiplexer.cpp
backends/bluetooth/bluetoothlinkprovider.cpp backends/bluetooth/bluetoothlinkprovider.cpp
backends/bluetooth/bluetoothdevicelink.cpp backends/bluetooth/bluetoothdevicelink.cpp
backends/bluetooth/bluetoothpairinghandler.cpp
backends/bluetooth/bluetoothdownloadjob.cpp backends/bluetooth/bluetoothdownloadjob.cpp
backends/bluetooth/bluetoothuploadjob.cpp backends/bluetooth/bluetoothuploadjob.cpp

View file

@ -22,7 +22,6 @@ BluetoothDeviceLink::BluetoothDeviceLink(const QString &deviceId,
, mSocketReader(new DeviceLineReader(socket.data(), this)) , mSocketReader(new DeviceLineReader(socket.data(), this))
, mConnection(connection) , mConnection(connection)
, mChannel(socket) , mChannel(socket)
, mPairingHandler(new BluetoothPairingHandler(this))
{ {
connect(mSocketReader, &DeviceLineReader::readyRead, this, &BluetoothDeviceLink::dataReceived); connect(mSocketReader, &DeviceLineReader::readyRead, this, &BluetoothDeviceLink::dataReceived);
@ -51,21 +50,6 @@ bool BluetoothDeviceLink::sendPacket(NetworkPacket &np)
return (written != -1); return (written != -1);
} }
void BluetoothDeviceLink::userRequestsPair()
{
mPairingHandler->requestPairing();
}
void BluetoothDeviceLink::userRequestsUnpair()
{
mPairingHandler->unpair();
}
bool BluetoothDeviceLink::linkShouldBeKeptAlive()
{
return pairStatus() == Paired;
}
void BluetoothDeviceLink::dataReceived() void BluetoothDeviceLink::dataReceived()
{ {
if (mSocketReader->bytesAvailable() == 0) if (mSocketReader->bytesAvailable() == 0)
@ -78,12 +62,6 @@ void BluetoothDeviceLink::dataReceived()
NetworkPacket packet((QString())); NetworkPacket packet((QString()));
NetworkPacket::unserialize(serializedPacket, &packet); NetworkPacket::unserialize(serializedPacket, &packet);
if (packet.type() == PACKET_TYPE_PAIR) {
// TODO: Handle pair/unpair requests and forward them (to the pairing handler?)
mPairingHandler->packetReceived(packet);
return;
}
if (packet.hasPayloadTransferInfo()) { if (packet.hasPayloadTransferInfo()) {
BluetoothDownloadJob *downloadJob = new BluetoothDownloadJob(mConnection, packet.payloadTransferInfo(), this); BluetoothDownloadJob *downloadJob = new BluetoothDownloadJob(mConnection, packet.payloadTransferInfo(), this);
downloadJob->start(); downloadJob->start();

View file

@ -14,7 +14,6 @@
#include "../devicelinereader.h" #include "../devicelinereader.h"
#include "../devicelink.h" #include "../devicelink.h"
#include "bluetoothpairinghandler.h"
class ConnectionMultiplexer; class ConnectionMultiplexer;
class MultiplexChannel; class MultiplexChannel;
@ -29,10 +28,6 @@ public:
virtual QString name() override; virtual QString name() override;
bool sendPacket(NetworkPacket &np) override; bool sendPacket(NetworkPacket &np) override;
virtual void userRequestsPair() override;
virtual void userRequestsUnpair() override;
virtual bool linkShouldBeKeptAlive() override;
QSslCertificate certificate() const override; QSslCertificate certificate() const override;
private Q_SLOTS: private Q_SLOTS:
@ -42,7 +37,6 @@ private:
DeviceLineReader *mSocketReader; DeviceLineReader *mSocketReader;
ConnectionMultiplexer *mConnection; ConnectionMultiplexer *mConnection;
QSharedPointer<MultiplexChannel> mChannel; QSharedPointer<MultiplexChannel> mChannel;
BluetoothPairingHandler *mPairingHandler;
void sendMessage(const QString mMessage); void sendMessage(const QString mMessage);
}; };

View file

@ -1,137 +0,0 @@
/**
* SPDX-FileCopyrightText: 2015 Vineet Garg <grg.vineet@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "bluetoothpairinghandler.h"
#include <KLocalizedString>
#include "core_debug.h"
#include "daemon.h"
#include "kdeconnectconfig.h"
#include "networkpackettypes.h"
BluetoothPairingHandler::BluetoothPairingHandler(DeviceLink *deviceLink)
: PairingHandler(deviceLink)
, m_status(NotPaired)
{
m_pairingTimeout.setSingleShot(true);
m_pairingTimeout.setInterval(pairingTimeoutMsec());
connect(&m_pairingTimeout, &QTimer::timeout, this, &BluetoothPairingHandler::pairingTimeout);
}
void BluetoothPairingHandler::packetReceived(const NetworkPacket &np)
{
qCDebug(KDECONNECT_CORE) << "Pairing packet received!" << np.serialize();
m_pairingTimeout.stop();
bool wantsPair = np.get<bool>(QStringLiteral("pair"));
if (wantsPair) {
if (isPairRequested()) { // We started pairing
qCDebug(KDECONNECT_CORE) << "Pair answer";
setInternalPairStatus(Paired);
} else {
qCDebug(KDECONNECT_CORE) << "Pair request";
if (isPaired()) { // I'm already paired, but they think I'm not
acceptPairing();
return;
}
setInternalPairStatus(RequestedByPeer);
}
} else { // wantsPair == false
qCDebug(KDECONNECT_CORE) << "Unpair request";
setInternalPairStatus(NotPaired);
if (isPairRequested()) {
Q_EMIT pairingError(i18n("Canceled by other peer"));
}
}
}
bool BluetoothPairingHandler::requestPairing()
{
switch (m_status) {
case Paired:
Q_EMIT pairingError(i18n("%1: Already paired", deviceLink()->name()));
return false;
case Requested:
Q_EMIT pairingError(i18n("%1: Pairing already requested for this device", deviceLink()->name()));
return false;
case RequestedByPeer:
qCDebug(KDECONNECT_CORE) << deviceLink()->name() << " : Pairing already started by the other end, accepting their request.";
acceptPairing();
return false;
case NotPaired:;
}
NetworkPacket np(PACKET_TYPE_PAIR);
np.set(QStringLiteral("pair"), true);
bool success;
success = deviceLink()->sendPacket(np);
if (success) {
setInternalPairStatus(Requested);
m_pairingTimeout.start();
}
return success;
}
bool BluetoothPairingHandler::acceptPairing()
{
qCDebug(KDECONNECT_CORE) << "User accepts pairing";
m_pairingTimeout.stop(); // Just in case it is started
NetworkPacket np(PACKET_TYPE_PAIR);
np.set(QStringLiteral("pair"), true);
bool success = deviceLink()->sendPacket(np);
if (success) {
setInternalPairStatus(Paired);
}
return success;
}
void BluetoothPairingHandler::rejectPairing()
{
qCDebug(KDECONNECT_CORE) << "User rejects pairing";
NetworkPacket np(PACKET_TYPE_PAIR);
np.set(QStringLiteral("pair"), false);
deviceLink()->sendPacket(np);
setInternalPairStatus(NotPaired);
}
void BluetoothPairingHandler::unpair()
{
NetworkPacket np(PACKET_TYPE_PAIR);
np.set(QStringLiteral("pair"), false);
deviceLink()->sendPacket(np);
setInternalPairStatus(NotPaired);
}
void BluetoothPairingHandler::pairingTimeout()
{
NetworkPacket np(PACKET_TYPE_PAIR);
np.set(QStringLiteral("pair"), false);
deviceLink()->sendPacket(np);
setInternalPairStatus(NotPaired); // Will emit the change as well
Q_EMIT pairingError(i18n("Timed out"));
}
void BluetoothPairingHandler::setInternalPairStatus(BluetoothPairingHandler::InternalPairStatus status)
{
m_status = status;
if (status == Paired) {
deviceLink()->setPairStatus(DeviceLink::Paired);
} else if (status == NotPaired) {
deviceLink()->setPairStatus(DeviceLink::NotPaired);
} else if (status == RequestedByPeer) {
Q_EMIT deviceLink()->pairingRequest(this);
}
}

View file

@ -1,58 +0,0 @@
/**
* SPDX-FileCopyrightText: 2015 Vineet Garg <grg.vineet@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#ifndef KDECONNECT_BLUETOOTHPAIRINGHANDLER_H
#define KDECONNECT_BLUETOOTHPAIRINGHANDLER_H
#include "../devicelink.h"
#include "../pairinghandler.h"
#include "device.h"
#include <QTimer>
// This class is used pairing related stuff. It has direct access to links and can directly send packets
class BluetoothPairingHandler : public PairingHandler
{
public:
enum InternalPairStatus {
NotPaired,
Requested,
RequestedByPeer,
Paired,
};
BluetoothPairingHandler(DeviceLink *deviceLink);
virtual ~BluetoothPairingHandler()
{
}
void packetReceived(const NetworkPacket &np) override;
bool requestPairing() override;
bool acceptPairing() override;
void rejectPairing() override;
void unpair() override;
bool isPairRequested() const
{
return m_status == Requested;
}
bool isPaired() const
{
return m_status == Paired;
}
private Q_SLOTS:
void pairingTimeout();
protected:
void setInternalPairStatus(InternalPairStatus status);
QTimer m_pairingTimeout;
InternalPairStatus m_status;
};
#endif // KDECONNECT_BLUETOOTHPAIRINGHANDLER_H

View file

@ -12,17 +12,9 @@ DeviceLink::DeviceLink(const QString &deviceId, LinkProvider *parent)
: QObject(parent) : QObject(parent)
, m_deviceId(deviceId) , m_deviceId(deviceId)
, m_linkProvider(parent) , m_linkProvider(parent)
, m_pairStatus(NotPaired)
{ {
Q_ASSERT(!deviceId.isEmpty()); Q_ASSERT(!deviceId.isEmpty());
setProperty("deviceId", deviceId); setProperty("deviceId", deviceId);
} }
void DeviceLink::setPairStatus(DeviceLink::PairStatus status)
{
if (m_pairStatus != status) {
m_pairStatus = status;
Q_EMIT pairStatusChanged(status);
}
}

View file

@ -22,7 +22,6 @@ class DeviceLink : public QObject
Q_OBJECT Q_OBJECT
public: public:
enum PairStatus { NotPaired, Paired };
DeviceLink(const QString &deviceId, LinkProvider *parent); DeviceLink(const QString &deviceId, LinkProvider *parent);
~DeviceLink() override = default; ~DeviceLink() override = default;
@ -40,35 +39,14 @@ public:
virtual bool sendPacket(NetworkPacket &np) = 0; virtual bool sendPacket(NetworkPacket &np) = 0;
// user actions
virtual void userRequestsPair() = 0;
virtual void userRequestsUnpair() = 0;
PairStatus pairStatus() const
{
return m_pairStatus;
}
virtual void setPairStatus(PairStatus status);
// The daemon will periodically destroy unpaired links if this returns false
virtual bool linkShouldBeKeptAlive()
{
return false;
}
virtual QSslCertificate certificate() const = 0; virtual QSslCertificate certificate() const = 0;
Q_SIGNALS: Q_SIGNALS:
void pairingRequest(PairingHandler *handler);
void pairingRequestExpired(PairingHandler *handler);
void pairStatusChanged(DeviceLink::PairStatus status);
void pairingError(const QString &error);
void receivedPacket(const NetworkPacket &np); void receivedPacket(const NetworkPacket &np);
private: private:
const QString m_deviceId; const QString m_deviceId;
LinkProvider *m_linkProvider; LinkProvider *m_linkProvider;
PairStatus m_pairStatus;
}; };
#endif #endif

View file

@ -5,7 +5,6 @@ set(backends_kdeconnect_SRCS
backends/lan/server.cpp backends/lan/server.cpp
backends/lan/lanlinkprovider.cpp backends/lan/lanlinkprovider.cpp
backends/lan/landevicelink.cpp backends/lan/landevicelink.cpp
backends/lan/lanpairinghandler.cpp
backends/lan/compositeuploadjob.cpp backends/lan/compositeuploadjob.cpp
backends/lan/uploadjob.cpp backends/lan/uploadjob.cpp
backends/lan/socketlinereader.cpp backends/lan/socketlinereader.cpp

View file

@ -43,7 +43,6 @@ void LanDeviceLink::reset(QSslSocket *socket, ConnectionStarted connectionSource
m_connectionSource = connectionSource; m_connectionSource = connectionSource;
QString certString = KdeConnectConfig::instance().getDeviceProperty(deviceId(), QStringLiteral("certificate")); QString certString = KdeConnectConfig::instance().getDeviceProperty(deviceId(), QStringLiteral("certificate"));
DeviceLink::setPairStatus(certString.isEmpty() ? PairStatus::NotPaired : PairStatus::Paired);
} }
QHostAddress LanDeviceLink::hostAddress() const QHostAddress LanDeviceLink::hostAddress() const
@ -109,12 +108,6 @@ void LanDeviceLink::dataReceived()
// qCDebug(KDECONNECT_CORE) << "LanDeviceLink dataReceived" << serializedPacket; // qCDebug(KDECONNECT_CORE) << "LanDeviceLink dataReceived" << serializedPacket;
if (packet.type() == PACKET_TYPE_PAIR) {
// TODO: Handle pair/unpair requests and forward them (to the pairing handler?)
qobject_cast<LanLinkProvider *>(provider())->incomingPairPacket(this, packet);
return;
}
if (packet.hasPayloadTransferInfo()) { if (packet.hasPayloadTransferInfo()) {
// qCDebug(KDECONNECT_CORE) << "HasPayloadTransferInfo"; // qCDebug(KDECONNECT_CORE) << "HasPayloadTransferInfo";
const QVariantMap transferInfo = packet.payloadTransferInfo(); const QVariantMap transferInfo = packet.payloadTransferInfo();
@ -140,43 +133,6 @@ void LanDeviceLink::dataReceived()
} }
} }
void LanDeviceLink::userRequestsPair()
{
if (m_socketLineReader->peerCertificate().isNull()) {
Q_EMIT pairingError(i18n("This device cannot be paired because it is running an old version of KDE Connect."));
} else {
qobject_cast<LanLinkProvider *>(provider())->userRequestsPair(deviceId());
}
}
void LanDeviceLink::userRequestsUnpair()
{
qobject_cast<LanLinkProvider *>(provider())->userRequestsUnpair(deviceId());
}
void LanDeviceLink::setPairStatus(PairStatus status)
{
if (status == Paired && m_socketLineReader->peerCertificate().isNull()) {
Q_EMIT pairingError(i18n("This device cannot be paired because it is running an old version of KDE Connect."));
return;
}
DeviceLink::setPairStatus(status);
if (status == Paired) {
Q_ASSERT(KdeConnectConfig::instance().trustedDevices().contains(deviceId()));
KdeConnectConfig::instance().setDeviceProperty(deviceId(), QStringLiteral("certificate"), QString::fromLatin1(certificate().toPem()));
}
}
bool LanDeviceLink::linkShouldBeKeptAlive()
{
return true; // FIXME: Current implementation is broken, so for now we will keep links always established
// We keep the remotely initiated connections, since the remotes require them if they want to request
// pairing to us, or connections that are already paired. TODO: Keep connections in the process of pairing
// return (mConnectionSource == ConnectionStarted::Remotely || pairStatus() == Paired);
}
QSslCertificate LanDeviceLink::certificate() const QSslCertificate LanDeviceLink::certificate() const
{ {
return m_socketLineReader->peerCertificate(); return m_socketLineReader->peerCertificate();

View file

@ -33,13 +33,6 @@ public:
QString name() override; QString name() override;
bool sendPacket(NetworkPacket &np) override; bool sendPacket(NetworkPacket &np) override;
void userRequestsPair() override;
void userRequestsUnpair() override;
void setPairStatus(PairStatus status) override;
bool linkShouldBeKeptAlive() override;
QHostAddress hostAddress() const; QHostAddress hostAddress() const;
QSslCertificate certificate() const override; QSslCertificate certificate() const override;

View file

@ -32,7 +32,6 @@
#include "daemon.h" #include "daemon.h"
#include "kdeconnectconfig.h" #include "kdeconnectconfig.h"
#include "landevicelink.h" #include "landevicelink.h"
#include "lanpairinghandler.h"
#include "qtcompat_p.h" #include "qtcompat_p.h"
#define MIN_VERSION_WITH_SSL_SUPPORT 6 #define MIN_VERSION_WITH_SSL_SUPPORT 6
@ -537,10 +536,6 @@ void LanLinkProvider::deviceLinkDestroyed(QObject *destroyedDeviceLink)
if (linkIterator != m_links.end()) { if (linkIterator != m_links.end()) {
Q_ASSERT(linkIterator.value() == destroyedDeviceLink); Q_ASSERT(linkIterator.value() == destroyedDeviceLink);
m_links.erase(linkIterator); m_links.erase(linkIterator);
auto pairingHandler = m_pairingHandlers.take(id);
if (pairingHandler) {
pairingHandler->deleteLater();
}
} }
} }
@ -648,42 +643,6 @@ void LanLinkProvider::addLink(const QString &deviceId, QSslSocket *socket, Netwo
} }
connect(deviceLink, &QObject::destroyed, this, &LanLinkProvider::deviceLinkDestroyed); connect(deviceLink, &QObject::destroyed, this, &LanLinkProvider::deviceLinkDestroyed);
m_links[deviceId] = deviceLink; m_links[deviceId] = deviceLink;
if (m_pairingHandlers.contains(deviceId)) {
// We shouldn't have a pairinghandler if we didn't have a link.
// Crash if debug, recover if release (by setting the new devicelink to the old pairinghandler)
Q_ASSERT(m_pairingHandlers.contains(deviceId));
m_pairingHandlers[deviceId]->setDeviceLink(deviceLink);
}
} }
Q_EMIT onConnectionReceived(*receivedPacket, deviceLink); Q_EMIT onConnectionReceived(*receivedPacket, deviceLink);
} }
LanPairingHandler *LanLinkProvider::createPairingHandler(DeviceLink *link)
{
LanPairingHandler *ph = m_pairingHandlers.value(link->deviceId());
if (!ph) {
ph = new LanPairingHandler(link);
qCDebug(KDECONNECT_CORE) << "creating pairing handler for" << link->deviceId();
connect(ph, &LanPairingHandler::pairingError, link, &DeviceLink::pairingError);
m_pairingHandlers[link->deviceId()] = ph;
}
return ph;
}
void LanLinkProvider::userRequestsPair(const QString &deviceId)
{
LanPairingHandler *ph = createPairingHandler(m_links.value(deviceId));
ph->requestPairing();
}
void LanLinkProvider::userRequestsUnpair(const QString &deviceId)
{
LanPairingHandler *ph = createPairingHandler(m_links.value(deviceId));
ph->unpair();
}
void LanLinkProvider::incomingPairPacket(DeviceLink *deviceLink, const NetworkPacket &np)
{
LanPairingHandler *ph = createPairingHandler(deviceLink);
ph->packetReceived(np);
}

View file

@ -19,7 +19,6 @@
#include "landevicelink.h" #include "landevicelink.h"
#include "server.h" #include "server.h"
class LanPairingHandler;
class KDECONNECTCORE_EXPORT LanLinkProvider : public LinkProvider class KDECONNECTCORE_EXPORT LanLinkProvider : public LinkProvider
{ {
Q_OBJECT Q_OBJECT
@ -42,10 +41,6 @@ public:
return PRIORITY_HIGH; return PRIORITY_HIGH;
} }
void userRequestsPair(const QString &deviceId);
void userRequestsUnpair(const QString &deviceId);
void incomingPairPacket(DeviceLink *device, const NetworkPacket &np);
static void configureSslSocket(QSslSocket *socket, const QString &deviceId, bool isDeviceTrusted); static void configureSslSocket(QSslSocket *socket, const QString &deviceId, bool isDeviceTrusted);
static void configureSocket(QSslSocket *socket); static void configureSocket(QSslSocket *socket);
@ -73,8 +68,6 @@ private Q_SLOTS:
void broadcastToNetwork(); void broadcastToNetwork();
private: private:
LanPairingHandler *createPairingHandler(DeviceLink *link);
void onNetworkConfigurationChanged(const QNetworkConfiguration &config); void onNetworkConfigurationChanged(const QNetworkConfiguration &config);
void addLink(const QString &deviceId, QSslSocket *socket, NetworkPacket *receivedPacket, LanDeviceLink::ConnectionStarted connectionOrigin); void addLink(const QString &deviceId, QSslSocket *socket, NetworkPacket *receivedPacket, LanDeviceLink::ConnectionStarted connectionOrigin);
QList<QHostAddress> getBroadcastAddresses(); QList<QHostAddress> getBroadcastAddresses();
@ -88,7 +81,6 @@ private:
quint16 m_udpListenPort; quint16 m_udpListenPort;
QMap<QString, LanDeviceLink *> m_links; QMap<QString, LanDeviceLink *> m_links;
QMap<QString, LanPairingHandler *> m_pairingHandlers;
struct PendingConnect { struct PendingConnect {
NetworkPacket *np; NetworkPacket *np;

View file

@ -1,129 +0,0 @@
/**
* SPDX-FileCopyrightText: 2015 Vineet Garg <grg.vineet@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "lanpairinghandler.h"
#include <KLocalizedString>
#include "core_debug.h"
#include "daemon.h"
#include "kdeconnectconfig.h"
#include "landevicelink.h"
#include "networkpackettypes.h"
LanPairingHandler::LanPairingHandler(DeviceLink *deviceLink)
: PairingHandler(deviceLink)
, m_status(NotPaired)
{
m_pairingTimeout.setSingleShot(true);
m_pairingTimeout.setInterval(pairingTimeoutMsec());
connect(&m_pairingTimeout, &QTimer::timeout, this, &LanPairingHandler::pairingTimeout);
}
void LanPairingHandler::packetReceived(const NetworkPacket &np)
{
bool wantsPair = np.get<bool>(QStringLiteral("pair"));
if (wantsPair) {
if (isPairRequested()) { // We started pairing
qCDebug(KDECONNECT_CORE) << "Pair answer";
setInternalPairStatus(Paired);
} else {
qCDebug(KDECONNECT_CORE) << "Pair request";
if (isPaired()) { // I'm already paired, but they think I'm not
acceptPairing();
return;
}
setInternalPairStatus(RequestedByPeer);
}
} else { // wantsPair == false
qCDebug(KDECONNECT_CORE) << "Unpair request";
if (isPairRequested()) {
Q_EMIT pairingError(i18n("Canceled by other peer"));
}
setInternalPairStatus(NotPaired);
}
}
bool LanPairingHandler::requestPairing()
{
if (m_status == Paired) {
Q_EMIT pairingError(i18n("%1: Already paired", deviceLink()->name()));
return false;
}
if (m_status == RequestedByPeer) {
qCDebug(KDECONNECT_CORE) << deviceLink()->name() << ": Pairing already started by the other end, accepting their request.";
return acceptPairing();
}
NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), true}});
const bool success = deviceLink()->sendPacket(np);
if (success) {
setInternalPairStatus(Requested);
}
return success;
}
bool LanPairingHandler::acceptPairing()
{
NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), true}});
bool success = deviceLink()->sendPacket(np);
if (success) {
setInternalPairStatus(Paired);
}
return success;
}
void LanPairingHandler::rejectPairing()
{
NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), false}});
deviceLink()->sendPacket(np);
setInternalPairStatus(NotPaired);
}
void LanPairingHandler::unpair()
{
NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), false}});
deviceLink()->sendPacket(np);
setInternalPairStatus(NotPaired);
}
void LanPairingHandler::pairingTimeout()
{
NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), false}});
deviceLink()->sendPacket(np);
setInternalPairStatus(NotPaired); // Will emit the change as well
Q_EMIT pairingError(i18n("Timed out"));
}
void LanPairingHandler::setInternalPairStatus(LanPairingHandler::InternalPairStatus status)
{
if (status == Requested || status == RequestedByPeer) {
m_pairingTimeout.start();
} else {
m_pairingTimeout.stop();
}
if (m_status == RequestedByPeer && (status == NotPaired || status == Paired)) {
Q_EMIT deviceLink()->pairingRequestExpired(this);
} else if (status == RequestedByPeer) {
Q_EMIT deviceLink()->pairingRequest(this);
}
m_status = status;
if (status == Paired) {
deviceLink()->setPairStatus(DeviceLink::Paired);
} else {
deviceLink()->setPairStatus(DeviceLink::NotPaired);
}
}

View file

@ -1,61 +0,0 @@
/**
* SPDX-FileCopyrightText: 2015 Vineet Garg <grg.vineet@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#ifndef KDECONNECT_LANPAIRINGHANDLER_H
#define KDECONNECT_LANPAIRINGHANDLER_H
#include <QObject>
#include <QTimer>
#include "backends/devicelink.h"
#include "backends/pairinghandler.h"
#include "device.h"
// This class is used pairing related stuff. It has direct access to links and can directly send packets
class LanPairingHandler : public PairingHandler
{
Q_OBJECT
public:
enum InternalPairStatus {
NotPaired,
Requested,
RequestedByPeer,
Paired,
};
LanPairingHandler(DeviceLink *deviceLink);
~LanPairingHandler() override
{
}
void packetReceived(const NetworkPacket &np) override;
bool requestPairing() override;
bool acceptPairing() override;
void rejectPairing() override;
void unpair() override;
bool isPairRequested() const
{
return m_status == Requested;
}
bool isPaired() const
{
return m_status == Paired;
}
private Q_SLOTS:
void pairingTimeout();
protected:
void setInternalPairStatus(InternalPairStatus status);
QTimer m_pairingTimeout;
InternalPairStatus m_status;
};
#endif // KDECONNECT_LANPAIRINGHANDLER_H

View file

@ -21,15 +21,6 @@ public:
QString name() override; QString name() override;
bool sendPacket(NetworkPacket &np) override; bool sendPacket(NetworkPacket &np) override;
void userRequestsPair() override
{
setPairStatus(Paired);
}
void userRequestsUnpair() override
{
setPairStatus(NotPaired);
}
QSslCertificate certificate() const override QSslCertificate certificate() const override
{ {
return QSslCertificate(); return QSslCertificate();

View file

@ -6,19 +6,138 @@
#include "pairinghandler.h" #include "pairinghandler.h"
PairingHandler::PairingHandler(DeviceLink *parent) #include "core_debug.h"
#include <KLocalizedString>
PairingHandler::PairingHandler(Device *parent, PairState initialState)
: QObject(parent) : QObject(parent)
, m_deviceLink(parent) , m_device(parent)
, m_pairState(initialState)
{ {
m_pairingTimeout.setSingleShot(true);
m_pairingTimeout.setInterval(pairingTimeoutMsec);
connect(&m_pairingTimeout, &QTimer::timeout, this, &PairingHandler::pairingTimeout);
} }
void PairingHandler::setDeviceLink(DeviceLink *dl) void PairingHandler::packetReceived(const NetworkPacket &np)
{ {
setParent(dl); m_pairingTimeout.stop();
m_deviceLink = dl; bool wantsPair = np.get<bool>(QStringLiteral("pair"));
if (wantsPair) {
switch (m_pairState) {
case PairState::Requested:
pairingDone();
break;
case PairState::RequestedByPeer:
qCDebug(KDECONNECT_CORE) << "Ignoring second pairing request before the first one timed out";
break;
case PairState::Paired:
qCDebug(KDECONNECT_CORE) << "Auto-accepting pairing request from a device we already trusted";
acceptPairing();
break;
case PairState::NotPaired:
m_pairState = PairState::RequestedByPeer;
m_pairingTimeout.start();
Q_EMIT incomingPairRequest();
break;
}
} else { // wantsPair == false
qCDebug(KDECONNECT_CORE) << "Unpair request received";
switch (m_pairState) {
case PairState::NotPaired:
qCDebug(KDECONNECT_CORE) << "Ignoring unpair request for already unpaired device";
break;
case PairState::Requested: // We started pairing and got rejected
case PairState::RequestedByPeer: // They stared pairing, then cancelled
m_pairState = PairState::NotPaired;
Q_EMIT pairingFailed(i18n("Canceled by other peer"));
break;
case PairState::Paired:
m_pairState = PairState::NotPaired;
Q_EMIT unpaired();
break;
}
}
} }
DeviceLink *PairingHandler::deviceLink() const bool PairingHandler::requestPairing()
{ {
return m_deviceLink; m_pairingTimeout.stop();
if (m_pairState == PairState::Paired) {
qWarning() << m_device->name() << ": requestPairing was called on an already paired device.";
Q_EMIT pairingFailed(i18n("%1: Already paired", m_device->name()));
return false;
}
if (m_pairState == PairState::RequestedByPeer) {
qWarning() << m_device->name() << ": Pairing already started by the other end, accepting their request.";
return acceptPairing();
}
if (!m_device->isReachable()) {
Q_EMIT pairingFailed(i18n("%1: Device not reachable", m_device->name()));
return false;
}
m_pairingTimeout.stop();
m_pairState = PairState::Requested;
NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), true}});
const bool success = m_device->sendPacket(np);
if (success) {
m_pairingTimeout.start();
} else {
qWarning() << m_device->name() << ": Failed to send pair request packet.";
m_pairState = PairState::NotPaired;
Q_EMIT pairingFailed(i18n("%1: Device not reachable", m_device->name()));
}
return success;
}
bool PairingHandler::acceptPairing()
{
m_pairingTimeout.stop();
NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), true}});
const bool success = m_device->sendPacket(np);
if (success) {
pairingDone();
} else {
qWarning() << "Failed to send packet accepting pairing";
m_pairState = PairState::NotPaired;
Q_EMIT pairingFailed(i18n("Device not reachable"));
}
return success;
}
void PairingHandler::cancelPairing()
{
m_pairingTimeout.stop();
m_pairState = PairState::NotPaired;
NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), false}});
m_device->sendPacket(np);
Q_EMIT pairingFailed(i18n("Cancelled by user"));
}
void PairingHandler::unpair()
{
m_pairState = PairState::NotPaired;
NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), false}});
m_device->sendPacket(np);
Q_EMIT unpaired();
}
void PairingHandler::pairingTimeout()
{
NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), false}});
m_device->sendPacket(np);
m_pairState = PairState::NotPaired;
Q_EMIT pairingFailed(i18n("Timed out"));
}
void PairingHandler::pairingDone() {
qCDebug(KDECONNECT_CORE) << "Pairing done";
m_pairState = PairState::Paired;
Q_EMIT pairingSuccessful();
} }

View file

@ -7,48 +7,48 @@
#ifndef KDECONNECT_PAIRINGHANDLER_H #ifndef KDECONNECT_PAIRINGHANDLER_H
#define KDECONNECT_PAIRINGHANDLER_H #define KDECONNECT_PAIRINGHANDLER_H
#include "devicelink.h" #include "device.h"
#include "networkpacket.h" #include "networkpacket.h"
#include "pairstate.h"
/* #include <QTimer>
* This class separates the pairing interface for each type of link.
* Since different links can pair via different methods, like for LanLink certificate and public key should be shared,
* for Bluetooth link they should be paired via bluetooth etc.
* Each "Device" instance maintains a hash map for these pairing handlers so that there can be single pairing handler per
* per link type per device.
* Pairing handler keeps information about device, latest link, and pair status of the link
* During first pairing process, the pairing process is nearly same as old process.
* After that if any one of the link is paired, then we can say that device is paired, so new link will pair automatically
*/
class KDECONNECTCORE_EXPORT PairingHandler : public QObject class KDECONNECTCORE_EXPORT PairingHandler : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
PairingHandler(DeviceLink *parent); const static int pairingTimeoutMsec = 30 * 1000; // 30 seconds of timeout
PairingHandler(Device *parent, PairState initialState);
~PairingHandler() override = default; ~PairingHandler() override = default;
DeviceLink *deviceLink() const; void packetReceived(const NetworkPacket &np);
void setDeviceLink(DeviceLink *dl);
virtual void packetReceived(const NetworkPacket &np) = 0; PairState pairState() { return m_pairState; }
virtual void unpair() = 0;
static int pairingTimeoutMsec()
{
return 30 * 1000;
} // 30 seconds of timeout (default), subclasses that use different values should override
public Q_SLOTS: public Q_SLOTS:
virtual bool requestPairing() = 0; bool requestPairing();
virtual bool acceptPairing() = 0; bool acceptPairing();
virtual void rejectPairing() = 0; void cancelPairing();
void unpair();
Q_SIGNALS: Q_SIGNALS:
void pairingError(const QString &errorMessage); void incomingPairRequest();
void pairingFailed(const QString &errorMessage);
void pairingSuccessful();
void unpaired();
private: private:
DeviceLink *m_deviceLink; void pairingDone();
QTimer m_pairingTimeout;
Device *m_device;
PairState m_pairState;
private Q_SLOTS:
void pairingTimeout();
}; };
#endif // KDECONNECT_PAIRINGHANDLER_H #endif // KDECONNECT_PAIRINGHANDLER_H

View file

@ -40,7 +40,6 @@ struct DaemonPrivate {
// Every known device // Every known device
QMap<QString, Device *> m_devices; QMap<QString, Device *> m_devices;
QSet<QString> m_discoveryModeAcquisitions;
bool m_testMode; bool m_testMode;
}; };
@ -101,28 +100,6 @@ void Daemon::init()
qCDebug(KDECONNECT_CORE) << "Daemon started"; qCDebug(KDECONNECT_CORE) << "Daemon started";
} }
void Daemon::acquireDiscoveryMode(const QString &key)
{
bool oldState = d->m_discoveryModeAcquisitions.isEmpty();
d->m_discoveryModeAcquisitions.insert(key);
if (oldState != d->m_discoveryModeAcquisitions.isEmpty()) {
forceOnNetworkChange();
}
}
void Daemon::releaseDiscoveryMode(const QString &key)
{
bool oldState = d->m_discoveryModeAcquisitions.isEmpty();
d->m_discoveryModeAcquisitions.remove(key);
if (oldState != d->m_discoveryModeAcquisitions.isEmpty()) {
cleanDevices();
}
}
void Daemon::removeDevice(Device *device) void Daemon::removeDevice(Device *device)
{ {
d->m_devices.remove(device->id()); d->m_devices.remove(device->id());
@ -131,20 +108,6 @@ void Daemon::removeDevice(Device *device)
Q_EMIT deviceListChanged(); Q_EMIT deviceListChanged();
} }
void Daemon::cleanDevices()
{
const auto devs = d->m_devices;
for (Device *device : devs) {
if (device->isTrusted()) {
continue;
}
device->cleanUnneededLinks();
// If there are no links remaining
if (!device->isReachable()) {
removeDevice(device);
}
}
}
void Daemon::forceOnNetworkChange() void Daemon::forceOnNetworkChange()
{ {
@ -175,7 +138,7 @@ QStringList Daemon::devices(bool onlyReachable, bool onlyTrusted) const
for (Device *device : qAsConst(d->m_devices)) { for (Device *device : qAsConst(d->m_devices)) {
if (onlyReachable && !device->isReachable()) if (onlyReachable && !device->isReachable())
continue; continue;
if (onlyTrusted && !device->isTrusted()) if (onlyTrusted && !device->isPaired())
continue; continue;
ret.append(device->id()); ret.append(device->id());
} }
@ -188,7 +151,7 @@ QMap<QString, QString> Daemon::deviceNames(bool onlyReachable, bool onlyTrusted)
for (Device *device : qAsConst(d->m_devices)) { for (Device *device : qAsConst(d->m_devices)) {
if (onlyReachable && !device->isReachable()) if (onlyReachable && !device->isReachable())
continue; continue;
if (onlyTrusted && !device->isTrusted()) if (onlyTrusted && !device->isPaired())
continue; continue;
ret[device->id()] = device->name(); ret[device->id()] = device->name();
} }
@ -213,13 +176,7 @@ void Daemon::onNewDeviceLink(const NetworkPacket &identityPacket, DeviceLink *dl
} else { } else {
qCDebug(KDECONNECT_CORE) << "It is a new device" << identityPacket.get<QString>(QStringLiteral("deviceName")); qCDebug(KDECONNECT_CORE) << "It is a new device" << identityPacket.get<QString>(QStringLiteral("deviceName"));
Device *device = new Device(this, identityPacket, dl); Device *device = new Device(this, identityPacket, dl);
addDevice(device);
// we discard the connections that we created but it's not paired.
if (!isDiscoveringDevices() && !device->isTrusted() && !dl->linkShouldBeKeptAlive()) {
device->deleteLater();
} else {
addDevice(device);
}
} }
} }
@ -229,7 +186,7 @@ void Daemon::onDeviceStatusChanged()
// qCDebug(KDECONNECT_CORE) << "Device" << device->name() << "status changed. Reachable:" << device->isReachable() << ". Paired: " << device->isPaired(); // qCDebug(KDECONNECT_CORE) << "Device" << device->name() << "status changed. Reachable:" << device->isReachable() << ". Paired: " << device->isPaired();
if (!device->isReachable() && !device->isTrusted()) { if (!device->isReachable() && !device->isPaired()) {
// qCDebug(KDECONNECT_CORE) << "Destroying device" << device->name(); // qCDebug(KDECONNECT_CORE) << "Destroying device" << device->name();
removeDevice(device); removeDevice(device);
} else { } else {
@ -284,15 +241,10 @@ QList<Device *> Daemon::devicesList() const
return d->m_devices.values(); return d->m_devices.values();
} }
bool Daemon::isDiscoveringDevices() const
{
return !d->m_discoveryModeAcquisitions.isEmpty();
}
QString Daemon::deviceIdByName(const QString &name) const QString Daemon::deviceIdByName(const QString &name) const
{ {
for (Device *device : qAsConst(d->m_devices)) { for (Device *device : qAsConst(d->m_devices)) {
if (device->name() == name && device->isTrusted()) if (device->name() == name && device->isPaired())
return device->id(); return device->id();
} }
return {}; return {};
@ -302,10 +254,11 @@ void Daemon::addDevice(Device *device)
{ {
const QString id = device->id(); const QString id = device->id();
connect(device, &Device::reachableChanged, this, &Daemon::onDeviceStatusChanged); connect(device, &Device::reachableChanged, this, &Daemon::onDeviceStatusChanged);
connect(device, &Device::trustedChanged, this, &Daemon::onDeviceStatusChanged); connect(device, &Device::pairStateChanged, this, &Daemon::onDeviceStatusChanged);
connect(device, &Device::hasPairingRequestsChanged, this, &Daemon::pairingRequestsChanged); connect(device, &Device::pairStateChanged, this, &Daemon::pairingRequestsChanged);
connect(device, &Device::hasPairingRequestsChanged, this, [this, device](bool hasPairingRequests) { connect(device, &Device::pairStateChanged, this, [this, device](int pairStateAsInt) {
if (hasPairingRequests) PairState pairState = (PairState)pairStateAsInt;
if (pairState == PairState::RequestedByPeer)
askPairingConfirmation(device); askPairingConfirmation(device);
}); });
d->m_devices[id] = device; d->m_devices[id] = device;
@ -318,7 +271,7 @@ QStringList Daemon::pairingRequests() const
{ {
QStringList ret; QStringList ret;
for (Device *dev : qAsConst(d->m_devices)) { for (Device *dev : qAsConst(d->m_devices)) {
if (dev->hasPairingRequests()) if (dev->isPairRequestedByPeer())
ret += dev->id(); ret += dev->id();
} }
return ret; return ret;

View file

@ -24,7 +24,6 @@ class KDECONNECTCORE_EXPORT Daemon : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.daemon") Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.daemon")
Q_PROPERTY(bool isDiscoveringDevices READ isDiscoveringDevices)
Q_PROPERTY(QStringList pairingRequests READ pairingRequests NOTIFY pairingRequestsChanged) Q_PROPERTY(QStringList pairingRequests READ pairingRequests NOTIFY pairingRequestsChanged)
Q_PROPERTY(QStringList customDevices READ customDevices WRITE setCustomDevices NOTIFY customDevicesChanged) Q_PROPERTY(QStringList customDevices READ customDevices WRITE setCustomDevices NOTIFY customDevicesChanged)
@ -54,9 +53,6 @@ public:
Q_SCRIPTABLE QString selfId() const; Q_SCRIPTABLE QString selfId() const;
public Q_SLOTS: public Q_SLOTS:
Q_SCRIPTABLE void acquireDiscoveryMode(const QString &id);
Q_SCRIPTABLE void releaseDiscoveryMode(const QString &id);
Q_SCRIPTABLE void forceOnNetworkChange(); Q_SCRIPTABLE void forceOnNetworkChange();
/// don't try to turn into Q_PROPERTY, it doesn't work /// don't try to turn into Q_PROPERTY, it doesn't work
@ -90,9 +86,7 @@ private:
protected: protected:
void addDevice(Device *device); void addDevice(Device *device);
bool isDiscoveringDevices() const;
void removeDevice(Device *d); void removeDevice(Device *d);
void cleanDevices();
QScopedPointer<struct DaemonPrivate> d; QScopedPointer<struct DaemonPrivate> d;
}; };

View file

@ -39,6 +39,7 @@ public:
~DevicePrivate() ~DevicePrivate()
{ {
delete m_pairingHandler;
qDeleteAll(m_deviceLinks); qDeleteAll(m_deviceLinks);
m_deviceLinks.clear(); m_deviceLinks.clear();
} }
@ -54,7 +55,7 @@ public:
QMultiMap<QString, KdeConnectPlugin *> m_pluginsByIncomingCapability; QMultiMap<QString, KdeConnectPlugin *> m_pluginsByIncomingCapability;
QSet<QString> m_supportedPlugins; QSet<QString> m_supportedPlugins;
QSet<QString> m_allPlugins; QSet<QString> m_allPlugins;
QSet<PairingHandler *> m_pairRequests; PairingHandler* m_pairingHandler;
}; };
static void warn(const QString &info) static void warn(const QString &info)
@ -66,6 +67,8 @@ Device::Device(QObject *parent, const QString &id)
: QObject(parent) : QObject(parent)
, d(new Device::DevicePrivate(id)) , d(new Device::DevicePrivate(id))
{ {
d->m_pairingHandler = new PairingHandler(this, PairState::Paired);
d->m_protocolVersion = NetworkPacket::s_protocolVersion; d->m_protocolVersion = NetworkPacket::s_protocolVersion;
KdeConnectConfig::DeviceInfo info = KdeConnectConfig::instance().getTrustedDevice(d->m_deviceId); KdeConnectConfig::DeviceInfo info = KdeConnectConfig::instance().getTrustedDevice(d->m_deviceId);
@ -79,13 +82,17 @@ Device::Device(QObject *parent, const QString &id)
d->m_allPlugins = PluginLoader::instance()->getPluginList().toSet(); d->m_allPlugins = PluginLoader::instance()->getPluginList().toSet();
d->m_supportedPlugins = d->m_allPlugins; d->m_supportedPlugins = d->m_allPlugins;
connect(this, &Device::pairingError, this, &warn); connect(d->m_pairingHandler, &PairingHandler::incomingPairRequest, this, &Device::pairingHandler_incomingPairRequest);
connect(d->m_pairingHandler, &PairingHandler::pairingFailed, this, &Device::pairingHandler_pairingFailed);
connect(d->m_pairingHandler, &PairingHandler::pairingSuccessful, this, &Device::pairingHandler_pairingSuccessful);
connect(d->m_pairingHandler, &PairingHandler::unpaired, this, &Device::pairingHandler_unpaired);
} }
Device::Device(QObject *parent, const NetworkPacket &identityPacket, DeviceLink *dl) Device::Device(QObject *parent, const NetworkPacket &identityPacket, DeviceLink *dl)
: QObject(parent) : QObject(parent)
, d(new Device::DevicePrivate(identityPacket.get<QString>(QStringLiteral("deviceId")))) , d(new Device::DevicePrivate(identityPacket.get<QString>(QStringLiteral("deviceId"))))
{ {
d->m_pairingHandler = new PairingHandler(this, PairState::NotPaired);
d->m_deviceName = identityPacket.get<QString>(QStringLiteral("deviceName")); d->m_deviceName = identityPacket.get<QString>(QStringLiteral("deviceName"));
d->m_allPlugins = PluginLoader::instance()->getPluginList().toSet(); d->m_allPlugins = PluginLoader::instance()->getPluginList().toSet();
@ -94,10 +101,15 @@ Device::Device(QObject *parent, const NetworkPacket &identityPacket, DeviceLink
// Register in bus // Register in bus
QDBusConnection::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAdaptors); QDBusConnection::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAdaptors);
connect(this, &Device::pairingError, this, &warn); connect(this, &Device::pairingFailed, this, &warn);
connect(this, &Device::reachableChanged, this, &Device::statusIconNameChanged); connect(this, &Device::reachableChanged, this, &Device::statusIconNameChanged);
connect(this, &Device::trustedChanged, this, &Device::statusIconNameChanged); connect(this, &Device::pairStateChanged, this, &Device::statusIconNameChanged);
connect(d->m_pairingHandler, &PairingHandler::incomingPairRequest, this, &Device::pairingHandler_incomingPairRequest);
connect(d->m_pairingHandler, &PairingHandler::pairingFailed, this, &Device::pairingHandler_pairingFailed);
connect(d->m_pairingHandler, &PairingHandler::pairingSuccessful, this, &Device::pairingHandler_pairingSuccessful);
connect(d->m_pairingHandler, &PairingHandler::unpaired, this, &Device::pairingHandler_unpaired);
} }
Device::~Device() Device::~Device()
@ -150,7 +162,7 @@ void Device::reloadPlugins()
QHash<QString, KdeConnectPlugin *> newPluginMap, oldPluginMap = d->m_plugins; QHash<QString, KdeConnectPlugin *> newPluginMap, oldPluginMap = d->m_plugins;
QMultiMap<QString, KdeConnectPlugin *> newPluginsByIncomingCapability; QMultiMap<QString, KdeConnectPlugin *> newPluginsByIncomingCapability;
if (isTrusted() && isReachable()) { // Do not load any plugin for unpaired devices, nor useless loading them for unreachable devices if (isPaired() && isReachable()) { // Do not load any plugin for unpaired devices, nor useless loading them for unreachable devices
PluginLoader *loader = PluginLoader::instance(); PluginLoader *loader = PluginLoader::instance();
@ -209,51 +221,60 @@ QString Device::pluginsConfigFile() const
return KdeConnectConfig::instance().deviceConfigDir(id()).absoluteFilePath(QStringLiteral("config")); return KdeConnectConfig::instance().deviceConfigDir(id()).absoluteFilePath(QStringLiteral("config"));
} }
void Device::requestPair() void Device::requestPairing()
{ {
if (isTrusted()) { qCDebug(KDECONNECT_CORE) << "Request pairing";
Q_EMIT pairingError(i18n("Already paired")); d->m_pairingHandler->requestPairing();
return; Q_EMIT pairStateChanged(pairStateAsInt());
}
if (!isReachable()) {
Q_EMIT pairingError(i18n("Device not reachable"));
return;
}
for (DeviceLink *dl : qAsConst(d->m_deviceLinks)) {
dl->userRequestsPair();
}
} }
void Device::unpair() void Device::unpair()
{ {
for (DeviceLink *dl : qAsConst(d->m_deviceLinks)) { qCDebug(KDECONNECT_CORE) << "Request unpairing";
dl->userRequestsUnpair(); d->m_pairingHandler->unpair();
}
KdeConnectConfig::instance().removeTrustedDevice(id());
Q_EMIT trustedChanged(false);
} }
void Device::pairStatusChanged(DeviceLink::PairStatus status) void Device::acceptPairing()
{ {
if (status == DeviceLink::NotPaired) { qCDebug(KDECONNECT_CORE) << "Accept pairing";
KdeConnectConfig::instance().removeTrustedDevice(id()); d->m_pairingHandler->acceptPairing();
}
for (DeviceLink *dl : qAsConst(d->m_deviceLinks)) { void Device::cancelPairing()
if (dl != sender()) { {
dl->setPairStatus(DeviceLink::NotPaired); qCDebug(KDECONNECT_CORE) << "Cancel pairing";
} d->m_pairingHandler->cancelPairing();
} }
} else {
KdeConnectConfig::instance().addTrustedDevice(id(), name(), type());
}
void Device::pairingHandler_incomingPairRequest()
{
Q_ASSERT(d->m_pairingHandler->pairState() == PairState::RequestedByPeer);
Q_EMIT pairStateChanged(pairStateAsInt());
}
void Device::pairingHandler_pairingSuccessful()
{
Q_ASSERT(d->m_pairingHandler->pairState() == PairState::Paired);
KdeConnectConfig::instance().addTrustedDevice(id(), name(), type());
KdeConnectConfig::instance().setDeviceProperty(d->m_deviceId, QStringLiteral("certificate"), QString::fromLatin1(certificate().toPem()));
reloadPlugins(); // Will load/unload plugins reloadPlugins(); // Will load/unload plugins
Q_EMIT pairStateChanged(pairStateAsInt());
}
bool isTrusted = (status == DeviceLink::Paired); void Device::pairingHandler_pairingFailed(const QString &errorMessage)
Q_EMIT trustedChanged(isTrusted); {
Q_ASSERT(isTrusted == this->isTrusted()); Q_ASSERT(d->m_pairingHandler->pairState() == PairState::NotPaired);
Q_EMIT pairingFailed(errorMessage);
Q_EMIT pairStateChanged(pairStateAsInt());
}
void Device::pairingHandler_unpaired()
{
Q_ASSERT(d->m_pairingHandler->pairState() == PairState::NotPaired);
qCDebug(KDECONNECT_CORE) << "Unpaired";
KdeConnectConfig::instance().removeTrustedDevice(id());
reloadPlugins(); // Will load/unload plugins
Q_EMIT pairStateChanged(pairStateAsInt());
} }
static bool lessThan(DeviceLink *p1, DeviceLink *p2) static bool lessThan(DeviceLink *p1, DeviceLink *p2)
@ -306,56 +327,6 @@ void Device::addLink(const NetworkPacket &identityPacket, DeviceLink *link)
if (d->m_deviceLinks.size() == 1) { if (d->m_deviceLinks.size() == 1) {
Q_EMIT reachableChanged(true); Q_EMIT reachableChanged(true);
} }
connect(link, &DeviceLink::pairStatusChanged, this, &Device::pairStatusChanged);
connect(link, &DeviceLink::pairingRequest, this, &Device::addPairingRequest);
connect(link, &DeviceLink::pairingRequestExpired, this, &Device::removePairingRequest);
connect(link, &DeviceLink::pairingError, this, &Device::pairingError);
}
void Device::addPairingRequest(PairingHandler *handler)
{
const bool wasEmpty = d->m_pairRequests.isEmpty();
d->m_pairRequests.insert(handler);
if (wasEmpty != d->m_pairRequests.isEmpty())
Q_EMIT hasPairingRequestsChanged(!d->m_pairRequests.isEmpty());
}
void Device::removePairingRequest(PairingHandler *handler)
{
const bool wasEmpty = d->m_pairRequests.isEmpty();
d->m_pairRequests.remove(handler);
if (wasEmpty != d->m_pairRequests.isEmpty())
Q_EMIT hasPairingRequestsChanged(!d->m_pairRequests.isEmpty());
}
bool Device::hasPairingRequests() const
{
return !d->m_pairRequests.isEmpty();
}
void Device::acceptPairing()
{
if (d->m_pairRequests.isEmpty())
qWarning() << "no pair requests to accept!";
// copying because the pairing handler will be removed upon accept
const auto prCopy = d->m_pairRequests;
for (auto ph : prCopy)
ph->acceptPairing();
}
void Device::rejectPairing()
{
if (d->m_pairRequests.isEmpty())
qWarning() << "no pair requests to reject!";
// copying because the pairing handler will be removed upon reject
const auto prCopy = d->m_pairRequests;
for (auto ph : prCopy)
ph->rejectPairing();
} }
void Device::linkDestroyed(QObject *o) void Device::linkDestroyed(QObject *o)
@ -377,8 +348,7 @@ void Device::removeLink(DeviceLink *link)
bool Device::sendPacket(NetworkPacket &np) bool Device::sendPacket(NetworkPacket &np)
{ {
Q_ASSERT(np.type() != PACKET_TYPE_PAIR); Q_ASSERT(isPaired() || np.type() == PACKET_TYPE_PAIR);
Q_ASSERT(isTrusted());
// Maybe we could block here any packet that is not an identity or a pairing packet to prevent sending non encrypted data // Maybe we could block here any packet that is not an identity or a pairing packet to prevent sending non encrypted data
for (DeviceLink *dl : qAsConst(d->m_deviceLinks)) { for (DeviceLink *dl : qAsConst(d->m_deviceLinks)) {
@ -391,8 +361,9 @@ bool Device::sendPacket(NetworkPacket &np)
void Device::privateReceivedPacket(const NetworkPacket &np) void Device::privateReceivedPacket(const NetworkPacket &np)
{ {
Q_ASSERT(np.type() != PACKET_TYPE_PAIR); if (np.type() == PACKET_TYPE_PAIR) {
if (isTrusted()) { d->m_pairingHandler->packetReceived(np);
} else if (isPaired()) {
const QList<KdeConnectPlugin *> plugins = d->m_pluginsByIncomingCapability.values(np.type()); const QList<KdeConnectPlugin *> plugins = d->m_pluginsByIncomingCapability.values(np.type());
if (plugins.isEmpty()) { if (plugins.isEmpty()) {
qWarning() << "discarding unsupported packet" << np.type() << "for" << name(); qWarning() << "discarding unsupported packet" << np.type() << "for" << name();
@ -406,11 +377,33 @@ void Device::privateReceivedPacket(const NetworkPacket &np)
} }
} }
bool Device::isTrusted() const PairState Device::pairState() const
{ {
return KdeConnectConfig::instance().trustedDevices().contains(id()); return d->m_pairingHandler->pairState();
} }
int Device::pairStateAsInt() const
{
return (int)pairState();
}
bool Device::isPaired() const
{
return d->m_pairingHandler->pairState() == PairState::Paired;
}
bool Device::isPairRequested() const
{
return d->m_pairingHandler->pairState() == PairState::Requested;
}
bool Device::isPairRequestedByPeer() const
{
return d->m_pairingHandler->pairState() == PairState::RequestedByPeer;
}
QStringList Device::availableLinks() const QStringList Device::availableLinks() const
{ {
QStringList sl; QStringList sl;
@ -421,22 +414,6 @@ QStringList Device::availableLinks() const
return sl; return sl;
} }
void Device::cleanUnneededLinks()
{
if (isTrusted()) {
return;
}
for (int i = 0; i < d->m_deviceLinks.size();) {
DeviceLink *dl = d->m_deviceLinks[i];
if (!dl->linkShouldBeKeptAlive()) {
dl->deleteLater();
d->m_deviceLinks.remove(i);
} else {
i++;
}
}
}
QHostAddress Device::getLocalIpAddress() const QHostAddress Device::getLocalIpAddress() const
{ {
for (DeviceLink *dl : qAsConst(d->m_deviceLinks)) { for (DeviceLink *dl : qAsConst(d->m_deviceLinks)) {
@ -480,7 +457,7 @@ QString Device::type2str(Device::DeviceType deviceType)
QString Device::statusIconName() const QString Device::statusIconName() const
{ {
return iconForStatus(isReachable(), isTrusted()); return iconForStatus(isReachable(), isPaired());
} }
QString Device::iconName() const QString Device::iconName() const

View file

@ -13,6 +13,7 @@
#include "backends/devicelink.h" #include "backends/devicelink.h"
#include "networkpacket.h" #include "networkpacket.h"
#include "pairstate.h"
class DeviceLink; class DeviceLink;
class KdeConnectPlugin; class KdeConnectPlugin;
@ -26,9 +27,11 @@ class KDECONNECTCORE_EXPORT Device : public QObject
Q_PROPERTY(QString iconName READ iconName CONSTANT) Q_PROPERTY(QString iconName READ iconName CONSTANT)
Q_PROPERTY(QString statusIconName READ statusIconName NOTIFY statusIconNameChanged) Q_PROPERTY(QString statusIconName READ statusIconName NOTIFY statusIconNameChanged)
Q_PROPERTY(bool isReachable READ isReachable NOTIFY reachableChanged) Q_PROPERTY(bool isReachable READ isReachable NOTIFY reachableChanged)
Q_PROPERTY(bool isTrusted READ isTrusted NOTIFY trustedChanged) Q_PROPERTY(bool isPaired READ isPaired NOTIFY pairStateChanged)
Q_PROPERTY(bool isPairRequested READ isPairRequested NOTIFY pairStateChanged)
Q_PROPERTY(bool isPairRequestedByPeer READ isPairRequestedByPeer NOTIFY pairStateChanged)
Q_PROPERTY(int pairState READ pairStateAsInt NOTIFY pairStateChanged)
Q_PROPERTY(QStringList supportedPlugins READ supportedPlugins NOTIFY pluginsChanged) Q_PROPERTY(QStringList supportedPlugins READ supportedPlugins NOTIFY pluginsChanged)
Q_PROPERTY(bool hasPairingRequests READ hasPairingRequests NOTIFY hasPairingRequestsChanged)
public: public:
enum DeviceType { enum DeviceType {
@ -72,7 +75,11 @@ public:
void addLink(const NetworkPacket &identityPacket, DeviceLink *); void addLink(const NetworkPacket &identityPacket, DeviceLink *);
void removeLink(DeviceLink *); void removeLink(DeviceLink *);
Q_SCRIPTABLE bool isTrusted() const; PairState pairState() const;
Q_SCRIPTABLE int pairStateAsInt() const; // Hack because qdbus doesn't like enums
Q_SCRIPTABLE bool isPaired() const;
Q_SCRIPTABLE bool isPairRequested() const;
Q_SCRIPTABLE bool isPairRequestedByPeer() const;
Q_SCRIPTABLE QStringList availableLinks() const; Q_SCRIPTABLE QStringList availableLinks() const;
virtual bool isReachable() const; virtual bool isReachable() const;
@ -86,8 +93,6 @@ public:
Q_SCRIPTABLE void setPluginEnabled(const QString &pluginName, bool enabled); Q_SCRIPTABLE void setPluginEnabled(const QString &pluginName, bool enabled);
Q_SCRIPTABLE bool isPluginEnabled(const QString &pluginName) const; Q_SCRIPTABLE bool isPluginEnabled(const QString &pluginName) const;
void cleanUnneededLinks();
int protocolVersion(); int protocolVersion();
QStringList supportedPlugins() const; QStringList supportedPlugins() const;
@ -100,33 +105,32 @@ public Q_SLOTS:
// Dbus operations // Dbus operations
public Q_SLOTS: public Q_SLOTS:
Q_SCRIPTABLE void requestPair(); // to all links Q_SCRIPTABLE void requestPairing();
Q_SCRIPTABLE void unpair(); // from all links Q_SCRIPTABLE void unpair();
Q_SCRIPTABLE void reloadPlugins(); // from kconf Q_SCRIPTABLE void reloadPlugins(); // from kconf
Q_SCRIPTABLE void acceptPairing(); Q_SCRIPTABLE void acceptPairing();
Q_SCRIPTABLE void rejectPairing(); Q_SCRIPTABLE void cancelPairing();
Q_SCRIPTABLE bool hasPairingRequests() const;
Q_SCRIPTABLE QString pluginIconName(const QString &pluginName); Q_SCRIPTABLE QString pluginIconName(const QString &pluginName);
private Q_SLOTS: private Q_SLOTS:
void privateReceivedPacket(const NetworkPacket &np); void privateReceivedPacket(const NetworkPacket &np);
void linkDestroyed(QObject *o); void linkDestroyed(QObject *o);
void pairStatusChanged(DeviceLink::PairStatus current);
void addPairingRequest(PairingHandler *handler); void pairingHandler_incomingPairRequest();
void removePairingRequest(PairingHandler *handler); void pairingHandler_pairingFailed(const QString &errorMessage);
void pairingHandler_pairingSuccessful();
void pairingHandler_unpaired();
Q_SIGNALS: Q_SIGNALS:
Q_SCRIPTABLE void pluginsChanged(); Q_SCRIPTABLE void pluginsChanged();
Q_SCRIPTABLE void reachableChanged(bool reachable); Q_SCRIPTABLE void reachableChanged(bool reachable);
Q_SCRIPTABLE void trustedChanged(bool trusted); Q_SCRIPTABLE void pairStateChanged(int pairState); // Hack because qdbus doesn't like enums
Q_SCRIPTABLE void pairingError(const QString &error); Q_SCRIPTABLE void pairingFailed(const QString &error);
Q_SCRIPTABLE void nameChanged(const QString &name); Q_SCRIPTABLE void nameChanged(const QString &name);
Q_SCRIPTABLE void typeChanged(const QString &type); Q_SCRIPTABLE void typeChanged(const QString &type);
Q_SCRIPTABLE void statusIconNameChanged(); Q_SCRIPTABLE void statusIconNameChanged();
Q_SCRIPTABLE void hasPairingRequestsChanged(bool hasPairingRequests);
private: // Methods private: // Methods
static DeviceType str2type(const QString &deviceType); static DeviceType str2type(const QString &deviceType);
static QString type2str(DeviceType deviceType); static QString type2str(DeviceType deviceType);

18
core/pairstate.h Normal file
View file

@ -0,0 +1,18 @@
/**
* SPDX-FileCopyrightText: 2023 Albert Vaca <albertvaka@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#ifndef PAIR_STATE_H
#define PAIR_STATE_H
enum class PairState {
NotPaired,
Requested,
RequestedByPeer,
Paired,
};
#endif

View file

@ -44,7 +44,7 @@ public:
void askPairingConfirmation(Device *device) override void askPairingConfirmation(Device *device) override
{ {
KNotification *notification = new KNotification(QStringLiteral("pairingRequest"), KNotification::NotificationFlag::Persistent); KNotification *notification = new KNotification(QStringLiteral("pairingRequest"), KNotification::NotificationFlag::Persistent);
QTimer::singleShot(PairingHandler::pairingTimeoutMsec(), notification, &KNotification::close); QTimer::singleShot(PairingHandler::pairingTimeoutMsec, notification, &KNotification::close);
notification->setIconName(QStringLiteral("dialog-information")); notification->setIconName(QStringLiteral("dialog-information"));
notification->setComponentName(QStringLiteral("kdeconnect")); notification->setComponentName(QStringLiteral("kdeconnect"));
notification->setTitle(QStringLiteral("KDE Connect")); notification->setTitle(QStringLiteral("KDE Connect"));
@ -53,7 +53,7 @@ public:
notification->setDefaultAction(i18n("Open")); notification->setDefaultAction(i18n("Open"));
notification->setActions(QStringList() << i18n("Accept") << i18n("Reject") << i18n("View key")); notification->setActions(QStringList() << i18n("Accept") << i18n("Reject") << i18n("View key"));
connect(notification, &KNotification::action1Activated, device, &Device::acceptPairing); connect(notification, &KNotification::action1Activated, device, &Device::acceptPairing);
connect(notification, &KNotification::action2Activated, device, &Device::rejectPairing); connect(notification, &KNotification::action2Activated, device, &Device::cancelPairing);
QString deviceId = device->id(); QString deviceId = device->id();
auto openSettings = [deviceId, notification] { auto openSettings = [deviceId, notification] {
OpenConfig oc; OpenConfig oc;

View file

@ -105,7 +105,7 @@ int main(int argc, char **argv)
DeviceDbusInterface *dev = new DeviceDbusInterface(req, menu); DeviceDbusInterface *dev = new DeviceDbusInterface(req, menu);
auto pairMenu = menu->addMenu(dev->name()); auto pairMenu = menu->addMenu(dev->name());
pairMenu->addAction(i18n("Pair"), dev, &DeviceDbusInterface::acceptPairing); pairMenu->addAction(i18n("Pair"), dev, &DeviceDbusInterface::acceptPairing);
pairMenu->addAction(i18n("Reject"), dev, &DeviceDbusInterface::rejectPairing); pairMenu->addAction(i18n("Reject"), dev, &DeviceDbusInterface::cancelPairing);
} }
} }
// Add quit menu // Add quit menu

View file

@ -39,10 +39,9 @@ DeviceDbusInterface::DeviceDbusInterface(const QString &id, QObject *parent)
parent) parent)
, m_id(id) , m_id(id)
{ {
connect(this, &OrgKdeKdeconnectDeviceInterface::trustedChanged, this, &DeviceDbusInterface::trustedChangedProxy); connect(this, &OrgKdeKdeconnectDeviceInterface::pairStateChanged, this, &DeviceDbusInterface::pairStateChangedProxy);
connect(this, &OrgKdeKdeconnectDeviceInterface::reachableChanged, this, &DeviceDbusInterface::reachableChangedProxy); connect(this, &OrgKdeKdeconnectDeviceInterface::reachableChanged, this, &DeviceDbusInterface::reachableChangedProxy);
connect(this, &OrgKdeKdeconnectDeviceInterface::nameChanged, this, &DeviceDbusInterface::nameChangedProxy); connect(this, &OrgKdeKdeconnectDeviceInterface::nameChanged, this, &DeviceDbusInterface::nameChangedProxy);
connect(this, &OrgKdeKdeconnectDeviceInterface::hasPairingRequestsChanged, this, &DeviceDbusInterface::hasPairingRequestsChangedProxy);
} }
DeviceDbusInterface::~DeviceDbusInterface() DeviceDbusInterface::~DeviceDbusInterface()

View file

@ -56,9 +56,11 @@ class KDECONNECTINTERFACES_EXPORT DeviceDbusInterface : public OrgKdeKdeconnectD
// TODO: Workaround because OrgKdeKdeconnectDeviceInterface is not generating // TODO: Workaround because OrgKdeKdeconnectDeviceInterface is not generating
// the signals for the properties // the signals for the properties
Q_PROPERTY(bool isReachable READ isReachable NOTIFY reachableChangedProxy) Q_PROPERTY(bool isReachable READ isReachable NOTIFY reachableChangedProxy)
Q_PROPERTY(bool isTrusted READ isTrusted NOTIFY trustedChangedProxy) Q_PROPERTY(bool isPaired READ isPaired NOTIFY pairStateChangedProxy)
Q_PROPERTY(bool isPairRequested READ isPairRequested NOTIFY pairStateChangedProxy)
Q_PROPERTY(bool isPairRequestedByPeer READ isPairRequestedByPeer NOTIFY pairStateChangedProxy)
Q_PROPERTY(int pairState READ pairState NOTIFY pairStateChangedProxy)
Q_PROPERTY(QString name READ name NOTIFY nameChangedProxy) Q_PROPERTY(QString name READ name NOTIFY nameChangedProxy)
Q_PROPERTY(bool hasPairingRequests READ hasPairingRequests NOTIFY hasPairingRequestsChangedProxy)
public: public:
explicit DeviceDbusInterface(const QString &deviceId, QObject *parent = nullptr); explicit DeviceDbusInterface(const QString &deviceId, QObject *parent = nullptr);
@ -69,9 +71,8 @@ public:
Q_SIGNALS: Q_SIGNALS:
void nameChangedProxy(const QString &name); void nameChangedProxy(const QString &name);
void trustedChangedProxy(bool paired); void pairStateChangedProxy(int pairState);
void reachableChangedProxy(bool reachable); void reachableChangedProxy(bool reachable);
void hasPairingRequestsChangedProxy(bool);
private: private:
const QString m_id; const QString m_id;

View file

@ -43,7 +43,6 @@ DevicesModel::DevicesModel(QObject *parent)
connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, &DevicesModel::refreshDeviceList); connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, &DevicesModel::refreshDeviceList);
connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &DevicesModel::clearDevices); connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &DevicesModel::clearDevices);
// refresh the view, acquireDiscoveryMode if necessary
setDisplayFilter(NoFilter); setDisplayFilter(NoFilter);
} }
@ -60,7 +59,6 @@ QHash<int, QByteArray> DevicesModel::roleNames() const
DevicesModel::~DevicesModel() DevicesModel::~DevicesModel()
{ {
m_dbusInterface->releaseDiscoveryMode(*s_keyId);
} }
int DevicesModel::rowForDevice(const QString &id) const int DevicesModel::rowForDevice(const QString &id) const
@ -140,12 +138,6 @@ void DevicesModel::setDisplayFilter(int flags)
{ {
m_displayFilter = (StatusFilterFlag)flags; m_displayFilter = (StatusFilterFlag)flags;
const bool reachableNeeded = (m_displayFilter & StatusFilterFlag::Reachable);
if (reachableNeeded)
m_dbusInterface->acquireDiscoveryMode(*s_keyId);
else
m_dbusInterface->releaseDiscoveryMode(*s_keyId);
refreshDeviceList(); refreshDeviceList();
} }
@ -239,7 +231,7 @@ QVariant DevicesModel::data(const QModelIndex &index, int role) const
case NameModelRole: case NameModelRole:
return device->name(); return device->name();
case Qt::ToolTipRole: { case Qt::ToolTipRole: {
bool trusted = device->isTrusted(); bool trusted = device->isPaired();
bool reachable = device->isReachable(); bool reachable = device->isReachable();
QString status = reachable ? (trusted ? i18n("Device trusted and connected") : i18n("Device not trusted")) : i18n("Device disconnected"); QString status = reachable ? (trusted ? i18n("Device trusted and connected") : i18n("Device not trusted")) : i18n("Device disconnected");
return status; return status;
@ -249,7 +241,7 @@ QVariant DevicesModel::data(const QModelIndex &index, int role) const
if (device->isReachable()) { if (device->isReachable()) {
status |= StatusFilterFlag::Reachable; status |= StatusFilterFlag::Reachable;
} }
if (device->isTrusted()) { if (device->isPaired()) {
status |= StatusFilterFlag::Paired; status |= StatusFilterFlag::Paired;
} }
return status; return status;
@ -287,5 +279,5 @@ bool DevicesModel::passesFilter(DeviceDbusInterface *dev) const
bool onlyPaired = (m_displayFilter & StatusFilterFlag::Paired); bool onlyPaired = (m_displayFilter & StatusFilterFlag::Paired);
bool onlyReachable = (m_displayFilter & StatusFilterFlag::Reachable); bool onlyReachable = (m_displayFilter & StatusFilterFlag::Reachable);
return !((onlyReachable && !dev->isReachable()) || (onlyPaired && !dev->isTrusted())); return !((onlyReachable && !dev->isReachable()) || (onlyPaired && !dev->isPaired()));
} }

View file

@ -84,8 +84,9 @@ KdeConnectKcm::KdeConnectKcm(QWidget *parent, const QVariantList &args)
connect(devicesModel, &QAbstractItemModel::dataChanged, this, &KdeConnectKcm::resetSelection); connect(devicesModel, &QAbstractItemModel::dataChanged, this, &KdeConnectKcm::resetSelection);
connect(kcmUi->deviceList->selectionModel(), &QItemSelectionModel::currentChanged, this, &KdeConnectKcm::deviceSelected); connect(kcmUi->deviceList->selectionModel(), &QItemSelectionModel::currentChanged, this, &KdeConnectKcm::deviceSelected);
connect(kcmUi->accept_button, &QAbstractButton::clicked, this, &KdeConnectKcm::acceptPairing); connect(kcmUi->accept_button, &QAbstractButton::clicked, this, &KdeConnectKcm::acceptPairing);
connect(kcmUi->reject_button, &QAbstractButton::clicked, this, &KdeConnectKcm::rejectPairing); connect(kcmUi->reject_button, &QAbstractButton::clicked, this, &KdeConnectKcm::cancelPairing);
connect(kcmUi->pair_button, &QAbstractButton::clicked, this, &KdeConnectKcm::requestPair); connect(kcmUi->cancel_button, &QAbstractButton::clicked, this, &KdeConnectKcm::cancelPairing);
connect(kcmUi->pair_button, &QAbstractButton::clicked, this, &KdeConnectKcm::requestPairing);
connect(kcmUi->unpair_button, &QAbstractButton::clicked, this, &KdeConnectKcm::unpair); connect(kcmUi->unpair_button, &QAbstractButton::clicked, this, &KdeConnectKcm::unpair);
connect(kcmUi->ping_button, &QAbstractButton::clicked, this, &KdeConnectKcm::sendPing); connect(kcmUi->ping_button, &QAbstractButton::clicked, this, &KdeConnectKcm::sendPing);
connect(kcmUi->refresh_button, &QAbstractButton::clicked, this, &KdeConnectKcm::refresh); connect(kcmUi->refresh_button, &QAbstractButton::clicked, this, &KdeConnectKcm::refresh);
@ -93,8 +94,6 @@ KdeConnectKcm::KdeConnectKcm(QWidget *parent, const QVariantList &args)
connect(kcmUi->renameDone_button, &QAbstractButton::clicked, this, &KdeConnectKcm::renameDone); connect(kcmUi->renameDone_button, &QAbstractButton::clicked, this, &KdeConnectKcm::renameDone);
connect(kcmUi->renameShow_button, &QAbstractButton::clicked, this, &KdeConnectKcm::renameShow); connect(kcmUi->renameShow_button, &QAbstractButton::clicked, this, &KdeConnectKcm::renameShow);
daemon->acquireDiscoveryMode(createId());
#if KCMUTILS_VERSION >= QT_VERSION_CHECK(5, 45, 0) #if KCMUTILS_VERSION >= QT_VERSION_CHECK(5, 45, 0)
if (!args.isEmpty() && args.first().type() == QVariant::String) { if (!args.isEmpty() && args.first().type() == QVariant::String) {
@ -147,13 +146,11 @@ void KdeConnectKcm::setRenameMode(bool b)
KdeConnectKcm::~KdeConnectKcm() KdeConnectKcm::~KdeConnectKcm()
{ {
daemon->releaseDiscoveryMode(createId());
delete kcmUi; delete kcmUi;
} }
void KdeConnectKcm::refresh() void KdeConnectKcm::refresh()
{ {
daemon->acquireDiscoveryMode(createId());
daemon->forceOnNetworkChange(); daemon->forceOnNetworkChange();
} }
@ -191,23 +188,8 @@ void KdeConnectKcm::deviceSelected(const QModelIndex &current)
resetDeviceView(); resetDeviceView();
connect(currentDevice, SIGNAL(pluginsChanged()), this, SLOT(resetCurrentDevice())); connect(currentDevice, SIGNAL(pluginsChanged()), this, SLOT(resetCurrentDevice()));
connect(currentDevice, SIGNAL(trustedChanged(bool)), this, SLOT(trustedChanged(bool))); connect(currentDevice, SIGNAL(pairingFailed(QString)), this, SLOT(pairingFailed(QString)));
connect(currentDevice, SIGNAL(pairingError(QString)), this, SLOT(pairingFailed(QString))); connect(currentDevice, &DeviceDbusInterface::pairStateChangedProxy, this, &KdeConnectKcm::setCurrentDevicePairState);
connect(currentDevice, &DeviceDbusInterface::hasPairingRequestsChangedProxy, this, &KdeConnectKcm::currentDevicePairingChanged);
}
void KdeConnectKcm::currentDevicePairingChanged(bool pairing)
{
if (pairing) {
setCurrentDeviceTrusted(RequestedByPeer);
} else {
setWhenAvailable(
currentDevice->isTrusted(),
[this](bool trusted) {
setCurrentDeviceTrusted(trusted ? Trusted : NotTrusted);
},
this);
}
} }
void KdeConnectKcm::resetCurrentDevice() void KdeConnectKcm::resetCurrentDevice()
@ -225,17 +207,9 @@ void KdeConnectKcm::resetDeviceView()
kcmUi->name_label->setText(currentDevice->name()); kcmUi->name_label->setText(currentDevice->name());
setWhenAvailable( setWhenAvailable(
currentDevice->isTrusted(), currentDevice->pairStateAsInt(),
[this](bool trusted) { [this](int pairStateAsInt) {
if (trusted) setCurrentDevicePairState(pairStateAsInt);
setCurrentDeviceTrusted(Trusted);
else
setWhenAvailable(
currentDevice->hasPairingRequests(),
[this](bool haspr) {
setCurrentDeviceTrusted(haspr ? RequestedByPeer : NotTrusted);
},
this);
}, },
this); this);
@ -257,7 +231,7 @@ void KdeConnectKcm::resetDeviceView()
connect(kcmUi->pluginSelector, &KPluginWidget::changed, this, &KdeConnectKcm::pluginsConfigChanged); connect(kcmUi->pluginSelector, &KPluginWidget::changed, this, &KdeConnectKcm::pluginsConfigChanged);
} }
void KdeConnectKcm::requestPair() void KdeConnectKcm::requestPairing()
{ {
if (!currentDevice) { if (!currentDevice) {
return; return;
@ -265,9 +239,7 @@ void KdeConnectKcm::requestPair()
kcmUi->messages->hide(); kcmUi->messages->hide();
setCurrentDeviceTrusted(Requested); currentDevice->requestPairing();
currentDevice->requestPair();
} }
void KdeConnectKcm::unpair() void KdeConnectKcm::unpair()
@ -276,7 +248,6 @@ void KdeConnectKcm::unpair()
return; return;
} }
setCurrentDeviceTrusted(NotTrusted);
currentDevice->unpair(); currentDevice->unpair();
} }
@ -289,13 +260,13 @@ void KdeConnectKcm::acceptPairing()
currentDevice->acceptPairing(); currentDevice->acceptPairing();
} }
void KdeConnectKcm::rejectPairing() void KdeConnectKcm::cancelPairing()
{ {
if (!currentDevice) { if (!currentDevice) {
return; return;
} }
currentDevice->rejectPairing(); currentDevice->cancelPairing();
} }
void KdeConnectKcm::pairingFailed(const QString &error) void KdeConnectKcm::pairingFailed(const QString &error)
@ -303,38 +274,32 @@ void KdeConnectKcm::pairingFailed(const QString &error)
if (sender() != currentDevice) if (sender() != currentDevice)
return; return;
setCurrentDeviceTrusted(NotTrusted);
kcmUi->messages->setText(i18n("Error trying to pair: %1", error)); kcmUi->messages->setText(i18n("Error trying to pair: %1", error));
kcmUi->messages->animatedShow(); kcmUi->messages->animatedShow();
} }
void KdeConnectKcm::trustedChanged(bool trusted)
{
DeviceDbusInterface *senderDevice = (DeviceDbusInterface *)sender();
if (senderDevice == currentDevice)
setCurrentDeviceTrusted(trusted ? Trusted : NotTrusted);
}
void KdeConnectKcm::setCurrentDeviceTrusted(KdeConnectKcm::TrustStatus trusted) void KdeConnectKcm::setCurrentDevicePairState(int pairStateAsInt)
{ {
kcmUi->accept_button->setVisible(trusted == RequestedByPeer); PairState state = (PairState)pairStateAsInt; // Hack because qdbus doesn't like enums
kcmUi->reject_button->setVisible(trusted == RequestedByPeer); kcmUi->accept_button->setVisible(state == PairState::RequestedByPeer);
kcmUi->pair_button->setVisible(trusted == NotTrusted); kcmUi->reject_button->setVisible(state == PairState::RequestedByPeer);
kcmUi->unpair_button->setVisible(trusted == Trusted); kcmUi->cancel_button->setVisible(state == PairState::Requested);
kcmUi->progressBar->setVisible(trusted == Requested); kcmUi->pair_button->setVisible(state == PairState::NotPaired);
kcmUi->ping_button->setVisible(trusted == Trusted); kcmUi->unpair_button->setVisible(state == PairState::Paired);
switch (trusted) { kcmUi->progressBar->setVisible(state == PairState::Requested);
case Trusted: kcmUi->ping_button->setVisible(state == PairState::Paired);
switch (state) {
case PairState::Paired:
kcmUi->status_label->setText(i18n("(paired)")); kcmUi->status_label->setText(i18n("(paired)"));
break; break;
case NotTrusted: case PairState::NotPaired:
kcmUi->status_label->setText(i18n("(not paired)")); kcmUi->status_label->setText(i18n("(not paired)"));
break; break;
case RequestedByPeer: case PairState::RequestedByPeer:
kcmUi->status_label->setText(i18n("(incoming pair request)")); kcmUi->status_label->setText(i18n("(incoming pair request)"));
break; break;
case Requested: case PairState::Requested:
kcmUi->status_label->setText(i18n("(pairing requested)")); kcmUi->status_label->setText(i18n("(pairing requested)"));
break; break;
} }

View file

@ -10,6 +10,8 @@
#include <KCModule> #include <KCModule>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <core/pairstate.h>
class QModelIndex; class QModelIndex;
class DeviceDbusInterface; class DeviceDbusInterface;
class DaemonDbusInterface; class DaemonDbusInterface;
@ -35,24 +37,22 @@ private:
private Q_SLOTS: private Q_SLOTS:
void deviceSelected(const QModelIndex &current); void deviceSelected(const QModelIndex &current);
void requestPair(); void requestPairing();
void pluginsConfigChanged(); void pluginsConfigChanged();
void sendPing(); void sendPing();
void resetSelection(); void resetSelection();
void trustedChanged(bool);
void pairingFailed(const QString &error); void pairingFailed(const QString &error);
void refresh(); void refresh();
void renameShow(); void renameShow();
void renameDone(); void renameDone();
void setRenameMode(bool b); void setRenameMode(bool b);
void resetCurrentDevice(); void resetCurrentDevice();
void currentDevicePairingChanged(bool pairing); void setCurrentDevicePairState(int pairStateAsInt);
void acceptPairing(); void acceptPairing();
void rejectPairing(); void cancelPairing();
private: private:
enum TrustStatus { NotTrusted, Requested, RequestedByPeer, Trusted };
void setCurrentDeviceTrusted(TrustStatus trusted);
void resetDeviceView(); void resetDeviceView();
Ui::KdeConnectKcmUi *kcmUi; Ui::KdeConnectKcmUi *kcmUi;

View file

@ -251,6 +251,19 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QPushButton" name="cancel_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QProgressBar" name="progressBar"> <widget class="QProgressBar" name="progressBar">
<property name="sizePolicy"> <property name="sizePolicy">

View file

@ -137,7 +137,7 @@ KIO::WorkerResult KioKdeconnect::listDevice(const QString &device)
DeviceDbusInterface dev(device); DeviceDbusInterface dev(device);
if (!dev.isTrusted()) { if (!dev.isPaired()) {
return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("%0 is not paired").arg(dev.name())); return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("%0 is not paired").arg(dev.name()));
} }

View file

@ -36,12 +36,11 @@ private Q_SLOTS:
} }
Device *d = nullptr; Device *d = nullptr;
m_daemon->acquireDiscoveryMode(QStringLiteral("plugintest"));
const QList<Device *> devicesList = m_daemon->devicesList(); const QList<Device *> devicesList = m_daemon->devicesList();
for (Device *id : devicesList) { for (Device *id : devicesList) {
if (id->isReachable()) { if (id->isReachable()) {
if (!id->isTrusted()) if (!id->isPaired())
id->requestPair(); id->requestPairing();
d = id; d = id;
break; break;
} }
@ -49,14 +48,13 @@ private Q_SLOTS:
if (d == nullptr) { if (d == nullptr) {
QFAIL("Unable to determine device"); QFAIL("Unable to determine device");
} }
m_daemon->releaseDiscoveryMode(QStringLiteral("plugintest"));
if (!d->loadedPlugins().contains(QStringLiteral("kdeconnect_remotecontrol"))) { if (!d->loadedPlugins().contains(QStringLiteral("kdeconnect_remotecontrol"))) {
QSKIP("kdeconnect_remotecontrol is required for this test"); QSKIP("kdeconnect_remotecontrol is required for this test");
} }
QVERIFY(d); QVERIFY(d);
QVERIFY(d->isTrusted()); QVERIFY(d->isPaired());
QVERIFY(d->isReachable()); QVERIFY(d->isReachable());
d->setPluginEnabled(QStringLiteral("kdeconnect_mousepad"), false); d->setPluginEnabled(QStringLiteral("kdeconnect_mousepad"), false);

View file

@ -40,22 +40,20 @@ private Q_SLOTS:
QFAIL("No links available, but loopback should have been provided by the test"); QFAIL("No links available, but loopback should have been provided by the test");
} }
m_daemon->acquireDiscoveryMode(QStringLiteral("test"));
Device *d = nullptr; Device *d = nullptr;
const QList<Device *> devicesList = m_daemon->devicesList(); const QList<Device *> devicesList = m_daemon->devicesList();
for (Device *id : devicesList) { for (Device *id : devicesList) {
if (id->isReachable()) { if (id->isReachable()) {
if (!id->isTrusted()) if (!id->isPaired())
id->requestPair(); id->requestPairing();
d = id; d = id;
} }
} }
if (d == nullptr) { if (d == nullptr) {
QFAIL("Unable to determine device"); QFAIL("Unable to determine device");
} }
m_daemon->releaseDiscoveryMode(QStringLiteral("test"));
QCOMPARE(d->isReachable(), true); QCOMPARE(d->isReachable(), true);
QCOMPARE(d->isTrusted(), true); QCOMPARE(d->isPaired(), true);
QByteArray content("12312312312313213123213123"); QByteArray content("12312312312313213123213123");