[OpenDocString] kdeconnect-kde (cpp)
landevicelink.cpp
LanDeviceLink::LanDeviceLink(const QString &deviceId, LinkProvider *parent, QSslSocket *socket, ConnectionStarted connectionSource)
    : DeviceLink(deviceId, parent)
    , m_socketLineReader(nullptr)
{
    reset(socket, connectionSource);
}
Constructs a device link object and sets up a socket line reader for reading data.
void LanDeviceLink::reset(QSslSocket *socket, ConnectionStarted connectionSource)
{
    if (m_socketLineReader) {
        disconnect(m_socketLineReader->m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
        delete m_socketLineReader;
    }

    m_socketLineReader = new SocketLineReader(socket, this);

    connect(socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
    connect(m_socketLineReader, &SocketLineReader::readyRead, this, &LanDeviceLink::dataReceived);

    // We take ownership of the socket.
    // When the link provider destroys us,
    // 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"));
    DeviceLink::setPairStatus(certString.isEmpty() ? PairStatus::NotPaired : PairStatus::Paired);
}
This sets up the socket line reader and sets up the socket parent for the device link. It also sets the connection source to the socket object and the socket line reader. It also sets the pair status to not paired if the certificate is empty.
QHostAddress LanDeviceLink::hostAddress() const
{
    if (!m_socketLineReader) {
        return QHostAddress::Null;
    }
    QHostAddress addr = m_socketLineReader->m_socket->peerAddress();
    if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
        bool success;
        QHostAddress convertedAddr = QHostAddress(addr.toIPv4Address(&success));
        if (success) {
            qCDebug(KDECONNECT_CORE) << "Converting IPv6" << addr << "to IPv4" << convertedAddr;
            addr = convertedAddr;
        }
    }
    return addr;
}
Returns the host address of the socket from the m_socketLineReader->m_socket->peerAddress() if it is an IPv6 address, and converts it to IPv4 if it is a IPv6 address.
QString LanDeviceLink::name()
{
    return QStringLiteral("LanLink"); // Should be same in both android and kde version
}
Returns the name as a QString. Should be same in both android and kde versions.
bool LanDeviceLink::sendPacket(NetworkPacket &np)
{
    if (np.payload()) {
        if (np.type() == PACKET_TYPE_SHARE_REQUEST && np.payloadSize() >= 0) {
            if (!m_compositeUploadJob || !m_compositeUploadJob->isRunning()) {
                m_compositeUploadJob = new CompositeUploadJob(deviceId(), true);
            }

            m_compositeUploadJob->addSubjob(new UploadJob(np));

            if (!m_compositeUploadJob->isRunning()) {
                m_compositeUploadJob->start();
            }
        } else { // Infinite stream
            CompositeUploadJob *fireAndForgetJob = new CompositeUploadJob(deviceId(), false);
            fireAndForgetJob->addSubjob(new UploadJob(np));
            fireAndForgetJob->start();
        }

        return true;
    } else {
        int written = m_socketLineReader->write(np.serialize());

        // Actually we can't detect if a packet is received or not. We keep TCP
        //"ESTABLISHED" connections that look legit (return true when we use them),
        // but that are actually broken (until keepalive detects that they are down).
        return (written != -1);
    }
}
This sends a network packet, if possible. It first tries to send the packet using the UploadJob singleton, if it doesn't exist or is already running. It first tries to send the packet using the UploadJob singleton, if it doesn't exist or is in progress. It returns true on the first successful attempt.
void LanDeviceLink::dataReceived()
{
    if (!m_socketLineReader->hasPacketsAvailable())
        return;

    const QByteArray serializedPacket = m_socketLineReader->readLine();
    NetworkPacket packet((QString()));
    NetworkPacket::unserialize(serializedPacket, &packet);

    // qCDebug(KDECONNECT_CORE) << "LanDeviceLink dataReceived" << serializedPacket;

    if (packet.type() == PACKET_TYPE_PAIR) {
        // TODO: Handle pair/unpair requests and forward them (to the pairing handler?)
        qobject_cast(provider())->incomingPairPacket(this, packet);
        return;
    }

    if (packet.hasPayloadTransferInfo()) {
        // qCDebug(KDECONNECT_CORE) << "HasPayloadTransferInfo";
        const QVariantMap transferInfo = packet.payloadTransferInfo();

        QSharedPointer socket(new QSslSocket);

        LanLinkProvider::configureSslSocket(socket.data(), deviceId(), true);

        // emit readChannelFinished when the socket gets disconnected. This seems to be a bug in upstream QSslSocket.
        // Needs investigation and upstreaming of the fix. QTBUG-62257
        connect(socket.data(), &QAbstractSocket::disconnected, socket.data(), &QAbstractSocket::readChannelFinished);

        const QString address = m_socketLineReader->peerAddress().toString();
        const quint16 port = transferInfo[QStringLiteral("port")].toInt();
        socket->connectToHostEncrypted(address, port, QIODevice::ReadWrite);
        packet.setPayload(socket, packet.payloadSize());
    }

    Q_EMIT receivedPacket(packet);

    if (m_socketLineReader->hasPacketsAvailable()) {
        QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection);
    }
}
This reads a network packet from the socket line reader, and converts it to a QObject, unmarshals the packet, sets the packet payload on the socket and emits the signal receivedPacket. Finally, it invokes the dataReceived method if the packet has been received.
void LanDeviceLink::userRequestsPair()
{
    if (m_socketLineReader->peerCertificate().isNull()) {
        Q_EMIT pairingError(i18n("This device cannot be paired because it is running an old version of KDE Connect."));
    } else {
        qobject_cast(provider())->userRequestsPair(deviceId());
    }
}
This requests that the device be paired. It checks that the peer certificate is not null. If it is the old version of KDE Connect, it logs a warning.
void LanDeviceLink::userRequestsUnpair()
{
    qobject_cast(provider())->userRequestsUnpair(deviceId());
}
This requests that the device be removed from the pairing list.
void LanDeviceLink::setPairStatus(PairStatus status)
{
    if (status == Paired && m_socketLineReader->peerCertificate().isNull()) {
        Q_EMIT pairingError(i18n("This device cannot be paired because it is running an old version of KDE Connect."));
        return;
    }

    DeviceLink::setPairStatus(status);
    if (status == Paired) {
        Q_ASSERT(KdeConnectConfig::instance().trustedDevices().contains(deviceId()));
        KdeConnectConfig::instance().setDeviceProperty(deviceId(), QStringLiteral("certificate"), QString::fromLatin1(certificate().toPem()));
    }
}
Sets the pair status on the device link. It checks that the device is trusted and sets the certificate to the device property.
bool LanDeviceLink::linkShouldBeKeptAlive()
{
    return true; // FIXME: Current implementation is broken, so for now we will keep links always established

    // We keep the remotely initiated connections, since the remotes require them if they want to request
    // pairing to us, or connections that are already paired. TODO: Keep connections in the process of pairing
    // return (mConnectionSource == ConnectionStarted::Remotely || pairStatus() == Paired);
}
This implements checking if the link should be kept alive. It is only valid for remotes that require pairing to us since the remotes require pairing to us since the connections that are already paired are already paired.
QSslCertificate LanDeviceLink::certificate() const
{
    return m_socketLineReader->peerCertificate();
}
Returns a copy of the certificate object, which uses implicit sharing for its data.