RelayClient: fix syntax error in savedServers(); strip trailing slash on relay URLs

This commit is contained in:
Zac Gaetano 2026-05-07 00:15:56 -04:00
parent 9bcef4c0a9
commit f270350ae6

View file

@ -14,23 +14,22 @@
RelayClient::RelayClient(QObject *parent)
: QObject(parent)
, m_nam(new QNetworkAccessManager(this))
, m_settings(new QSettings(QStringLiteral("WildDragon"), QStringLiteral("DragonMoonlight"), this))
, m_settings(new QSettings(QStringLiteral("WildDragon"),
QStringLiteral("DragonMoonlight"), this))
{
}
RelayClient::~RelayClient() = default;
// ─── URL helpers ──────────────────────────────────────────────────────────────
QUrl RelayClient::apiUrl(const QString &path) const
{
QUrl u = m_relayUrl;
u.setPath(u.path() + path);
QString base = u.path();
while (base.endsWith(QLatin1Char('/'))) base.chop(1);
u.setPath(base + path);
return u;
}
// ─── Auth-header injection ────────────────────────────────────────────────
QNetworkReply *RelayClient::authedGet(const QString &path)
{
QNetworkRequest req(apiUrl(path));
@ -53,8 +52,6 @@ QNetworkReply *RelayClient::authedDelete(const QString &path)
return m_nam->deleteResource(req);
}
// ─── login() ─────────────────────────────────────────────────────────────────
void RelayClient::login(const QUrl &relayUrl, const QString &username, const QString &password)
{
m_relayUrl = relayUrl;
@ -62,7 +59,8 @@ void RelayClient::login(const QUrl &relayUrl, const QString &username, const QSt
m_jwt.clear();
QNetworkRequest req(apiUrl(QStringLiteral("/api/auth/login")));
req.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/json"));
req.setHeader(QNetworkRequest::ContentTypeHeader,
QByteArrayLiteral("application/json"));
QJsonObject body;
body[QStringLiteral("username")] = username;
@ -87,7 +85,8 @@ void RelayClient::onLoginReply(QNetworkReply *reply, const QString &username,
const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object();
const QString token = json[QStringLiteral("token")].toString();
if (token.isEmpty()) {
const QString err = json[QStringLiteral("error")].toString(QStringLiteral("Unknown error"));
const QString err = json[QStringLiteral("error")]
.toString(QStringLiteral("Unknown error"));
emit loginFailed(err);
return;
}
@ -98,11 +97,8 @@ void RelayClient::onLoginReply(QNetworkReply *reply, const QString &username,
emit loginSucceeded();
}
// ─── provisionVPN() ───────────────────────────────────────────────────────────
void RelayClient::provisionVPN(const QString &deviceName)
{
// Use the machine's host name if the caller didn't provide one.
const QString device = deviceName.isEmpty()
? QSysInfo::machineHostName()
: deviceName;
@ -128,36 +124,34 @@ void RelayClient::onProvisionReply(QNetworkReply *reply)
const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object();
const QString conf = json[QStringLiteral("conf")].toString();
if (conf.isEmpty()) {
const QString err = json[QStringLiteral("error")].toString(QStringLiteral("No conf in response"));
const QString err = json[QStringLiteral("error")]
.toString(QStringLiteral("No conf in response"));
emit vpnProvisionFailed(err);
return;
}
m_vpnPeerId = json[QStringLiteral("id")].toInt(-1);
// Persist peer ID so we can revoke it even after a restart.
m_settings->setValue(QStringLiteral("vpn/peer_id"), m_vpnPeerId);
m_settings->setValue(QStringLiteral("vpn/relay_url"), m_relayUrl.toString());
m_settings->setValue(QStringLiteral("vpn/peer_id"), m_vpnPeerId);
m_settings->setValue(QStringLiteral("vpn/relay_url"), m_relayUrl.toString());
RelayVPNConf vpnConf;
vpnConf.conf = conf;
emit vpnProvisioned(vpnConf);
}
// ─── revokeVPN() ─────────────────────────────────────────────────────────────
void RelayClient::revokeVPN()
{
if (m_vpnPeerId < 0) {
// Try to load from settings (post-restart cleanup).
m_vpnPeerId = m_settings->value(QStringLiteral("vpn/peer_id"), -1).toInt();
}
if (m_vpnPeerId < 0) {
emit vpnRevoked(); // nothing to do
emit vpnRevoked();
return;
}
const QString path = QStringLiteral("/api/vpn/peer/") + QString::number(m_vpnPeerId);
const QString path = QStringLiteral("/api/vpn/peer/")
+ QString::number(m_vpnPeerId);
QNetworkReply *reply = authedDelete(path);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
@ -175,8 +169,6 @@ void RelayClient::onRevokeReply(QNetworkReply *reply)
emit vpnRevoked();
}
// ─── fetchHosts() ─────────────────────────────────────────────────────────────
void RelayClient::fetchHosts()
{
QNetworkReply *reply = authedGet(QStringLiteral("/api/hosts"));
@ -202,21 +194,21 @@ void RelayClient::onHostsReply(QNetworkReply *reply)
h.ip = obj[QStringLiteral("ip")].toString();
h.port = obj[QStringLiteral("port")].toInt(47984);
// Parse displays array if present
const QJsonArray displaysArr = obj[QStringLiteral("displays")].toArray();
for (const QJsonValue &dv : displaysArr) {
const QJsonObject displayObj = dv.toObject();
QVariantMap displayMap;
displayMap[QStringLiteral("name")] = displayObj[QStringLiteral("name")].toString();
displayMap[QStringLiteral("name")] = displayObj[QStringLiteral("name")].toString();
displayMap[QStringLiteral("friendlyName")] = displayObj[QStringLiteral("friendlyName")].toString();
displayMap[QStringLiteral("width")] = displayObj[QStringLiteral("width")].toInt();
displayMap[QStringLiteral("height")] = displayObj[QStringLiteral("height")].toInt();
displayMap[QStringLiteral("isPrimary")] = displayObj[QStringLiteral("isPrimary")].toBool();
displayMap[QStringLiteral("width")] = displayObj[QStringLiteral("width")].toInt();
displayMap[QStringLiteral("height")] = displayObj[QStringLiteral("height")].toInt();
displayMap[QStringLiteral("isPrimary")] = displayObj[QStringLiteral("isPrimary")].toBool();
h.displays.append(displayMap);
}
if (h.displays.isEmpty()) {
qWarning() << "Host" << h.name << "has no display data in relay response";
qDebug() << "Host" << h.name
<< "has no display data in relay response (older Artemis?)";
}
if (!h.ip.isEmpty())
@ -225,21 +217,15 @@ void RelayClient::onHostsReply(QNetworkReply *reply)
emit hostsReady(hosts);
}
// ─── Persistence ──────────────────────────────────────────────────────────────
void RelayClient::persistJwt()
{
// NOTE: QSettings on macOS stores in ~/Library/Preferences.
// The JWT has a limited lifetime — don't rely on it across sessions;
// the app will re-authenticate on next launch using saved credentials.
m_settings->setValue(QStringLiteral("auth/relay_url"), m_relayUrl.toString());
m_settings->setValue(QStringLiteral("auth/username"), m_username);
// We do NOT persist the JWT itself or the password.
}
void RelayClient::loadJwt()
{
// Intentionally empty re-auth on each launch for security.
// Intentionally empty - we re-auth on each launch for security.
}
void RelayClient::saveServer(const QString &label, const QUrl &url,
@ -248,8 +234,7 @@ void RelayClient::saveServer(const QString &label, const QUrl &url,
m_settings->beginGroup(QStringLiteral("servers/") + label);
m_settings->setValue(QStringLiteral("url"), url.toString());
m_settings->setValue(QStringLiteral("username"), username);
// password stored in the system keychain would be better; plain QSettings
// as a baseline — replace with QKeychain in production.
// TODO: replace with QKeychain so the password isn't stored in plaintext.
m_settings->setValue(QStringLiteral("password"), password);
m_settings->endGroup();
}
@ -260,13 +245,13 @@ QList<RelayClient::SavedServer> RelayClient::savedServers() const
const QStringList groups = m_settings->childGroups();
for (const QString &g : groups) {
if (!g.startsWith(QStringLiteral("servers/"))) continue;
const QString label = g.mid(8);
const QString label = g.mid(8); // length of "servers/"
m_settings->beginGroup(g);
SavedServer s;
s.label = label;
s.url = QUrl(m_settings->value(QStringLiteral("url")).toString());
s.username = m_settings->value(QStringLiteral("username")).toString();
s.password = m_settings->value(QStringLiteral("password")).toString());
s.password = m_settings->value(QStringLiteral("password")).toString();
m_settings->endGroup();
list << s;
}