From f99964b2bb9d0c22524c2b9b6097092e9b40a7a4 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 11 Oct 2023 14:13:42 -0400 Subject: [PATCH] Overhaul all the login classes, make the code way clearer The old pre-async login system (especially SE's) was this weird mess and it wasn't clear what data was flowing where, etc. Now it's a flat function with comments and a bit better logging. --- launcher/CMakeLists.txt | 10 +- launcher/include/launchercore.h | 11 +- launcher/include/profile.h | 5 +- launcher/include/sapphirelauncher.h | 20 -- launcher/include/sapphirelogin.h | 23 +++ launcher/include/squareboot.h | 30 --- launcher/include/squareenixlogin.h | 57 ++++++ launcher/include/squarelauncher.h | 34 ---- launcher/src/launchercore.cpp | 18 +- launcher/src/main.cpp | 2 +- ...sapphirelauncher.cpp => sapphirelogin.cpp} | 43 +++-- launcher/src/squareboot.cpp | 96 ---------- ...squarelauncher.cpp => squareenixlogin.cpp} | 180 ++++++++++++++---- 13 files changed, 268 insertions(+), 261 deletions(-) delete mode 100644 launcher/include/sapphirelauncher.h create mode 100644 launcher/include/sapphirelogin.h delete mode 100644 launcher/include/squareboot.h create mode 100644 launcher/include/squareenixlogin.h delete mode 100644 launcher/include/squarelauncher.h rename launcher/src/{sapphirelauncher.cpp => sapphirelogin.cpp} (61%) delete mode 100644 launcher/src/squareboot.cpp rename launcher/src/{squarelauncher.cpp => squareenixlogin.cpp} (61%) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 176a026..08241ac 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -55,9 +55,8 @@ target_sources(astra PRIVATE include/processlogger.h include/profile.h include/profilemanager.h - include/sapphirelauncher.h - include/squareboot.h - include/squarelauncher.h + include/sapphirelogin.h + include/squareenixlogin.h include/steamapi.h include/utility.h @@ -75,9 +74,8 @@ target_sources(astra PRIVATE src/processlogger.cpp src/profile.cpp src/profilemanager.cpp - src/sapphirelauncher.cpp - src/squareboot.cpp - src/squarelauncher.cpp + src/sapphirelogin.cpp + src/squareenixlogin.cpp src/steamapi.cpp src/utility.cpp ) diff --git a/launcher/include/launchercore.h b/launcher/include/launchercore.h index a4ecb4f..15bb0a2 100755 --- a/launcher/include/launchercore.h +++ b/launcher/include/launchercore.h @@ -14,11 +14,10 @@ #include "launchersettings.h" #include "profile.h" #include "profilemanager.h" -#include "squareboot.h" #include "steamapi.h" -class SapphireLauncher; -class SquareLauncher; +class SapphireLogin; +class SquareEnixLogin; class AssetUpdater; class GameInstaller; class CompatibilityToolInstaller; @@ -62,7 +61,6 @@ class LauncherCore : public QObject Q_PROPERTY(bool isSteam READ isSteam CONSTANT) Q_PROPERTY(bool isSteamDeck READ isSteamDeck CONSTANT) Q_PROPERTY(LauncherSettings *settings READ settings CONSTANT) - Q_PROPERTY(SquareBoot *squareBoot MEMBER m_squareBoot) Q_PROPERTY(ProfileManager *profileManager READ profileManager CONSTANT) Q_PROPERTY(AccountManager *accountManager READ accountManager CONSTANT) Q_PROPERTY(Headline *headline READ headline NOTIFY newsChanged) @@ -181,9 +179,8 @@ private: ProfileManager *m_profileManager = nullptr; AccountManager *m_accountManager = nullptr; - SapphireLauncher *m_sapphireLauncher = nullptr; - SquareBoot *m_squareBoot = nullptr; - SquareLauncher *m_squareLauncher = nullptr; + SapphireLogin *m_sapphireLogin = nullptr; + SquareEnixLogin *m_squareEnixLogin = nullptr; QNetworkAccessManager *m_mgr = nullptr; Headline *m_headline = nullptr; diff --git a/launcher/include/profile.h b/launcher/include/profile.h index 6058ab8..9459404 100644 --- a/launcher/include/profile.h +++ b/launcher/include/profile.h @@ -4,12 +4,13 @@ #pragma once #include +#include #include - -#include "squareboot.h" +#include class Account; class ProfileConfig; +class LauncherCore; class Profile : public QObject { diff --git a/launcher/include/sapphirelauncher.h b/launcher/include/sapphirelauncher.h deleted file mode 100644 index 55a2671..0000000 --- a/launcher/include/sapphirelauncher.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Joshua Goins -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -#include "launchercore.h" - -class SapphireLauncher : QObject -{ -public: - explicit SapphireLauncher(LauncherCore &window, QObject *parent = nullptr); - - void login(const QString &lobbyUrl, const LoginInformation &info); - void registerAccount(const QString &lobbyUrl, const LoginInformation &info); - -private: - LauncherCore &m_launcher; -}; \ No newline at end of file diff --git a/launcher/include/sapphirelogin.h b/launcher/include/sapphirelogin.h new file mode 100644 index 0000000..f842e9b --- /dev/null +++ b/launcher/include/sapphirelogin.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "launchercore.h" + +class SapphireLogin : QObject +{ +public: + explicit SapphireLogin(LauncherCore &window, QObject *parent = nullptr); + + /// Begins the login process for Sapphire servers + /// \param info The required login information + QCoro::Task> login(const QString &lobbyUrl, const LoginInformation &info); + + void registerAccount(const QString &lobbyUrl, const LoginInformation &info); + +private: + LauncherCore &m_launcher; +}; \ No newline at end of file diff --git a/launcher/include/squareboot.h b/launcher/include/squareboot.h deleted file mode 100644 index 6824bf7..0000000 --- a/launcher/include/squareboot.h +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Joshua Goins -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -#include "patcher.h" - -class SquareLauncher; -class LauncherCore; -class LoginInformation; - -class SquareBoot : public QObject -{ - Q_OBJECT - -public: - SquareBoot(LauncherCore &window, SquareLauncher &launcher, QObject *parent = nullptr); - - QCoro::Task<> checkGateStatus(const LoginInformation &info); - -private: - QCoro::Task<> bootCheck(const LoginInformation &info); - - Patcher *m_patcher = nullptr; - - LauncherCore &m_launcher; - SquareLauncher &m_squareLauncher; -}; \ No newline at end of file diff --git a/launcher/include/squareenixlogin.h b/launcher/include/squareenixlogin.h new file mode 100644 index 0000000..f3743ad --- /dev/null +++ b/launcher/include/squareenixlogin.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2023 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "launchercore.h" +#include "patcher.h" + +class SquareEnixLogin : public QObject +{ + Q_OBJECT + +public: + explicit SquareEnixLogin(LauncherCore &window, QObject *parent = nullptr); + + /// Begins the login process for official Square Enix servers + /// \param info The required login information + /// \return Arguments used for logging into the game, if successful + QCoro::Task> login(LoginInformation *info); + +private: + /// Checks the gate status to see if the servers are closed for maintenance + /// \return False if the gate is closed, true if open. + QCoro::Task checkGateStatus(); + + /// Check for updates to the boot components. Even though we don't use these, it's checked by later login steps. + QCoro::Task<> checkBootUpdates(); + + using StoredInfo = std::pair; + + /// Get the _STORED_ value used in the oauth step + QCoro::Task> getStoredValue(); + + /// Logs into the server + /// \return Returns false if the oauth call failed for some reason + QCoro::Task loginOauth(); + + /// Registers a new session with the login server and patches the game if necessary + /// \return Returns false if the session registration failed for some reason + QCoro::Task registerSession(); + + /// Returns the hashes of the boot components + QCoro::Task getBootHash(); + + /// Gets the SHA1 hash of a file + static QString getFileHash(const QString &file); + + Patcher *m_patcher = nullptr; + + QString m_SID, m_username; + LoginAuth m_auth; + LoginInformation *m_info = nullptr; + + LauncherCore &m_launcher; +}; diff --git a/launcher/include/squarelauncher.h b/launcher/include/squarelauncher.h deleted file mode 100644 index 73f5b2d..0000000 --- a/launcher/include/squarelauncher.h +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Joshua Goins -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -#include "launchercore.h" -#include "patcher.h" - -class SquareLauncher : public QObject -{ - Q_OBJECT - -public: - explicit SquareLauncher(LauncherCore &window, QObject *parent = nullptr); - - QCoro::Task<> login(const LoginInformation &info); - -private: - using StoredInfo = std::pair; - QCoro::Task> getStored(const LoginInformation &info); - - QCoro::Task<> registerSession(const LoginInformation &info); - - static QCoro::Task getBootHash(const LoginInformation &info); - - Patcher *m_patcher = nullptr; - - QString m_SID, m_username; - LoginAuth m_auth; - - LauncherCore &m_launcher; -}; diff --git a/launcher/src/launchercore.cpp b/launcher/src/launchercore.cpp index 4eac106..498f0e4 100755 --- a/launcher/src/launchercore.cpp +++ b/launcher/src/launchercore.cpp @@ -24,8 +24,8 @@ #include "encryptedarg.h" #include "launchercore.h" #include "processlogger.h" -#include "sapphirelauncher.h" -#include "squarelauncher.h" +#include "sapphirelogin.h" +#include "squareenixlogin.h" #include "utility.h" void LauncherCore::setSSL(QNetworkRequest &request) @@ -80,10 +80,15 @@ QCoro::Task<> LauncherCore::beginLogin(LoginInformation &info) auto assetUpdater = new AssetUpdater(*info.profile, *this, this); if (co_await assetUpdater->update()) { + std::optional auth; if (info.profile->account()->isSapphire()) { - m_sapphireLauncher->login(info.profile->account()->lobbyUrl(), info); + auth = co_await m_sapphireLogin->login(info.profile->account()->lobbyUrl(), info); } else { - m_squareBoot->checkGateStatus(info); + auth = co_await m_squareEnixLogin->login(&info); + } + + if (auth != std::nullopt) { + launchGame(*info.profile, *auth); } } @@ -414,9 +419,8 @@ LauncherCore::LauncherCore() { m_settings = new LauncherSettings(this); m_mgr = new QNetworkAccessManager(this); - m_sapphireLauncher = new SapphireLauncher(*this, this); - m_squareLauncher = new SquareLauncher(*this, this); - m_squareBoot = new SquareBoot(*this, *m_squareLauncher, this); + m_sapphireLogin = new SapphireLogin(*this, this); + m_squareEnixLogin = new SquareEnixLogin(*this, this); m_profileManager = new ProfileManager(*this, this); m_accountManager = new AccountManager(*this, this); diff --git a/launcher/src/main.cpp b/launcher/src/main.cpp index f051061..147e581 100755 --- a/launcher/src/main.cpp +++ b/launcher/src/main.cpp @@ -17,7 +17,7 @@ #include "launchercore.h" #include "logger.h" #include "physis_logger.h" -#include "sapphirelauncher.h" +#include "sapphirelogin.h" int main(int argc, char *argv[]) { diff --git a/launcher/src/sapphirelauncher.cpp b/launcher/src/sapphirelogin.cpp similarity index 61% rename from launcher/src/sapphirelauncher.cpp rename to launcher/src/sapphirelogin.cpp index adb2b83..60cd9e8 100644 --- a/launcher/src/sapphirelauncher.cpp +++ b/launcher/src/sapphirelogin.cpp @@ -1,21 +1,22 @@ // SPDX-FileCopyrightText: 2023 Joshua Goins // SPDX-License-Identifier: GPL-3.0-or-later -#include "sapphirelauncher.h" +#include "sapphirelogin.h" #include "utility.h" #include #include #include #include +#include -SapphireLauncher::SapphireLauncher(LauncherCore &window, QObject *parent) +SapphireLogin::SapphireLogin(LauncherCore &window, QObject *parent) : QObject(parent) , m_launcher(window) { } -void SapphireLauncher::login(const QString &lobbyUrl, const LoginInformation &info) +QCoro::Task> SapphireLogin::login(const QString &lobbyUrl, const LoginInformation &info) { const QJsonObject data{{QStringLiteral("username"), info.username}, {QStringLiteral("pass"), info.password}}; @@ -26,29 +27,29 @@ void SapphireLauncher::login(const QString &lobbyUrl, const LoginInformation &in Utility::printRequest(QStringLiteral("POST"), request); const auto reply = m_launcher.mgr()->post(request, QJsonDocument(data).toJson(QJsonDocument::JsonFormat::Compact)); + co_await reply; - connect(reply, &QNetworkReply::finished, [this, reply, &info] { - if (reply->error() != QNetworkReply::NetworkError::NoError) { - Q_EMIT m_launcher.loginError(i18n("Could not contact lobby server.\n\n%1", reply->errorString())); - return; - } + if (reply->error() != QNetworkReply::NetworkError::NoError) { + Q_EMIT m_launcher.loginError(i18n("Could not contact lobby server.\n\n%1", reply->errorString())); + co_return std::nullopt; + } - const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); - if (!document.isEmpty()) { - LoginAuth auth; - auth.SID = document[QLatin1String("sId")].toString(); - auth.lobbyhost = document[QLatin1String("lobbyHost")].toString(); - auth.frontierHost = document[QLatin1String("frontierHost")].toString(); - auth.region = 3; + const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isEmpty()) { + LoginAuth auth; + auth.SID = document[QLatin1String("sId")].toString(); + auth.lobbyhost = document[QLatin1String("lobbyHost")].toString(); + auth.frontierHost = document[QLatin1String("frontierHost")].toString(); + auth.region = 3; - m_launcher.launchGame(*info.profile, auth); - } else { - Q_EMIT m_launcher.loginError(i18n("Invalid username or password.")); - } - }); + co_return auth; + } else { + Q_EMIT m_launcher.loginError(i18n("Invalid username or password.")); + co_return std::nullopt; + } } -void SapphireLauncher::registerAccount(const QString &lobbyUrl, const LoginInformation &info) +void SapphireLogin::registerAccount(const QString &lobbyUrl, const LoginInformation &info) { const QJsonObject data{{QStringLiteral("username"), info.username}, {QStringLiteral("pass"), info.password}}; const QUrl url(lobbyUrl + QStringLiteral("/sapphire-api/lobby/createAccount")); diff --git a/launcher/src/squareboot.cpp b/launcher/src/squareboot.cpp deleted file mode 100644 index 5fd690a..0000000 --- a/launcher/src/squareboot.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Joshua Goins -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "squareboot.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "account.h" -#include "squarelauncher.h" -#include "utility.h" - -SquareBoot::SquareBoot(LauncherCore &window, SquareLauncher &launcher, QObject *parent) - : QObject(parent) - , m_launcher(window) - , m_squareLauncher(launcher) -{ -} - -QCoro::Task<> SquareBoot::bootCheck(const LoginInformation &info) -{ - Q_EMIT m_launcher.stageChanged(i18n("Checking for launcher updates...")); - qDebug() << "Performing boot check..."; - - const QUrlQuery query{{QStringLiteral("time"), QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyy-MM-dd-HH-mm"))}}; - - QUrl url; - url.setScheme(QStringLiteral("http")); - url.setHost(QStringLiteral("patch-bootver.%1").arg(m_launcher.settings()->squareEnixServer())); - url.setPath(QStringLiteral("/http/win32/ffxivneo_release_boot/%1").arg(info.profile->bootVersion())); - url.setQuery(query); - - auto request = QNetworkRequest(url); - if (info.profile->account()->license() == Account::GameLicense::macOS) { - request.setRawHeader(QByteArrayLiteral("User-Agent"), QByteArrayLiteral("FFXIV-MAC PATCH CLIENT")); - } else { - request.setRawHeader(QByteArrayLiteral("User-Agent"), QByteArrayLiteral("FFXIV PATCH CLIENT")); - } - - request.setRawHeader(QByteArrayLiteral("Host"), QStringLiteral("patch-bootver.%1").arg(m_launcher.settings()->squareEnixServer()).toUtf8()); - Utility::printRequest(QStringLiteral("GET"), request); - - const auto reply = m_launcher.mgr()->get(request); - co_await reply; - - const QString patchList = reply->readAll(); - if (!patchList.isEmpty()) { - m_patcher = new Patcher(m_launcher, info.profile->gamePath() + QStringLiteral("/boot"), *info.profile->bootData(), this); - const bool hasPatched = co_await m_patcher->patch(PatchList(patchList)); - if (hasPatched) { - // update game version information - info.profile->readGameVersion(); - } - m_patcher->deleteLater(); - } - - m_squareLauncher.login(info); -} - -QCoro::Task<> SquareBoot::checkGateStatus(const LoginInformation &info) -{ - Q_EMIT m_launcher.stageChanged(i18n("Checking gate...")); - qDebug() << "Checking gate..."; - - QUrl url; - url.setScheme(m_launcher.settings()->preferredProtocol()); - url.setHost(QStringLiteral("frontier.%1").arg(m_launcher.settings()->squareEnixServer())); - url.setPath(QStringLiteral("/worldStatus/gate_status.json")); - url.setQuery(QString::number(QDateTime::currentMSecsSinceEpoch())); - - QNetworkRequest request(url); - - // TODO: really? - m_launcher.buildRequest(*info.profile, request); - - Utility::printRequest(QStringLiteral("GET"), request); - - const auto reply = m_launcher.mgr()->get(request); - m_launcher.setupIgnoreSSL(reply); - co_await reply; - - const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); - const bool isGateOpen = !document.isEmpty() && document.object()[QLatin1String("status")].toInt() != 0; - - if (isGateOpen) { - bootCheck(info); - } else { - Q_EMIT m_launcher.loginError(i18n("The login gate is closed, the game may be under maintenance.\n\n%1", reply->errorString())); - } -} diff --git a/launcher/src/squarelauncher.cpp b/launcher/src/squareenixlogin.cpp similarity index 61% rename from launcher/src/squarelauncher.cpp rename to launcher/src/squareenixlogin.cpp index d8c0e24..40263bc 100644 --- a/launcher/src/squarelauncher.cpp +++ b/launcher/src/squareenixlogin.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 Joshua Goins // SPDX-License-Identifier: GPL-3.0-or-later -#include "squarelauncher.h" +#include "squareenixlogin.h" #include #include @@ -14,29 +14,117 @@ #include #include "account.h" +#include "astra_log.h" #include "launchercore.h" #include "utility.h" -SquareLauncher::SquareLauncher(LauncherCore &window, QObject *parent) +SquareEnixLogin::SquareEnixLogin(LauncherCore &window, QObject *parent) : QObject(parent) , m_launcher(window) { } -QString getFileHash(const QString &file) +QCoro::Task> SquareEnixLogin::login(LoginInformation *info) { - auto f = QFile(file); - if (!f.open(QIODevice::ReadOnly)) - return {}; + Q_ASSERT(info != nullptr); + m_info = info; - QCryptographicHash hash(QCryptographicHash::Sha1); - hash.addData(&f); + if (!co_await checkGateStatus()) { + co_return std::nullopt; + } - return QStringLiteral("%1/%2").arg(QString::number(f.size()), hash.result().toHex()); + co_await checkBootUpdates(); + + if (!co_await loginOauth()) { + co_return std::nullopt; + } + + if (!co_await registerSession()) { + co_return std::nullopt; + } + + co_return m_auth; } -QCoro::Task> SquareLauncher::getStored(const LoginInformation &info) +QCoro::Task SquareEnixLogin::checkGateStatus() { + Q_EMIT m_launcher.stageChanged(i18n("Checking gate...")); + qInfo(ASTRA_LOG) << "Checking if the gate is open..."; + + QUrl url; + url.setScheme(m_launcher.settings()->preferredProtocol()); + url.setHost(QStringLiteral("frontier.%1").arg(m_launcher.settings()->squareEnixServer())); + url.setPath(QStringLiteral("/worldStatus/gate_status.json")); + url.setQuery(QString::number(QDateTime::currentMSecsSinceEpoch())); + + QNetworkRequest request(url); + + // TODO: really? + m_launcher.buildRequest(*m_info->profile, request); + + Utility::printRequest(QStringLiteral("GET"), request); + + const auto reply = m_launcher.mgr()->get(request); + m_launcher.setupIgnoreSSL(reply); + co_await reply; + + const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + const bool isGateOpen = !document.isEmpty() && document.object()[QLatin1String("status")].toInt() != 0; + + if (isGateOpen) { + qInfo(ASTRA_LOG) << "Gate is open!"; + co_return true; + } else { + qInfo(ASTRA_LOG) << "Gate is closed!"; + Q_EMIT m_launcher.loginError(i18n("The login gate is closed, the game may be under maintenance.\n\n%1", reply->errorString())); + co_return false; + } +} + +QCoro::Task<> SquareEnixLogin::checkBootUpdates() +{ + Q_EMIT m_launcher.stageChanged(i18n("Checking for launcher updates...")); + qInfo(ASTRA_LOG) << "Checking for updates to boot components..."; + + const QUrlQuery query{{QStringLiteral("time"), QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyy-MM-dd-HH-mm"))}}; + + QUrl url; + url.setScheme(QStringLiteral("http")); + url.setHost(QStringLiteral("patch-bootver.%1").arg(m_launcher.settings()->squareEnixServer())); + url.setPath(QStringLiteral("/http/win32/ffxivneo_release_boot/%1").arg(m_info->profile->bootVersion())); + url.setQuery(query); + + auto request = QNetworkRequest(url); + if (m_info->profile->account()->license() == Account::GameLicense::macOS) { + request.setRawHeader(QByteArrayLiteral("User-Agent"), QByteArrayLiteral("FFXIV-MAC PATCH CLIENT")); + } else { + request.setRawHeader(QByteArrayLiteral("User-Agent"), QByteArrayLiteral("FFXIV PATCH CLIENT")); + } + + request.setRawHeader(QByteArrayLiteral("Host"), QStringLiteral("patch-bootver.%1").arg(m_launcher.settings()->squareEnixServer()).toUtf8()); + Utility::printRequest(QStringLiteral("GET"), request); + + const auto reply = m_launcher.mgr()->get(request); + co_await reply; + + const QString patchList = reply->readAll(); + if (!patchList.isEmpty()) { + m_patcher = new Patcher(m_launcher, m_info->profile->gamePath() + QStringLiteral("/boot"), *m_info->profile->bootData(), this); + const bool hasPatched = co_await m_patcher->patch(PatchList(patchList)); + if (hasPatched) { + // update game version information + m_info->profile->readGameVersion(); + } + m_patcher->deleteLater(); + } + + co_return; +} + +QCoro::Task> SquareEnixLogin::getStoredValue() +{ + qInfo(ASTRA_LOG) << "Getting the STORED value..."; + Q_EMIT m_launcher.stageChanged(i18n("Logging in...")); QUrlQuery query; @@ -44,12 +132,12 @@ QCoro::Task> SquareLauncher::getStored query.addQueryItem(QStringLiteral("lng"), QStringLiteral("en")); // for some reason, we always use region 3. the actual region is acquired later query.addQueryItem(QStringLiteral("rgn"), QStringLiteral("3")); - query.addQueryItem(QStringLiteral("isft"), info.profile->account()->isFreeTrial() ? QStringLiteral("1") : QStringLiteral("0")); + query.addQueryItem(QStringLiteral("isft"), m_info->profile->account()->isFreeTrial() ? QStringLiteral("1") : QStringLiteral("0")); query.addQueryItem(QStringLiteral("cssmode"), QStringLiteral("1")); query.addQueryItem(QStringLiteral("isnew"), QStringLiteral("1")); query.addQueryItem(QStringLiteral("launchver"), QStringLiteral("3")); - if (info.profile->account()->license() == Account::GameLicense::WindowsSteam) { + if (m_info->profile->account()->license() == Account::GameLicense::WindowsSteam) { query.addQueryItem(QStringLiteral("issteam"), QStringLiteral("1")); // TODO: get steam ticket information from steam api @@ -64,7 +152,7 @@ QCoro::Task> SquareLauncher::getStored url.setQuery(query); auto request = QNetworkRequest(url); - m_launcher.buildRequest(*info.profile, request); + m_launcher.buildRequest(*m_info->profile, request); Utility::printRequest(QStringLiteral("GET"), request); @@ -74,7 +162,7 @@ QCoro::Task> SquareLauncher::getStored const QString str = reply->readAll(); // fetches Steam username - if (info.profile->account()->license() == Account::GameLicense::WindowsSteam) { + if (m_info->profile->account()->license() == Account::GameLicense::WindowsSteam) { const QRegularExpression re(QStringLiteral(R"lit(.*)""\/>)lit")); const QRegularExpressionMatch match = re.match(str); @@ -84,7 +172,7 @@ QCoro::Task> SquareLauncher::getStored Q_EMIT m_launcher.loginError(i18n("Could not get Steam username, have you attached your account?")); } } else { - m_username = info.username; + m_username = m_info->username; } const QRegularExpression re(QStringLiteral(R"lit(\t<\s*input .* name="_STORED_" value="(?.*)">)lit")); @@ -98,22 +186,22 @@ QCoro::Task> SquareLauncher::getStored } } -QCoro::Task<> SquareLauncher::login(const LoginInformation &info) +QCoro::Task SquareEnixLogin::loginOauth() { - const auto storedResult = co_await getStored(info); + const auto storedResult = co_await getStoredValue(); if (storedResult == std::nullopt) { - co_return; + co_return false; } const auto [stored, referer] = *storedResult; - qInfo() << "Performing oauth..."; + qInfo(ASTRA_LOG) << "Logging in..."; QUrlQuery postData; postData.addQueryItem(QStringLiteral("_STORED_"), stored); - postData.addQueryItem(QStringLiteral("sqexid"), info.username); - postData.addQueryItem(QStringLiteral("password"), info.password); - postData.addQueryItem(QStringLiteral("otppw"), info.oneTimePassword); + postData.addQueryItem(QStringLiteral("sqexid"), m_info->username); + postData.addQueryItem(QStringLiteral("password"), m_info->password); + postData.addQueryItem(QStringLiteral("otppw"), m_info->oneTimePassword); QUrl url; url.setScheme(QStringLiteral("https")); @@ -121,7 +209,7 @@ QCoro::Task<> SquareLauncher::login(const LoginInformation &info) url.setPath(QStringLiteral("/oauth/ffxivarr/login/login.send")); QNetworkRequest request(url); - m_launcher.buildRequest(*info.profile, request); + m_launcher.buildRequest(*m_info->profile, request); request.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/x-www-form-urlencoded")); request.setRawHeader(QByteArrayLiteral("Referer"), referer.toEncoded()); request.setRawHeader(QByteArrayLiteral("Cache-Control"), QByteArrayLiteral("no-cache")); @@ -144,34 +232,38 @@ QCoro::Task<> SquareLauncher::login(const LoginInformation &info) if (!playable) { Q_EMIT m_launcher.loginError(i18n("Your account is unplayable. Check that you have the correct license, and a valid subscription.")); - co_return; + co_return false; } if (!terms) { Q_EMIT m_launcher.loginError(i18n("Your account is unplayable. You need to accept the terms of service from the official launcher first.")); - co_return; + co_return false; } m_SID = parts[1]; m_auth.region = parts[5].toInt(); m_auth.maxExpansion = parts[13].toInt(); - registerSession(info); + co_return true; } else { const QRegularExpression re(QStringLiteral(R"lit(window.external.user\("login=auth,ng,err,(?.*)\);)lit")); const QRegularExpressionMatch match = re.match(str); // there's a stray quote at the end of the error string, so let's remove that Q_EMIT m_launcher.loginError(match.captured(1).chopped(1)); + + co_return false; } } -QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info) +QCoro::Task SquareEnixLogin::registerSession() { + qInfo(ASTRA_LOG) << "Registering the session..."; + QUrl url; url.setScheme(QStringLiteral("https")); url.setHost(QStringLiteral("patch-gamever.%1").arg(m_launcher.settings()->squareEnixServer())); - url.setPath(QStringLiteral("/http/win32/ffxivneo_release_game/%1/%2").arg(info.profile->baseGameVersion(), m_SID)); + url.setPath(QStringLiteral("/http/win32/ffxivneo_release_game/%1/%2").arg(m_info->profile->baseGameVersion(), m_SID)); auto request = QNetworkRequest(url); m_launcher.setSSL(request); @@ -179,10 +271,10 @@ QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info) request.setRawHeader(QByteArrayLiteral("User-Agent"), QByteArrayLiteral("FFXIV PATCH CLIENT")); request.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/x-www-form-urlencoded")); - QString report = QStringLiteral("%1=%2").arg(info.profile->bootVersion(), co_await getBootHash(info)); + QString report = QStringLiteral("%1=%2").arg(m_info->profile->bootVersion(), co_await getBootHash()); for (int i = 0; i < m_auth.maxExpansion; i++) { - if (i < static_cast(info.profile->numInstalledExpansions())) { - report += QStringLiteral("\nex%1\t%2").arg(QString::number(i + 1), info.profile->expansionVersion(i)); + if (i < static_cast(m_info->profile->numInstalledExpansions())) { + report += QStringLiteral("\nex%1\t%2").arg(QString::number(i + 1), m_info->profile->expansionVersion(i)); } else { report += QStringLiteral("\nex%1\t2012.01.01.0000.0000").arg(QString::number(i + 1)); } @@ -205,18 +297,18 @@ QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info) const QString body = reply->readAll(); if (!body.isEmpty()) { - m_patcher = new Patcher(m_launcher, info.profile->gamePath() + QStringLiteral("/game"), *info.profile->gameData(), this); + m_patcher = new Patcher(m_launcher, m_info->profile->gamePath() + QStringLiteral("/game"), *m_info->profile->gameData(), this); const bool hasPatched = co_await m_patcher->patch(PatchList(body)); if (hasPatched) { // re-read game version if it has updated - info.profile->readGameVersion(); + m_info->profile->readGameVersion(); } m_patcher->deleteLater(); } m_auth.SID = patchUniqueId; - m_launcher.launchGame(*info.profile, m_auth); + co_return true; } else { Q_EMIT m_launcher.loginError(i18n("Fatal error, request was successful but X-Patch-Unique-Id was not recieved.")); } @@ -232,9 +324,11 @@ QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info) Q_EMIT m_launcher.loginError(i18n("Unknown error when registering the session.")); } } + + co_return false; } -QCoro::Task SquareLauncher::getBootHash(const LoginInformation &info) +QCoro::Task SquareEnixLogin::getBootHash() { const QList fileList = {QStringLiteral("ffxivboot.exe"), QStringLiteral("ffxivboot64.exe"), @@ -243,8 +337,8 @@ QCoro::Task SquareLauncher::getBootHash(const LoginInformation &info) QStringLiteral("ffxivupdater.exe"), QStringLiteral("ffxivupdater64.exe")}; - const auto hashFuture = QtConcurrent::mapped(fileList, [&info](const auto &filename) -> QString { - return getFileHash(info.profile->gamePath() + QStringLiteral("/boot/") + filename); + const auto hashFuture = QtConcurrent::mapped(fileList, [this](const auto &filename) -> QString { + return getFileHash(m_info->profile->gamePath() + QStringLiteral("/boot/") + filename); }); co_await hashFuture; @@ -259,4 +353,16 @@ QCoro::Task SquareLauncher::getBootHash(const LoginInformation &info) } co_return result; +} + +QString SquareEnixLogin::getFileHash(const QString &file) +{ + auto f = QFile(file); + if (!f.open(QIODevice::ReadOnly)) + return {}; + + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(&f); + + return QStringLiteral("%1/%2").arg(QString::number(f.size()), hash.result().toHex()); } \ No newline at end of file