From 9bcef4c0a996e7e90a2822ef479ebb6180cd0eaf Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Thu, 7 May 2026 00:15:23 -0400 Subject: [PATCH] Backend: align signal connections with new TunnelManager API; fix fromConf usage --- app/vpn/dragonrelaybackend.cpp | 144 +++++++++++---------------------- 1 file changed, 47 insertions(+), 97 deletions(-) diff --git a/app/vpn/dragonrelaybackend.cpp b/app/vpn/dragonrelaybackend.cpp index 18764d1..d5b0e94 100644 --- a/app/vpn/dragonrelaybackend.cpp +++ b/app/vpn/dragonrelaybackend.cpp @@ -6,36 +6,36 @@ #include #include -// ── Constructor / Destructor ────────────────────────────────────────────────── +// Constructor / Destructor DragonRelayBackend::DragonRelayBackend(QObject *parent) : QObject(parent) , m_settings(QStringLiteral("WildDragon"), QStringLiteral("DragonMoonlight")) { - // ── Wire RelayClient signals ────────────────────────────────────────────── + // Wire RelayClient signals connect(&m_relay, &RelayClient::loginSucceeded, this, [this]() { onLoginDone(true, QString()); }); - connect(&m_relay, QOverload::of(&RelayClient::loginFailed), + connect(&m_relay, &RelayClient::loginFailed, this, [this](const QString &err) { onLoginDone(false, err); }); connect(&m_relay, &RelayClient::vpnProvisioned, this, [this](const RelayVPNConf &conf) { onVPNPeerProvisioned(conf); }); - connect(&m_relay, QOverload::of(&RelayClient::vpnProvisionFailed), + connect(&m_relay, &RelayClient::vpnProvisionFailed, this, [this](const QString &err) { onVPNError(err); }); connect(&m_relay, &RelayClient::hostsReady, this, [this](const QList &hosts) { onHostsFetched(hosts); }); - connect(&m_relay, QOverload::of(&RelayClient::hostsFetchFailed), + connect(&m_relay, &RelayClient::hostsFetchFailed, this, [this](const QString &err) { onHostsError(err); }); - // ── Wire TunnelManager signals ──────────────────────────────────────────── + // Wire TunnelManager signals connect(&m_tunnel, &TunnelManager::tunnelUp, this, &DragonRelayBackend::onTunnelUp); connect(&m_tunnel, &TunnelManager::tunnelDown, this, &DragonRelayBackend::onTunnelDown); - connect(&m_tunnel, &TunnelManager::error, + connect(&m_tunnel, &TunnelManager::tunnelError, this, &DragonRelayBackend::onTunnelError); - // ── Host poll timer (every 30 s while tunnel is up) ─────────────────────── - m_hostPollTimer.setInterval(30'000); + // Host poll timer (every 30 s while tunnel is up) + m_hostPollTimer.setInterval(30 * 1000); m_hostPollTimer.setSingleShot(false); connect(&m_hostPollTimer, &QTimer::timeout, this, &DragonRelayBackend::pollHosts); @@ -46,13 +46,10 @@ DragonRelayBackend::DragonRelayBackend(QObject *parent) } DragonRelayBackend::~DragonRelayBackend() { - // Best-effort cleanup — don't block the destructor m_hostPollTimer.stop(); m_tunnel.stop(); } -// ── Status helper ───────────────────────────────────────────────────────────── - void DragonRelayBackend::setStatus(Status s, const QString &text) { bool changed = (m_status != s) || (!text.isEmpty() && text != m_statusText); m_status = s; @@ -62,8 +59,6 @@ void DragonRelayBackend::setStatus(Status s, const QString &text) { emit statusChanged(); } -// ── connectRelay ────────────────────────────────────────────────────────────── - void DragonRelayBackend::connectRelay(const QString &url, const QString &username, const QString &password) { @@ -74,16 +69,14 @@ void DragonRelayBackend::connectRelay(const QString &url, m_savedUser = username; m_savedPass = password; - // Persist URL + username (NOT password) + // Persist URL + username (NOT password - keychain integration is a TODO). m_settings.setValue(QStringLiteral("relay/url"), url); m_settings.setValue(QStringLiteral("relay/username"), username); - setStatus(Connecting, QStringLiteral("Logging in…")); + setStatus(Connecting, QStringLiteral("Logging in...")); m_relay.login(QUrl(url), username, password); } -// ── disconnectRelay ─────────────────────────────────────────────────────────── - void DragonRelayBackend::disconnectRelay() { stopHostPoll(); m_tunnel.stop(); // triggers onTunnelDown asynchronously @@ -97,97 +90,53 @@ void DragonRelayBackend::disconnectRelay() { setStatus(Disconnected, QStringLiteral("Disconnected")); } -// ── streamHost ──────────────────────────────────────────────────────────────── - void DragonRelayBackend::streamHost(const QString &ip, const QString &app, int displayIndex) { if (m_status != Ready && m_status != TunnelUp) { qWarning() << "DragonRelayBackend::streamHost called while not ready"; return; } - // Delegate to moonlight-qt's existing stream launch mechanism. - // The standard Moonlight PC model uses ComputerManager; we invoke it - // via a QProcess for now so we don't have to patch PC discovery internals. - - qDebug() << "DragonRelayBackend::streamHost called with ip=" << ip - << "app=" << app << "displayIndex=" << displayIndex; - -#if defined(Q_OS_WIN) - QStringList args{ - QStringLiteral("stream"), - ip, - app - }; - if (displayIndex > 0) { - // --display : moonlight-qt CLI display selection flag (0 = primary) - // Verify with: moonlight --help - args << QStringLiteral("--display") << QString::number(displayIndex); - } - QProcess::startDetached(QStringLiteral("moonlight"), args); -#elif defined(Q_OS_MACOS) - QStringList args{ - QStringLiteral("stream"), - ip, - app - }; - if (displayIndex > 0) { - // --display : moonlight-qt CLI display selection flag (0 = primary) - // Verify with: moonlight --help - args << QStringLiteral("--display") << QString::number(displayIndex); - } - QProcess::startDetached(QStringLiteral("moonlight"), args); -#else - QStringList args{ - QStringLiteral("stream"), - ip, - app - }; - if (displayIndex > 0) { - // --display : moonlight-qt CLI display selection flag (0 = primary) - // Verify with: moonlight --help - args << QStringLiteral("--display") << QString::number(displayIndex); - } - QProcess::startDetached(QStringLiteral("moonlight"), args); -#endif -} -// ── displaysForHost ─────────────────────────────────────────────────────────── + qDebug() << "DragonRelayBackend::streamHost ip=" << ip + << "app=" << app << "displayIndex=" << displayIndex; + + QStringList args; + args << QStringLiteral("stream") << ip << app; + if (displayIndex > 0) { + args << QStringLiteral("--display") << QString::number(displayIndex); + } + + QProcess::startDetached(QStringLiteral("moonlight"), args); +} QVariantList DragonRelayBackend::displaysForHost(const QString &hostIP) const { return m_hostDisplays.value(hostIP); } -// ── streamHostDisplay ───────────────────────────────────────────────────────── - void DragonRelayBackend::streamHostDisplay(const QString &hostIP, int displayIndex) { qDebug() << "Streaming host" << hostIP << "display index" << displayIndex; - - // Add displayIndex validation near the top of streamHost() + const QVariantList displays = displaysForHost(hostIP); if (displayIndex < 0 || (!displays.isEmpty() && displayIndex >= displays.size())) { qWarning() << "DragonRelayBackend: displayIndex" << displayIndex - << "out of bounds for host" << hostIP << "(has" << displays.size() << "displays)"; + << "out of bounds for host" << hostIP + << "(has" << displays.size() << "displays)"; displayIndex = 0; // fall back to primary } - - // Pass displayIndex to streamHost + streamHost(hostIP, QStringLiteral("Desktop"), displayIndex); } -// ── refreshHosts ───────────────────────────────────────────────────────────── - void DragonRelayBackend::refreshHosts() { if (m_status == TunnelUp || m_status == Ready) m_relay.fetchHosts(); } -// ── pollHosts ───────────────────────────────────────────────────────────────── - void DragonRelayBackend::pollHosts() { m_relay.fetchHosts(); } void DragonRelayBackend::startHostPoll() { - m_relay.fetchHosts(); // immediate fetch + m_relay.fetchHosts(); m_hostPollTimer.start(); } @@ -195,30 +144,30 @@ void DragonRelayBackend::stopHostPoll() { m_hostPollTimer.stop(); } -// ── RelayClient slots ───────────────────────────────────────────────────────── - void DragonRelayBackend::onLoginDone(bool ok, const QString &err) { if (!ok) { setStatus(Error, QStringLiteral("Login failed: ") + err); return; } - setStatus(Connecting, QStringLiteral("Provisioning VPN peer…")); + setStatus(Connecting, QStringLiteral("Provisioning VPN peer...")); m_relay.provisionVPN(); } void DragonRelayBackend::onVPNPeerProvisioned(const RelayVPNConf &conf) { - setStatus(Connecting, QStringLiteral("Starting WireGuard tunnel…")); + setStatus(Connecting, QStringLiteral("Starting WireGuard tunnel...")); - WireGuardConfig cfg; - QString parseErr; - if (!WireGuardConfig::fromConf(conf.conf, cfg, parseErr)) { - setStatus(Error, QStringLiteral("Bad VPN config: ") + parseErr); + WireGuardConfig cfg = WireGuardConfig::fromConf(conf.conf); + if (!cfg.isValid()) { + setStatus(Error, QStringLiteral( + "Bad VPN config: missing PrivateKey, Address, PublicKey, or Endpoint")); m_relay.revokeVPN(); return; } - m_tunnel.start(cfg); - // onTunnelUp/onTunnelError fired from TunnelManager + if (!m_tunnel.start(cfg)) { + // tunnel.start() emits tunnelError() synchronously on failure, + // which routes through onTunnelError(). + } } void DragonRelayBackend::onVPNError(const QString &err) { @@ -228,7 +177,7 @@ void DragonRelayBackend::onVPNError(const QString &err) { void DragonRelayBackend::onHostsFetched(const QList &hosts) { m_hosts.clear(); m_hostDisplays.clear(); - + for (const auto &h : hosts) { QVariantMap m; m[QStringLiteral("name")] = h.name; @@ -237,8 +186,7 @@ void DragonRelayBackend::onHostsFetched(const QList &hosts) { m[QStringLiteral("online")] = true; m[QStringLiteral("source")] = QStringLiteral("relay"); m_hosts.append(m); - - // Store displays for this host + m_hostDisplays[h.ip] = h.displays; } emit hostsChanged(); @@ -246,23 +194,25 @@ void DragonRelayBackend::onHostsFetched(const QList &hosts) { Status next = m_hosts.isEmpty() ? TunnelUp : Ready; if (m_status != next) { QString text = m_hosts.isEmpty() - ? QStringLiteral("Tunnel up — no hosts yet") - : QStringLiteral("Connected — %1 host(s)").arg(m_hosts.size()); + ? QStringLiteral("Tunnel up - no hosts yet") + : QStringLiteral("Connected - %1 host(s)").arg(m_hosts.size()); setStatus(next, text); } } void DragonRelayBackend::onHostsError(const QString &err) { qWarning() << "DragonRelayBackend: hosts fetch error:" << err; - // Don't downgrade to Error state — the tunnel is still up; just log it + // Don't downgrade to Error state - tunnel is still up. } -// ── TunnelManager slots ─────────────────────────────────────────────────────── - void DragonRelayBackend::onTunnelUp(const QString &localIP) { + if (m_tunnelIP == localIP && (m_status == TunnelUp || m_status == Ready)) + return; // ignore duplicate signals (first-packet path + 1.5 s fallback timer) + m_tunnelIP = localIP; emit tunnelIPChanged(); - setStatus(TunnelUp, QStringLiteral("Tunnel up (%1) — fetching hosts…").arg(localIP)); + setStatus(TunnelUp, + QStringLiteral("Tunnel up (%1) - fetching hosts...").arg(localIP)); startHostPoll(); }