Pith - wedge_linux
wedge_linux/WedgeNetwork.cpp [8.9 kb]
Modified: 18:56:19 154 026 (20 Aug 026)
9 Days Ago
#include "WedgeNetwork.h"
#include <QByteArray>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include "WedgeConfig.h"

NetworkManager::NetworkManager(QObject *parent)
    : QObject(parent), socket(nullptr), server(nullptr), m_connected(false),
      adbTracker(nullptr), m_metaSeen(false), m_syncPending(false), m_adbForwarded(false),
      m_autoMerge(ConfigManager::loadNote().autoMerge), m_peerAutoMerge(false),
      m_peerLastModified(0) {
    dialTimer = new QTimer(this);
    dialTimer->setSingleShot(true);
    dialTimer->setInterval(5000);
    connect(dialTimer, &QTimer::timeout, this, &NetworkManager::handleDialTimeout);

    idleTimer = new QTimer(this);
    idleTimer->setSingleShot(true);
    idleTimer->setInterval(3000);
    connect(idleTimer, &QTimer::timeout, this, &NetworkManager::handleIdleTimeout);
}

NetworkManager::~NetworkManager() {
    disconnectFromPeer();
    teardownAdb();
}

void NetworkManager::startListener() {
    if (server) return;
    server = new QTcpServer(this);
    connect(server, &QTcpServer::newConnection, this, &NetworkManager::handleNewConnection);
    server->listen(QHostAddress::Any, PORT_HOST);
    startAdbTracker();
}

// adb tunnel on connect
void NetworkManager::startAdbTracker() {
    adbTracker = new QProcess(this);
    connect(adbTracker, &QProcess::readyReadStandardOutput,
            this, &NetworkManager::handleAdbTrackerOutput);
    connect(adbTracker, &QProcess::finished, this, [this]() {
        QTimer::singleShot(2000, this, [this]() {
            adbTracker->start("adb", QStringList() << "track-devices");
        });
    });
    adbTracker->start("adb", QStringList() << "track-devices");
}

void NetworkManager::handleAdbTrackerOutput() {
    const QString chunk = QString::fromUtf8(adbTracker->readAllStandardOutput());
    const bool devicePresent = chunk.contains("\tdevice");
    if (devicePresent && !m_adbForwarded) {
        runAdbTunnels();
        emit statusChanged("Idle", false);
        if (m_autoMerge && !m_connected &&
            ConfigManager::loadNote().networkRoute == 0) { dial(Route::USB); }
    } else if (!devicePresent) {
        m_adbForwarded = false;
    }
}

void NetworkManager::requestSync(Route route) {
    if (m_metaSeen && socket && socket->state() == QAbstractSocket::ConnectedState) {
        emit linkReady();
        return;
    }
    m_syncPending = true;
    if (route == Route::USB && !m_adbForwarded) { runAdbTunnels(); }
    dial(route);
}

void NetworkManager::runAdbTunnels() {
    emit statusChanged("ADB Tunnel", false);
    QString phonePort = QString("tcp:%1").arg(PORT_PHONE);
    QString hostPort = QString("tcp:%1").arg(PORT_HOST);
    QProcess::execute("adb", QStringList() << "forward" << phonePort << phonePort);
    QProcess::execute("adb", QStringList() << "reverse" << hostPort << hostPort);
    m_adbForwarded = true;
}

void NetworkManager::teardownAdb() {
    if (!m_adbForwarded) return;
    m_adbForwarded = false;
    QProcess::execute("adb", QStringList() << "forward" << "--remove-all");
    QProcess::execute("adb", QStringList() << "reverse" << "--remove-all");
}

void NetworkManager::dial(Route route) {
    if (socket) {
        socket->disconnect(this);
        socket->abort();
        socket->deleteLater();
        socket = nullptr;
    }
    m_metaSeen = false;

    socket = new QTcpSocket(this);
    attachSocket(socket);

    emit statusChanged("Connecting", false);

    dialTimer->start();
    
    QString targetHost = (route == Route::USB) ? "127.0.0.1" : ConfigManager::loadNote().ipAddress;
    socket->connectToHost(targetHost, PORT_PHONE);
}

void NetworkManager::attachSocket(QTcpSocket *s) {
    connect(s, &QTcpSocket::connected, this, &NetworkManager::handleConnected);
    connect(s, &QTcpSocket::disconnected, this, &NetworkManager::handleDisconnected);
    connect(s, &QTcpSocket::readyRead, this, &NetworkManager::handleReadyRead);
    connect(s, &QTcpSocket::errorOccurred, this, &NetworkManager::handleSocketError);
}

void NetworkManager::handleNewConnection() {
    if (!server) return;
    
    QTcpSocket *incoming = server->nextPendingConnection();
    if (socket) { incoming->abort(); incoming->deleteLater(); return; }

    socket = incoming;
    m_metaSeen = false;
    attachSocket(socket);

    handleConnected();
}

void NetworkManager::handleConnected() {
    m_connected = true;
    emit statusChanged("Connected", true);
    sendMeta();
}

