1 /**
2 * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6
7 #include "landevicelink.h"
8
9 #include <KLocalizedString>
10
11 #include "core_debug.h"
12 #include "kdeconnectconfig.h"
13 #include "backends/linkprovider.h"
14 #include "socketlinereader.h"
15 #include "lanlinkprovider.h"
16 #include "plugins/share/shareplugin.h"
17
LanDeviceLink(const QString & deviceId,LinkProvider * parent,QSslSocket * socket,ConnectionStarted connectionSource)18 LanDeviceLink::LanDeviceLink(const QString& deviceId, LinkProvider* parent, QSslSocket* socket, ConnectionStarted connectionSource)
19 : DeviceLink(deviceId, parent)
20 , m_socketLineReader(nullptr)
21 {
22 reset(socket, connectionSource);
23 }
24
reset(QSslSocket * socket,ConnectionStarted connectionSource)25 void LanDeviceLink::reset(QSslSocket* socket, ConnectionStarted connectionSource)
26 {
27 if (m_socketLineReader) {
28 disconnect(m_socketLineReader->m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
29 delete m_socketLineReader;
30 }
31
32 m_socketLineReader = new SocketLineReader(socket, this);
33
34 connect(socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
35 connect(m_socketLineReader, &SocketLineReader::readyRead, this, &LanDeviceLink::dataReceived);
36
37 //We take ownership of the socket.
38 //When the link provider destroys us,
39 //the socket (and the reader) will be
40 //destroyed as well
41 socket->setParent(m_socketLineReader);
42
43 m_connectionSource = connectionSource;
44
45 QString certString = KdeConnectConfig::instance().getDeviceProperty(deviceId(), QStringLiteral("certificate"));
46 DeviceLink::setPairStatus(certString.isEmpty()? PairStatus::NotPaired : PairStatus::Paired);
47 }
48
hostAddress() const49 QHostAddress LanDeviceLink::hostAddress() const
50 {
51 if (!m_socketLineReader) {
52 return QHostAddress::Null;
53 }
54 QHostAddress addr = m_socketLineReader->m_socket->peerAddress();
55 if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
56 bool success;
57 QHostAddress convertedAddr = QHostAddress(addr.toIPv4Address(&success));
58 if (success) {
59 qCDebug(KDECONNECT_CORE) << "Converting IPv6" << addr << "to IPv4" << convertedAddr;
60 addr = convertedAddr;
61 }
62 }
63 return addr;
64 }
65
name()66 QString LanDeviceLink::name()
67 {
68 return QStringLiteral("LanLink"); // Should be same in both android and kde version
69 }
70
sendPacket(NetworkPacket & np)71 bool LanDeviceLink::sendPacket(NetworkPacket& np)
72 {
73 if (np.payload()) {
74 if (np.type() == PACKET_TYPE_SHARE_REQUEST && np.payloadSize() >= 0) {
75 if (!m_compositeUploadJob || !m_compositeUploadJob->isRunning()) {
76 m_compositeUploadJob = new CompositeUploadJob(deviceId(), true);
77 }
78
79 m_compositeUploadJob->addSubjob(new UploadJob(np));
80
81 if (!m_compositeUploadJob->isRunning()) {
82 m_compositeUploadJob->start();
83 }
84 } else { //Infinite stream
85 CompositeUploadJob* fireAndForgetJob = new CompositeUploadJob(deviceId(), false);
86 fireAndForgetJob->addSubjob(new UploadJob(np));
87 fireAndForgetJob->start();
88 }
89
90 return true;
91 } else {
92 int written = m_socketLineReader->write(np.serialize());
93
94 //Actually we can't detect if a packet is received or not. We keep TCP
95 //"ESTABLISHED" connections that look legit (return true when we use them),
96 //but that are actually broken (until keepalive detects that they are down).
97 return (written != -1);
98 }
99 }
100
dataReceived()101 void LanDeviceLink::dataReceived()
102 {
103 if (!m_socketLineReader->hasPacketsAvailable()) return;
104
105 const QByteArray serializedPacket = m_socketLineReader->readLine();
106 NetworkPacket packet((QString()));
107 NetworkPacket::unserialize(serializedPacket, &packet);
108
109 //qCDebug(KDECONNECT_CORE) << "LanDeviceLink dataReceived" << serializedPacket;
110
111 if (packet.type() == PACKET_TYPE_PAIR) {
112 //TODO: Handle pair/unpair requests and forward them (to the pairing handler?)
113 qobject_cast<LanLinkProvider*>(provider())->incomingPairPacket(this, packet);
114 return;
115 }
116
117 if (packet.hasPayloadTransferInfo()) {
118 //qCDebug(KDECONNECT_CORE) << "HasPayloadTransferInfo";
119 const QVariantMap transferInfo = packet.payloadTransferInfo();
120
121 QSharedPointer<QSslSocket> socket(new QSslSocket);
122
123 LanLinkProvider::configureSslSocket(socket.data(), deviceId(), true);
124
125 // emit readChannelFinished when the socket gets disconnected. This seems to be a bug in upstream QSslSocket.
126 // Needs investigation and upstreaming of the fix. QTBUG-62257
127 connect(socket.data(), &QAbstractSocket::disconnected, socket.data(), &QAbstractSocket::readChannelFinished);
128
129 const QString address = m_socketLineReader->peerAddress().toString();
130 const quint16 port = transferInfo[QStringLiteral("port")].toInt();
131 socket->connectToHostEncrypted(address, port, QIODevice::ReadWrite);
132 packet.setPayload(socket, packet.payloadSize());
133 }
134
135 Q_EMIT receivedPacket(packet);
136
137 if (m_socketLineReader->hasPacketsAvailable()) {
138 QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection);
139 }
140
141 }
142
userRequestsPair()143 void LanDeviceLink::userRequestsPair()
144 {
145 if (m_socketLineReader->peerCertificate().isNull()) {
146 Q_EMIT pairingError(i18n("This device cannot be paired because it is running an old version of KDE Connect."));
147 } else {
148 qobject_cast<LanLinkProvider*>(provider())->userRequestsPair(deviceId());
149 }
150 }
151
userRequestsUnpair()152 void LanDeviceLink::userRequestsUnpair()
153 {
154 qobject_cast<LanLinkProvider*>(provider())->userRequestsUnpair(deviceId());
155 }
156
setPairStatus(PairStatus status)157 void LanDeviceLink::setPairStatus(PairStatus status)
158 {
159 if (status == Paired && m_socketLineReader->peerCertificate().isNull()) {
160 Q_EMIT pairingError(i18n("This device cannot be paired because it is running an old version of KDE Connect."));
161 return;
162 }
163
164 DeviceLink::setPairStatus(status);
165 if (status == Paired) {
166 Q_ASSERT(KdeConnectConfig::instance().trustedDevices().contains(deviceId()));
167 KdeConnectConfig::instance().setDeviceProperty(deviceId(), QStringLiteral("certificate"), QString::fromLatin1(certificate().toPem()));
168 }
169 }
170
linkShouldBeKeptAlive()171 bool LanDeviceLink::linkShouldBeKeptAlive() {
172
173 return true; //FIXME: Current implementation is broken, so for now we will keep links always established
174
175 //We keep the remotely initiated connections, since the remotes require them if they want to request
176 //pairing to us, or connections that are already paired. TODO: Keep connections in the process of pairing
177 //return (mConnectionSource == ConnectionStarted::Remotely || pairStatus() == Paired);
178
179 }
180
certificate() const181 QSslCertificate LanDeviceLink::certificate() const
182 {
183 return m_socketLineReader->peerCertificate();
184 }
185