diff --git a/core/backends/bluetooth/bluetoothdevicelink.cpp b/core/backends/bluetooth/bluetoothdevicelink.cpp index e0f30f48a..925b4ccf9 100644 --- a/core/backends/bluetooth/bluetoothdevicelink.cpp +++ b/core/backends/bluetooth/bluetoothdevicelink.cpp @@ -14,14 +14,15 @@ #include "core_debug.h" #include "multiplexchannel.h" -BluetoothDeviceLink::BluetoothDeviceLink(const QString &deviceId, - LinkProvider *parent, +BluetoothDeviceLink::BluetoothDeviceLink(const DeviceInfo &deviceInfo, + BluetoothLinkProvider *parent, ConnectionMultiplexer *connection, QSharedPointer socket) - : DeviceLink(deviceId, parent) + : DeviceLink(deviceInfo.id, parent) , mSocketReader(new DeviceLineReader(socket.data(), this)) , mConnection(connection) , mChannel(socket) + , mDeviceInfo(deviceInfo) { connect(mSocketReader, &DeviceLineReader::readyRead, this, &BluetoothDeviceLink::dataReceived); @@ -69,8 +70,3 @@ void BluetoothDeviceLink::dataReceived() QMetaObject::invokeMethod(this, &BluetoothDeviceLink::dataReceived, Qt::QueuedConnection); } } - -QSslCertificate BluetoothDeviceLink::certificate() const -{ - return QSslCertificate({}); // TODO Not sure what to do here. For LanDeviceLink we use the SSL connection's certificate, but we don't have that here -} diff --git a/core/backends/bluetooth/bluetoothdevicelink.h b/core/backends/bluetooth/bluetoothdevicelink.h index f63be1bb3..3b9947bad 100644 --- a/core/backends/bluetooth/bluetoothdevicelink.h +++ b/core/backends/bluetooth/bluetoothdevicelink.h @@ -17,17 +17,24 @@ class ConnectionMultiplexer; class MultiplexChannel; +class BluetoothLinkProvider; class KDECONNECTCORE_EXPORT BluetoothDeviceLink : public DeviceLink { Q_OBJECT public: - BluetoothDeviceLink(const QString &deviceId, LinkProvider *parent, ConnectionMultiplexer *connection, QSharedPointer socket); + BluetoothDeviceLink(const DeviceInfo &deviceInfo, + BluetoothLinkProvider *parent, + ConnectionMultiplexer *connection, + QSharedPointer socket); bool sendPacket(NetworkPacket &np) override; - QSslCertificate certificate() const override; + DeviceInfo deviceInfo() const override + { + return mDeviceInfo; + } private Q_SLOTS: void dataReceived(); @@ -36,6 +43,7 @@ private: DeviceLineReader *mSocketReader; ConnectionMultiplexer *mConnection; QSharedPointer mChannel; + DeviceInfo mDeviceInfo; void sendMessage(const QString mMessage); }; diff --git a/core/backends/bluetooth/bluetoothlinkprovider.cpp b/core/backends/bluetooth/bluetoothlinkprovider.cpp index c8e74521c..56ad5c9e7 100644 --- a/core/backends/bluetooth/bluetoothlinkprovider.cpp +++ b/core/backends/bluetooth/bluetoothlinkprovider.cpp @@ -8,6 +8,7 @@ #include "bluetoothdevicelink.h" #include "connectionmultiplexer.h" #include "core_debug.h" +#include "kdeconnectconfig.h" #include "multiplexchannel.h" #include @@ -83,13 +84,7 @@ void BluetoothLinkProvider::connectError() void BluetoothLinkProvider::addLink(BluetoothDeviceLink *deviceLink, const QString &deviceId) { QMap::iterator oldLinkIterator = mLinks.find(deviceId); - if (oldLinkIterator != mLinks.end()) { - DeviceLink *oldLink = oldLinkIterator.value(); - disconnect(oldLink, SIGNAL(destroyed(QObject *)), this, SLOT(deviceLinkDestroyed(QObject *))); - oldLink->deleteLater(); - mLinks.erase(oldLinkIterator); - } - + delete oldLinkIterator.value(); // not calling deleteLater because this triggers onLinkDestroyed mLinks[deviceId] = deviceLink; } @@ -187,22 +182,22 @@ void BluetoothLinkProvider::clientIdentityReceived(const QBluetoothAddress &peer // TODO? // disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); - const QString &deviceId = receivedPacket.get(QStringLiteral("deviceId")); - BluetoothDeviceLink *deviceLink = new BluetoothDeviceLink(deviceId, this, mSockets[peer], socket); + QSslCertificate receivedCertificate(receivedPacket.get(QStringLiteral("certificate")).toLatin1()); + DeviceInfo deviceInfo = deviceInfo.FromIdentityPacketAndCert(receivedPacket, receivedCertificate); + BluetoothDeviceLink *deviceLink = new BluetoothDeviceLink(deviceInfo, this, mSockets[peer], socket); - NetworkPacket np2; - NetworkPacket::createIdentityPacket(&np2); - success = deviceLink->sendPacket(np2); + DeviceInfo myDeviceInfo = KdeConnectConfig::instance().deviceInfo(); + NetworkPacket myIdentity = myDeviceInfo.toIdentityPacket(); + myIdentity.set(QStringLiteral("certificate"), QString::fromLatin1(myDeviceInfo.certificate.toPem())); + success = deviceLink->sendPacket(myIdentity); if (success) { qCDebug(KDECONNECT_CORE) << "Handshaking done (I'm the new device)"; - connect(deviceLink, SIGNAL(destroyed(QObject *)), this, SLOT(deviceLinkDestroyed(QObject *))); - - Q_EMIT onConnectionReceived(receivedPacket, deviceLink); + Q_EMIT onConnectionReceived(deviceLink); // We kill any possible link from this same device - addLink(deviceLink, deviceId); + addLink(deviceLink, deviceInfo.id); } else { // Connection might be lost. Delete it. @@ -246,9 +241,10 @@ void BluetoothLinkProvider::serverNewConnection() return; } - NetworkPacket np2; - NetworkPacket::createIdentityPacket(&np2); - socket->write(np2.serialize()); + DeviceInfo myDeviceInfo = KdeConnectConfig::instance().deviceInfo(); + NetworkPacket myIdentity = myDeviceInfo.toIdentityPacket(); + myIdentity.set(QStringLiteral("certificate"), QString::fromLatin1(myDeviceInfo.certificate.toPem())); + socket->write(myIdentity.serialize()); qCDebug(KDECONNECT_CORE()) << "Sent identity packet to" << socket->peerAddress(); } @@ -281,24 +277,20 @@ void BluetoothLinkProvider::serverDataReceived(const QBluetoothAddress &peer, QS qCDebug(KDECONNECT_CORE()) << "Received identity packet from" << peer; - const QString &deviceId = receivedPacket.get(QStringLiteral("deviceId")); - BluetoothDeviceLink *deviceLink = new BluetoothDeviceLink(deviceId, this, mSockets[peer], socket); + QSslCertificate receivedCertificate(receivedPacket.get(QStringLiteral("certificate")).toLatin1()); + DeviceInfo deviceInfo = deviceInfo.FromIdentityPacketAndCert(receivedPacket, receivedCertificate); + BluetoothDeviceLink *deviceLink = new BluetoothDeviceLink(deviceInfo, this, mSockets[peer], socket); - connect(deviceLink, SIGNAL(destroyed(QObject *)), this, SLOT(deviceLinkDestroyed(QObject *))); + Q_EMIT onConnectionReceived(deviceLink); - Q_EMIT onConnectionReceived(receivedPacket, deviceLink); - - addLink(deviceLink, deviceId); + addLink(deviceLink, deviceInfo.id); } -void BluetoothLinkProvider::deviceLinkDestroyed(QObject *destroyedDeviceLink) +void BluetoothLinkProvider::onLinkDestroyed(const QString &deviceId, DeviceLink *oldPtr) { - const QString id = destroyedDeviceLink->property("deviceId").toString(); - qCDebug(KDECONNECT_CORE()) << "Device disconnected:" << id; - QMap::iterator oldLinkIterator = mLinks.find(id); - if (oldLinkIterator != mLinks.end() && oldLinkIterator.value() == destroyedDeviceLink) { - mLinks.erase(oldLinkIterator); - } + qCDebug(KDECONNECT_CORE) << "BluetoothLinkProvider deviceLinkDestroyed" << deviceId; + DeviceLink *link = mLinks.take(deviceId); + Q_ASSERT(link == oldPtr); } void BluetoothLinkProvider::socketDisconnected(const QBluetoothAddress &peer, MultiplexChannel *socket) diff --git a/core/backends/bluetooth/bluetoothlinkprovider.h b/core/backends/bluetooth/bluetoothlinkprovider.h index ddedd963c..91103b6b7 100644 --- a/core/backends/bluetooth/bluetoothlinkprovider.h +++ b/core/backends/bluetooth/bluetoothlinkprovider.h @@ -41,10 +41,10 @@ public Q_SLOTS: virtual void onNetworkChange() override; virtual void onStart() override; virtual void onStop() override; + virtual void onLinkDestroyed(const QString &deviceId, DeviceLink *oldPtr) override; void connectError(); private Q_SLOTS: - void deviceLinkDestroyed(QObject *destroyedDeviceLink); void socketDisconnected(const QBluetoothAddress &peerAddress, MultiplexChannel *socket); void serverNewConnection(); diff --git a/core/backends/devicelink.cpp b/core/backends/devicelink.cpp index 7a29a1c0e..01888926e 100644 --- a/core/backends/devicelink.cpp +++ b/core/backends/devicelink.cpp @@ -5,16 +5,14 @@ */ #include "devicelink.h" -#include "kdeconnectconfig.h" + #include "linkprovider.h" DeviceLink::DeviceLink(const QString &deviceId, LinkProvider *parent) : QObject(parent) - , m_deviceId(deviceId) - , m_linkProvider(parent) { - Q_ASSERT(!deviceId.isEmpty()); - - setProperty("deviceId", deviceId); + connect(this, &QObject::destroyed, [this, deviceId, parent]() { + parent->onLinkDestroyed(deviceId, this); + }); } diff --git a/core/backends/devicelink.h b/core/backends/devicelink.h index 4eaa56592..57aacdcf2 100644 --- a/core/backends/devicelink.h +++ b/core/backends/devicelink.h @@ -9,42 +9,29 @@ #include +#include "deviceinfo.h" #include "networkpacket.h" -class PairingHandler; -class NetworkPacket; class LinkProvider; -class Device; -class QSslCertificate; class DeviceLink : public QObject { Q_OBJECT - public: - DeviceLink(const QString &deviceId, LinkProvider *parent); ~DeviceLink() override = default; - const QString &deviceId() const + QString deviceId() const { - return m_deviceId; - } - LinkProvider *provider() - { - return m_linkProvider; + return deviceInfo().id; } virtual bool sendPacket(NetworkPacket &np) = 0; - virtual QSslCertificate certificate() const = 0; + virtual DeviceInfo deviceInfo() const = 0; Q_SIGNALS: void receivedPacket(const NetworkPacket &np); - -private: - const QString m_deviceId; - LinkProvider *m_linkProvider; }; #endif diff --git a/core/backends/lan/landevicelink.cpp b/core/backends/lan/landevicelink.cpp index 89540aa49..e9669f23e 100644 --- a/core/backends/lan/landevicelink.cpp +++ b/core/backends/lan/landevicelink.cpp @@ -15,14 +15,15 @@ #include "plugins/share/shareplugin.h" #include "socketlinereader.h" -LanDeviceLink::LanDeviceLink(const QString &deviceId, LinkProvider *parent, QSslSocket *socket, ConnectionStarted connectionSource) - : DeviceLink(deviceId, parent) +LanDeviceLink::LanDeviceLink(const DeviceInfo &deviceInfo, LanLinkProvider *parent, QSslSocket *socket) + : DeviceLink(deviceInfo.id, parent) , m_socketLineReader(nullptr) + , m_deviceInfo(deviceInfo) { - reset(socket, connectionSource); + reset(socket); } -void LanDeviceLink::reset(QSslSocket *socket, ConnectionStarted connectionSource) +void LanDeviceLink::reset(QSslSocket *socket) { if (m_socketLineReader) { disconnect(m_socketLineReader->m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); @@ -39,10 +40,6 @@ void LanDeviceLink::reset(QSslSocket *socket, ConnectionStarted connectionSource // the socket (and the reader) will be // destroyed as well socket->setParent(m_socketLineReader); - - m_connectionSource = connectionSource; - - QString certString = KdeConnectConfig::instance().getDeviceProperty(deviceId(), QStringLiteral("certificate")); } QHostAddress LanDeviceLink::hostAddress() const @@ -98,7 +95,7 @@ void LanDeviceLink::dataReceived() return; const QByteArray serializedPacket = m_socketLineReader->readLine(); - NetworkPacket packet((QString())); + NetworkPacket packet; NetworkPacket::unserialize(serializedPacket, &packet); // qCDebug(KDECONNECT_CORE) << "LanDeviceLink dataReceived" << serializedPacket; @@ -127,8 +124,3 @@ void LanDeviceLink::dataReceived() QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection); } } - -QSslCertificate LanDeviceLink::certificate() const -{ - return m_socketLineReader->peerCertificate(); -} diff --git a/core/backends/lan/landevicelink.h b/core/backends/lan/landevicelink.h index cd337075b..0debee782 100644 --- a/core/backends/lan/landevicelink.h +++ b/core/backends/lan/landevicelink.h @@ -9,16 +9,17 @@ #include #include -#include #include #include #include "backends/devicelink.h" #include "compositeuploadjob.h" +#include "deviceinfo.h" #include "uploadjob.h" #include class SocketLineReader; +class LanLinkProvider; class KDECONNECTCORE_EXPORT LanDeviceLink : public DeviceLink { @@ -27,13 +28,17 @@ class KDECONNECTCORE_EXPORT LanDeviceLink : public DeviceLink public: enum ConnectionStarted : bool { Locally, Remotely }; - LanDeviceLink(const QString &deviceId, LinkProvider *parent, QSslSocket *socket, ConnectionStarted connectionSource); - void reset(QSslSocket *socket, ConnectionStarted connectionSource); + LanDeviceLink(const DeviceInfo &deviceInfo, LanLinkProvider *parent, QSslSocket *socket); + void reset(QSslSocket *socket); bool sendPacket(NetworkPacket &np) override; + DeviceInfo deviceInfo() const override + { + return m_deviceInfo; + } + QHostAddress hostAddress() const; - QSslCertificate certificate() const override; private Q_SLOTS: void dataReceived(); @@ -42,6 +47,7 @@ private: SocketLineReader *m_socketLineReader; ConnectionStarted m_connectionSource; QPointer m_compositeUploadJob; + DeviceInfo m_deviceInfo; }; #endif diff --git a/core/backends/lan/lanlinkprovider.cpp b/core/backends/lan/lanlinkprovider.cpp index eaa40f291..763afdb42 100644 --- a/core/backends/lan/lanlinkprovider.cpp +++ b/core/backends/lan/lanlinkprovider.cpp @@ -34,8 +34,6 @@ #include "landevicelink.h" #include "qtcompat_p.h" -#define MIN_VERSION_WITH_SSL_SUPPORT 6 - static const int MAX_UNPAIRED_CONNECTIONS = 42; static const int MAX_REMEMBERED_IDENTITY_PACKETS = 42; @@ -137,8 +135,7 @@ void LanLinkProvider::broadcastToNetwork() QList destinations = getBroadcastAddresses(); - NetworkPacket np; - NetworkPacket::createIdentityPacket(&np); + NetworkPacket np = KdeConnectConfig::instance().deviceInfo().toIdentityPacket(); np.set(QStringLiteral("tcpPort"), m_tcpPort); #if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) // On macOS and FreeBSD, the too large UDP packet (larger than MTU) causes @@ -235,7 +232,7 @@ void LanLinkProvider::udpBroadcastReceived() if (sender.isLoopback() && !m_testMode) continue; - NetworkPacket *receivedPacket = new NetworkPacket(QLatin1String("")); + NetworkPacket *receivedPacket = new NetworkPacket(); bool success = NetworkPacket::unserialize(datagram, receivedPacket); // qCDebug(KDECONNECT_CORE) << "udp connection from " << receivedPacket->; @@ -298,8 +295,7 @@ void LanLinkProvider::connectError(QAbstractSocket::SocketError socketError) qCDebug(KDECONNECT_CORE) << "Socket error" << socketError; qCDebug(KDECONNECT_CORE) << "Fallback (1), try reverse connection (send udp packet)" << socket->errorString(); - NetworkPacket np(QLatin1String("")); - NetworkPacket::createIdentityPacket(&np); + NetworkPacket np = KdeConnectConfig::instance().deviceInfo().toIdentityPacket(); np.set(QStringLiteral("tcpPort"), m_tcpPort); m_udpSocket.writeDatagram(np.serialize(), m_receivedIdentityPackets[socket].sender, m_udpBroadcastPort); @@ -335,8 +331,7 @@ void LanLinkProvider::tcpSocketConnected() // qCDebug(KDECONNECT_CORE) << "tcpSocketConnected" << socket->isWritable(); // If network is on ssl, do not believe when they are connected, believe when handshake is completed - NetworkPacket np2(QLatin1String("")); - NetworkPacket::createIdentityPacket(&np2); + NetworkPacket np2 = KdeConnectConfig::instance().deviceInfo().toIdentityPacket(); socket->write(np2.serialize()); bool success = socket->waitForBytesWritten(); @@ -344,33 +339,26 @@ void LanLinkProvider::tcpSocketConnected() qCDebug(KDECONNECT_CORE) << "TCP connection done (i'm the existing device)"; // if ssl supported - if (receivedPacket->get(QStringLiteral("protocolVersion")) >= MIN_VERSION_WITH_SSL_SUPPORT) { - bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId); - configureSslSocket(socket, deviceId, isDeviceTrusted); + bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId); + configureSslSocket(socket, deviceId, isDeviceTrusted); - qCDebug(KDECONNECT_CORE) << "Starting server ssl (I'm the client TCP socket)"; + qCDebug(KDECONNECT_CORE) << "Starting server ssl (I'm the client TCP socket)"; - connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted); + connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted); - connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors); - - socket->startServerEncryption(); - - return; // Return statement prevents from deleting received packet, needed in slot "encrypted" - } else { - qWarning() << receivedPacket->get(QStringLiteral("deviceName")) << "uses an old protocol version, this won't work"; - // addLink(deviceId, socket, receivedPacket, LanDeviceLink::Remotely); - } + connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors); + socket->startServerEncryption(); } else { - // I think this will never happen, but if it happens the deviceLink - //(or the socket that is now inside it) might not be valid. Delete them. + // The socket doesn't seem to work, so we can't create the connection. + qCDebug(KDECONNECT_CORE) << "Fallback (2), try reverse connection (send udp packet)"; m_udpSocket.writeDatagram(np2.serialize(), m_receivedIdentityPackets[socket].sender, m_udpBroadcastPort); - } - delete m_receivedIdentityPackets.take(socket).np; - // We don't delete the socket because now it's owned by the LanDeviceLink + // Cleanup the network packet now. The socket should be deleted via the disconnected() signal. + // We don't do this on success, because it is done later in the encrypted() slot. + delete m_receivedIdentityPackets.take(socket).np; + } } void LanLinkProvider::encrypted() @@ -382,20 +370,14 @@ void LanLinkProvider::encrypted() return; Q_ASSERT(socket->mode() != QSslSocket::UnencryptedMode); - LanDeviceLink::ConnectionStarted connectionOrigin = (socket->mode() == QSslSocket::SslClientMode) ? LanDeviceLink::Locally : LanDeviceLink::Remotely; - NetworkPacket *receivedPacket = m_receivedIdentityPackets[socket].np; - const QString &deviceId = socket->peerCertificate().subjectDisplayName(); + NetworkPacket *identityPacket = m_receivedIdentityPackets[socket].np; - if (m_links.contains(deviceId) && m_links[deviceId]->certificate() != socket->peerCertificate()) { - socket->disconnectFromHost(); - qCWarning(KDECONNECT_CORE) << "Got connection for the same deviceId but certificates don't match. Ignoring " << deviceId; - return; - } + DeviceInfo deviceInfo = DeviceInfo::FromIdentityPacketAndCert(*identityPacket, socket->peerCertificate()); - addLink(deviceId, socket, receivedPacket, connectionOrigin); + addLink(socket, deviceInfo); - // Copied from tcpSocketConnected slot, now delete received packet + // We don't delete the socket because now it's owned by the LanDeviceLink delete m_receivedIdentityPackets.take(socket).np; } @@ -472,7 +454,7 @@ void LanLinkProvider::dataReceived() qCDebug(KDECONNECT_CORE) << "LanLinkProvider received reply:" << data; - NetworkPacket *np = new NetworkPacket(QLatin1String("")); + NetworkPacket *np = new NetworkPacket(); bool success = NetworkPacket::unserialize(data, np); #if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) @@ -510,37 +492,25 @@ void LanLinkProvider::dataReceived() // This socket will now be owned by the LanDeviceLink or we don't want more data to be received, forget about it disconnect(socket, &QIODevice::readyRead, this, &LanLinkProvider::dataReceived); - if (np->get(QStringLiteral("protocolVersion")) >= MIN_VERSION_WITH_SSL_SUPPORT) { - bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId); - configureSslSocket(socket, deviceId, isDeviceTrusted); + bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId); + configureSslSocket(socket, deviceId, isDeviceTrusted); - qCDebug(KDECONNECT_CORE) << "Starting client ssl (but I'm the server TCP socket)"; + qCDebug(KDECONNECT_CORE) << "Starting client ssl (but I'm the server TCP socket)"; - connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted); + connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted); - if (isDeviceTrusted) { - connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors); - } - - socket->startClientEncryption(); - - } else { - qWarning() << np->get(QStringLiteral("deviceName")) << "uses an old protocol version, this won't work"; - // addLink(deviceId, socket, np, LanDeviceLink::Locally); - delete m_receivedIdentityPackets.take(socket).np; + if (isDeviceTrusted) { + connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors); } + + socket->startClientEncryption(); } -void LanLinkProvider::deviceLinkDestroyed(QObject *destroyedDeviceLink) +void LanLinkProvider::onLinkDestroyed(const QString &deviceId, DeviceLink *oldPtr) { - const QString id = destroyedDeviceLink->property("deviceId").toString(); - // qCDebug(KDECONNECT_CORE) << "deviceLinkDestroyed" << id; - QMap::iterator linkIterator = m_links.find(id); - Q_ASSERT(linkIterator != m_links.end()); - if (linkIterator != m_links.end()) { - Q_ASSERT(linkIterator.value() == destroyedDeviceLink); - m_links.erase(linkIterator); - } + qCDebug(KDECONNECT_CORE) << "LanLinkProvider deviceLinkDestroyed" << deviceId; + DeviceLink *link = m_links.take(deviceId); + Q_ASSERT(link == oldPtr); } void LanLinkProvider::configureSslSocket(QSslSocket *socket, const QString &deviceId, bool isDeviceTrusted) @@ -558,8 +528,8 @@ void LanLinkProvider::configureSslSocket(QSslSocket *socket, const QString &devi sslConfig.setPrivateKey(privateKey); if (isDeviceTrusted) { - QString certString = KdeConnectConfig::instance().getDeviceProperty(deviceId, QStringLiteral("certificate"), QString()); - sslConfig.setCaCertificates({QSslCertificate(certString.toLatin1())}); + QSslCertificate certificate = KdeConnectConfig::instance().getTrustedDeviceCertificate(deviceId); + sslConfig.setCaCertificates({certificate}); sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer); } else { sslConfig.setPeerVerifyMode(QSslSocket::QueryPeer); @@ -622,31 +592,41 @@ void LanLinkProvider::configureSocket(QSslSocket *socket) #endif } -void LanLinkProvider::addLink(const QString &deviceId, QSslSocket *socket, NetworkPacket *receivedPacket, LanDeviceLink::ConnectionStarted connectionOrigin) +void LanLinkProvider::addLink(QSslSocket *socket, const DeviceInfo &deviceInfo) { + QString certDeviceId = socket->peerCertificate().subjectDisplayName(); + if (deviceInfo.id != certDeviceId) { + socket->disconnectFromHost(); + qCWarning(KDECONNECT_CORE) << "DeviceID in cert doesn't match deviceID in identity packet. " << deviceInfo.id << " vs " << certDeviceId; + return; + } + // Socket disconnection will now be handled by LanDeviceLink disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater); LanDeviceLink *deviceLink; // Do we have a link for this device already? - QMap::iterator linkIterator = m_links.find(deviceId); + QMap::iterator linkIterator = m_links.find(deviceInfo.id); if (linkIterator != m_links.end()) { - // qCDebug(KDECONNECT_CORE) << "Reusing link to" << deviceId; deviceLink = linkIterator.value(); - deviceLink->reset(socket, connectionOrigin); + if (deviceLink->deviceInfo().certificate != deviceInfo.certificate) { + qWarning() << "LanLink was asked to replace a socket but the certificate doesn't match, aborting"; + return; + } + // qCDebug(KDECONNECT_CORE) << "Reusing link to" << deviceId; + deviceLink->reset(socket); } else { - deviceLink = new LanDeviceLink(deviceId, this, socket, connectionOrigin); + deviceLink = new LanDeviceLink(deviceInfo, this, socket); // Socket disconnection will now be handled by LanDeviceLink disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater); - bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId); + bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceInfo.id); if (!isDeviceTrusted && m_links.size() > MAX_UNPAIRED_CONNECTIONS) { - qCWarning(KDECONNECT_CORE) << "Too many unpaired devices to remember them all. Ignoring " << deviceId; + qCWarning(KDECONNECT_CORE) << "Too many unpaired devices to remember them all. Ignoring " << deviceInfo.id; socket->disconnectFromHost(); socket->deleteLater(); return; } - connect(deviceLink, &QObject::destroyed, this, &LanLinkProvider::deviceLinkDestroyed); - m_links[deviceId] = deviceLink; + m_links[deviceInfo.id] = deviceLink; } - Q_EMIT onConnectionReceived(*receivedPacket, deviceLink); + Q_EMIT onConnectionReceived(deviceLink); } diff --git a/core/backends/lan/lanlinkprovider.h b/core/backends/lan/lanlinkprovider.h index 31064f889..c8e684e1d 100644 --- a/core/backends/lan/lanlinkprovider.h +++ b/core/backends/lan/lanlinkprovider.h @@ -49,6 +49,7 @@ public: public Q_SLOTS: void onNetworkChange() override; + void onLinkDestroyed(const QString &deviceId, DeviceLink *oldPtr) override; void onStart() override; void onStop() override; void tcpSocketConnected(); @@ -59,13 +60,12 @@ private Q_SLOTS: void udpBroadcastReceived(); void newConnection(); void dataReceived(); - void deviceLinkDestroyed(QObject *destroyedDeviceLink); void sslErrors(const QList &errors); void broadcastToNetwork(); private: void onNetworkConfigurationChanged(const QNetworkConfiguration &config); - void addLink(const QString &deviceId, QSslSocket *socket, NetworkPacket *receivedPacket, LanDeviceLink::ConnectionStarted connectionOrigin); + void addLink(QSslSocket *socket, const DeviceInfo &deviceInfo); QList getBroadcastAddresses(); void sendBroadcasts(QUdpSocket &socket, const NetworkPacket &np, const QList &addresses); diff --git a/core/backends/linkprovider.h b/core/backends/linkprovider.h index 2088dbded..93602214d 100644 --- a/core/backends/linkprovider.h +++ b/core/backends/linkprovider.h @@ -28,14 +28,12 @@ public Q_SLOTS: virtual void onStart() = 0; virtual void onStop() = 0; virtual void onNetworkChange() = 0; + virtual void onLinkDestroyed(const QString &deviceId, DeviceLink *oldPtr) = 0; void suspend(bool suspend); Q_SIGNALS: - // NOTE: The provider will destroy the DeviceLink when it's no longer accessible, - // and every user should listen to the destroyed signal to remove its references. - // That's the reason because there is no "onConnectionLost". - void onConnectionReceived(const NetworkPacket &identityPacket, DeviceLink *); + void onConnectionReceived(DeviceLink *); }; #endif diff --git a/core/backends/loopback/loopbackdevicelink.cpp b/core/backends/loopback/loopbackdevicelink.cpp index e5a0cbd95..e002b2371 100644 --- a/core/backends/loopback/loopbackdevicelink.cpp +++ b/core/backends/loopback/loopbackdevicelink.cpp @@ -6,10 +6,11 @@ #include "loopbackdevicelink.h" +#include "kdeconnectconfig.h" #include "loopbacklinkprovider.h" -LoopbackDeviceLink::LoopbackDeviceLink(const QString &deviceId, LoopbackLinkProvider *provider) - : DeviceLink(deviceId, provider) +LoopbackDeviceLink::LoopbackDeviceLink(LoopbackLinkProvider *parent) + : DeviceLink(KdeConnectConfig::instance().deviceId(), parent) { } @@ -29,3 +30,8 @@ bool LoopbackDeviceLink::sendPacket(NetworkPacket &input) return true; } + +DeviceInfo LoopbackDeviceLink::deviceInfo() const +{ + return KdeConnectConfig::instance().deviceInfo(); +} diff --git a/core/backends/loopback/loopbackdevicelink.h b/core/backends/loopback/loopbackdevicelink.h index af3ab8cae..4f21e93b9 100644 --- a/core/backends/loopback/loopbackdevicelink.h +++ b/core/backends/loopback/loopbackdevicelink.h @@ -8,7 +8,7 @@ #define LOOPBACKDEVICELINK_H #include "../devicelink.h" -#include +#include "deviceinfo.h" class LoopbackLinkProvider; @@ -16,14 +16,11 @@ class LoopbackDeviceLink : public DeviceLink { Q_OBJECT public: - LoopbackDeviceLink(const QString &d, LoopbackLinkProvider *a); + LoopbackDeviceLink(LoopbackLinkProvider *a); - bool sendPacket(NetworkPacket &np) override; + virtual bool sendPacket(NetworkPacket &np) override; - QSslCertificate certificate() const override - { - return QSslCertificate(); - } + virtual DeviceInfo deviceInfo() const override; }; #endif diff --git a/core/backends/loopback/loopbacklinkprovider.cpp b/core/backends/loopback/loopbacklinkprovider.cpp index d16b18511..83b383af7 100644 --- a/core/backends/loopback/loopbacklinkprovider.cpp +++ b/core/backends/loopback/loopbacklinkprovider.cpp @@ -9,9 +9,7 @@ #include "core_debug.h" LoopbackLinkProvider::LoopbackLinkProvider() - : identityPacket(PACKET_TYPE_IDENTITY) { - NetworkPacket::createIdentityPacket(&identityPacket); } LoopbackLinkProvider::~LoopbackLinkProvider() @@ -20,8 +18,8 @@ LoopbackLinkProvider::~LoopbackLinkProvider() void LoopbackLinkProvider::onNetworkChange() { - LoopbackDeviceLink *newLoopbackDeviceLink = new LoopbackDeviceLink(QStringLiteral("loopback"), this); - Q_EMIT onConnectionReceived(identityPacket, newLoopbackDeviceLink); + LoopbackDeviceLink *newLoopbackDeviceLink = new LoopbackDeviceLink(this); + Q_EMIT onConnectionReceived(newLoopbackDeviceLink); if (loopbackDeviceLink) { delete loopbackDeviceLink; diff --git a/core/backends/loopback/loopbacklinkprovider.h b/core/backends/loopback/loopbacklinkprovider.h index 0eb7400b1..b33e657e5 100644 --- a/core/backends/loopback/loopbacklinkprovider.h +++ b/core/backends/loopback/loopbacklinkprovider.h @@ -26,10 +26,12 @@ public: void onStart() override; void onStop() override; void onNetworkChange() override; + void onLinkDestroyed(const QString &, DeviceLink *) override + { + } private: QPointer loopbackDeviceLink; - NetworkPacket identityPacket; }; #endif diff --git a/core/daemon.cpp b/core/daemon.cpp index e06abae7b..9fdb8f1bf 100644 --- a/core/daemon.cpp +++ b/core/daemon.cpp @@ -158,24 +158,24 @@ QMap Daemon::deviceNames(bool onlyReachable, bool onlyTrusted) return ret; } -void Daemon::onNewDeviceLink(const NetworkPacket &identityPacket, DeviceLink *dl) +void Daemon::onNewDeviceLink(DeviceLink *link) { - const QString &id = identityPacket.get(QStringLiteral("deviceId")); + const QString &id = link->deviceId(); - // qCDebug(KDECONNECT_CORE) << "Device discovered" << id << "via" << dl->provider()->name(); + // qCDebug(KDECONNECT_CORE) << "Device discovered" << id << "via" << dl->name(); if (d->m_devices.contains(id)) { - qCDebug(KDECONNECT_CORE) << "It is a known device" << identityPacket.get(QStringLiteral("deviceName")); + qCDebug(KDECONNECT_CORE) << "It is a known device" << link->deviceInfo().name; Device *device = d->m_devices[id]; bool wasReachable = device->isReachable(); - device->addLink(identityPacket, dl); + device->addLink(link); if (!wasReachable) { Q_EMIT deviceVisibilityChanged(id, true); Q_EMIT deviceListChanged(); } } else { - qCDebug(KDECONNECT_CORE) << "It is a new device" << identityPacket.get(QStringLiteral("deviceName")); - Device *device = new Device(this, identityPacket, dl); + qCDebug(KDECONNECT_CORE) << "It is a new device" << link->deviceInfo().name; + Device *device = new Device(this, link); addDevice(device); } } diff --git a/core/daemon.h b/core/daemon.h index f0d5e3af4..ebad9b28c 100644 --- a/core/daemon.h +++ b/core/daemon.h @@ -78,7 +78,7 @@ Q_SIGNALS: Q_SCRIPTABLE void customDevicesChanged(const QStringList &customDevices); private Q_SLOTS: - void onNewDeviceLink(const NetworkPacket &identityPacket, DeviceLink *dl); + void onNewDeviceLink(DeviceLink *dl); void onDeviceStatusChanged(); private: diff --git a/core/device.cpp b/core/device.cpp index fb6cdec4a..5f1d68ee2 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -32,8 +32,8 @@ class Device::DevicePrivate { public: - DevicePrivate(const QString &id) - : m_deviceId(id) + DevicePrivate(const DeviceInfo &deviceInfo) + : m_deviceInfo(deviceInfo) { } @@ -43,17 +43,13 @@ public: m_deviceLinks.clear(); } - const QString m_deviceId; - QString m_deviceName; - DeviceType m_deviceType; - int m_protocolVersion; + DeviceInfo m_deviceInfo; QVector m_deviceLinks; QHash m_plugins; QMultiMap m_pluginsByIncomingCapability; QSet m_supportedPlugins; - QSet m_allPlugins; PairingHandler* m_pairingHandler; }; @@ -64,38 +60,31 @@ static void warn(const QString &info) Device::Device(QObject *parent, const QString &id) : QObject(parent) - , d(new Device::DevicePrivate(id)) { + DeviceInfo info = KdeConnectConfig::instance().getTrustedDevice(id); + d = new Device::DevicePrivate(info); + d->m_pairingHandler = new PairingHandler(this, PairState::Paired); - - d->m_protocolVersion = NetworkPacket::s_protocolVersion; - KdeConnectConfig::DeviceInfo info = KdeConnectConfig::instance().getTrustedDevice(d->m_deviceId); - - d->m_deviceName = info.deviceName; - d->m_deviceType = str2type(info.deviceType); + d->m_supportedPlugins = PluginLoader::instance()->getPluginList().toSet(); // Assume every plugin is supported until we get the capabilities // Register in bus QDBusConnection::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAdaptors); - // Assume every plugin is supported until addLink is called and we can get the actual list - d->m_allPlugins = PluginLoader::instance()->getPluginList().toSet(); - d->m_supportedPlugins = d->m_allPlugins; - 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, DeviceLink *dl) : QObject(parent) - , d(new Device::DevicePrivate(identityPacket.get(QStringLiteral("deviceId")))) { - d->m_pairingHandler = new PairingHandler(this, PairState::NotPaired); - d->m_deviceName = identityPacket.get(QStringLiteral("deviceName")); - d->m_allPlugins = PluginLoader::instance()->getPluginList().toSet(); + d = new Device::DevicePrivate(dl->deviceInfo()); - addLink(identityPacket, dl); + d->m_pairingHandler = new PairingHandler(this, PairState::NotPaired); + d->m_supportedPlugins = PluginLoader::instance()->getPluginList().toSet(); // Assume every plugin is supported until we get the capabilities + + addLink(dl); // Register in bus QDBusConnection::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAdaptors); @@ -109,6 +98,10 @@ Device::Device(QObject *parent, const NetworkPacket &identityPacket, DeviceLink 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); + + if (protocolVersion() != NetworkPacket::s_protocolVersion) { + qCWarning(KDECONNECT_CORE) << name() << " uses a different protocol version" << protocolVersion() << "expected" << NetworkPacket::s_protocolVersion; + } } Device::~Device() @@ -118,17 +111,17 @@ Device::~Device() QString Device::id() const { - return d->m_deviceId; + return d->m_deviceInfo.id; } QString Device::name() const { - return d->m_deviceName; + return d->m_deviceInfo.name; } -QString Device::type() const +DeviceType Device::type() const { - return type2str(d->m_deviceType); + return d->m_deviceInfo.type; } bool Device::isReachable() const @@ -138,7 +131,7 @@ bool Device::isReachable() const int Device::protocolVersion() { - return d->m_protocolVersion; + return d->m_deviceInfo.protocolVersion; } QStringList Device::supportedPlugins() const @@ -158,6 +151,8 @@ QStringList Device::loadedPlugins() const void Device::reloadPlugins() { + qCDebug(KDECONNECT_CORE) << name() << "- reload plugins"; + QHash newPluginMap, oldPluginMap = d->m_plugins; QMultiMap newPluginsByIncomingCapability; @@ -254,8 +249,7 @@ void Device::pairingHandler_incomingPairRequest() 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())); + KdeConnectConfig::instance().addTrustedDevice(d->m_deviceInfo); reloadPlugins(); // Will load/unload plugins Q_EMIT pairStateChanged(pairStateAsInt()); } @@ -276,49 +270,53 @@ void Device::pairingHandler_unpaired() Q_EMIT pairStateChanged(pairStateAsInt()); } -void Device::addLink(const NetworkPacket &identityPacket, DeviceLink *link) +void Device::addLink(DeviceLink *link) { - // qCDebug(KDECONNECT_CORE) << "Adding link to" << id() << "via" << link->provider(); - - setName(identityPacket.get(QStringLiteral("deviceName"))); - setType(identityPacket.get(QStringLiteral("deviceType"))); - if (d->m_deviceLinks.contains(link)) { return; } - d->m_protocolVersion = identityPacket.get(QStringLiteral("protocolVersion"), -1); - if (d->m_protocolVersion != NetworkPacket::s_protocolVersion) { - qCWarning(KDECONNECT_CORE) << d->m_deviceName << "- warning, device uses a different protocol version" << d->m_protocolVersion << "expected" - << NetworkPacket::s_protocolVersion; - } - - connect(link, &QObject::destroyed, this, &Device::linkDestroyed); - d->m_deviceLinks.append(link); - // Theoretically we will never add two links from the same provider (the provider should destroy - // the old one before this is called), so we do not have to worry about destroying old links. - //-- Actually, we should not destroy them or the provider will store an invalid ref! - + connect(link, &QObject::destroyed, this, &Device::linkDestroyed); connect(link, &DeviceLink::receivedPacket, this, &Device::privateReceivedPacket); - const bool capabilitiesSupported = identityPacket.has(QStringLiteral("incomingCapabilities")) || identityPacket.has(QStringLiteral("outgoingCapabilities")); - if (capabilitiesSupported) { - const QSet outgoingCapabilities = identityPacket.get(QStringLiteral("outgoingCapabilities")).toSet(), - incomingCapabilities = identityPacket.get(QStringLiteral("incomingCapabilities")).toSet(); - - d->m_supportedPlugins = PluginLoader::instance()->pluginsForCapabilities(incomingCapabilities, outgoingCapabilities); - // qDebug() << "new plugins for" << m_deviceName << m_supportedPlugins << incomingCapabilities << outgoingCapabilities; - } else { - d->m_supportedPlugins = PluginLoader::instance()->getPluginList().toSet(); - } - - reloadPlugins(); + bool hasChanges = updateDeviceInfo(link->deviceInfo()); if (d->m_deviceLinks.size() == 1) { Q_EMIT reachableChanged(true); + hasChanges = true; } + + if (hasChanges) { + reloadPlugins(); + } +} + +bool Device::updateDeviceInfo(const DeviceInfo &newDeviceInfo) +{ + bool hasChanges = false; + if (d->m_deviceInfo.name != newDeviceInfo.name || d->m_deviceInfo.type != newDeviceInfo.type) { + hasChanges = true; + d->m_deviceInfo.name = newDeviceInfo.name; + d->m_deviceInfo.type = newDeviceInfo.type; + Q_EMIT typeChanged(d->m_deviceInfo.type.toString()); + Q_EMIT nameChanged(d->m_deviceInfo.name); + if (isPaired()) { + KdeConnectConfig::instance().updateTrustedDeviceInfo(d->m_deviceInfo); + } + } + + if (d->m_deviceInfo.outgoingCapabilities != newDeviceInfo.outgoingCapabilities + || d->m_deviceInfo.incomingCapabilities != newDeviceInfo.incomingCapabilities) { + if (!newDeviceInfo.incomingCapabilities.isEmpty() && !newDeviceInfo.outgoingCapabilities.isEmpty()) { + hasChanges = true; + d->m_supportedPlugins = PluginLoader::instance()->pluginsForCapabilities(newDeviceInfo.incomingCapabilities, newDeviceInfo.outgoingCapabilities); + qDebug() << "new capabilities for " << name(); + } + } + + return hasChanges; } void Device::linkDestroyed(QObject *o) @@ -395,17 +393,6 @@ bool Device::isPairRequestedByPeer() const return d->m_pairingHandler->pairState() == PairState::RequestedByPeer; } - -QStringList Device::availableLinks() const -{ - QStringList sl; - sl.reserve(d->m_deviceLinks.size()); - for (DeviceLink *dl : qAsConst(d->m_deviceLinks)) { - sl.append(dl->provider()->name()); - } - return sl; -} - QHostAddress Device::getLocalIpAddress() const { for (DeviceLink *dl : qAsConst(d->m_deviceLinks)) { @@ -417,78 +404,14 @@ QHostAddress Device::getLocalIpAddress() const return QHostAddress::Null; } -Device::DeviceType Device::str2type(const QString &deviceType) +QString Device::iconName() const { - if (deviceType == QLatin1String("desktop")) - return Desktop; - if (deviceType == QLatin1String("laptop")) - return Laptop; - if (deviceType == QLatin1String("smartphone") || deviceType == QLatin1String("phone")) - return Phone; - if (deviceType == QLatin1String("tablet")) - return Tablet; - if (deviceType == QLatin1String("tv")) - return Tv; - return Unknown; -} - -QString Device::type2str(Device::DeviceType deviceType) -{ - if (deviceType == Desktop) - return QStringLiteral("desktop"); - if (deviceType == Laptop) - return QStringLiteral("laptop"); - if (deviceType == Phone) - return QStringLiteral("smartphone"); - if (deviceType == Tablet) - return QStringLiteral("tablet"); - if (deviceType == Tv) - return QStringLiteral("tv"); - return QStringLiteral("unknown"); + return d->m_deviceInfo.type.icon(); } QString Device::statusIconName() const { - return iconForStatus(isReachable(), isPaired()); -} - -QString Device::iconName() const -{ - return iconForStatus(true, false); -} - -QString Device::iconForStatus(bool reachable, bool trusted) const -{ - Device::DeviceType deviceType = d->m_deviceType; - if (deviceType == Device::Unknown) { - deviceType = Device::Phone; // Assume phone if we don't know the type - } else if (deviceType == Device::Desktop) { - deviceType = Device::Device::Laptop; // We don't have desktop icon yet - } - - QString status = (reachable ? (trusted ? QStringLiteral("connected") : QStringLiteral("disconnected")) : QStringLiteral("trusted")); - QString type = type2str(deviceType); - - return type + status; -} - -void Device::setName(const QString &name) -{ - if (d->m_deviceName != name) { - d->m_deviceName = name; - KdeConnectConfig::instance().setDeviceProperty(d->m_deviceId, QStringLiteral("name"), name); - Q_EMIT nameChanged(name); - } -} - -void Device::setType(const QString &strtype) -{ - auto type = str2type(strtype); - if (d->m_deviceType != type) { - d->m_deviceType = type; - KdeConnectConfig::instance().setDeviceProperty(d->m_deviceId, QStringLiteral("type"), strtype); - Q_EMIT typeChanged(strtype); - } + return d->m_deviceInfo.type.iconForStatus(isReachable(), isPaired()); } KdeConnectPlugin *Device::plugin(const QString &pluginName) const @@ -498,7 +421,8 @@ KdeConnectPlugin *Device::plugin(const QString &pluginName) const void Device::setPluginEnabled(const QString &pluginName, bool enabled) { - if (!d->m_allPlugins.contains(pluginName)) { + if (!PluginLoader::instance()->doesPluginExist(pluginName)) { + qWarning() << "Tried to enable a plugin that doesn't exist" << pluginName; return; } @@ -529,9 +453,7 @@ QString Device::encryptionInfo() const } result += i18n("SHA256 fingerprint of your device certificate is: %1\n", localChecksum); - std::string remotePem = KdeConnectConfig::instance().getDeviceProperty(id(), QStringLiteral("certificate")).toStdString(); - QSslCertificate remoteCertificate = QSslCertificate(QByteArray(remotePem.c_str(), (int)remotePem.size())); - QString remoteChecksum = QString::fromLatin1(remoteCertificate.digest(digestAlgorithm).toHex()); + QString remoteChecksum = QString::fromLatin1(certificate().digest(digestAlgorithm).toHex()); for (int i = 2; i < remoteChecksum.size(); i += 3) { remoteChecksum.insert(i, QStringLiteral(":")); // Improve readability } @@ -542,10 +464,7 @@ QString Device::encryptionInfo() const QSslCertificate Device::certificate() const { - if (!d->m_deviceLinks.isEmpty()) { - return d->m_deviceLinks[0]->certificate(); - } - return QSslCertificate(); + return d->m_deviceInfo.certificate; } QByteArray Device::verificationKey() const diff --git a/core/device.h b/core/device.h index f649638f4..c5fb8e293 100644 --- a/core/device.h +++ b/core/device.h @@ -12,6 +12,7 @@ #include #include "backends/devicelink.h" +#include "deviceinfo.h" #include "networkpacket.h" #include "pairstate.h" @@ -22,7 +23,7 @@ class KDECONNECTCORE_EXPORT Device : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device") - Q_PROPERTY(QString type READ type NOTIFY typeChanged) + Q_PROPERTY(QString type READ typeAsString NOTIFY typeChanged) Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString iconName READ iconName CONSTANT) Q_PROPERTY(QString statusIconName READ statusIconName NOTIFY statusIconNameChanged) @@ -34,15 +35,6 @@ class KDECONNECTCORE_EXPORT Device : public QObject Q_PROPERTY(QStringList supportedPlugins READ supportedPlugins NOTIFY pluginsChanged) public: - enum DeviceType { - Unknown, - Desktop, - Laptop, - Phone, - Tablet, - Tv, - }; - /** * Restores the @p device from the saved configuration * @@ -55,7 +47,7 @@ public: * * We know everything but we don't trust it yet */ - Device(QObject *parent, const NetworkPacket &np, DeviceLink *dl); + Device(QObject *parent, DeviceLink *link); ~Device() override; @@ -65,23 +57,27 @@ public: { return QStringLiteral("/modules/kdeconnect/devices/") + id(); } - QString type() const; + DeviceType type() const; + QString typeAsString() const + { + return type().toString(); + }; QString iconName() const; QString statusIconName() const; Q_SCRIPTABLE QByteArray verificationKey() const; Q_SCRIPTABLE QString encryptionInfo() const; // Add and remove links - void addLink(const NetworkPacket &identityPacket, DeviceLink *); - void removeLink(DeviceLink *); + void addLink(DeviceLink *link); + void removeLink(DeviceLink *link); + + bool updateDeviceInfo(const DeviceInfo &deviceInfo); 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; virtual bool isReachable() const; Q_SCRIPTABLE QStringList loadedPlugins() const; @@ -132,12 +128,6 @@ Q_SIGNALS: Q_SCRIPTABLE void statusIconNameChanged(); private: // Methods - static DeviceType str2type(const QString &deviceType); - static QString type2str(DeviceType deviceType); - - void setName(const QString &name); - void setType(const QString &strtype); - QString iconForStatus(bool reachable, bool paired) const; QSslCertificate certificate() const; private: diff --git a/core/deviceinfo.h b/core/deviceinfo.h new file mode 100644 index 000000000..2b22a67f3 --- /dev/null +++ b/core/deviceinfo.h @@ -0,0 +1,149 @@ +/** + * SPDX-FileCopyrightText: 2023 Albert Vaca + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + */ + +#ifndef DEVICE_INFO_H +#define DEVICE_INFO_H + +#include "networkpacket.h" +#include +#include +#include + +struct DeviceType { + enum Value { + Unknown, + Desktop, + Laptop, + Phone, + Tablet, + Tv, + }; + + static DeviceType FromString(const QString &deviceType) + { + if (deviceType == QLatin1String("desktop")) + return DeviceType::Desktop; + if (deviceType == QLatin1String("laptop")) + return DeviceType::Laptop; + if (deviceType == QLatin1String("smartphone") || deviceType == QLatin1String("phone")) + return DeviceType::Phone; + if (deviceType == QLatin1String("tablet")) + return DeviceType::Tablet; + if (deviceType == QLatin1String("tv")) + return DeviceType::Tv; + return DeviceType::Unknown; + } + + QString toString() const + { + if (value == DeviceType::Desktop) + return QStringLiteral("desktop"); + if (value == DeviceType::Laptop) + return QStringLiteral("laptop"); + if (value == DeviceType::Phone) + return QStringLiteral("phone"); + if (value == DeviceType::Tablet) + return QStringLiteral("tablet"); + if (value == DeviceType::Tv) + return QStringLiteral("tv"); + return QStringLiteral("unknown"); + } + + QString icon() const + { + return iconForStatus(true, false); + } + + QString iconForStatus(bool reachable, bool trusted) const + { + QString type; + switch (value) { + case DeviceType::Unknown: + case DeviceType::Phone: + type = QStringLiteral("smartphone"); + break; + case DeviceType::Desktop: // We don't have desktop icon yet + case DeviceType::Laptop: + type = QStringLiteral("laptop"); + break; + default: + type = toString(); + } + QString status = (reachable ? (trusted ? QStringLiteral("connected") : QStringLiteral("disconnected")) : QStringLiteral("trusted")); + return type + status; + } + + constexpr DeviceType(Value value) + : value(value) + { + } + constexpr bool operator==(DeviceType a) const + { + return value == a.value; + } + constexpr bool operator!=(DeviceType a) const + { + return value != a.value; + } + +private: + Value value; +}; + +struct DeviceInfo { + QString id; + QSslCertificate certificate; + QString name; + DeviceType type; + int protocolVersion; + QSet incomingCapabilities; + QSet outgoingCapabilities; + + DeviceInfo(const QString &id, + const QSslCertificate &certificate, + const QString &name, + DeviceType type, + int protocolVersion = 0, + const QSet &incomingCapabilities = QSet(), + const QSet &outgoingCapabilities = QSet()) + : id(id) + , certificate(certificate) + , name(name) + , type(type) + , protocolVersion(protocolVersion) + , incomingCapabilities(incomingCapabilities) + , outgoingCapabilities(outgoingCapabilities) + { + } + + NetworkPacket toIdentityPacket() + { + NetworkPacket np(PACKET_TYPE_IDENTITY); + np.set(QStringLiteral("deviceId"), id); + np.set(QStringLiteral("deviceName"), name); + np.set(QStringLiteral("deviceType"), type.toString()); + np.set(QStringLiteral("protocolVersion"), protocolVersion); + np.set(QStringLiteral("incomingCapabilities"), incomingCapabilities.values()); + np.set(QStringLiteral("outgoingCapabilities"), outgoingCapabilities.values()); + return np; + } + + static DeviceInfo FromIdentityPacketAndCert(const NetworkPacket &np, const QSslCertificate &certificate) + { + QStringList incomingCapabilities = np.get(QStringLiteral("incomingCapabilities")); + QStringList outgoingCapabilities = np.get(QStringLiteral("outgoingCapabilities")); + + return DeviceInfo(np.get(QStringLiteral("deviceId")), + certificate, + np.get(QStringLiteral("deviceName")), + DeviceType::FromString(np.get(QStringLiteral("deviceType"))), + np.get(QStringLiteral("protocolVersion"), -1), + QSet(incomingCapabilities.begin(), incomingCapabilities.end()), + QSet(outgoingCapabilities.begin(), outgoingCapabilities.end())); + } +}; + +#endif diff --git a/core/kdeconnectconfig.cpp b/core/kdeconnectconfig.cpp index 812d070ae..96c993aa8 100644 --- a/core/kdeconnectconfig.cpp +++ b/core/kdeconnectconfig.cpp @@ -24,6 +24,8 @@ #include "core_debug.h" #include "daemon.h" #include "dbushelper.h" +#include "deviceinfo.h" +#include "pluginloader.h" const QFile::Permissions strictPermissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser; @@ -105,24 +107,24 @@ void KdeConnectConfig::setName(const QString &name) d->m_config->sync(); } -QString KdeConnectConfig::deviceType() +DeviceType KdeConnectConfig::deviceType() { #ifdef SAILFISHOS - return QStringLiteral("phone"); + return DeviceType::Phone; #else const QByteArrayList platforms = qgetenv("PLASMA_PLATFORM").split(':'); if (platforms.contains("phone")) { - return QStringLiteral("phone"); + return DeviceType::Phone; } else if (platforms.contains("tablet")) { - return QStringLiteral("tablet"); + return DeviceType::Tablet; } else if (platforms.contains("mediacenter")) { - return QStringLiteral("tv"); + return DeviceType::Tv; } // TODO non-Plasma mobile platforms - return QStringLiteral("desktop"); + return DeviceType::Desktop; #endif } @@ -146,6 +148,17 @@ QSslCertificate KdeConnectConfig::certificate() return d->m_certificate; } +DeviceInfo KdeConnectConfig::deviceInfo() +{ + return DeviceInfo(deviceId(), + certificate(), + name(), + deviceType(), + NetworkPacket::s_protocolVersion, + PluginLoader::instance()->incomingCapabilities().toSet(), + PluginLoader::instance()->outgoingCapabilities().toSet()); +} + QDir KdeConnectConfig::baseConfigDir() { QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); @@ -159,27 +172,54 @@ QStringList KdeConnectConfig::trustedDevices() return list; } -void KdeConnectConfig::addTrustedDevice(const QString &id, const QString &name, const QString &type) +void KdeConnectConfig::addTrustedDevice(const DeviceInfo &deviceInfo) { - d->m_trustedDevices->beginGroup(id); - d->m_trustedDevices->setValue(QStringLiteral("name"), name); - d->m_trustedDevices->setValue(QStringLiteral("type"), type); + d->m_trustedDevices->beginGroup(deviceInfo.id); + d->m_trustedDevices->setValue(QStringLiteral("name"), deviceInfo.name); + d->m_trustedDevices->setValue(QStringLiteral("type"), deviceInfo.type.toString()); + QString certString = QString::fromLatin1(deviceInfo.certificate.toPem()); + d->m_trustedDevices->setValue(QStringLiteral("certificate"), certString); d->m_trustedDevices->endGroup(); d->m_trustedDevices->sync(); - QDir().mkpath(deviceConfigDir(id).path()); + QDir().mkpath(deviceConfigDir(deviceInfo.id).path()); } -KdeConnectConfig::DeviceInfo KdeConnectConfig::getTrustedDevice(const QString &id) +void KdeConnectConfig::updateTrustedDeviceInfo(const DeviceInfo &deviceInfo) +{ + if (!trustedDevices().contains(deviceInfo.id)) { + // do not store values for untrusted devices (it would make them trusted) + return; + } + + d->m_trustedDevices->beginGroup(deviceInfo.id); + d->m_trustedDevices->setValue(QStringLiteral("name"), deviceInfo.name); + d->m_trustedDevices->setValue(QStringLiteral("type"), deviceInfo.type.toString()); + d->m_trustedDevices->endGroup(); + d->m_trustedDevices->sync(); +} + +QSslCertificate KdeConnectConfig::getTrustedDeviceCertificate(const QString &id) +{ + d->m_trustedDevices->beginGroup(id); + QString certString = d->m_trustedDevices->value(QStringLiteral("certificate"), QString()).toString(); + d->m_trustedDevices->endGroup(); + return QSslCertificate(certString.toLatin1()); +} + +DeviceInfo KdeConnectConfig::getTrustedDevice(const QString &id) { d->m_trustedDevices->beginGroup(id); - KdeConnectConfig::DeviceInfo info; - info.deviceName = d->m_trustedDevices->value(QStringLiteral("name"), QLatin1String("unnamed")).toString(); - info.deviceType = d->m_trustedDevices->value(QStringLiteral("type"), QLatin1String("unknown")).toString(); + QString certString = d->m_trustedDevices->value(QStringLiteral("certificate"), QString()).toString(); + QSslCertificate certificate(certString.toLatin1()); + QString name = d->m_trustedDevices->value(QStringLiteral("name"), QLatin1String("unnamed")).toString(); + QString typeString = d->m_trustedDevices->value(QStringLiteral("type"), QLatin1String("unknown")).toString(); + DeviceType type = DeviceType::FromString(typeString); d->m_trustedDevices->endGroup(); - return info; + + return DeviceInfo(id, certificate, name, type); } void KdeConnectConfig::removeTrustedDevice(const QString &deviceId) diff --git a/core/kdeconnectconfig.h b/core/kdeconnectconfig.h index 6de569d1c..ba2aa76ae 100644 --- a/core/kdeconnectconfig.h +++ b/core/kdeconnectconfig.h @@ -9,6 +9,7 @@ #include +#include "deviceinfo.h" #include "kdeconnectcore_export.h" class QSslCertificate; @@ -16,11 +17,6 @@ class QSslCertificate; class KDECONNECTCORE_EXPORT KdeConnectConfig { public: - struct DeviceInfo { - QString deviceName; - QString deviceType; - }; - static KdeConnectConfig &instance(); /* @@ -29,11 +25,11 @@ public: QString deviceId(); QString name(); - QString deviceType(); - + DeviceType deviceType(); + QSslCertificate certificate(); + DeviceInfo deviceInfo(); QString privateKeyPath(); QString certificatePath(); - QSslCertificate certificate(); void setName(const QString &name); @@ -43,8 +39,10 @@ public: QStringList trustedDevices(); // list of ids void removeTrustedDevice(const QString &id); - void addTrustedDevice(const QString &id, const QString &name, const QString &type); - KdeConnectConfig::DeviceInfo getTrustedDevice(const QString &id); + void addTrustedDevice(const DeviceInfo &deviceInfo); + void updateTrustedDeviceInfo(const DeviceInfo &deviceInfo); + DeviceInfo getTrustedDevice(const QString &id); + QSslCertificate getTrustedDeviceCertificate(const QString &id); void setDeviceProperty(const QString &deviceId, const QString &name, const QString &value); QString getDeviceProperty(const QString &deviceId, const QString &name, const QString &defaultValue = QString()); diff --git a/core/networkpacket.cpp b/core/networkpacket.cpp index ddf1a7b35..12ed62a58 100644 --- a/core/networkpacket.cpp +++ b/core/networkpacket.cpp @@ -41,22 +41,6 @@ NetworkPacket::NetworkPacket(const QString &type, const QVariantMap &body) { } -void NetworkPacket::createIdentityPacket(NetworkPacket *np) -{ - np->m_id = QString::number(QDateTime::currentMSecsSinceEpoch()); - np->m_type = PACKET_TYPE_IDENTITY; - np->m_payload = QSharedPointer(); - np->m_payloadSize = 0; - np->set(QStringLiteral("deviceId"), KdeConnectConfig::instance().deviceId()); - np->set(QStringLiteral("deviceName"), KdeConnectConfig::instance().name()); - np->set(QStringLiteral("deviceType"), KdeConnectConfig::instance().deviceType()); - np->set(QStringLiteral("protocolVersion"), NetworkPacket::s_protocolVersion); - np->set(QStringLiteral("incomingCapabilities"), PluginLoader::instance()->incomingCapabilities()); - np->set(QStringLiteral("outgoingCapabilities"), PluginLoader::instance()->outgoingCapabilities()); - - // qCDebug(KDECONNECT_CORE) << "createIdentityPacket" << np->serialize(); -} - QByteArray NetworkPacket::serialize() const { // Object -> QVariant diff --git a/core/networkpacket.h b/core/networkpacket.h index 000b2f301..0a2c1c011 100644 --- a/core/networkpacket.h +++ b/core/networkpacket.h @@ -32,12 +32,10 @@ class KDECONNECTCORE_EXPORT NetworkPacket public: const static int s_protocolVersion; - explicit NetworkPacket(const QString &type = QStringLiteral("empty"), const QVariantMap &body = {}); + explicit NetworkPacket(const QString &type = QString(), const QVariantMap &body = {}); NetworkPacket(const NetworkPacket &other) = default; // Copy constructor, required for QMetaType and queued signals NetworkPacket &operator=(const NetworkPacket &other) = default; - static void createIdentityPacket(NetworkPacket *); - QByteArray serialize() const; static bool unserialize(const QByteArray &json, NetworkPacket *out); diff --git a/core/pluginloader.cpp b/core/pluginloader.cpp index 7f3efcaa5..55ed95af0 100644 --- a/core/pluginloader.cpp +++ b/core/pluginloader.cpp @@ -52,6 +52,11 @@ QStringList PluginLoader::getPluginList() const return plugins.keys(); } +bool PluginLoader::doesPluginExist(const QString &name) const +{ + return plugins.contains(name); +} + KPluginMetaData PluginLoader::getPluginInfo(const QString &name) const { return plugins.value(name); @@ -116,7 +121,7 @@ QSet PluginLoader::pluginsForCapabilities(const QSet &incoming { QSet ret; - QString myDeviceType = KdeConnectConfig::instance().deviceType(); + QString myDeviceType = KdeConnectConfig::instance().deviceType().toString(); for (const KPluginMetaData &service : qAsConst(plugins)) { diff --git a/core/pluginloader.h b/core/pluginloader.h index 53093dbc4..d6017f08f 100644 --- a/core/pluginloader.h +++ b/core/pluginloader.h @@ -25,6 +25,7 @@ public: static PluginLoader *instance(); QStringList getPluginList() const; + bool doesPluginExist(const QString &name) const; KPluginMetaData getPluginInfo(const QString &name) const; KdeConnectPlugin *instantiatePluginForDevice(const QString &name, Device *device) const; diff --git a/tests/sendfiletest.cpp b/tests/sendfiletest.cpp index d17dc2732..1c5345a4a 100644 --- a/tests/sendfiletest.cpp +++ b/tests/sendfiletest.cpp @@ -84,24 +84,18 @@ private Q_SLOTS: const QString destFile = QDir::tempPath() + QStringLiteral("/kdeconnect-test-sentfile"); QFile(destFile).remove(); - const QString deviceId = KdeConnectConfig::instance().deviceId(), deviceName = QStringLiteral("testdevice"), - deviceType = KdeConnectConfig::instance().deviceType(); - - KdeConnectConfig::instance().addTrustedDevice(deviceId, deviceName, deviceType); - KdeConnectConfig::instance().setDeviceProperty( - deviceId, - QStringLiteral("certificate"), - QString::fromLatin1(KdeConnectConfig::instance().certificate().toPem())); // Using same certificate from kcc, instead of generating + DeviceInfo deviceInfo = KdeConnectConfig::instance().deviceInfo(); + KdeConnectConfig::instance().addTrustedDevice(deviceInfo); // We need the device to be loaded on the daemon, otherwise CompositeUploadJob will get a null device - Device *device = new Device(this, deviceId); + Device *device = new Device(this, deviceInfo.id); m_daemon->addDevice(device); QSharedPointer f(new QFile(aFile)); NetworkPacket np(PACKET_TYPE_SHARE_REQUEST); np.setPayload(f, f->size()); - CompositeUploadJob *job = new CompositeUploadJob(deviceId, false); + CompositeUploadJob *job = new CompositeUploadJob(deviceInfo.id, false); UploadJob *uj = new UploadJob(np); job->addSubjob(uj);