void NetworkManager::handleDisconnected() {
    m_connected = false;
    m_metaSeen = false;
    m_syncPending = false;
    m_peerAutoMerge = false;
    dialTimer->stop();
    idleTimer->stop();
    emit statusChanged("Idle", false);
    
    if (socket) {
        socket->disconnect();
        socket->deleteLater();
        socket = nullptr;
    }
}

void NetworkManager::handleDialTimeout() {
    if (m_metaSeen) return;
    if (socket && socket->state() != QAbstractSocket::UnconnectedState) {
        socket->abort();
    }
    handleDisconnected();
    emit statusChanged("No Peer", false);
}

void NetworkManager::handleSocketError(QAbstractSocket::SocketError) {
    if (socket) {
        emit statusChanged(socket->errorString(), false);
        handleDisconnected();
    }
}

void NetworkManager::handleReadyRead() {
    if (!socket) return;

    while (socket->canReadLine()) {
        QByteArray line = socket->readLine().trimmed();
        if (!line.isEmpty()) {
            QByteArray decodedBytes = QByteArray::fromBase64(line);
            QJsonDocument doc = QJsonDocument::fromJson(decodedBytes);
            
            if (doc.isObject()) {
                QJsonObject json = doc.object();
                QString receivedKey = json["key"].toString();

                QString localKey = ConfigManager::loadNote().passKey;
                if (receivedKey != localKey) {
                    qDebug() << "Key Fail";
                    emit statusChanged("Pass Key Incorrect", false);
                    socket->abort();
                    return;
                }

                const QString type = json.contains("type") ? json["type"].toString() : "data";
                if (type == "meta") {
                    m_peerLastModified = json["date"].toVariant().toLongLong();
                    m_peerAutoMerge = json["auto"].toBool();
                    emit statusChanged("Connected", true);
                    m_metaSeen = true;
                    dialTimer->stop();
                    if (m_syncPending) { m_syncPending = false; emit linkReady(); }
                    if (autoMergeReady()) emit autoSyncDue();
                } else {
                    m_peerLastModified = json["date"].toVariant().toLongLong();
                    m_peerAutoMerge = json["auto"].toBool();
                    QString cleanMessage = json["data"].toString();
                    emit contentReceived(cleanMessage);
                    emit statusChanged("Received", true);
                    m_metaSeen = true;
                    dialTimer->stop();
                    if (m_syncPending) { m_syncPending = false; emit linkReady(); }
                    noteActivity();
                }
            }
        }
    }
}

void NetworkManager::sendNoteContent(const QString &content, const QString &passKey, qint64 lastModified) {
    if (!m_connected || !socket) return;

    QJsonObject json;
    json["type"] = "data";
    json["key"] = passKey;
    json["date"] = lastModified;
    json["auto"] = m_autoMerge;
    json["data"] = content;

    QByteArray encodedPayload = QJsonDocument(json).toJson(QJsonDocument::Compact).toBase64();
    encodedPayload.append('\n');
    if (socket->write(encodedPayload) != -1) {
        socket->flush();
        m_peerLastModified = lastModified;
        emit statusChanged("Sent", true);
    }
}

bool NetworkManager::isConnected() const {
    return m_connected;
}

qint64 NetworkManager::peerLastModified() const {
    return m_peerLastModified;
}

void NetworkManager::sendMeta() {
    if (!socket) return;
    NoteData local = ConfigManager::loadNote();
    QJsonObject json;
    json["type"] = "meta";
    json["key"] = local.passKey;
    json["date"] = local.lastModified;
    json["auto"] = m_autoMerge;
    QByteArray payload = QJsonDocument(json).toJson(QJsonDocument::Compact).toBase64();
    payload.append('\n');
    if (socket->write(payload) != -1) {
        socket->flush();
    }
}

void NetworkManager::disconnectFromPeer() {
    if (socket && socket->state() != QAbstractSocket::UnconnectedState) {
        socket->abort(); 
    } else {
        handleDisconnected();
    }
}

bool NetworkManager::autoMergeReady() const {
    return m_connected && m_autoMerge && m_peerAutoMerge;
}

void NetworkManager::setAutoMerge(bool on) {
    if (m_autoMerge == on) return;
    m_autoMerge = on;
    if (m_connected) sendMeta();
    if (autoMergeReady()) emit autoSyncDue();
    else idleTimer->stop();
}

void NetworkManager::noteActivity() {
    if (!autoMergeReady()) { idleTimer->stop(); return; }
    idleTimer->start();
}

void NetworkManager::handleIdleTimeout() {
    if (autoMergeReady()) emit autoSyncDue();
}
Updates
Stave - Android 158.026
Kerf - Android 157.026
Kiln - Android 157.026
Wedge - Android 156.026
Whittle - Linux 155.026

Menu
Calendar
Project Tin (024/029)
Miter
RSS Feed
User Avatar
@vgmlr
=SUM(parts)
0.00195
260,116 (+530)