Add DeviceInfo class

Equivalent to this Android MR (see description there): https://invent.kde.org/network/kdeconnect-android/-/merge_requests/374
This commit is contained in:
Albert Vaca Cintora 2023-06-27 11:10:59 +00:00
parent 70bb10d105
commit db546e7608
27 changed files with 450 additions and 412 deletions

View file

@ -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<MultiplexChannel> 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
}

View file

@ -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<MultiplexChannel> socket);
BluetoothDeviceLink(const DeviceInfo &deviceInfo,
BluetoothLinkProvider *parent,
ConnectionMultiplexer *connection,
QSharedPointer<MultiplexChannel> 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<MultiplexChannel> mChannel;
DeviceInfo mDeviceInfo;
void sendMessage(const QString mMessage);
};

View file

@ -8,6 +8,7 @@
#include "bluetoothdevicelink.h"
#include "connectionmultiplexer.h"
#include "core_debug.h"
#include "kdeconnectconfig.h"
#include "multiplexchannel.h"
#include <QBluetoothServiceInfo>
@ -83,13 +84,7 @@ void BluetoothLinkProvider::connectError()
void BluetoothLinkProvider::addLink(BluetoothDeviceLink *deviceLink, const QString &deviceId)
{
QMap<QString, DeviceLink *>::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<QString>(QStringLiteral("deviceId"));
BluetoothDeviceLink *deviceLink = new BluetoothDeviceLink(deviceId, this, mSockets[peer], socket);
QSslCertificate receivedCertificate(receivedPacket.get<QString>(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<QString>(QStringLiteral("deviceId"));
BluetoothDeviceLink *deviceLink = new BluetoothDeviceLink(deviceId, this, mSockets[peer], socket);
QSslCertificate receivedCertificate(receivedPacket.get<QString>(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<QString, DeviceLink *>::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)

View file

@ -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();

View file

@ -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);
});
}

View file

@ -9,42 +9,29 @@
#include <QObject>
#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

View file

@ -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();
}

View file

@ -9,16 +9,17 @@
#include <QObject>
#include <QPointer>
#include <QSslCertificate>
#include <QSslSocket>
#include <QString>
#include "backends/devicelink.h"
#include "compositeuploadjob.h"
#include "deviceinfo.h"
#include "uploadjob.h"
#include <kdeconnectcore_export.h>
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<CompositeUploadJob> m_compositeUploadJob;
DeviceInfo m_deviceInfo;
};
#endif

View file

@ -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<QHostAddress> 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<int>(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<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors);
socket->startServerEncryption();
return; // Return statement prevents from deleting received packet, needed in slot "encrypted"
} else {
qWarning() << receivedPacket->get<QString>(QStringLiteral("deviceName")) << "uses an old protocol version, this won't work";
// addLink(deviceId, socket, receivedPacket, LanDeviceLink::Remotely);
}
connect(socket, QOverload<const QList<QSslError> &>::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<int>(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<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors);
}
socket->startClientEncryption();
} else {
qWarning() << np->get<QString>(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<const QList<QSslError> &>::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<QString, LanDeviceLink *>::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<QString, LanDeviceLink *>::iterator linkIterator = m_links.find(deviceId);
QMap<QString, LanDeviceLink *>::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);
}

View file

@ -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<QSslError> &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<QHostAddress> getBroadcastAddresses();
void sendBroadcasts(QUdpSocket &socket, const NetworkPacket &np, const QList<QHostAddress> &addresses);

View file

@ -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

View file

@ -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();
}

View file

@ -8,7 +8,7 @@
#define LOOPBACKDEVICELINK_H
#include "../devicelink.h"
#include <QSslCertificate>
#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

View file

@ -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;

View file

@ -26,10 +26,12 @@ public:
void onStart() override;
void onStop() override;
void onNetworkChange() override;
void onLinkDestroyed(const QString &, DeviceLink *) override
{
}
private:
QPointer<LoopbackDeviceLink> loopbackDeviceLink;
NetworkPacket identityPacket;
};
#endif

View file

@ -158,24 +158,24 @@ QMap<QString, QString> 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<QString>(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<QString>(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<QString>(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);
}
}

View file

@ -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:

View file

@ -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<DeviceLink *> m_deviceLinks;
QHash<QString, KdeConnectPlugin *> m_plugins;
QMultiMap<QString, KdeConnectPlugin *> m_pluginsByIncomingCapability;
QSet<QString> m_supportedPlugins;
QSet<QString> 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<QString>(QStringLiteral("deviceId"))))
{
d->m_pairingHandler = new PairingHandler(this, PairState::NotPaired);
d->m_deviceName = identityPacket.get<QString>(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<QString, KdeConnectPlugin *> newPluginMap, oldPluginMap = d->m_plugins;
QMultiMap<QString, KdeConnectPlugin *> 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<QString>(QStringLiteral("deviceName")));
setType(identityPacket.get<QString>(QStringLiteral("deviceType")));
if (d->m_deviceLinks.contains(link)) {
return;
}
d->m_protocolVersion = identityPacket.get<int>(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<QString> outgoingCapabilities = identityPacket.get<QStringList>(QStringLiteral("outgoingCapabilities")).toSet(),
incomingCapabilities = identityPacket.get<QStringList>(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

View file

@ -12,6 +12,7 @@
#include <QString>
#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:

149
core/deviceinfo.h Normal file
View file

@ -0,0 +1,149 @@
/**
* 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 DEVICE_INFO_H
#define DEVICE_INFO_H
#include "networkpacket.h"
#include <QSet>
#include <QSslCertificate>
#include <QString>
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<QString> incomingCapabilities;
QSet<QString> outgoingCapabilities;
DeviceInfo(const QString &id,
const QSslCertificate &certificate,
const QString &name,
DeviceType type,
int protocolVersion = 0,
const QSet<QString> &incomingCapabilities = QSet<QString>(),
const QSet<QString> &outgoingCapabilities = QSet<QString>())
: 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<QStringList>(QStringLiteral("incomingCapabilities"));
QStringList outgoingCapabilities = np.get<QStringList>(QStringLiteral("outgoingCapabilities"));
return DeviceInfo(np.get<QString>(QStringLiteral("deviceId")),
certificate,
np.get<QString>(QStringLiteral("deviceName")),
DeviceType::FromString(np.get<QString>(QStringLiteral("deviceType"))),
np.get<int>(QStringLiteral("protocolVersion"), -1),
QSet<QString>(incomingCapabilities.begin(), incomingCapabilities.end()),
QSet<QString>(outgoingCapabilities.begin(), outgoingCapabilities.end()));
}
};
#endif

View file

@ -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)

View file

@ -9,6 +9,7 @@
#include <QDir>
#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());

View file

@ -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<QIODevice>();
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

View file

@ -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);

View file

@ -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<QString> PluginLoader::pluginsForCapabilities(const QSet<QString> &incoming
{
QSet<QString> ret;
QString myDeviceType = KdeConnectConfig::instance().deviceType();
QString myDeviceType = KdeConnectConfig::instance().deviceType().toString();
for (const KPluginMetaData &service : qAsConst(plugins)) {

View file

@ -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;

View file

@ -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<QFile> 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);