diff options
Diffstat (limited to 'launcher/minecraft/auth')
50 files changed, 1119 insertions, 1362 deletions
diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp index 0b78cb0c..474bf7c6 100644 --- a/launcher/minecraft/auth/AccountData.cpp +++ b/launcher/minecraft/auth/AccountData.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -34,87 +34,90 @@ */ #include "AccountData.h" +#include <QDebug> +#include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> -#include <QJsonArray> -#include <QDebug> -#include <QUuid> #include <QRegularExpression> +#include <QUuid> namespace { -void tokenToJSONV3(QJsonObject &parent, Katabasis::Token t, const char * tokenName) { - if(!t.persistent) { +void tokenToJSONV3(QJsonObject& parent, Katabasis::Token t, const char* tokenName) +{ + if (!t.persistent) { return; } QJsonObject out; - if(t.issueInstant.isValid()) { + if (t.issueInstant.isValid()) { out["iat"] = QJsonValue(t.issueInstant.toMSecsSinceEpoch() / 1000); } - if(t.notAfter.isValid()) { + if (t.notAfter.isValid()) { out["exp"] = QJsonValue(t.notAfter.toMSecsSinceEpoch() / 1000); } bool save = false; - if(!t.token.isEmpty()) { + if (!t.token.isEmpty()) { out["token"] = QJsonValue(t.token); save = true; } - if(!t.refresh_token.isEmpty()) { + if (!t.refresh_token.isEmpty()) { out["refresh_token"] = QJsonValue(t.refresh_token); save = true; } - if(t.extra.size()) { + if (t.extra.size()) { out["extra"] = QJsonObject::fromVariantMap(t.extra); save = true; } - if(save) { + if (save) { parent[tokenName] = out; } } -Katabasis::Token tokenFromJSONV3(const QJsonObject &parent, const char * tokenName) { +Katabasis::Token tokenFromJSONV3(const QJsonObject& parent, const char* tokenName) +{ Katabasis::Token out; auto tokenObject = parent.value(tokenName).toObject(); - if(tokenObject.isEmpty()) { + if (tokenObject.isEmpty()) { return out; } auto issueInstant = tokenObject.value("iat"); - if(issueInstant.isDouble()) { - out.issueInstant = QDateTime::fromMSecsSinceEpoch(((int64_t) issueInstant.toDouble()) * 1000); + if (issueInstant.isDouble()) { + out.issueInstant = QDateTime::fromMSecsSinceEpoch(((int64_t)issueInstant.toDouble()) * 1000); } auto notAfter = tokenObject.value("exp"); - if(notAfter.isDouble()) { - out.notAfter = QDateTime::fromMSecsSinceEpoch(((int64_t) notAfter.toDouble()) * 1000); + if (notAfter.isDouble()) { + out.notAfter = QDateTime::fromMSecsSinceEpoch(((int64_t)notAfter.toDouble()) * 1000); } auto token = tokenObject.value("token"); - if(token.isString()) { + if (token.isString()) { out.token = token.toString(); out.validity = Katabasis::Validity::Assumed; } auto refresh_token = tokenObject.value("refresh_token"); - if(refresh_token.isString()) { + if (refresh_token.isString()) { out.refresh_token = refresh_token.toString(); } auto extra = tokenObject.value("extra"); - if(extra.isObject()) { + if (extra.isObject()) { out.extra = extra.toObject().toVariantMap(); } return out; } -void profileToJSONV3(QJsonObject &parent, MinecraftProfile p, const char * tokenName) { - if(p.id.isEmpty()) { +void profileToJSONV3(QJsonObject& parent, MinecraftProfile p, const char* tokenName) +{ + if (p.id.isEmpty()) { return; } QJsonObject out; out["id"] = QJsonValue(p.id); out["name"] = QJsonValue(p.name); - if(!p.currentCape.isEmpty()) { + if (!p.currentCape.isEmpty()) { out["cape"] = p.currentCape; } @@ -123,19 +126,19 @@ void profileToJSONV3(QJsonObject &parent, MinecraftProfile p, const char * token skinObj["id"] = p.skin.id; skinObj["url"] = p.skin.url; skinObj["variant"] = p.skin.variant; - if(p.skin.data.size()) { + if (p.skin.data.size()) { skinObj["data"] = QString::fromLatin1(p.skin.data.toBase64()); } out["skin"] = skinObj; } QJsonArray capesArray; - for(auto & cape: p.capes) { + for (auto& cape : p.capes) { QJsonObject capeObj; capeObj["id"] = cape.id; capeObj["url"] = cape.url; capeObj["alias"] = cape.alias; - if(cape.data.size()) { + if (cape.data.size()) { capeObj["data"] = QString::fromLatin1(cape.data.toBase64()); } capesArray.push_back(capeObj); @@ -144,16 +147,17 @@ void profileToJSONV3(QJsonObject &parent, MinecraftProfile p, const char * token parent[tokenName] = out; } -MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * tokenName) { +MinecraftProfile profileFromJSONV3(const QJsonObject& parent, const char* tokenName) +{ MinecraftProfile out; auto tokenObject = parent.value(tokenName).toObject(); - if(tokenObject.isEmpty()) { + if (tokenObject.isEmpty()) { return out; } { auto idV = tokenObject.value("id"); auto nameV = tokenObject.value("name"); - if(!idV.isString() || !nameV.isString()) { + if (!idV.isString() || !nameV.isString()) { qWarning() << "mandatory profile attributes are missing or of unexpected type"; return MinecraftProfile(); } @@ -163,7 +167,7 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token { auto skinV = tokenObject.value("skin"); - if(!skinV.isObject()) { + if (!skinV.isObject()) { qWarning() << "skin is missing"; return MinecraftProfile(); } @@ -171,7 +175,7 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token auto idV = skinObj.value("id"); auto urlV = skinObj.value("url"); auto variantV = skinObj.value("variant"); - if(!idV.isString() || !urlV.isString() || !variantV.isString()) { + if (!idV.isString() || !urlV.isString() || !variantV.isString()) { qWarning() << "mandatory skin attributes are missing or of unexpected type"; return MinecraftProfile(); } @@ -181,11 +185,10 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token // data for skin is optional auto dataV = skinObj.value("data"); - if(dataV.isString()) { + if (dataV.isString()) { // TODO: validate base64 out.skin.data = QByteArray::fromBase64(dataV.toString().toLatin1()); - } - else if (!dataV.isUndefined()) { + } else if (!dataV.isUndefined()) { qWarning() << "skin data is something unexpected"; return MinecraftProfile(); } @@ -193,13 +196,13 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token { auto capesV = tokenObject.value("capes"); - if(!capesV.isArray()) { + if (!capesV.isArray()) { qWarning() << "capes is not an array!"; return MinecraftProfile(); } auto capesArray = capesV.toArray(); - for(auto capeV: capesArray) { - if(!capeV.isObject()) { + for (auto capeV : capesArray) { + if (!capeV.isObject()) { qWarning() << "cape is not an object!"; return MinecraftProfile(); } @@ -207,7 +210,7 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token auto idV = capeObj.value("id"); auto urlV = capeObj.value("url"); auto aliasV = capeObj.value("alias"); - if(!idV.isString() || !urlV.isString() || !aliasV.isString()) { + if (!idV.isString() || !urlV.isString() || !aliasV.isString()) { qWarning() << "mandatory skin attributes are missing or of unexpected type"; return MinecraftProfile(); } @@ -218,11 +221,10 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token // data for cape is optional. auto dataV = capeObj.value("data"); - if(dataV.isString()) { + if (dataV.isString()) { // TODO: validate base64 cape.data = QByteArray::fromBase64(dataV.toString().toLatin1()); - } - else if (!dataV.isUndefined()) { + } else if (!dataV.isUndefined()) { qWarning() << "cape data is something unexpected"; return MinecraftProfile(); } @@ -232,9 +234,9 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token // current cape { auto capeV = tokenObject.value("cape"); - if(capeV.isString()) { + if (capeV.isString()) { auto currentCape = capeV.toString(); - if(out.capes.contains(currentCape)) { + if (out.capes.contains(currentCape)) { out.currentCape = currentCape; } } @@ -243,8 +245,9 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token return out; } -void entitlementToJSONV3(QJsonObject &parent, MinecraftEntitlement p) { - if(p.validity == Katabasis::Validity::None) { +void entitlementToJSONV3(QJsonObject& parent, MinecraftEntitlement p) +{ + if (p.validity == Katabasis::Validity::None) { return; } QJsonObject out; @@ -253,15 +256,16 @@ void entitlementToJSONV3(QJsonObject &parent, MinecraftEntitlement p) { parent["entitlement"] = out; } -bool entitlementFromJSONV3(const QJsonObject &parent, MinecraftEntitlement & out) { +bool entitlementFromJSONV3(const QJsonObject& parent, MinecraftEntitlement& out) +{ auto entitlementObject = parent.value("entitlement").toObject(); - if(entitlementObject.isEmpty()) { + if (entitlementObject.isEmpty()) { return false; } { auto ownsMinecraftV = entitlementObject.value("ownsMinecraft"); auto canPlayMinecraftV = entitlementObject.value("canPlayMinecraft"); - if(!ownsMinecraftV.isBool() || !canPlayMinecraftV.isBool()) { + if (!ownsMinecraftV.isBool() || !canPlayMinecraftV.isBool()) { qWarning() << "mandatory attributes are missing or of unexpected type"; return false; } @@ -272,12 +276,12 @@ bool entitlementFromJSONV3(const QJsonObject &parent, MinecraftEntitlement & out return true; } -} +} // namespace -bool AccountData::resumeStateFromV2(QJsonObject data) { +bool AccountData::resumeStateFromV2(QJsonObject data) +{ // The JSON object must at least have a username for it to be valid. - if (!data.value("username").isString()) - { + if (!data.value("username").isString()) { qCritical() << "Can't load Mojang account info from JSON object. Username field is missing or of the wrong type."; return false; } @@ -287,14 +291,12 @@ bool AccountData::resumeStateFromV2(QJsonObject data) { QString accessToken = data.value("accessToken").toString(""); QJsonArray profileArray = data.value("profiles").toArray(); - if (profileArray.size() < 1) - { + if (profileArray.size() < 1) { qCritical() << "Can't load Mojang account with username \"" << userName << "\". No profiles found."; return false; } - struct AccountProfile - { + struct AccountProfile { QString id; QString name; bool legacy; @@ -304,24 +306,22 @@ bool AccountData::resumeStateFromV2(QJsonObject data) { int currentProfileIndex = 0; int index = -1; QString currentProfile = data.value("activeProfile").toString(""); - for (QJsonValue profileVal : profileArray) - { + for (QJsonValue profileVal : profileArray) { index++; QJsonObject profileObject = profileVal.toObject(); QString id = profileObject.value("id").toString(""); QString name = profileObject.value("name").toString(""); - bool legacy = profileObject.value("legacy").toBool(false); - if (id.isEmpty() || name.isEmpty()) - { + bool legacy_ = profileObject.value("legacy").toBool(false); + if (id.isEmpty() || name.isEmpty()) { qWarning() << "Unable to load a profile" << name << "because it was missing an ID or a name."; continue; } - if(id == currentProfile) { + if (id == currentProfile) { currentProfileIndex = index; } - profiles.append({id, name, legacy}); + profiles.append({ id, name, legacy_ }); } - auto & profile = profiles[currentProfileIndex]; + auto& profile = profiles[currentProfileIndex]; type = AccountType::Mojang; legacy = profile.legacy; @@ -339,14 +339,15 @@ bool AccountData::resumeStateFromV2(QJsonObject data) { return true; } -bool AccountData::resumeStateFromV3(QJsonObject data) { +bool AccountData::resumeStateFromV3(QJsonObject data) +{ auto typeV = data.value("type"); - if(!typeV.isString()) { + if (!typeV.isString()) { qWarning() << "Failed to parse account data: type is missing."; return false; } auto typeS = typeV.toString(); - if(typeS == "MSA") { + if (typeS == "MSA") { type = AccountType::MSA; } else if (typeS == "Mojang") { type = AccountType::Mojang; @@ -357,16 +358,16 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { return false; } - if(type == AccountType::Mojang) { + if (type == AccountType::Mojang) { legacy = data.value("legacy").toBool(false); canMigrateToMSA = data.value("canMigrateToMSA").toBool(false); } - if(type == AccountType::MSA) { + if (type == AccountType::MSA) { auto clientIDV = data.value("msa-client-id"); if (clientIDV.isString()) { msaClientID = clientIDV.toString(); - } // leave msaClientID empty if it doesn't exist or isn't a string + } // leave msaClientID empty if it doesn't exist or isn't a string msaToken = tokenFromJSONV3(data, "msa"); userToken = tokenFromJSONV3(data, "utoken"); xboxApiToken = tokenFromJSONV3(data, "xrp-main"); @@ -379,8 +380,8 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { yggdrasilToken.token = "0"; minecraftProfile = profileFromJSONV3(data, "profile"); - if(!entitlementFromJSONV3(data, minecraftEntitlement)) { - if(minecraftProfile.validity != Katabasis::Validity::None) { + if (!entitlementFromJSONV3(data, minecraftEntitlement)) { + if (minecraftProfile.validity != Katabasis::Validity::None) { minecraftEntitlement.canPlayMinecraft = true; minecraftEntitlement.ownsMinecraft = true; minecraftEntitlement.validity = Katabasis::Validity::Assumed; @@ -391,26 +392,25 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { return true; } -QJsonObject AccountData::saveState() const { +QJsonObject AccountData::saveState() const +{ QJsonObject output; - if(type == AccountType::Mojang) { + if (type == AccountType::Mojang) { output["type"] = "Mojang"; - if(legacy) { + if (legacy) { output["legacy"] = true; } - if(canMigrateToMSA) { + if (canMigrateToMSA) { output["canMigrateToMSA"] = true; } - } - else if (type == AccountType::MSA) { + } else if (type == AccountType::MSA) { output["type"] = "MSA"; output["msa-client-id"] = msaClientID; tokenToJSONV3(output, msaToken, "msa"); tokenToJSONV3(output, userToken, "utoken"); tokenToJSONV3(output, xboxApiToken, "xrp-main"); tokenToJSONV3(output, mojangservicesToken, "xrp-mc"); - } - else if (type == AccountType::Offline) { + } else if (type == AccountType::Offline) { output["type"] = "Offline"; } @@ -420,60 +420,68 @@ QJsonObject AccountData::saveState() const { return output; } -QString AccountData::userName() const { - if(type == AccountType::MSA) { +QString AccountData::userName() const +{ + if (type == AccountType::MSA) { return QString(); } return yggdrasilToken.extra["userName"].toString(); } -QString AccountData::accessToken() const { +QString AccountData::accessToken() const +{ return yggdrasilToken.token; } -QString AccountData::clientToken() const { - if(type != AccountType::Mojang) { +QString AccountData::clientToken() const +{ + if (type != AccountType::Mojang) { return QString(); } return yggdrasilToken.extra["clientToken"].toString(); } -void AccountData::setClientToken(QString clientToken) { - if(type != AccountType::Mojang) { +void AccountData::setClientToken(QString clientToken) +{ + if (type != AccountType::Mojang) { return; } yggdrasilToken.extra["clientToken"] = clientToken; } -void AccountData::generateClientTokenIfMissing() { - if(yggdrasilToken.extra.contains("clientToken")) { +void AccountData::generateClientTokenIfMissing() +{ + if (yggdrasilToken.extra.contains("clientToken")) { return; } invalidateClientToken(); } -void AccountData::invalidateClientToken() { - if(type != AccountType::Mojang) { +void AccountData::invalidateClientToken() +{ + if (type != AccountType::Mojang) { return; } yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{-}]")); } -QString AccountData::profileId() const { +QString AccountData::profileId() const +{ return minecraftProfile.id; } -QString AccountData::profileName() const { - if(minecraftProfile.name.size() == 0) { +QString AccountData::profileName() const +{ + if (minecraftProfile.name.size() == 0) { return QObject::tr("No profile (%1)").arg(accountDisplayString()); - } - else { + } else { return minecraftProfile.name; } } -QString AccountData::accountDisplayString() const { - switch(type) { +QString AccountData::accountDisplayString() const +{ + switch (type) { case AccountType::Mojang: { return userName(); } @@ -481,7 +489,7 @@ QString AccountData::accountDisplayString() const { return QObject::tr("<Offline>"); } case AccountType::MSA: { - if(xboxApiToken.extra.contains("gtg")) { + if (xboxApiToken.extra.contains("gtg")) { return xboxApiToken.extra["gtg"].toString(); } return "Xbox profile missing"; @@ -492,6 +500,7 @@ QString AccountData::accountDisplayString() const { } } -QString AccountData::lastError() const { +QString AccountData::lastError() const +{ return errorString; } diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h index 092e1691..9b626c34 100644 --- a/launcher/minecraft/auth/AccountData.h +++ b/launcher/minecraft/auth/AccountData.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -34,11 +34,11 @@ */ #pragma once -#include <QString> -#include <QByteArray> -#include <QVector> #include <katabasis/Bits.h> +#include <QByteArray> #include <QJsonObject> +#include <QString> +#include <QVector> struct Skin { QString id; @@ -71,22 +71,9 @@ struct MinecraftProfile { Katabasis::Validity validity = Katabasis::Validity::None; }; -enum class AccountType { - MSA, - Mojang, - Offline -}; +enum class AccountType { MSA, Mojang, Offline }; -enum class AccountState { - Unchecked, - Offline, - Working, - Online, - Disabled, - Errored, - Expired, - Gone -}; +enum class AccountState { Unchecked, Offline, Working, Online, Disabled, Errored, Expired, Gone }; struct AccountData { QJsonObject saveState() const; diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index d6f42b75..c534ea3b 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -37,14 +37,14 @@ #include "AccountData.h" #include "AccountTask.h" -#include <QIODevice> +#include <QDir> #include <QFile> -#include <QTextStream> -#include <QJsonDocument> +#include <QIODevice> #include <QJsonArray> +#include <QJsonDocument> #include <QJsonObject> #include <QJsonParseError> -#include <QDir> +#include <QTextStream> #include <QTimer> #include <QDebug> @@ -54,12 +54,10 @@ #include <chrono> -enum AccountListVersion { - MojangOnly = 2, - MojangMSA = 3 -}; +enum AccountListVersion { MojangOnly = 2, MojangMSA = 3 }; -AccountList::AccountList(QObject *parent) : QAbstractListModel(parent) { +AccountList::AccountList(QObject* parent) : QAbstractListModel(parent) +{ m_refreshTimer = new QTimer(this); m_refreshTimer->setSingleShot(true); connect(m_refreshTimer, &QTimer::timeout, this, &AccountList::fillQueue); @@ -70,7 +68,8 @@ AccountList::AccountList(QObject *parent) : QAbstractListModel(parent) { AccountList::~AccountList() noexcept {} -int AccountList::findAccountByProfileId(const QString& profileId) const { +int AccountList::findAccountByProfileId(const QString& profileId) const +{ for (int i = 0; i < count(); i++) { MinecraftAccountPtr account = at(i); if (account->profileId() == profileId) { @@ -80,7 +79,8 @@ int AccountList::findAccountByProfileId(const QString& profileId) const { return -1; } -MinecraftAccountPtr AccountList::getAccountByProfileName(const QString& profileName) const { +MinecraftAccountPtr AccountList::getAccountByProfileName(const QString& profileName) const +{ for (int i = 0; i < count(); i++) { MinecraftAccountPtr account = at(i); if (account->profileName() == profileName) { @@ -95,11 +95,12 @@ const MinecraftAccountPtr AccountList::at(int i) const return MinecraftAccountPtr(m_accounts.at(i)); } -QStringList AccountList::profileNames() const { +QStringList AccountList::profileNames() const +{ QStringList out; - for(auto & account: m_accounts) { - auto profileName = account->profileName(); - if(profileName.isEmpty()) { + for (auto& account : m_accounts) { + auto profileName = account->profileName(); + if (profileName.isEmpty()) { continue; } out.append(profileName); @@ -122,14 +123,14 @@ void AccountList::addAccount(const MinecraftAccountPtr account) // override/replace existing account with the same profileId auto profileId = account->profileId(); - if(profileId.size()) { + if (profileId.size()) { auto existingAccount = findAccountByProfileId(profileId); - if(existingAccount != -1) { + if (existingAccount != -1) { qDebug() << "Replacing old account with a new one with the same profile ID!"; MinecraftAccountPtr existingAccountPtr = m_accounts[existingAccount]; m_accounts[existingAccount] = account; - if(m_defaultAccount == existingAccountPtr) { + if (m_defaultAccount == existingAccountPtr) { m_defaultAccount = account; } // disconnect notifications for changes in the account being replaced @@ -154,11 +155,9 @@ void AccountList::addAccount(const MinecraftAccountPtr account) void AccountList::removeAccount(QModelIndex index) { int row = index.row(); - if(index.isValid() && row >= 0 && row < m_accounts.size()) - { - auto & account = m_accounts[row]; - if(account == m_defaultAccount) - { + if (index.isValid() && row >= 0 && row < m_accounts.size()) { + auto& account = m_accounts[row]; + if (account == m_defaultAccount) { m_defaultAccount = nullptr; onDefaultAccountChanged(); } @@ -178,43 +177,34 @@ MinecraftAccountPtr AccountList::defaultAccount() const void AccountList::setDefaultAccount(MinecraftAccountPtr newAccount) { - if (!newAccount && m_defaultAccount) - { + if (!newAccount && m_defaultAccount) { int idx = 0; auto previousDefaultAccount = m_defaultAccount; m_defaultAccount = nullptr; - for (MinecraftAccountPtr account : m_accounts) - { - if (account == previousDefaultAccount) - { + for (MinecraftAccountPtr account : m_accounts) { + if (account == previousDefaultAccount) { emit dataChanged(index(idx), index(idx, columnCount(QModelIndex()) - 1)); } - idx ++; + idx++; } onDefaultAccountChanged(); - } - else - { + } else { auto currentDefaultAccount = m_defaultAccount; int currentDefaultAccountIdx = -1; auto newDefaultAccount = m_defaultAccount; int newDefaultAccountIdx = -1; int idx = 0; - for (MinecraftAccountPtr account : m_accounts) - { - if (account == newAccount) - { + for (MinecraftAccountPtr account : m_accounts) { + if (account == newAccount) { newDefaultAccount = account; newDefaultAccountIdx = idx; } - if(currentDefaultAccount == account) - { + if (currentDefaultAccount == account) { currentDefaultAccountIdx = idx; } idx++; } - if(currentDefaultAccount != newDefaultAccount) - { + if (currentDefaultAccount != newDefaultAccount) { emit dataChanged(index(currentDefaultAccountIdx), index(currentDefaultAccountIdx, columnCount(QModelIndex()) - 1)); emit dataChanged(index(newDefaultAccountIdx), index(newDefaultAccountIdx, columnCount(QModelIndex()) - 1)); m_defaultAccount = newDefaultAccount; @@ -231,27 +221,25 @@ void AccountList::accountChanged() void AccountList::accountActivityChanged(bool active) { - MinecraftAccount *account = qobject_cast<MinecraftAccount *>(sender()); + MinecraftAccount* account = qobject_cast<MinecraftAccount*>(sender()); bool found = false; for (int i = 0; i < count(); i++) { if (at(i).get() == account) { - emit dataChanged(index(i), index(i, columnCount(QModelIndex()) - 1)); + emit dataChanged(index(i), index(i, columnCount(QModelIndex()) - 1)); found = true; break; } } - if(found) { + if (found) { emit listActivityChanged(); - if(active) { + if (active) { beginActivity(); - } - else { + } else { endActivity(); } } } - void AccountList::onListChanged() { if (m_autosave) @@ -274,7 +262,7 @@ int AccountList::count() const return m_accounts.count(); } -QVariant AccountList::data(const QModelIndex &index, int role) const +QVariant AccountList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); @@ -284,70 +272,67 @@ QVariant AccountList::data(const QModelIndex &index, int role) const MinecraftAccountPtr account = at(index.row()); - switch (role) - { + switch (role) { case Qt::DisplayRole: - switch (index.column()) - { - case ProfileNameColumn: { - return account->profileName(); - } + switch (index.column()) { + case ProfileNameColumn: { + return account->profileName(); + } - case NameColumn: - return account->accountDisplayString(); + case NameColumn: + return account->accountDisplayString(); - case TypeColumn: { - auto typeStr = account->typeString(); - typeStr[0] = typeStr[0].toUpper(); - return typeStr; - } + case TypeColumn: { + auto typeStr = account->typeString(); + typeStr[0] = typeStr[0].toUpper(); + return typeStr; + } - case StatusColumn: { - switch(account->accountState()) { - case AccountState::Unchecked: { - return tr("Unchecked", "Account status"); - } - case AccountState::Offline: { - return tr("Offline", "Account status"); - } - case AccountState::Online: { - return tr("Ready", "Account status"); - } - case AccountState::Working: { - return tr("Working", "Account status"); - } - case AccountState::Errored: { - return tr("Errored", "Account status"); - } - case AccountState::Expired: { - return tr("Expired", "Account status"); - } - case AccountState::Disabled: { - return tr("Disabled", "Account status"); + case StatusColumn: { + switch (account->accountState()) { + case AccountState::Unchecked: { + return tr("Unchecked", "Account status"); + } + case AccountState::Offline: { + return tr("Offline", "Account status"); + } + case AccountState::Online: { + return tr("Ready", "Account status"); + } + case AccountState::Working: { + return tr("Working", "Account status"); + } + case AccountState::Errored: { + return tr("Errored", "Account status"); + } + case AccountState::Expired: { + return tr("Expired", "Account status"); + } + case AccountState::Disabled: { + return tr("Disabled", "Account status"); + } + case AccountState::Gone: { + return tr("Gone", "Account status"); + } + default: { + return tr("Unknown", "Account status"); + } } - case AccountState::Gone: { - return tr("Gone", "Account status"); + } + + case MigrationColumn: { + if (account->isMSA() || account->isOffline()) { + return tr("N/A", "Can Migrate"); } - default: { - return tr("Unknown", "Account status"); + if (account->canMigrate()) { + return tr("Yes", "Can Migrate"); + } else { + return tr("No", "Can Migrate"); } } - } - case MigrationColumn: { - if(account->isMSA() || account->isOffline()) { - return tr("N/A", "Can Migrate"); - } - if (account->canMigrate()) { - return tr("Yes", "Can Migrate"); - } - else { - return tr("No", "Can Migrate"); - } - } - - default: - return QVariant(); + default: + return QVariant(); } case Qt::ToolTipRole: @@ -362,88 +347,80 @@ QVariant AccountList::data(const QModelIndex &index, int role) const } else { return QVariant(); } - default: return QVariant(); } } -QVariant AccountList::headerData(int section, Qt::Orientation orientation, int role) const -{ - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case ProfileNameColumn: - return tr("Username"); - case NameColumn: - return tr("Account"); - case TypeColumn: - return tr("Type"); - case StatusColumn: - return tr("Status"); - case MigrationColumn: - return tr("Can Migrate?"); - default: - return QVariant(); - } +QVariant AccountList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const +{ + switch (role) { + case Qt::DisplayRole: + switch (section) { + case ProfileNameColumn: + return tr("Username"); + case NameColumn: + return tr("Account"); + case TypeColumn: + return tr("Type"); + case StatusColumn: + return tr("Status"); + case MigrationColumn: + return tr("Can Migrate?"); + default: + return QVariant(); + } + + case Qt::ToolTipRole: + switch (section) { + case ProfileNameColumn: + return tr("Minecraft username associated with the account."); + case NameColumn: + return tr("User name of the account."); + case TypeColumn: + return tr("Type of the account - Mojang or MSA."); + case StatusColumn: + return tr("Current status of the account."); + case MigrationColumn: + return tr("Can this account migrate to a Microsoft account?"); + default: + return QVariant(); + } - case Qt::ToolTipRole: - switch (section) - { - case ProfileNameColumn: - return tr("Minecraft username associated with the account."); - case NameColumn: - return tr("User name of the account."); - case TypeColumn: - return tr("Type of the account - Mojang or MSA."); - case StatusColumn: - return tr("Current status of the account."); - case MigrationColumn: - return tr("Can this account migrate to a Microsoft account?"); default: return QVariant(); - } - - default: - return QVariant(); } } -int AccountList::rowCount(const QModelIndex &parent) const +int AccountList::rowCount(const QModelIndex& parent) const { // Return count return parent.isValid() ? 0 : count(); } -int AccountList::columnCount(const QModelIndex &parent) const +int AccountList::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } -Qt::ItemFlags AccountList::flags(const QModelIndex &index) const +Qt::ItemFlags AccountList::flags(const QModelIndex& index) const { - if (index.row() < 0 || index.row() >= rowCount(index.parent()) || !index.isValid()) - { + if (index.row() < 0 || index.row() >= rowCount(index.parent()) || !index.isValid()) { return Qt::NoItemFlags; } return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } -bool AccountList::setData(const QModelIndex &idx, const QVariant &value, int role) +bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int role) { - if (idx.row() < 0 || idx.row() >= rowCount(idx) || !idx.isValid()) - { + if (idx.row() < 0 || idx.row() >= rowCount(idx) || !idx.isValid()) { return false; } - if(role == Qt::CheckStateRole) - { - if(value == Qt::Checked) - { + if (role == Qt::CheckStateRole) { + if (value == Qt::Checked) { MinecraftAccountPtr account = at(idx.row()); setDefaultAccount(account); } @@ -455,8 +432,7 @@ bool AccountList::setData(const QModelIndex &idx, const QVariant &value, int rol bool AccountList::loadList() { - if (m_listFilePath.isEmpty()) - { + if (m_listFilePath.isEmpty()) { qCritical() << "Can't load Mojang account list. No file path given and no default set."; return false; } @@ -465,8 +441,7 @@ bool AccountList::loadList() // Try to open the file and fail if we can't. // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::ReadOnly)) - { + if (!file.open(QIODevice::ReadOnly)) { qCritical() << QString("Failed to read the account list file (%1).").arg(m_listFilePath).toUtf8(); return false; } @@ -479,17 +454,15 @@ bool AccountList::loadList() QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); // Fail if the JSON is invalid. - if (parseError.error != QJsonParseError::NoError) - { + if (parseError.error != QJsonParseError::NoError) { qCritical() << QString("Failed to parse account list file: %1 at offset %2") - .arg(parseError.errorString(), QString::number(parseError.offset)) - .toUtf8(); + .arg(parseError.errorString(), QString::number(parseError.offset)) + .toUtf8(); return false; } // Make sure the root is an object. - if (!jsonDoc.isObject()) - { + if (!jsonDoc.isObject()) { qCritical() << "Invalid account list JSON: Root should be an array."; return false; } @@ -498,15 +471,13 @@ bool AccountList::loadList() // Make sure the format version matches. auto listVersion = root.value("formatVersion").toVariant().toInt(); - switch(listVersion) { + switch (listVersion) { case AccountListVersion::MojangOnly: { return loadV2(root); - } - break; + } break; case AccountListVersion::MojangMSA: { return loadV3(root); - } - break; + } break; default: { QString newName = "accounts-old.json"; qWarning() << "Unknown format version when loading account list. Existing one will be renamed to" << newName; @@ -517,21 +488,20 @@ bool AccountList::loadList() } } -bool AccountList::loadV2(QJsonObject& root) { +bool AccountList::loadV2(QJsonObject& root) +{ beginResetModel(); auto defaultUserName = root.value("activeAccount").toString(""); QJsonArray accounts = root.value("accounts").toArray(); - for (QJsonValue accountVal : accounts) - { + for (QJsonValue accountVal : accounts) { QJsonObject accountObj = accountVal.toObject(); MinecraftAccountPtr account = MinecraftAccount::loadFromJsonV2(accountObj); - if (account.get() != nullptr) - { + if (account.get() != nullptr) { auto profileId = account->profileId(); - if(!profileId.size()) { + if (!profileId.size()) { continue; } - if(findAccountByProfileId(profileId) != -1) { + if (findAccountByProfileId(profileId) != -1) { continue; } connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged); @@ -540,9 +510,7 @@ bool AccountList::loadV2(QJsonObject& root) { if (defaultUserName.size() && account->mojangUserName() == defaultUserName) { m_defaultAccount = account; } - } - else - { + } else { qWarning() << "Failed to load an account."; } } @@ -550,30 +518,27 @@ bool AccountList::loadV2(QJsonObject& root) { return true; } -bool AccountList::loadV3(QJsonObject& root) { +bool AccountList::loadV3(QJsonObject& root) +{ beginResetModel(); QJsonArray accounts = root.value("accounts").toArray(); - for (QJsonValue accountVal : accounts) - { + for (QJsonValue accountVal : accounts) { QJsonObject accountObj = accountVal.toObject(); MinecraftAccountPtr account = MinecraftAccount::loadFromJsonV3(accountObj); - if (account.get() != nullptr) - { + if (account.get() != nullptr) { auto profileId = account->profileId(); - if(profileId.size()) { - if(findAccountByProfileId(profileId) != -1) { + if (profileId.size()) { + if (findAccountByProfileId(profileId) != -1) { continue; } } connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged); connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged); m_accounts.append(account); - if(accountObj.value("active").toBool(false)) { + if (accountObj.value("active").toBool(false)) { m_defaultAccount = account; } - } - else - { + } else { qWarning() << "Failed to load an account."; } } @@ -581,23 +546,20 @@ bool AccountList::loadV3(QJsonObject& root) { return true; } - bool AccountList::saveList() { - if (m_listFilePath.isEmpty()) - { + if (m_listFilePath.isEmpty()) { qCritical() << "Can't save Mojang account list. No file path given and no default set."; return false; } // make sure the parent folder exists - if(!FS::ensureFilePathExists(m_listFilePath)) + if (!FS::ensureFilePathExists(m_listFilePath)) return false; // make sure the file wasn't overwritten with a folder before (fixes a bug) QFileInfo finfo(m_listFilePath); - if(finfo.isDir()) - { + if (finfo.isDir()) { QDir badDir(m_listFilePath); badDir.removeRecursively(); } @@ -613,10 +575,9 @@ bool AccountList::saveList() // Build a list of accounts. qDebug() << "Building account array."; QJsonArray accounts; - for (MinecraftAccountPtr account : m_accounts) - { + for (MinecraftAccountPtr account : m_accounts) { QJsonObject accountObj = account->saveToJson(); - if(m_defaultAccount == account) { + if (m_defaultAccount == account) { accountObj["active"] = true; } accounts.append(accountObj); @@ -634,20 +595,18 @@ bool AccountList::saveList() // Try to open the file and fail if we can't. // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::WriteOnly)) - { + if (!file.open(QIODevice::WriteOnly)) { qCritical() << QString("Failed to read the account list file (%1).").arg(m_listFilePath).toUtf8(); return false; } // Write the JSON to the file. file.write(doc.toJson()); - file.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser); - if(file.commit()) { + file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser); + if (file.commit()) { qDebug() << "Saved account list to" << m_listFilePath; return true; - } - else { + } else { qDebug() << "Failed to save accounts to" << m_listFilePath; return false; } @@ -661,30 +620,29 @@ void AccountList::setListFilePath(QString path, bool autosave) bool AccountList::anyAccountIsValid() { - for(auto account: m_accounts) - { - if(account->ownsMinecraft()) { + for (auto account : m_accounts) { + if (account->ownsMinecraft()) { return true; } } return false; } -void AccountList::fillQueue() { - - if(m_defaultAccount && m_defaultAccount->shouldRefresh()) { +void AccountList::fillQueue() +{ + if (m_defaultAccount && m_defaultAccount->shouldRefresh()) { auto idToRefresh = m_defaultAccount->internalId(); m_refreshQueue.push_back(idToRefresh); qDebug() << "AccountList: Queued default account with internal ID " << idToRefresh << " to refresh first"; } - for(int i = 0; i < count(); i++) { + for (int i = 0; i < count(); i++) { auto account = at(i); - if(account == m_defaultAccount) { + if (account == m_defaultAccount) { continue; } - if(account->shouldRefresh()) { + if (account->shouldRefresh()) { auto idToRefresh = account->internalId(); queueRefresh(idToRefresh); } @@ -692,40 +650,43 @@ void AccountList::fillQueue() { tryNext(); } -void AccountList::requestRefresh(QString accountId) { +void AccountList::requestRefresh(QString accountId) +{ auto index = m_refreshQueue.indexOf(accountId); - if(index != -1) { + if (index != -1) { m_refreshQueue.removeAt(index); } m_refreshQueue.push_front(accountId); qDebug() << "AccountList: Pushed account with internal ID " << accountId << " to the front of the queue"; - if(!isActive()) { + if (!isActive()) { tryNext(); } } -void AccountList::queueRefresh(QString accountId) { - if(m_refreshQueue.indexOf(accountId) != -1) { +void AccountList::queueRefresh(QString accountId) +{ + if (m_refreshQueue.indexOf(accountId) != -1) { return; } m_refreshQueue.push_back(accountId); qDebug() << "AccountList: Queued account with internal ID " << accountId << " to refresh"; } - -void AccountList::tryNext() { +void AccountList::tryNext() +{ while (m_refreshQueue.length()) { auto accountId = m_refreshQueue.front(); m_refreshQueue.pop_front(); - for(int i = 0; i < count(); i++) { + for (int i = 0; i < count(); i++) { auto account = at(i); - if(account->internalId() == accountId) { + if (account->internalId() == accountId) { m_currentTask = account->refresh(); - if(m_currentTask) { + if (m_currentTask) { connect(m_currentTask.get(), &AccountTask::succeeded, this, &AccountList::authSucceeded); connect(m_currentTask.get(), &AccountTask::failed, this, &AccountList::authFailed); m_currentTask->start(); - qDebug() << "RefreshSchedule: Processing account " << account->accountDisplayString() << " with internal ID " << accountId; + qDebug() << "RefreshSchedule: Processing account " << account->accountDisplayString() << " with internal ID " + << accountId; return; } } @@ -736,38 +697,43 @@ void AccountList::tryNext() { m_refreshTimer->start(1000 * 3600); } -void AccountList::authSucceeded() { +void AccountList::authSucceeded() +{ qDebug() << "RefreshSchedule: Background account refresh succeeded"; m_currentTask.reset(); m_nextTimer->start(1000 * 20); } -void AccountList::authFailed(QString reason) { +void AccountList::authFailed(QString reason) +{ qDebug() << "RefreshSchedule: Background account refresh failed: " << reason; m_currentTask.reset(); m_nextTimer->start(1000 * 20); } -bool AccountList::isActive() const { +bool AccountList::isActive() const +{ return m_activityCount != 0; } -void AccountList::beginActivity() { +void AccountList::beginActivity() +{ bool activating = m_activityCount == 0; m_activityCount++; - if(activating) { + if (activating) { emit activityChanged(true); } } -void AccountList::endActivity() { - if(m_activityCount == 0) { +void AccountList::endActivity() +{ + if (m_activityCount == 0) { qWarning() << m_name << " - Activity count would become below zero"; return; } bool deactivating = m_activityCount == 1; m_activityCount--; - if(deactivating) { + if (deactivating) { emit activityChanged(false); } } diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h index a8c3529a..6a0b0191 100644 --- a/launcher/minecraft/auth/AccountList.h +++ b/launcher/minecraft/auth/AccountList.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -37,26 +37,21 @@ #include "MinecraftAccount.h" -#include <QObject> -#include <QVariant> #include <QAbstractListModel> +#include <QObject> #include <QSharedPointer> +#include <QVariant> /*! * List of available Mojang accounts. * This should be loaded in the background by Prism Launcher on startup. */ -class AccountList : public QAbstractListModel -{ +class AccountList : public QAbstractListModel { Q_OBJECT -public: - enum ModelRoles - { - PointerRole = 0x34B1CB48 - }; + public: + enum ModelRoles { PointerRole = 0x34B1CB48 }; - enum VListColumns - { + enum VListColumns { // TODO: Add icon column. ProfileNameColumn = 0, NameColumn, @@ -67,24 +62,24 @@ public: NUM_COLUMNS }; - explicit AccountList(QObject *parent = 0); + explicit AccountList(QObject* parent = 0); virtual ~AccountList() noexcept; const MinecraftAccountPtr at(int i) const; int count() const; //////// List Model Functions //////// - QVariant data(const QModelIndex &index, int role) const override; + QVariant data(const QModelIndex& index, int role) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - virtual int rowCount(const QModelIndex &parent) const override; - virtual int columnCount(const QModelIndex &parent) const override; - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; - virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override; + virtual int rowCount(const QModelIndex& parent) const override; + virtual int columnCount(const QModelIndex& parent) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override; void addAccount(const MinecraftAccountPtr account); void removeAccount(QModelIndex index); - int findAccountByProfileId(const QString &profileId) const; - MinecraftAccountPtr getAccountByProfileName(const QString &profileName) const; + int findAccountByProfileId(const QString& profileId) const; + MinecraftAccountPtr getAccountByProfileName(const QString& profileName) const; QStringList profileNames() const; // requesting a refresh pushes it to the front of the queue @@ -102,8 +97,8 @@ public: void setListFilePath(QString path, bool autosave = false); bool loadList(); - bool loadV2(QJsonObject &root); - bool loadV3(QJsonObject &root); + bool loadV2(QJsonObject& root); + bool loadV3(QJsonObject& root); bool saveList(); MinecraftAccountPtr defaultAccount() const; @@ -112,20 +107,20 @@ public: bool isActive() const; -protected: + protected: void beginActivity(); void endActivity(); -private: + private: const char* m_name; uint32_t m_activityCount = 0; -signals: + signals: void listChanged(); void listActivityChanged(); void defaultAccountChanged(); void activityChanged(bool active); -public slots: + public slots: /** * This is called when one of the accounts changes and the list needs to be updated */ @@ -141,16 +136,16 @@ public slots: */ void fillQueue(); -private slots: + private slots: void tryNext(); void authSucceeded(); void authFailed(QString reason); -protected: + protected: QList<QString> m_refreshQueue; - QTimer *m_refreshTimer; - QTimer *m_nextTimer; + QTimer* m_refreshTimer; + QTimer* m_nextTimer; shared_qobject_ptr<AccountTask> m_currentTask; /*! @@ -178,4 +173,3 @@ protected: */ bool m_autosave = false; }; - diff --git a/launcher/minecraft/auth/AccountTask.cpp b/launcher/minecraft/auth/AccountTask.cpp index 4118c3c5..4c3d6ee1 100644 --- a/launcher/minecraft/auth/AccountTask.cpp +++ b/launcher/minecraft/auth/AccountTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -36,43 +36,41 @@ #include "AccountTask.h" #include "MinecraftAccount.h" -#include <QObject> -#include <QString> -#include <QJsonObject> +#include <QByteArray> #include <QJsonDocument> +#include <QJsonObject> #include <QNetworkReply> -#include <QByteArray> +#include <QObject> +#include <QString> #include <QDebug> -AccountTask::AccountTask(AccountData *data, QObject *parent) - : Task(parent), m_data(data) +AccountTask::AccountTask(AccountData* data, QObject* parent) : Task(parent), m_data(data) { changeState(AccountTaskState::STATE_CREATED); } QString AccountTask::getStateMessage() const { - switch (m_taskState) - { - case AccountTaskState::STATE_CREATED: - return "Waiting..."; - case AccountTaskState::STATE_WORKING: - return tr("Sending request to auth servers..."); - case AccountTaskState::STATE_SUCCEEDED: - return tr("Authentication task succeeded."); - case AccountTaskState::STATE_OFFLINE: - return tr("Failed to contact the authentication server."); - case AccountTaskState::STATE_DISABLED: - return tr("Client ID has changed. New session needs to be created."); - case AccountTaskState::STATE_FAILED_SOFT: - return tr("Encountered an error during authentication."); - case AccountTaskState::STATE_FAILED_HARD: - return tr("Failed to authenticate. The session has expired."); - case AccountTaskState::STATE_FAILED_GONE: - return tr("Failed to authenticate. The account no longer exists."); - default: - return tr("..."); + switch (m_taskState) { + case AccountTaskState::STATE_CREATED: + return "Waiting..."; + case AccountTaskState::STATE_WORKING: + return tr("Sending request to auth servers..."); + case AccountTaskState::STATE_SUCCEEDED: + return tr("Authentication task succeeded."); + case AccountTaskState::STATE_OFFLINE: + return tr("Failed to contact the authentication server."); + case AccountTaskState::STATE_DISABLED: + return tr("Client ID has changed. New session needs to be created."); + case AccountTaskState::STATE_FAILED_SOFT: + return tr("Encountered an error during authentication."); + case AccountTaskState::STATE_FAILED_HARD: + return tr("Failed to authenticate. The session has expired."); + case AccountTaskState::STATE_FAILED_GONE: + return tr("Failed to authenticate. The account no longer exists."); + default: + return tr("..."); } } @@ -82,7 +80,7 @@ bool AccountTask::changeState(AccountTaskState newState, QString reason) // FIXME: virtual method invoked in constructor. // We want that behavior, but maybe make it less weird? setStatus(getStateMessage()); - switch(newState) { + switch (newState) { case AccountTaskState::STATE_CREATED: { m_data->errorString.clear(); return true; diff --git a/launcher/minecraft/auth/AccountTask.h b/launcher/minecraft/auth/AccountTask.h index 1bd6c59f..82332c0b 100644 --- a/launcher/minecraft/auth/AccountTask.h +++ b/launcher/minecraft/auth/AccountTask.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -37,10 +37,10 @@ #include <tasks/Task.h> -#include <QString> +#include <qsslerror.h> #include <QJsonObject> +#include <QString> #include <QTimer> -#include <qsslerror.h> #include "MinecraftAccount.h" @@ -50,37 +50,32 @@ class QNetworkReply; * Enum for describing the state of the current task. * Used by the getStateMessage function to determine what the status message should be. */ -enum class AccountTaskState -{ +enum class AccountTaskState { STATE_CREATED, STATE_WORKING, STATE_SUCCEEDED, - STATE_DISABLED, //!< MSA Client ID has changed. Tell user to reloginn - STATE_FAILED_SOFT, //!< soft failure. authentication went through partially - STATE_FAILED_HARD, //!< hard failure. main tokens are invalid - STATE_FAILED_GONE, //!< hard failure. main tokens are invalid, and the account no longer exists - STATE_OFFLINE //!< soft failure. authentication failed in the first step in a 'soft' way + STATE_DISABLED, //!< MSA Client ID has changed. Tell user to reloginn + STATE_FAILED_SOFT, //!< soft failure. authentication went through partially + STATE_FAILED_HARD, //!< hard failure. main tokens are invalid + STATE_FAILED_GONE, //!< hard failure. main tokens are invalid, and the account no longer exists + STATE_OFFLINE //!< soft failure. authentication failed in the first step in a 'soft' way }; -class AccountTask : public Task -{ +class AccountTask : public Task { Q_OBJECT -public: - explicit AccountTask(AccountData * data, QObject *parent = 0); - virtual ~AccountTask() {}; + public: + explicit AccountTask(AccountData* data, QObject* parent = 0); + virtual ~AccountTask(){}; AccountTaskState m_taskState = AccountTaskState::STATE_CREATED; - AccountTaskState taskState() { - return m_taskState; - } + AccountTaskState taskState() { return m_taskState; } -signals: - void showVerificationUriAndCode(const QUrl &uri, const QString &code, int expiresIn); + signals: + void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); void hideVerificationUriAndCode(); -protected: - + protected: /** * Returns the state message for the given state. * Used to set the status message for the task. @@ -88,10 +83,10 @@ protected: */ virtual QString getStateMessage() const; -protected slots: + protected slots: // NOTE: true -> non-terminal state, false -> terminal state bool changeState(AccountTaskState newState, QString reason = QString()); -protected: - AccountData *m_data = nullptr; + protected: + AccountData* m_data = nullptr; }; diff --git a/launcher/minecraft/auth/AuthRequest.cpp b/launcher/minecraft/auth/AuthRequest.cpp index a21634b7..189978cc 100644 --- a/launcher/minecraft/auth/AuthRequest.cpp +++ b/launcher/minecraft/auth/AuthRequest.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -35,44 +35,44 @@ #include <cassert> +#include <QBuffer> #include <QDebug> #include <QTimer> -#include <QBuffer> #include <QUrlQuery> #include "Application.h" #include "AuthRequest.h" #include "katabasis/Globals.h" -AuthRequest::AuthRequest(QObject *parent): QObject(parent) { -} +AuthRequest::AuthRequest(QObject* parent) : QObject(parent) {} -AuthRequest::~AuthRequest() { -} +AuthRequest::~AuthRequest() {} -void AuthRequest::get(const QNetworkRequest &req, int timeout/* = 60*1000*/) { +void AuthRequest::get(const QNetworkRequest& req, int timeout /* = 60*1000*/) +{ setup(req, QNetworkAccessManager::GetOperation); reply_ = APPLICATION->network()->get(request_); status_ = Requesting; timedReplies_.add(new Katabasis::Reply(reply_, timeout)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(reply_, &QNetworkReply::errorOccurred, this, &AuthRequest::onRequestError); -#else // &QNetworkReply::error SIGNAL depricated +#else // &QNetworkReply::error SIGNAL depricated connect(reply_, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &AuthRequest::onRequestError); #endif connect(reply_, &QNetworkReply::finished, this, &AuthRequest::onRequestFinished); connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors); } -void AuthRequest::post(const QNetworkRequest &req, const QByteArray &data, int timeout/* = 60*1000*/) { +void AuthRequest::post(const QNetworkRequest& req, const QByteArray& data, int timeout /* = 60*1000*/) +{ setup(req, QNetworkAccessManager::PostOperation); data_ = data; status_ = Requesting; reply_ = APPLICATION->network()->post(request_, data_); timedReplies_.add(new Katabasis::Reply(reply_, timeout)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(reply_, &QNetworkReply::errorOccurred, this, &AuthRequest::onRequestError); -#else // &QNetworkReply::error SIGNAL depricated +#else // &QNetworkReply::error SIGNAL depricated connect(reply_, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &AuthRequest::onRequestError); #endif connect(reply_, &QNetworkReply::finished, this, &AuthRequest::onRequestFinished); @@ -80,35 +80,39 @@ void AuthRequest::post(const QNetworkRequest &req, const QByteArray &data, int t connect(reply_, &QNetworkReply::uploadProgress, this, &AuthRequest::onUploadProgress); } -void AuthRequest::onRequestFinished() { +void AuthRequest::onRequestFinished() +{ if (status_ == Idle) { return; } - if (reply_ != qobject_cast<QNetworkReply *>(sender())) { + if (reply_ != qobject_cast<QNetworkReply*>(sender())) { return; } httpStatus_ = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); finish(); } -void AuthRequest::onRequestError(QNetworkReply::NetworkError error) { +void AuthRequest::onRequestError(QNetworkReply::NetworkError error) +{ qWarning() << "AuthRequest::onRequestError: Error" << (int)error; if (status_ == Idle) { return; } - if (reply_ != qobject_cast<QNetworkReply *>(sender())) { + if (reply_ != qobject_cast<QNetworkReply*>(sender())) { return; } errorString_ = reply_->errorString(); httpStatus_ = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); error_ = error; qWarning() << "AuthRequest::onRequestError: Error string: " << errorString_; - qWarning() << "AuthRequest::onRequestError: HTTP status" << httpStatus_ << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + qWarning() << "AuthRequest::onRequestError: HTTP status" << httpStatus_ + << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); // QTimer::singleShot(10, this, SLOT(finish())); } -void AuthRequest::onSslErrors(QList<QSslError> errors) { +void AuthRequest::onSslErrors(QList<QSslError> errors) +{ int i = 1; for (auto error : errors) { qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString(); @@ -118,23 +122,25 @@ void AuthRequest::onSslErrors(QList<QSslError> errors) { } } -void AuthRequest::onUploadProgress(qint64 uploaded, qint64 total) { +void AuthRequest::onUploadProgress(qint64 uploaded, qint64 total) +{ if (status_ == Idle) { qWarning() << "AuthRequest::onUploadProgress: No pending request"; return; } - if (reply_ != qobject_cast<QNetworkReply *>(sender())) { + if (reply_ != qobject_cast<QNetworkReply*>(sender())) { return; } // Restart timeout because request in progress - Katabasis::Reply *o2Reply = timedReplies_.find(reply_); - if(o2Reply) { + Katabasis::Reply* o2Reply = timedReplies_.find(reply_); + if (o2Reply) { o2Reply->start(); } emit uploadProgress(uploaded, total); } -void AuthRequest::setup(const QNetworkRequest &req, QNetworkAccessManager::Operation operation, const QByteArray &verb) { +void AuthRequest::setup(const QNetworkRequest& req, QNetworkAccessManager::Operation operation, const QByteArray& verb) +{ request_ = req; operation_ = operation; url_ = req.url(); @@ -152,7 +158,8 @@ void AuthRequest::setup(const QNetworkRequest &req, QNetworkAccessManager::Opera httpStatus_ = 0; } -void AuthRequest::finish() { +void AuthRequest::finish() +{ QByteArray data; if (status_ == Idle) { qWarning() << "AuthRequest::finish: No pending request"; diff --git a/launcher/minecraft/auth/AuthRequest.h b/launcher/minecraft/auth/AuthRequest.h index 89f7a123..84d2a7d6 100644 --- a/launcher/minecraft/auth/AuthRequest.h +++ b/launcher/minecraft/auth/AuthRequest.h @@ -1,27 +1,26 @@ #pragma once -#include <QObject> -#include <QNetworkRequest> -#include <QNetworkReply> +#include <QByteArray> #include <QNetworkAccessManager> +#include <QNetworkReply> +#include <QNetworkRequest> +#include <QObject> #include <QUrl> -#include <QByteArray> #include "katabasis/Reply.h" /// Makes authentication requests. -class AuthRequest: public QObject { +class AuthRequest : public QObject { Q_OBJECT -public: - explicit AuthRequest(QObject *parent = 0); + public: + explicit AuthRequest(QObject* parent = 0); ~AuthRequest(); -public slots: - void get(const QNetworkRequest &req, int timeout = 60*1000); - void post(const QNetworkRequest &req, const QByteArray &data, int timeout = 60*1000); - + public slots: + void get(const QNetworkRequest& req, int timeout = 60 * 1000); + void post(const QNetworkRequest& req, const QByteArray& data, int timeout = 60 * 1000); -signals: + signals: /// Emitted when a request has been completed or failed. void finished(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers); @@ -29,7 +28,7 @@ signals: /// Emitted when an upload has progressed. void uploadProgress(qint64 bytesSent, qint64 bytesTotal); -protected slots: + protected slots: /// Handle request finished. void onRequestFinished(); @@ -46,25 +45,23 @@ protected slots: /// Handle upload progress. void onUploadProgress(qint64 uploaded, qint64 total); -public: + public: QNetworkReply::NetworkError error_; int httpStatus_ = 0; QString errorString_; -protected: - void setup(const QNetworkRequest &request, QNetworkAccessManager::Operation operation, const QByteArray &verb = QByteArray()); + protected: + void setup(const QNetworkRequest& request, QNetworkAccessManager::Operation operation, const QByteArray& verb = QByteArray()); - enum Status { - Idle, Requesting, ReRequesting - }; + enum Status { Idle, Requesting, ReRequesting }; QNetworkRequest request_; QByteArray data_; - QNetworkReply *reply_; + QNetworkReply* reply_; Status status_; QNetworkAccessManager::Operation operation_; QUrl url_; Katabasis::ReplyList timedReplies_; - QTimer *timer_; + QTimer* timer_; }; diff --git a/launcher/minecraft/auth/AuthSession.cpp b/launcher/minecraft/auth/AuthSession.cpp index 2c06beaa..37534f98 100644 --- a/launcher/minecraft/auth/AuthSession.cpp +++ b/launcher/minecraft/auth/AuthSession.cpp @@ -1,7 +1,7 @@ #include "AuthSession.h" -#include <QJsonObject> #include <QJsonArray> #include <QJsonDocument> +#include <QJsonObject> #include <QStringList> QString AuthSession::serializeUserProperties() @@ -16,13 +16,11 @@ QString AuthSession::serializeUserProperties() */ QJsonDocument value(userAttrs); return value.toJson(QJsonDocument::Compact); - } bool AuthSession::MakeOffline(QString offline_playername) { - if (status != PlayableOffline && status != PlayableOnline) - { + if (status != PlayableOffline && status != PlayableOnline) { return false; } session = "-"; @@ -32,7 +30,8 @@ bool AuthSession::MakeOffline(QString offline_playername) return true; } -void AuthSession::MakeDemo() { +void AuthSession::MakeDemo() +{ player_name = "Player"; demo = true; } diff --git a/launcher/minecraft/auth/AuthSession.h b/launcher/minecraft/auth/AuthSession.h index a75df506..40519476 100644 --- a/launcher/minecraft/auth/AuthSession.h +++ b/launcher/minecraft/auth/AuthSession.h @@ -1,22 +1,20 @@ #pragma once -#include <QString> #include <QMultiMap> +#include <QString> #include <memory> #include "QObjectPtr.h" class MinecraftAccount; class QNetworkAccessManager; -struct AuthSession -{ +struct AuthSession { bool MakeOffline(QString offline_playername); void MakeDemo(); QString serializeUserProperties(); - enum Status - { + enum Status { Undetermined, RequiresOAuth, RequiresPassword, @@ -45,7 +43,7 @@ struct AuthSession // Did the user request online mode? bool wants_online = true; - //Is this a demo session? + // Is this a demo session? bool demo = false; }; diff --git a/launcher/minecraft/auth/AuthStep.cpp b/launcher/minecraft/auth/AuthStep.cpp index ffa2581b..6240cc54 100644 --- a/launcher/minecraft/auth/AuthStep.cpp +++ b/launcher/minecraft/auth/AuthStep.cpp @@ -1,7 +1,5 @@ #include "AuthStep.h" -AuthStep::AuthStep(AccountData *data) : QObject(nullptr), m_data(data) { -} +AuthStep::AuthStep(AccountData* data) : QObject(nullptr), m_data(data) {} AuthStep::~AuthStep() noexcept = default; - diff --git a/launcher/minecraft/auth/AuthStep.h b/launcher/minecraft/auth/AuthStep.h index 2a8dc2ca..becd9b0c 100644 --- a/launcher/minecraft/auth/AuthStep.h +++ b/launcher/minecraft/auth/AuthStep.h @@ -1,33 +1,33 @@ #pragma once -#include <QObject> #include <QList> #include <QNetworkReply> +#include <QObject> +#include "AccountTask.h" #include "QObjectPtr.h" #include "minecraft/auth/AccountData.h" -#include "AccountTask.h" class AuthStep : public QObject { Q_OBJECT -public: + public: using Ptr = shared_qobject_ptr<AuthStep>; -public: - explicit AuthStep(AccountData *data); + public: + explicit AuthStep(AccountData* data); virtual ~AuthStep() noexcept; virtual QString describe() = 0; -public slots: + public slots: virtual void perform() = 0; virtual void rehydrate() = 0; -signals: + signals: void finished(AccountTaskState resultingState, QString message); - void showVerificationUriAndCode(const QUrl &uri, const QString &code, int expiresIn); + void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); void hideVerificationUriAndCode(); -protected: - AccountData *m_data; + protected: + AccountData* m_data; }; diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 5d279af1..6c2f0805 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -37,13 +37,14 @@ #include "MinecraftAccount.h" +#include <QColor> #include <QCryptographicHash> -#include <QUuid> -#include <QJsonObject> #include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> #include <QRegularExpression> #include <QStringList> -#include <QJsonDocument> +#include <QUuid> #include <QDebug> @@ -53,28 +54,30 @@ #include "flows/Mojang.h" #include "flows/Offline.h" -MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) { +MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) +{ data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); } - -MinecraftAccountPtr MinecraftAccount::loadFromJsonV2(const QJsonObject& json) { +MinecraftAccountPtr MinecraftAccount::loadFromJsonV2(const QJsonObject& json) +{ MinecraftAccountPtr account(new MinecraftAccount()); - if(account->data.resumeStateFromV2(json)) { + if (account->data.resumeStateFromV2(json)) { return account; } return nullptr; } -MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json) { +MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json) +{ MinecraftAccountPtr account(new MinecraftAccount()); - if(account->data.resumeStateFromV3(json)) { + if (account->data.resumeStateFromV3(json)) { return account; } return nullptr; } -MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString &username) +MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString& username) { auto account = makeShared<MinecraftAccount>(); account->data.type = AccountType::Mojang; @@ -90,7 +93,7 @@ MinecraftAccountPtr MinecraftAccount::createBlankMSA() return account; } -MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username) +MinecraftAccountPtr MinecraftAccount::createOffline(const QString& username) { auto account = makeShared<MinecraftAccount>(); account->data.type = AccountType::Offline; @@ -107,89 +110,96 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username) return account; } - QJsonObject MinecraftAccount::saveToJson() const { return data.saveState(); } -AccountState MinecraftAccount::accountState() const { +AccountState MinecraftAccount::accountState() const +{ return data.accountState; } -QPixmap MinecraftAccount::getFace() const { +QPixmap MinecraftAccount::getFace() const +{ QPixmap skinTexture; - if(!skinTexture.loadFromData(data.minecraftProfile.skin.data, "PNG")) { + if (!skinTexture.loadFromData(data.minecraftProfile.skin.data, "PNG")) { return QPixmap(); } QPixmap skin = QPixmap(8, 8); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + skin.fill(QColorConstants::Transparent); +#else + skin.fill(QColor(0, 0, 0, 0)); +#endif QPainter painter(&skin); painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8)); painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8)); return skin.scaled(64, 64, Qt::KeepAspectRatio); } - -shared_qobject_ptr<AccountTask> MinecraftAccount::login(QString password) { +shared_qobject_ptr<AccountTask> MinecraftAccount::login(QString password) +{ Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new MojangLogin(&data, password)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); - connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); + connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } -shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA() { +shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA() +{ Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new MSAInteractive(&data)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); - connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); + connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } -shared_qobject_ptr<AccountTask> MinecraftAccount::loginOffline() { +shared_qobject_ptr<AccountTask> MinecraftAccount::loginOffline() +{ Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new OfflineLogin(&data)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); - connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); + connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } -shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() { - if(m_currentTask) { +shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() +{ + if (m_currentTask) { return m_currentTask; } - if(data.type == AccountType::MSA) { + if (data.type == AccountType::MSA) { m_currentTask.reset(new MSASilent(&data)); - } - else if(data.type == AccountType::Offline) { + } else if (data.type == AccountType::Offline) { m_currentTask.reset(new OfflineRefresh(&data)); - } - else { + } else { m_currentTask.reset(new MojangRefresh(&data)); } connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); - connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); + connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } -shared_qobject_ptr<AccountTask> MinecraftAccount::currentTask() { +shared_qobject_ptr<AccountTask> MinecraftAccount::currentTask() +{ return m_currentTask; } - void MinecraftAccount::authSucceeded() { m_currentTask.reset(); @@ -206,28 +216,24 @@ void MinecraftAccount::authFailed(QString reason) } case AccountTaskState::STATE_FAILED_SOFT: { // NOTE: this doesn't do much. There was an error of some sort. - } - break; + } break; case AccountTaskState::STATE_FAILED_HARD: { - if(isMSA()) { + if (isMSA()) { data.msaToken.token = QString(); data.msaToken.refresh_token = QString(); data.msaToken.validity = Katabasis::Validity::None; data.validity_ = Katabasis::Validity::None; - } - else { + } else { data.yggdrasilToken.token = QString(); data.yggdrasilToken.validity = Katabasis::Validity::None; data.validity_ = Katabasis::Validity::None; } emit changed(); - } - break; + } break; case AccountTaskState::STATE_FAILED_GONE: { data.validity_ = Katabasis::Validity::None; emit changed(); - } - break; + } break; case AccountTaskState::STATE_CREATED: case AccountTaskState::STATE_WORKING: case AccountTaskState::STATE_SUCCEEDED: { @@ -238,21 +244,23 @@ void MinecraftAccount::authFailed(QString reason) emit activityChanged(false); } -bool MinecraftAccount::isActive() const { +bool MinecraftAccount::isActive() const +{ return !m_currentTask.isNull(); } -bool MinecraftAccount::shouldRefresh() const { +bool MinecraftAccount::shouldRefresh() const +{ /* * Never refresh accounts that are being used by the game, it breaks the game session. * Always refresh accounts that have not been refreshed yet during this session. * Don't refresh broken accounts. * Refresh accounts that would expire in the next 12 hours (fresh token validity is 24 hours). */ - if(isInUse()) { + if (isInUse()) { return false; } - switch(data.validity_) { + switch (data.validity_) { case Katabasis::Validity::Certain: { break; } @@ -267,7 +275,7 @@ bool MinecraftAccount::shouldRefresh() const { auto issuedTimestamp = data.yggdrasilToken.issueInstant; auto expiresTimestamp = data.yggdrasilToken.notAfter; - if(!expiresTimestamp.isValid()) { + if (!expiresTimestamp.isValid()) { expiresTimestamp = issuedTimestamp.addSecs(24 * 3600); } if (now.secsTo(expiresTimestamp) < (12 * 3600)) { @@ -278,14 +286,12 @@ bool MinecraftAccount::shouldRefresh() const { void MinecraftAccount::fillSession(AuthSessionPtr session) { - if(ownsMinecraft() && !hasProfile()) { + if (ownsMinecraft() && !hasProfile()) { session->status = AuthSession::RequiresProfileSetup; - } - else { - if(session->wants_online) { + } else { + if (session->wants_online) { session->status = AuthSession::PlayableOnline; - } - else { + } else { session->status = AuthSession::PlayableOffline; } } @@ -303,12 +309,9 @@ void MinecraftAccount::fillSession(AuthSessionPtr session) session->uuid = data.profileId(); // 'legacy' or 'mojang', depending on account type session->user_type = typeString(); - if (!session->access_token.isEmpty()) - { + if (!session->access_token.isEmpty()) { session->session = "token:" + data.accessToken() + ":" + data.profileId(); - } - else - { + } else { session->session = "-"; } } @@ -316,8 +319,7 @@ void MinecraftAccount::fillSession(AuthSessionPtr session) void MinecraftAccount::decrementUses() { Usable::decrementUses(); - if(!isInUse()) - { + if (!isInUse()) { emit changed(); // FIXME: we now need a better way to identify accounts... qWarning() << "Profile" << data.profileId() << "is no longer in use."; @@ -328,39 +330,31 @@ void MinecraftAccount::incrementUses() { bool wasInUse = isInUse(); Usable::incrementUses(); - if(!wasInUse) - { + if (!wasInUse) { emit changed(); // FIXME: we now need a better way to identify accounts... qWarning() << "Profile" << data.profileId() << "is now in use."; } } -QUuid MinecraftAccount::uuidFromUsername(QString username) { +QUuid MinecraftAccount::uuidFromUsername(QString username) +{ auto input = QString("OfflinePlayer:%1").arg(username).toUtf8(); // basically a reimplementation of Java's UUID#nameUUIDFromBytes QByteArray digest = QCryptographicHash::hash(input, QCryptographicHash::Md5); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - auto bOr = [](QByteArray& array, int index, char value) { - array[index] = array.at(index) | value; - }; - auto bAnd = [](QByteArray& array, int index, char value) { - array[index] = array.at(index) & value; - }; + auto bOr = [](QByteArray& array, int index, char value) { array[index] = array.at(index) | value; }; + auto bAnd = [](QByteArray& array, int index, char value) { array[index] = array.at(index) & value; }; #else - auto bOr = [](QByteArray& array, qsizetype index, char value) { - array[index] |= value; - }; - auto bAnd = [](QByteArray& array, qsizetype index, char value) { - array[index] &= value; - }; + auto bOr = [](QByteArray& array, qsizetype index, char value) { array[index] |= value; }; + auto bAnd = [](QByteArray& array, qsizetype index, char value) { array[index] &= value; }; #endif - bAnd(digest, 6, (char) 0x0f); // clear version - bOr(digest, 6, (char) 0x30); // set to version 3 - bAnd(digest, 8, (char) 0x3f); // clear variant - bOr(digest, 8, (char) 0x80); // set to IETF variant + bAnd(digest, 6, (char)0x0f); // clear version + bOr(digest, 6, (char)0x30); // set to version 3 + bAnd(digest, 8, (char)0x3f); // clear variant + bOr(digest, 8, (char)0x80); // set to IETF variant return QUuid::fromRfc4122(digest); } diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index 67623a5a..f04f947f 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -35,20 +35,20 @@ #pragma once -#include <QObject> -#include <QString> -#include <QList> #include <QJsonObject> -#include <QPair> +#include <QList> #include <QMap> +#include <QObject> +#include <QPair> #include <QPixmap> +#include <QString> #include <memory> -#include "AuthSession.h" -#include "Usable.h" #include "AccountData.h" +#include "AuthSession.h" #include "QObjectPtr.h" +#include "Usable.h" class Task; class AccountTask; @@ -64,8 +64,7 @@ Q_DECLARE_METATYPE(MinecraftAccountPtr) * but we might as well add some things for it in Prism Launcher right now so * we don't have to rip the code to pieces to add it later. */ -struct AccountProfile -{ +struct AccountProfile { QString id; QString name; bool legacy; @@ -77,34 +76,30 @@ struct AccountProfile * Said information may include things such as that account's username, client token, and access * token if the user chose to stay logged in. */ -class MinecraftAccount : - public QObject, - public Usable -{ +class MinecraftAccount : public QObject, public Usable { Q_OBJECT -public: /* construction */ + public: /* construction */ //! Do not copy accounts. ever. - explicit MinecraftAccount(const MinecraftAccount &other, QObject *parent) = delete; + explicit MinecraftAccount(const MinecraftAccount& other, QObject* parent) = delete; //! Default constructor - explicit MinecraftAccount(QObject *parent = 0); + explicit MinecraftAccount(QObject* parent = 0); - static MinecraftAccountPtr createFromUsername(const QString &username); + static MinecraftAccountPtr createFromUsername(const QString& username); static MinecraftAccountPtr createBlankMSA(); - static MinecraftAccountPtr createOffline(const QString &username); + static MinecraftAccountPtr createOffline(const QString& username); - static MinecraftAccountPtr loadFromJsonV2(const QJsonObject &json); - static MinecraftAccountPtr loadFromJsonV3(const QJsonObject &json); + static MinecraftAccountPtr loadFromJsonV2(const QJsonObject& json); + static MinecraftAccountPtr loadFromJsonV3(const QJsonObject& json); static QUuid uuidFromUsername(QString username); //! Saves a MinecraftAccount to a JSON object and returns it. QJsonObject saveToJson() const; -public: /* manipulation */ - + public: /* manipulation */ /** * Attempt to login. Empty password means we use the token. * If the attempt fails because we already are performing some task, it returns false. @@ -119,70 +114,46 @@ public: /* manipulation */ shared_qobject_ptr<AccountTask> currentTask(); -public: /* queries */ - QString internalId() const { - return data.internalId; - } + public: /* queries */ + QString internalId() const { return data.internalId; } - QString accountDisplayString() const { - return data.accountDisplayString(); - } + QString accountDisplayString() const { return data.accountDisplayString(); } - QString mojangUserName() const { - return data.userName(); - } + QString mojangUserName() const { return data.userName(); } - QString accessToken() const { - return data.accessToken(); - } + QString accessToken() const { return data.accessToken(); } - QString profileId() const { - return data.profileId(); - } + QString profileId() const { return data.profileId(); } - QString profileName() const { - return data.profileName(); - } + QString profileName() const { return data.profileName(); } bool isActive() const; - bool canMigrate() const { - return data.canMigrateToMSA; - } + bool canMigrate() const { return data.canMigrateToMSA; } - bool isMSA() const { - return data.type == AccountType::MSA; - } + bool isMSA() const { return data.type == AccountType::MSA; } - bool isOffline() const { - return data.type == AccountType::Offline; - } + bool isOffline() const { return data.type == AccountType::Offline; } - bool ownsMinecraft() const { - return data.minecraftEntitlement.ownsMinecraft; - } + bool ownsMinecraft() const { return data.minecraftEntitlement.ownsMinecraft; } - bool hasProfile() const { - return data.profileId().size() != 0; - } + bool hasProfile() const { return data.profileId().size() != 0; } - QString typeString() const { - switch(data.type) { + QString typeString() const + { + switch (data.type) { case AccountType::Mojang: { - if(data.legacy) { + if (data.legacy) { return "legacy"; } return "mojang"; - } - break; + } break; case AccountType::MSA: { return "msa"; - } - break; + } break; case AccountType::Offline: { return "offline"; - } - break; + } break; default: { return "unknown"; } @@ -194,19 +165,15 @@ public: /* queries */ //! Returns the current state of the account AccountState accountState() const; - AccountData * accountData() { - return &data; - } + AccountData* accountData() { return &data; } bool shouldRefresh() const; void fillSession(AuthSessionPtr session); - QString lastError() const { - return data.lastError(); - } + QString lastError() const { return data.lastError(); } -signals: + signals: /** * This signal is emitted when the account changes */ @@ -216,20 +183,17 @@ signals: // TODO: better signalling for the various possible state changes - especially errors -protected: /* variables */ + protected: /* variables */ AccountData data; // current task we are executing here shared_qobject_ptr<AccountTask> m_currentTask; -protected: /* methods */ - + protected: /* methods */ void incrementUses() override; void decrementUses() override; -private -slots: + private slots: void authSucceeded(); void authFailed(QString reason); }; - diff --git a/launcher/minecraft/auth/Parsers.cpp b/launcher/minecraft/auth/Parsers.cpp index f3d9ad56..f6179a93 100644 --- a/launcher/minecraft/auth/Parsers.cpp +++ b/launcher/minecraft/auth/Parsers.cpp @@ -2,46 +2,51 @@ #include "Json.h" #include "Logging.h" -#include <QJsonDocument> -#include <QJsonArray> #include <QDebug> +#include <QJsonArray> +#include <QJsonDocument> namespace Parsers { -bool getDateTime(QJsonValue value, QDateTime & out) { - if(!value.isString()) { +bool getDateTime(QJsonValue value, QDateTime& out) +{ + if (!value.isString()) { return false; } out = QDateTime::fromString(value.toString(), Qt::ISODate); return out.isValid(); } -bool getString(QJsonValue value, QString & out) { - if(!value.isString()) { +bool getString(QJsonValue value, QString& out) +{ + if (!value.isString()) { return false; } out = value.toString(); return true; } -bool getNumber(QJsonValue value, double & out) { - if(!value.isDouble()) { +bool getNumber(QJsonValue value, double& out) +{ + if (!value.isDouble()) { return false; } out = value.toDouble(); return true; } -bool getNumber(QJsonValue value, int64_t & out) { - if(!value.isDouble()) { +bool getNumber(QJsonValue value, int64_t& out) +{ + if (!value.isDouble()) { return false; } - out = (int64_t) value.toDouble(); + out = (int64_t)value.toDouble(); return true; } -bool getBool(QJsonValue value, bool & out) { - if(!value.isBool()) { +bool getBool(QJsonValue value, bool& out) +{ + if (!value.isBool()) { return false; } out = value.toBool(); @@ -74,49 +79,50 @@ bool getBool(QJsonValue value, bool & out) { // 2148916238 = child account not linked to a family */ -bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString name) { - qDebug() << "Parsing" << name <<":"; +bool parseXTokenResponse(QByteArray& data, Katabasis::Token& output, QString name) +{ + qDebug() << "Parsing" << name << ":"; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); return false; } auto obj = doc.object(); - if(!getDateTime(obj.value("IssueInstant"), output.issueInstant)) { + if (!getDateTime(obj.value("IssueInstant"), output.issueInstant)) { qWarning() << "User IssueInstant is not a timestamp"; return false; } - if(!getDateTime(obj.value("NotAfter"), output.notAfter)) { + if (!getDateTime(obj.value("NotAfter"), output.notAfter)) { qWarning() << "User NotAfter is not a timestamp"; return false; } - if(!getString(obj.value("Token"), output.token)) { + if (!getString(obj.value("Token"), output.token)) { qWarning() << "User Token is not a string"; return false; } auto arrayVal = obj.value("DisplayClaims").toObject().value("xui"); - if(!arrayVal.isArray()) { + if (!arrayVal.isArray()) { qWarning() << "Missing xui claims array"; return false; } bool foundUHS = false; - for(auto item: arrayVal.toArray()) { - if(!item.isObject()) { + for (auto item : arrayVal.toArray()) { + if (!item.isObject()) { continue; } - auto obj = item.toObject(); - if(obj.contains("uhs")) { + auto obj_ = item.toObject(); + if (obj_.contains("uhs")) { foundUHS = true; } else { continue; } // consume all 'display claims' ... whatever that means - for(auto iter = obj.begin(); iter != obj.end(); iter++) { + for (auto iter = obj_.begin(); iter != obj_.end(); iter++) { QString claim; - if(!getString(obj.value(iter.key()), claim)) { + if (!getString(obj_.value(iter.key()), claim)) { qWarning() << "display claim " << iter.key() << " is not a string..."; return false; } @@ -125,7 +131,7 @@ bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString na break; } - if(!foundUHS) { + if (!foundUHS) { qWarning() << "Missing uhs"; return false; } @@ -134,46 +140,47 @@ bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString na return true; } -bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) { +bool parseMinecraftProfile(QByteArray& data, MinecraftProfile& output) +{ qDebug() << "Parsing Minecraft profile..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); return false; } auto obj = doc.object(); - if(!getString(obj.value("id"), output.id)) { + if (!getString(obj.value("id"), output.id)) { qWarning() << "Minecraft profile id is not a string"; return false; } - if(!getString(obj.value("name"), output.name)) { + if (!getString(obj.value("name"), output.name)) { qWarning() << "Minecraft profile name is not a string"; return false; } auto skinsArray = obj.value("skins").toArray(); - for(auto skin: skinsArray) { + for (auto skin : skinsArray) { auto skinObj = skin.toObject(); Skin skinOut; - if(!getString(skinObj.value("id"), skinOut.id)) { + if (!getString(skinObj.value("id"), skinOut.id)) { continue; } QString state; - if(!getString(skinObj.value("state"), state)) { + if (!getString(skinObj.value("state"), state)) { continue; } - if(state != "ACTIVE") { + if (state != "ACTIVE") { continue; } - if(!getString(skinObj.value("url"), skinOut.url)) { + if (!getString(skinObj.value("url"), skinOut.url)) { continue; } - if(!getString(skinObj.value("variant"), skinOut.variant)) { + if (!getString(skinObj.value("variant"), skinOut.variant)) { continue; } // we deal with only the active skin @@ -183,23 +190,23 @@ bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) { auto capesArray = obj.value("capes").toArray(); QString currentCape; - for(auto cape: capesArray) { + for (auto cape : capesArray) { auto capeObj = cape.toObject(); Cape capeOut; - if(!getString(capeObj.value("id"), capeOut.id)) { + if (!getString(capeObj.value("id"), capeOut.id)) { continue; } QString state; - if(!getString(capeObj.value("state"), state)) { + if (!getString(capeObj.value("state"), state)) { continue; } - if(state == "ACTIVE") { + if (state == "ACTIVE") { currentCape = capeOut.id; } - if(!getString(capeObj.value("url"), capeOut.url)) { + if (!getString(capeObj.value("url"), capeOut.url)) { continue; } - if(!getString(capeObj.value("alias"), capeOut.alias)) { + if (!getString(capeObj.value("alias"), capeOut.alias)) { continue; } @@ -211,30 +218,33 @@ bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) { } namespace { - // these skin URLs are for the MHF_Steve and MHF_Alex accounts (made by a Mojang employee) - // they are needed because the session server doesn't return skin urls for default skins - static const QString SKIN_URL_STEVE = "http://textures.minecraft.net/texture/1a4af718455d4aab528e7a61f86fa25e6a369d1768dcb13f7df319a713eb810b"; - static const QString SKIN_URL_ALEX = "http://textures.minecraft.net/texture/83cee5ca6afcdb171285aa00e8049c297b2dbeba0efb8ff970a5677a1b644032"; - - bool isDefaultModelSteve(QString uuid) { - // need to calculate *Java* hashCode of UUID - // if number is even, skin/model is steve, otherwise it is alex - - // just in case dashes are in the id - uuid.remove('-'); +// these skin URLs are for the MHF_Steve and MHF_Alex accounts (made by a Mojang employee) +// they are needed because the session server doesn't return skin urls for default skins +static const QString SKIN_URL_STEVE = + "http://textures.minecraft.net/texture/1a4af718455d4aab528e7a61f86fa25e6a369d1768dcb13f7df319a713eb810b"; +static const QString SKIN_URL_ALEX = + "http://textures.minecraft.net/texture/83cee5ca6afcdb171285aa00e8049c297b2dbeba0efb8ff970a5677a1b644032"; + +bool isDefaultModelSteve(QString uuid) +{ + // need to calculate *Java* hashCode of UUID + // if number is even, skin/model is steve, otherwise it is alex - if (uuid.size() != 32) { - return true; - } + // just in case dashes are in the id + uuid.remove('-'); - // qulonglong is guaranteed to be 64 bits - // we need to use unsigned numbers to guarantee truncation below - qulonglong most = uuid.left(16).toULongLong(nullptr, 16); - qulonglong least = uuid.right(16).toULongLong(nullptr, 16); - qulonglong xored = most ^ least; - return ((static_cast<quint32>(xored >> 32)) ^ static_cast<quint32>(xored)) % 2 == 0; + if (uuid.size() != 32) { + return true; } + + // qulonglong is guaranteed to be 64 bits + // we need to use unsigned numbers to guarantee truncation below + qulonglong most = uuid.left(16).toULongLong(nullptr, 16); + qulonglong least = uuid.right(16).toULongLong(nullptr, 16); + qulonglong xored = most ^ least; + return ((static_cast<quint32>(xored >> 32)) ^ static_cast<quint32>(xored)) % 2 == 0; } +} // namespace /** Uses session server for skin/cape lookup instead of profile, @@ -270,31 +280,32 @@ decoded base64 "value": } */ -bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { +bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output) +{ qDebug() << "Parsing Minecraft profile..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response as JSON: " << jsonError.errorString(); return false; } auto obj = Json::requireObject(doc, "mojang minecraft profile"); - if(!getString(obj.value("id"), output.id)) { + if (!getString(obj.value("id"), output.id)) { qWarning() << "Minecraft profile id is not a string"; return false; } - if(!getString(obj.value("name"), output.name)) { + if (!getString(obj.value("name"), output.name)) { qWarning() << "Minecraft profile name is not a string"; return false; } auto propsArray = obj.value("properties").toArray(); QByteArray texturePayload; - for( auto p : propsArray) { + for (auto p : propsArray) { auto pObj = p.toObject(); auto name = pObj.value("name"); if (!name.isString() || name.toString() != "textures") { @@ -321,7 +332,7 @@ bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { } doc = QJsonDocument::fromJson(texturePayload, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response as JSON: " << jsonError.errorString(); return false; } @@ -357,8 +368,7 @@ bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { // might not be present getString(meta.value("model"), skinOut.variant); } - } - else if (idx.key() == "CAPE") { + } else if (idx.key() == "CAPE") { auto cape = idx->toObject(); if (!getString(cape.value("url"), capeOut.url)) { qWarning() << "Cape url is not a string"; @@ -374,7 +384,7 @@ bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { output.skin = skinOut; if (capeOut.alias == "cape") { - output.capes = QMap<QString, Cape>({{capeOut.alias, capeOut}}); + output.capes = QMap<QString, Cape>({ { capeOut.alias, capeOut } }); output.currentCape = capeOut.alias; } @@ -382,13 +392,14 @@ bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { return true; } -bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) { +bool parseMinecraftEntitlements(QByteArray& data, MinecraftEntitlement& output) +{ qDebug() << "Parsing Minecraft entitlements..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); return false; } @@ -398,16 +409,16 @@ bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) output.ownsMinecraft = false; auto itemsArray = obj.value("items").toArray(); - for(auto item: itemsArray) { + for (auto item : itemsArray) { auto itemObj = item.toObject(); QString name; - if(!getString(itemObj.value("name"), name)) { + if (!getString(itemObj.value("name"), name)) { continue; } - if(name == "game_minecraft") { + if (name == "game_minecraft") { output.canPlayMinecraft = true; } - if(name == "product_minecraft") { + if (name == "product_minecraft") { output.ownsMinecraft = true; } } @@ -415,47 +426,50 @@ bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) return true; } -bool parseRolloutResponse(QByteArray & data, bool& result) { +bool parseRolloutResponse(QByteArray& data, bool& result) +{ qDebug() << "Parsing Rollout response..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { - qWarning() << "Failed to parse response from https://api.minecraftservices.com/rollout/v1/msamigration as JSON: " << jsonError.errorString(); + if (jsonError.error) { + qWarning() << "Failed to parse response from https://api.minecraftservices.com/rollout/v1/msamigration as JSON: " + << jsonError.errorString(); return false; } auto obj = doc.object(); QString feature; - if(!getString(obj.value("feature"), feature)) { + if (!getString(obj.value("feature"), feature)) { qWarning() << "Rollout feature is not a string"; return false; } - if(feature != "msamigration") { + if (feature != "msamigration") { qWarning() << "Rollout feature is not what we expected (msamigration), but is instead \"" << feature << "\""; return false; } - if(!getBool(obj.value("rollout"), result)) { + if (!getBool(obj.value("rollout"), result)) { qWarning() << "Rollout feature is not a string"; return false; } return true; } -bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) { +bool parseMojangResponse(QByteArray& data, Katabasis::Token& output) +{ QJsonParseError jsonError; qDebug() << "Parsing Mojang response..."; qCDebug(authCredentials()) << data; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON: " << jsonError.errorString(); return false; } auto obj = doc.object(); double expires_in = 0; - if(!getNumber(obj.value("expires_in"), expires_in)) { + if (!getNumber(obj.value("expires_in"), expires_in)) { qWarning() << "expires_in is not a valid number"; return false; } @@ -464,13 +478,13 @@ bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) { output.notAfter = currentTime.addSecs(expires_in); QString username; - if(!getString(obj.value("username"), username)) { + if (!getString(obj.value("username"), username)) { qWarning() << "username is not valid"; return false; } // TODO: it's a JWT... validate it? - if(!getString(obj.value("access_token"), output.token)) { + if (!getString(obj.value("access_token"), output.token)) { qWarning() << "access_token is not valid"; return false; } @@ -479,4 +493,4 @@ bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) { return true; } -} +} // namespace Parsers diff --git a/launcher/minecraft/auth/Parsers.h b/launcher/minecraft/auth/Parsers.h index 2666d890..d073f999 100644 --- a/launcher/minecraft/auth/Parsers.h +++ b/launcher/minecraft/auth/Parsers.h @@ -2,19 +2,18 @@ #include "AccountData.h" -namespace Parsers -{ - bool getDateTime(QJsonValue value, QDateTime & out); - bool getString(QJsonValue value, QString & out); - bool getNumber(QJsonValue value, double & out); - bool getNumber(QJsonValue value, int64_t & out); - bool getBool(QJsonValue value, bool & out); +namespace Parsers { +bool getDateTime(QJsonValue value, QDateTime& out); +bool getString(QJsonValue value, QString& out); +bool getNumber(QJsonValue value, double& out); +bool getNumber(QJsonValue value, int64_t& out); +bool getBool(QJsonValue value, bool& out); - bool parseXTokenResponse(QByteArray &data, Katabasis::Token &output, QString name); - bool parseMojangResponse(QByteArray &data, Katabasis::Token &output); +bool parseXTokenResponse(QByteArray& data, Katabasis::Token& output, QString name); +bool parseMojangResponse(QByteArray& data, Katabasis::Token& output); - bool parseMinecraftProfile(QByteArray &data, MinecraftProfile &output); - bool parseMinecraftProfileMojang(QByteArray &data, MinecraftProfile &output); - bool parseMinecraftEntitlements(QByteArray &data, MinecraftEntitlement &output); - bool parseRolloutResponse(QByteArray &data, bool& result); -} +bool parseMinecraftProfile(QByteArray& data, MinecraftProfile& output); +bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output); +bool parseMinecraftEntitlements(QByteArray& data, MinecraftEntitlement& output); +bool parseRolloutResponse(QByteArray& data, bool& result); +} // namespace Parsers diff --git a/launcher/minecraft/auth/Yggdrasil.cpp b/launcher/minecraft/auth/Yggdrasil.cpp index d3e7ccdd..97f2a78d 100644 --- a/launcher/minecraft/auth/Yggdrasil.cpp +++ b/launcher/minecraft/auth/Yggdrasil.cpp @@ -16,24 +16,24 @@ #include "Yggdrasil.h" #include "AccountData.h" -#include <QObject> -#include <QString> -#include <QJsonObject> +#include <QByteArray> #include <QJsonDocument> +#include <QJsonObject> #include <QNetworkReply> -#include <QByteArray> +#include <QObject> +#include <QString> #include <QDebug> #include "Application.h" -Yggdrasil::Yggdrasil(AccountData *data, QObject *parent) - : AccountTask(data, parent) +Yggdrasil::Yggdrasil(AccountData* data, QObject* parent) : AccountTask(data, parent) { changeState(AccountTaskState::STATE_CREATED); } -void Yggdrasil::sendRequest(QUrl endpoint, QByteArray content) { +void Yggdrasil::sendRequest(QUrl endpoint, QByteArray content) +{ changeState(AccountTaskState::STATE_WORKING); QNetworkRequest netRequest(endpoint); @@ -52,10 +52,10 @@ void Yggdrasil::sendRequest(QUrl endpoint, QByteArray content) { connect(&counter, &QTimer::timeout, this, &Yggdrasil::heartbeat); } -void Yggdrasil::executeTask() { -} +void Yggdrasil::executeTask() {} -void Yggdrasil::refresh() { +void Yggdrasil::refresh() +{ start(); /* * { @@ -90,7 +90,8 @@ void Yggdrasil::refresh() { sendRequest(reqUrl, requestData); } -void Yggdrasil::login(QString password) { +void Yggdrasil::login(QString password) +{ start(); /* * { @@ -136,20 +137,21 @@ void Yggdrasil::login(QString password) { sendRequest(reqUrl, requestData); } - - -void Yggdrasil::refreshTimers(qint64, qint64) { +void Yggdrasil::refreshTimers(qint64, qint64) +{ timeout_keeper.stop(); timeout_keeper.start(timeout_max); progress(count = 0, timeout_max); } -void Yggdrasil::heartbeat() { +void Yggdrasil::heartbeat() +{ count += time_step; progress(count, timeout_max); } -bool Yggdrasil::abort() { +bool Yggdrasil::abort() +{ progress(timeout_max, timeout_max); // TODO: actually use this in a meaningful way m_aborted = Yggdrasil::BY_USER; @@ -157,14 +159,16 @@ bool Yggdrasil::abort() { return true; } -void Yggdrasil::abortByTimeout() { +void Yggdrasil::abortByTimeout() +{ progress(timeout_max, timeout_max); // TODO: actually use this in a meaningful way m_aborted = Yggdrasil::BY_TIMEOUT; m_netReply->abort(); } -void Yggdrasil::sslErrors(QList<QSslError> errors) { +void Yggdrasil::sslErrors(QList<QSslError> errors) +{ int i = 1; for (auto error : errors) { qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString(); @@ -174,7 +178,8 @@ void Yggdrasil::sslErrors(QList<QSslError> errors) { } } -void Yggdrasil::processResponse(QJsonObject responseData) { +void Yggdrasil::processResponse(QJsonObject responseData) +{ // Read the response data. We need to get the client token, access token, and the selected // profile. qDebug() << "Processing authentication response."; @@ -188,11 +193,11 @@ void Yggdrasil::processResponse(QJsonObject responseData) { changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server didn't send a client token.")); return; } - if(m_data->clientToken().isEmpty()) { + if (m_data->clientToken().isEmpty()) { m_data->setClientToken(clientToken); - } - else if(clientToken != m_data->clientToken()) { - changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported.")); + } else if (clientToken != m_data->clientToken()) { + changeState(AccountTaskState::STATE_FAILED_HARD, + tr("Authentication server attempted to change the client token. This isn't supported.")); return; } @@ -220,8 +225,7 @@ void Yggdrasil::processResponse(QJsonObject responseData) { for (auto i = profileObj.constBegin(); i != profileObj.constEnd(); ++i) { if (i.key() == "name" && i.value().isString()) { m_data->minecraftProfile.name = i->toString(); - } - else if (i.key() == "id" && i.value().isString()) { + } else if (i.key() == "id" && i.value().isString()) { m_data->minecraftProfile.id = i->toString(); } } @@ -237,50 +241,43 @@ void Yggdrasil::processResponse(QJsonObject responseData) { changeState(AccountTaskState::STATE_SUCCEEDED); } -void Yggdrasil::processReply() { +void Yggdrasil::processReply() +{ changeState(AccountTaskState::STATE_WORKING); - switch (m_netReply->error()) - { - case QNetworkReply::NoError: - break; - case QNetworkReply::TimeoutError: - changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation timed out.")); - return; - case QNetworkReply::OperationCanceledError: - changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation cancelled.")); - return; - case QNetworkReply::SslHandshakeFailedError: - changeState( - AccountTaskState::STATE_FAILED_SOFT, - tr( - "<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>" - "<ul>" - "<li>You use Windows and need to update your root certificates, please install any outstanding updates.</li>" - "<li>Some device on your network is interfering with SSL traffic. In that case, " - "you have bigger worries than Minecraft not starting.</li>" - "<li>Possibly something else. Check the log file for details</li>" - "</ul>" - ) - ); - return; - // used for invalid credentials and similar errors. Fall through. - case QNetworkReply::ContentAccessDenied: - case QNetworkReply::ContentOperationNotPermittedError: - break; - case QNetworkReply::ContentGoneError: { - changeState( - AccountTaskState::STATE_FAILED_GONE, - tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.") - ); - return; - } - default: - changeState( - AccountTaskState::STATE_FAILED_SOFT, - tr("Authentication operation failed due to a network error: %1 (%2)").arg(m_netReply->errorString()).arg(m_netReply->error()) - ); - return; + switch (m_netReply->error()) { + case QNetworkReply::NoError: + break; + case QNetworkReply::TimeoutError: + changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation timed out.")); + return; + case QNetworkReply::OperationCanceledError: + changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation cancelled.")); + return; + case QNetworkReply::SslHandshakeFailedError: + changeState(AccountTaskState::STATE_FAILED_SOFT, + tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>" + "<ul>" + "<li>You use Windows and need to update your root certificates, please install any outstanding updates.</li>" + "<li>Some device on your network is interfering with SSL traffic. In that case, " + "you have bigger worries than Minecraft not starting.</li>" + "<li>Possibly something else. Check the log file for details</li>" + "</ul>")); + return; + // used for invalid credentials and similar errors. Fall through. + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + break; + case QNetworkReply::ContentGoneError: { + changeState(AccountTaskState::STATE_FAILED_GONE, + tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")); + return; + } + default: + changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation failed due to a network error: %1 (%2)") + .arg(m_netReply->errorString()) + .arg(m_netReply->error())); + return; } // Try to parse the response regardless of the response code. @@ -299,12 +296,11 @@ void Yggdrasil::processReply() { if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) { processResponse(replyData.size() > 0 ? doc.object() : QJsonObject()); return; - } - else { - changeState( - AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to parse authentication server response JSON response: %1 at offset %2.").arg(jsonError.errorString()).arg(jsonError.offset) - ); + } else { + changeState(AccountTaskState::STATE_FAILED_SOFT, + tr("Failed to parse authentication server response JSON response: %1 at offset %2.") + .arg(jsonError.errorString()) + .arg(jsonError.offset)); qCritical() << replyData; } return; @@ -320,34 +316,26 @@ void Yggdrasil::processReply() { // stuff there. qDebug() << "The request failed, but the server gave us an error message. Processing error."; processError(doc.object()); - } - else { + } else { // The server didn't say anything regarding the error. Give the user an unknown // error. qDebug() << "The request failed and the server gave no error message. Unknown error."; changeState( AccountTaskState::STATE_FAILED_SOFT, - tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(m_netReply->errorString()) - ); + tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(m_netReply->errorString())); } } -void Yggdrasil::processError(QJsonObject responseData) { +void Yggdrasil::processError(QJsonObject responseData) +{ QJsonValue errorVal = responseData.value("error"); QJsonValue errorMessageValue = responseData.value("errorMessage"); QJsonValue causeVal = responseData.value("cause"); if (errorVal.isString() && errorMessageValue.isString()) { - m_error = std::shared_ptr<Error>( - new Error { - errorVal.toString(""), - errorMessageValue.toString(""), - causeVal.toString("") - } - ); + m_error = std::shared_ptr<Error>(new Error{ errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("") }); changeState(AccountTaskState::STATE_FAILED_HARD, m_error->m_errorMessageVerbose); - } - else { + } else { // Error is not in standard format. Don't set m_error and return unknown error. changeState(AccountTaskState::STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred.")); } diff --git a/launcher/minecraft/auth/Yggdrasil.h b/launcher/minecraft/auth/Yggdrasil.h index 4f52a04c..560d7fb8 100644 --- a/launcher/minecraft/auth/Yggdrasil.h +++ b/launcher/minecraft/auth/Yggdrasil.h @@ -17,10 +17,10 @@ #include "AccountTask.h" -#include <QString> +#include <qsslerror.h> #include <QJsonObject> +#include <QString> #include <QTimer> -#include <qsslerror.h> #include "MinecraftAccount.h" @@ -30,35 +30,25 @@ class QNetworkReply; /** * A Yggdrasil task is a task that performs an operation on a given mojang account. */ -class Yggdrasil : public AccountTask -{ +class Yggdrasil : public AccountTask { Q_OBJECT -public: - explicit Yggdrasil( - AccountData *data, - QObject *parent = 0 - ); + public: + explicit Yggdrasil(AccountData* data, QObject* parent = 0); virtual ~Yggdrasil() = default; void refresh(); void login(QString password); - struct Error - { + struct Error { QString m_errorMessageShort; QString m_errorMessageVerbose; QString m_cause; }; std::shared_ptr<Error> m_error; - enum AbortedBy - { - BY_NOTHING, - BY_USER, - BY_TIMEOUT - } m_aborted = BY_NOTHING; + enum AbortedBy { BY_NOTHING, BY_USER, BY_TIMEOUT } m_aborted = BY_NOTHING; -protected: + protected: void executeTask() override; /** @@ -78,24 +68,24 @@ protected: */ virtual void processError(QJsonObject responseData); -protected slots: + protected slots: void processReply(); void refreshTimers(qint64, qint64); void heartbeat(); void sslErrors(QList<QSslError>); void abortByTimeout(); -public slots: + public slots: virtual bool abort() override; -private: + private: void sendRequest(QUrl endpoint, QByteArray content); -protected: - QNetworkReply *m_netReply = nullptr; + protected: + QNetworkReply* m_netReply = nullptr; QTimer timeout_keeper; QTimer counter; - int count = 0; // num msec since time reset + int count = 0; // num msec since time reset const int timeout_max = 30000; const int time_step = 50; diff --git a/launcher/minecraft/auth/flows/AuthFlow.cpp b/launcher/minecraft/auth/flows/AuthFlow.cpp index 4f78e8c3..c51839a8 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.cpp +++ b/launcher/minecraft/auth/flows/AuthFlow.cpp @@ -1,36 +1,33 @@ +#include <QDebug> #include <QNetworkAccessManager> -#include <QNetworkRequest> #include <QNetworkReply> -#include <QDebug> +#include <QNetworkRequest> #include "AuthFlow.h" #include "katabasis/Globals.h" #include <Application.h> -AuthFlow::AuthFlow(AccountData * data, QObject *parent) : - AccountTask(data, parent) -{ -} +AuthFlow::AuthFlow(AccountData* data, QObject* parent) : AccountTask(data, parent) {} -void AuthFlow::succeed() { +void AuthFlow::succeed() +{ m_data->validity_ = Katabasis::Validity::Certain; - changeState( - AccountTaskState::STATE_SUCCEEDED, - tr("Finished all authentication steps") - ); + changeState(AccountTaskState::STATE_SUCCEEDED, tr("Finished all authentication steps")); } -void AuthFlow::executeTask() { - if(m_currentStep) { +void AuthFlow::executeTask() +{ + if (m_currentStep) { return; } changeState(AccountTaskState::STATE_WORKING, tr("Initializing")); nextStep(); } -void AuthFlow::nextStep() { - if(m_steps.size() == 0) { +void AuthFlow::nextStep() +{ + if (m_steps.size() == 0) { // we got to the end without an incident... assume this is all. m_currentStep.reset(); succeed(); @@ -46,15 +43,13 @@ void AuthFlow::nextStep() { m_currentStep->perform(); } - -QString AuthFlow::getStateMessage() const { - switch (m_taskState) - { +QString AuthFlow::getStateMessage() const +{ + switch (m_taskState) { case AccountTaskState::STATE_WORKING: { - if(m_currentStep) { + if (m_currentStep) { return m_currentStep->describe(); - } - else { + } else { return tr("Working..."); } } @@ -64,8 +59,9 @@ QString AuthFlow::getStateMessage() const { } } -void AuthFlow::stepFinished(AccountTaskState resultingState, QString message) { - if(changeState(resultingState, message)) { +void AuthFlow::stepFinished(AccountTaskState resultingState, QString message) +{ + if (changeState(resultingState, message)) { nextStep(); } } diff --git a/launcher/minecraft/auth/flows/AuthFlow.h b/launcher/minecraft/auth/flows/AuthFlow.h index e067cc99..c2c412ab 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.h +++ b/launcher/minecraft/auth/flows/AuthFlow.h @@ -1,45 +1,42 @@ #pragma once -#include <QObject> +#include <QImage> #include <QList> -#include <QVector> -#include <QSet> #include <QNetworkReply> -#include <QImage> +#include <QObject> +#include <QSet> +#include <QVector> #include <katabasis/DeviceFlow.h> -#include "minecraft/auth/Yggdrasil.h" #include "minecraft/auth/AccountData.h" #include "minecraft/auth/AccountTask.h" #include "minecraft/auth/AuthStep.h" +#include "minecraft/auth/Yggdrasil.h" -class AuthFlow : public AccountTask -{ +class AuthFlow : public AccountTask { Q_OBJECT -public: - explicit AuthFlow(AccountData * data, QObject *parent = 0); + public: + explicit AuthFlow(AccountData* data, QObject* parent = 0); - Katabasis::Validity validity() { - return m_data->validity_; - }; + Katabasis::Validity validity() { return m_data->validity_; }; QString getStateMessage() const override; void executeTask() override; -signals: + signals: void activityChanged(Katabasis::Activity activity); -private slots: + private slots: void stepFinished(AccountTaskState resultingState, QString message); -protected: + protected: void succeed(); void nextStep(); -protected: + protected: QList<AuthStep::Ptr> m_steps; AuthStep::Ptr m_currentStep; }; diff --git a/launcher/minecraft/auth/flows/MSA.cpp b/launcher/minecraft/auth/flows/MSA.cpp index f1987e0c..f0399342 100644 --- a/launcher/minecraft/auth/flows/MSA.cpp +++ b/launcher/minecraft/auth/flows/MSA.cpp @@ -1,15 +1,16 @@ #include "MSA.h" +#include "minecraft/auth/steps/EntitlementsStep.h" +#include "minecraft/auth/steps/GetSkinStep.h" +#include "minecraft/auth/steps/LauncherLoginStep.h" #include "minecraft/auth/steps/MSAStep.h" -#include "minecraft/auth/steps/XboxUserStep.h" +#include "minecraft/auth/steps/MinecraftProfileStep.h" #include "minecraft/auth/steps/XboxAuthorizationStep.h" -#include "minecraft/auth/steps/LauncherLoginStep.h" #include "minecraft/auth/steps/XboxProfileStep.h" -#include "minecraft/auth/steps/EntitlementsStep.h" -#include "minecraft/auth/steps/MinecraftProfileStep.h" -#include "minecraft/auth/steps/GetSkinStep.h" +#include "minecraft/auth/steps/XboxUserStep.h" -MSASilent::MSASilent(AccountData* data, QObject* parent) : AuthFlow(data, parent) { +MSASilent::MSASilent(AccountData* data, QObject* parent) : AuthFlow(data, parent) +{ m_steps.append(makeShared<MSAStep>(m_data, MSAStep::Action::Refresh)); m_steps.append(makeShared<XboxUserStep>(m_data)); m_steps.append(makeShared<XboxAuthorizationStep>(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); @@ -21,10 +22,8 @@ MSASilent::MSASilent(AccountData* data, QObject* parent) : AuthFlow(data, parent m_steps.append(makeShared<GetSkinStep>(m_data)); } -MSAInteractive::MSAInteractive( - AccountData* data, - QObject* parent -) : AuthFlow(data, parent) { +MSAInteractive::MSAInteractive(AccountData* data, QObject* parent) : AuthFlow(data, parent) +{ m_steps.append(makeShared<MSAStep>(m_data, MSAStep::Action::Login)); m_steps.append(makeShared<XboxUserStep>(m_data)); m_steps.append(makeShared<XboxAuthorizationStep>(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); diff --git a/launcher/minecraft/auth/flows/MSA.h b/launcher/minecraft/auth/flows/MSA.h index 14a4ff43..e403d530 100644 --- a/launcher/minecraft/auth/flows/MSA.h +++ b/launcher/minecraft/auth/flows/MSA.h @@ -1,22 +1,14 @@ #pragma once #include "AuthFlow.h" -class MSAInteractive : public AuthFlow -{ +class MSAInteractive : public AuthFlow { Q_OBJECT -public: - explicit MSAInteractive( - AccountData *data, - QObject *parent = 0 - ); + public: + explicit MSAInteractive(AccountData* data, QObject* parent = 0); }; -class MSASilent : public AuthFlow -{ +class MSASilent : public AuthFlow { Q_OBJECT -public: - explicit MSASilent( - AccountData * data, - QObject *parent = 0 - ); + public: + explicit MSASilent(AccountData* data, QObject* parent = 0); }; diff --git a/launcher/minecraft/auth/flows/Mojang.cpp b/launcher/minecraft/auth/flows/Mojang.cpp index 5900ea98..7e2db16f 100644 --- a/launcher/minecraft/auth/flows/Mojang.cpp +++ b/launcher/minecraft/auth/flows/Mojang.cpp @@ -1,25 +1,20 @@ #include "Mojang.h" -#include "minecraft/auth/steps/YggdrasilStep.h" -#include "minecraft/auth/steps/MinecraftProfileStepMojang.h" -#include "minecraft/auth/steps/MigrationEligibilityStep.h" #include "minecraft/auth/steps/GetSkinStep.h" +#include "minecraft/auth/steps/MigrationEligibilityStep.h" +#include "minecraft/auth/steps/MinecraftProfileStepMojang.h" +#include "minecraft/auth/steps/YggdrasilStep.h" -MojangRefresh::MojangRefresh( - AccountData *data, - QObject *parent -) : AuthFlow(data, parent) { +MojangRefresh::MojangRefresh(AccountData* data, QObject* parent) : AuthFlow(data, parent) +{ m_steps.append(makeShared<YggdrasilStep>(m_data, QString())); m_steps.append(makeShared<MinecraftProfileStepMojang>(m_data)); m_steps.append(makeShared<MigrationEligibilityStep>(m_data)); m_steps.append(makeShared<GetSkinStep>(m_data)); } -MojangLogin::MojangLogin( - AccountData *data, - QString password, - QObject *parent -): AuthFlow(data, parent), m_password(password) { +MojangLogin::MojangLogin(AccountData* data, QString password, QObject* parent) : AuthFlow(data, parent), m_password(password) +{ m_steps.append(makeShared<YggdrasilStep>(m_data, m_password)); m_steps.append(makeShared<MinecraftProfileStepMojang>(m_data)); m_steps.append(makeShared<MigrationEligibilityStep>(m_data)); diff --git a/launcher/minecraft/auth/flows/Mojang.h b/launcher/minecraft/auth/flows/Mojang.h index c09c81a8..779ca7e3 100644 --- a/launcher/minecraft/auth/flows/Mojang.h +++ b/launcher/minecraft/auth/flows/Mojang.h @@ -1,26 +1,17 @@ #pragma once #include "AuthFlow.h" -class MojangRefresh : public AuthFlow -{ +class MojangRefresh : public AuthFlow { Q_OBJECT -public: - explicit MojangRefresh( - AccountData *data, - QObject *parent = 0 - ); + public: + explicit MojangRefresh(AccountData* data, QObject* parent = 0); }; -class MojangLogin : public AuthFlow -{ +class MojangLogin : public AuthFlow { Q_OBJECT -public: - explicit MojangLogin( - AccountData *data, - QString password, - QObject *parent = 0 - ); + public: + explicit MojangLogin(AccountData* data, QString password, QObject* parent = 0); -private: + private: QString m_password; }; diff --git a/launcher/minecraft/auth/flows/Offline.cpp b/launcher/minecraft/auth/flows/Offline.cpp index d5c63271..3770b869 100644 --- a/launcher/minecraft/auth/flows/Offline.cpp +++ b/launcher/minecraft/auth/flows/Offline.cpp @@ -2,16 +2,12 @@ #include "minecraft/auth/steps/OfflineStep.h" -OfflineRefresh::OfflineRefresh( - AccountData *data, - QObject *parent -) : AuthFlow(data, parent) { +OfflineRefresh::OfflineRefresh(AccountData* data, QObject* parent) : AuthFlow(data, parent) +{ m_steps.append(makeShared<OfflineStep>(m_data)); } -OfflineLogin::OfflineLogin( - AccountData *data, - QObject *parent -) : AuthFlow(data, parent) { +OfflineLogin::OfflineLogin(AccountData* data, QObject* parent) : AuthFlow(data, parent) +{ m_steps.append(makeShared<OfflineStep>(m_data)); } diff --git a/launcher/minecraft/auth/flows/Offline.h b/launcher/minecraft/auth/flows/Offline.h index 5d1f83a4..2bc9c761 100644 --- a/launcher/minecraft/auth/flows/Offline.h +++ b/launcher/minecraft/auth/flows/Offline.h @@ -1,22 +1,14 @@ #pragma once #include "AuthFlow.h" -class OfflineRefresh : public AuthFlow -{ +class OfflineRefresh : public AuthFlow { Q_OBJECT -public: - explicit OfflineRefresh( - AccountData *data, - QObject *parent = 0 - ); + public: + explicit OfflineRefresh(AccountData* data, QObject* parent = 0); }; -class OfflineLogin : public AuthFlow -{ +class OfflineLogin : public AuthFlow { Q_OBJECT -public: - explicit OfflineLogin( - AccountData *data, - QObject *parent = 0 - ); + public: + explicit OfflineLogin(AccountData* data, QObject* parent = 0); }; diff --git a/launcher/minecraft/auth/steps/EntitlementsStep.cpp b/launcher/minecraft/auth/steps/EntitlementsStep.cpp index bd604292..0573dcb6 100644 --- a/launcher/minecraft/auth/steps/EntitlementsStep.cpp +++ b/launcher/minecraft/auth/steps/EntitlementsStep.cpp @@ -11,12 +11,13 @@ EntitlementsStep::EntitlementsStep(AccountData* data) : AuthStep(data) {} EntitlementsStep::~EntitlementsStep() noexcept = default; -QString EntitlementsStep::describe() { +QString EntitlementsStep::describe() +{ return tr("Determining game ownership."); } - -void EntitlementsStep::perform() { +void EntitlementsStep::perform() +{ auto uuid = QUuid::createUuid(); m_entitlementsRequestId = uuid.toString().remove('{').remove('}'); auto url = "https://api.minecraftservices.com/entitlements/license?requestId=" + m_entitlementsRequestId; @@ -24,22 +25,22 @@ void EntitlementsStep::perform() { request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &EntitlementsStep::onRequestDone); requestor->get(request); qDebug() << "Getting entitlements..."; } -void EntitlementsStep::rehydrate() { +void EntitlementsStep::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void EntitlementsStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers -) { - auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); +void EntitlementsStep::onRequestDone([[maybe_unused]] QNetworkReply::NetworkError error, + QByteArray data, + [[maybe_unused]] QList<QNetworkReply::RawHeaderPair> headers) +{ + auto requestor = qobject_cast<AuthRequest*>(QObject::sender()); requestor->deleteLater(); qCDebug(authCredentials()) << data; diff --git a/launcher/minecraft/auth/steps/EntitlementsStep.h b/launcher/minecraft/auth/steps/EntitlementsStep.h index 9412ae79..be16bda1 100644 --- a/launcher/minecraft/auth/steps/EntitlementsStep.h +++ b/launcher/minecraft/auth/steps/EntitlementsStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class EntitlementsStep : public AuthStep { Q_OBJECT -public: - explicit EntitlementsStep(AccountData *data); + public: + explicit EntitlementsStep(AccountData* data); virtual ~EntitlementsStep() noexcept; void perform() override; @@ -17,9 +16,9 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>); -private: + private: QString m_entitlementsRequestId; }; diff --git a/launcher/minecraft/auth/steps/GetSkinStep.cpp b/launcher/minecraft/auth/steps/GetSkinStep.cpp index 3521f8dc..52087702 100644 --- a/launcher/minecraft/auth/steps/GetSkinStep.cpp +++ b/launcher/minecraft/auth/steps/GetSkinStep.cpp @@ -6,34 +6,32 @@ #include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" -GetSkinStep::GetSkinStep(AccountData* data) : AuthStep(data) { - -} +GetSkinStep::GetSkinStep(AccountData* data) : AuthStep(data) {} GetSkinStep::~GetSkinStep() noexcept = default; -QString GetSkinStep::describe() { +QString GetSkinStep::describe() +{ return tr("Getting skin."); } -void GetSkinStep::perform() { +void GetSkinStep::perform() +{ auto url = QUrl(m_data->minecraftProfile.skin.url); QNetworkRequest request = QNetworkRequest(url); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &GetSkinStep::onRequestDone); requestor->get(request); } -void GetSkinStep::rehydrate() { +void GetSkinStep::rehydrate() +{ // NOOP, for now. } -void GetSkinStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers -) { - auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); +void GetSkinStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers) +{ + auto requestor = qobject_cast<AuthRequest*>(QObject::sender()); requestor->deleteLater(); if (error == QNetworkReply::NoError) { diff --git a/launcher/minecraft/auth/steps/GetSkinStep.h b/launcher/minecraft/auth/steps/GetSkinStep.h index 6b97371e..105e497d 100644 --- a/launcher/minecraft/auth/steps/GetSkinStep.h +++ b/launcher/minecraft/auth/steps/GetSkinStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class GetSkinStep : public AuthStep { Q_OBJECT -public: - explicit GetSkinStep(AccountData *data); + public: + explicit GetSkinStep(AccountData* data); virtual ~GetSkinStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>); }; diff --git a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp index 8a26cbe7..c57f5111 100644 --- a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp +++ b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp @@ -8,17 +8,17 @@ #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) { - -} +LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) {} LauncherLoginStep::~LauncherLoginStep() noexcept = default; -QString LauncherLoginStep::describe() { +QString LauncherLoginStep::describe() +{ return tr("Accessing Mojang services."); } -void LauncherLoginStep::perform() { +void LauncherLoginStep::perform() +{ auto requestURL = "https://api.minecraftservices.com/launcher/login"; auto uhs = m_data->mojangservicesToken.extra["uhs"].toString(); auto xToken = m_data->mojangservicesToken.token; @@ -34,22 +34,20 @@ void LauncherLoginStep::perform() { QNetworkRequest request = QNetworkRequest(QUrl(requestURL)); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &LauncherLoginStep::onRequestDone); requestor->post(request, requestBody.toUtf8()); qDebug() << "Getting Minecraft access token..."; } -void LauncherLoginStep::rehydrate() { +void LauncherLoginStep::rehydrate() +{ // TODO: check the token validity } -void LauncherLoginStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers -) { - auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); +void LauncherLoginStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers) +{ + auto requestor = qobject_cast<AuthRequest*>(QObject::sender()); requestor->deleteLater(); qCDebug(authCredentials()) << data; @@ -57,27 +55,17 @@ void LauncherLoginStep::onRequestDone( qWarning() << "Reply error:" << error; qCDebug(authCredentials()) << data; if (Net::isApplicationError(error)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_) - ); - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_)); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_)); } return; } - if(!Parsers::parseMojangResponse(data, m_data->yggdrasilToken)) { + if (!Parsers::parseMojangResponse(data, m_data->yggdrasilToken)) { qWarning() << "Could not parse login_with_xbox response..."; qCDebug(authCredentials()) << data; - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to parse the Minecraft access token response.") - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to parse the Minecraft access token response.")); return; } emit finished(AccountTaskState::STATE_WORKING, tr("")); diff --git a/launcher/minecraft/auth/steps/LauncherLoginStep.h b/launcher/minecraft/auth/steps/LauncherLoginStep.h index e06a306f..30c18e67 100644 --- a/launcher/minecraft/auth/steps/LauncherLoginStep.h +++ b/launcher/minecraft/auth/steps/LauncherLoginStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class LauncherLoginStep : public AuthStep { Q_OBJECT -public: - explicit LauncherLoginStep(AccountData *data); + public: + explicit LauncherLoginStep(AccountData* data); virtual ~LauncherLoginStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>); }; diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 6fc8d468..1aa22765 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -47,7 +47,8 @@ using OAuth2 = Katabasis::DeviceFlow; using Activity = Katabasis::Activity; -MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(action) { +MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(action) +{ m_clientId = APPLICATION->getMSAClientID(); OAuth2::Options opts; opts.scope = "XboxLive.signin offline_access"; @@ -64,13 +65,14 @@ MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(ac MSAStep::~MSAStep() noexcept = default; -QString MSAStep::describe() { +QString MSAStep::describe() +{ return tr("Logging in with Microsoft account."); } - -void MSAStep::rehydrate() { - switch(m_action) { +void MSAStep::rehydrate() +{ + switch (m_action) { case Refresh: { // TODO: check the tokens and see if they are old (older than a day) return; @@ -82,12 +84,14 @@ void MSAStep::rehydrate() { } } -void MSAStep::perform() { - switch(m_action) { +void MSAStep::perform() +{ + switch (m_action) { case Refresh: { if (m_data->msaClientID != m_clientId) { emit hideVerificationUriAndCode(); - emit finished(AccountTaskState::STATE_DISABLED, tr("Microsoft user authentication failed - client identification has changed.")); + emit finished(AccountTaskState::STATE_DISABLED, + tr("Microsoft user authentication failed - client identification has changed.")); } m_oauth2->refresh(); return; @@ -105,8 +109,9 @@ void MSAStep::perform() { } } -void MSAStep::onOAuthActivityChanged(Katabasis::Activity activity) { - switch(activity) { +void MSAStep::onOAuthActivityChanged(Katabasis::Activity activity) +{ + switch (activity) { case Katabasis::Activity::Idle: case Katabasis::Activity::LoggingIn: case Katabasis::Activity::Refreshing: diff --git a/launcher/minecraft/auth/steps/MSAStep.h b/launcher/minecraft/auth/steps/MSAStep.h index e9a1524e..b6635d4a 100644 --- a/launcher/minecraft/auth/steps/MSAStep.h +++ b/launcher/minecraft/auth/steps/MSAStep.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * This program is free software: you can redistribute it and/or modify @@ -43,13 +43,11 @@ class MSAStep : public AuthStep { Q_OBJECT -public: - enum Action { - Refresh, - Login - }; -public: - explicit MSAStep(AccountData *data, Action action); + public: + enum Action { Refresh, Login }; + + public: + explicit MSAStep(AccountData* data, Action action); virtual ~MSAStep() noexcept; void perform() override; @@ -57,11 +55,11 @@ public: QString describe() override; -private slots: + private slots: void onOAuthActivityChanged(Katabasis::Activity activity); -private: - Katabasis::DeviceFlow *m_oauth2 = nullptr; + private: + Katabasis::DeviceFlow* m_oauth2 = nullptr; Action m_action; QString m_clientId; }; diff --git a/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp b/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp index f5b5637a..5ce953df 100644 --- a/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp +++ b/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp @@ -5,37 +5,37 @@ #include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" -MigrationEligibilityStep::MigrationEligibilityStep(AccountData* data) : AuthStep(data) { - -} +MigrationEligibilityStep::MigrationEligibilityStep(AccountData* data) : AuthStep(data) {} MigrationEligibilityStep::~MigrationEligibilityStep() noexcept = default; -QString MigrationEligibilityStep::describe() { +QString MigrationEligibilityStep::describe() +{ return tr("Checking for migration eligibility."); } -void MigrationEligibilityStep::perform() { +void MigrationEligibilityStep::perform() +{ auto url = QUrl("https://api.minecraftservices.com/rollout/v1/msamigration"); QNetworkRequest request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &MigrationEligibilityStep::onRequestDone); requestor->get(request); } -void MigrationEligibilityStep::rehydrate() { +void MigrationEligibilityStep::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void MigrationEligibilityStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers -) { - auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); +void MigrationEligibilityStep::onRequestDone(QNetworkReply::NetworkError error, + QByteArray data, + QList<QNetworkReply::RawHeaderPair> headers) +{ + auto requestor = qobject_cast<AuthRequest*>(QObject::sender()); requestor->deleteLater(); if (error == QNetworkReply::NoError) { diff --git a/launcher/minecraft/auth/steps/MigrationEligibilityStep.h b/launcher/minecraft/auth/steps/MigrationEligibilityStep.h index b1bf9cbf..8638975d 100644 --- a/launcher/minecraft/auth/steps/MigrationEligibilityStep.h +++ b/launcher/minecraft/auth/steps/MigrationEligibilityStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class MigrationEligibilityStep : public AuthStep { Q_OBJECT -public: - explicit MigrationEligibilityStep(AccountData *data); + public: + explicit MigrationEligibilityStep(AccountData* data); virtual ~MigrationEligibilityStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>); }; diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp index 6cfa7c1c..7cdce23f 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp +++ b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp @@ -7,52 +7,46 @@ #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -MinecraftProfileStep::MinecraftProfileStep(AccountData* data) : AuthStep(data) { - -} +MinecraftProfileStep::MinecraftProfileStep(AccountData* data) : AuthStep(data) {} MinecraftProfileStep::~MinecraftProfileStep() noexcept = default; -QString MinecraftProfileStep::describe() { +QString MinecraftProfileStep::describe() +{ return tr("Fetching the Minecraft profile."); } - -void MinecraftProfileStep::perform() { +void MinecraftProfileStep::perform() +{ auto url = QUrl("https://api.minecraftservices.com/minecraft/profile"); QNetworkRequest request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &MinecraftProfileStep::onRequestDone); requestor->get(request); } -void MinecraftProfileStep::rehydrate() { +void MinecraftProfileStep::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void MinecraftProfileStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers -) { - auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); +void MinecraftProfileStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers) +{ + auto requestor = qobject_cast<AuthRequest*>(QObject::sender()); requestor->deleteLater(); qCDebug(authCredentials()) << data; if (error == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. - if(m_data->type == AccountType::Mojang) { + if (m_data->type == AccountType::Mojang) { m_data->minecraftEntitlement.canPlayMinecraft = false; m_data->minecraftEntitlement.ownsMinecraft = false; } m_data->minecraftProfile = MinecraftProfile(); - emit finished( - AccountTaskState::STATE_SUCCEEDED, - tr("Account has no Minecraft profile.") - ); + emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile.")); return; } if (error != QNetworkReply::NoError) { @@ -65,35 +59,24 @@ void MinecraftProfileStep::onRequestDone( qWarning() << QString::fromUtf8(data); if (Net::isApplicationError(error)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_) - ); - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, + tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); } return; } - if(!Parsers::parseMinecraftProfile(data, m_data->minecraftProfile)) { + if (!Parsers::parseMinecraftProfile(data, m_data->minecraftProfile)) { m_data->minecraftProfile = MinecraftProfile(); - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Minecraft Java profile response could not be parsed") - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Minecraft Java profile response could not be parsed")); return; } - if(m_data->type == AccountType::Mojang) { + if (m_data->type == AccountType::Mojang) { auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain; m_data->minecraftEntitlement.canPlayMinecraft = validProfile; m_data->minecraftEntitlement.ownsMinecraft = validProfile; } - emit finished( - AccountTaskState::STATE_WORKING, - tr("Minecraft Java profile acquisition succeeded.") - ); + emit finished(AccountTaskState::STATE_WORKING, tr("Minecraft Java profile acquisition succeeded.")); } diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStep.h b/launcher/minecraft/auth/steps/MinecraftProfileStep.h index 8ef3395c..cb30dab2 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStep.h +++ b/launcher/minecraft/auth/steps/MinecraftProfileStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class MinecraftProfileStep : public AuthStep { Q_OBJECT -public: - explicit MinecraftProfileStep(AccountData *data); + public: + explicit MinecraftProfileStep(AccountData* data); virtual ~MinecraftProfileStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>); }; diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp index 8c378588..d035e39a 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp +++ b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp @@ -7,18 +7,17 @@ #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) { - -} +MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) {} MinecraftProfileStepMojang::~MinecraftProfileStepMojang() noexcept = default; -QString MinecraftProfileStepMojang::describe() { +QString MinecraftProfileStepMojang::describe() +{ return tr("Fetching the Minecraft profile."); } - -void MinecraftProfileStepMojang::perform() { +void MinecraftProfileStepMojang::perform() +{ if (m_data->minecraftProfile.id.isEmpty()) { emit finished(AccountTaskState::STATE_FAILED_HARD, tr("A UUID is required to get the profile.")); return; @@ -27,35 +26,32 @@ void MinecraftProfileStepMojang::perform() { // use session server instead of profile due to profile endpoint being locked for locked Mojang accounts QUrl url = QUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + m_data->minecraftProfile.id); QNetworkRequest req = QNetworkRequest(url); - AuthRequest *request = new AuthRequest(this); + AuthRequest* request = new AuthRequest(this); connect(request, &AuthRequest::finished, this, &MinecraftProfileStepMojang::onRequestDone); request->get(req); } -void MinecraftProfileStepMojang::rehydrate() { +void MinecraftProfileStepMojang::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void MinecraftProfileStepMojang::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers -) { - auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); +void MinecraftProfileStepMojang::onRequestDone(QNetworkReply::NetworkError error, + QByteArray data, + QList<QNetworkReply::RawHeaderPair> headers) +{ + auto requestor = qobject_cast<AuthRequest*>(QObject::sender()); requestor->deleteLater(); qCDebug(authCredentials()) << data; if (error == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. - if(m_data->type == AccountType::Mojang) { + if (m_data->type == AccountType::Mojang) { m_data->minecraftEntitlement.canPlayMinecraft = false; m_data->minecraftEntitlement.ownsMinecraft = false; } m_data->minecraftProfile = MinecraftProfile(); - emit finished( - AccountTaskState::STATE_SUCCEEDED, - tr("Account has no Minecraft profile.") - ); + emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile.")); return; } if (error != QNetworkReply::NoError) { @@ -68,35 +64,24 @@ void MinecraftProfileStepMojang::onRequestDone( qWarning() << QString::fromUtf8(data); if (Net::isApplicationError(error)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_) - ); - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, + tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); } return; } - if(!Parsers::parseMinecraftProfileMojang(data, m_data->minecraftProfile)) { + if (!Parsers::parseMinecraftProfileMojang(data, m_data->minecraftProfile)) { m_data->minecraftProfile = MinecraftProfile(); - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Minecraft Java profile response could not be parsed") - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Minecraft Java profile response could not be parsed")); return; } - if(m_data->type == AccountType::Mojang) { + if (m_data->type == AccountType::Mojang) { auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain; m_data->minecraftEntitlement.canPlayMinecraft = validProfile; m_data->minecraftEntitlement.ownsMinecraft = validProfile; } - emit finished( - AccountTaskState::STATE_WORKING, - tr("Minecraft Java profile acquisition succeeded.") - ); + emit finished(AccountTaskState::STATE_WORKING, tr("Minecraft Java profile acquisition succeeded.")); } diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h index e06b30ab..730ec3f6 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h +++ b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class MinecraftProfileStepMojang : public AuthStep { Q_OBJECT -public: - explicit MinecraftProfileStepMojang(AccountData *data); + public: + explicit MinecraftProfileStepMojang(AccountData* data); virtual ~MinecraftProfileStepMojang() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>); }; diff --git a/launcher/minecraft/auth/steps/OfflineStep.cpp b/launcher/minecraft/auth/steps/OfflineStep.cpp index dc092bfd..bf111abe 100644 --- a/launcher/minecraft/auth/steps/OfflineStep.cpp +++ b/launcher/minecraft/auth/steps/OfflineStep.cpp @@ -5,14 +5,17 @@ OfflineStep::OfflineStep(AccountData* data) : AuthStep(data) {} OfflineStep::~OfflineStep() noexcept = default; -QString OfflineStep::describe() { +QString OfflineStep::describe() +{ return tr("Creating offline account."); } -void OfflineStep::rehydrate() { +void OfflineStep::rehydrate() +{ // NOOP } -void OfflineStep::perform() { +void OfflineStep::perform() +{ emit finished(AccountTaskState::STATE_WORKING, tr("Created offline account.")); } diff --git a/launcher/minecraft/auth/steps/OfflineStep.h b/launcher/minecraft/auth/steps/OfflineStep.h index 436597cd..3bf123d6 100644 --- a/launcher/minecraft/auth/steps/OfflineStep.h +++ b/launcher/minecraft/auth/steps/OfflineStep.h @@ -8,8 +8,8 @@ class OfflineStep : public AuthStep { Q_OBJECT -public: - explicit OfflineStep(AccountData *data); + public: + explicit OfflineStep(AccountData* data); virtual ~OfflineStep() noexcept; void perform() override; diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp index b397b734..c33d7e62 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp @@ -1,33 +1,32 @@ #include "XboxAuthorizationStep.h" -#include <QNetworkRequest> -#include <QJsonParseError> #include <QJsonDocument> +#include <QJsonParseError> +#include <QNetworkRequest> #include "Logging.h" #include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -XboxAuthorizationStep::XboxAuthorizationStep(AccountData* data, Katabasis::Token *token, QString relyingParty, QString authorizationKind): - AuthStep(data), - m_token(token), - m_relyingParty(relyingParty), - m_authorizationKind(authorizationKind) -{ -} +XboxAuthorizationStep::XboxAuthorizationStep(AccountData* data, Katabasis::Token* token, QString relyingParty, QString authorizationKind) + : AuthStep(data), m_token(token), m_relyingParty(relyingParty), m_authorizationKind(authorizationKind) +{} XboxAuthorizationStep::~XboxAuthorizationStep() noexcept = default; -QString XboxAuthorizationStep::describe() { +QString XboxAuthorizationStep::describe() +{ return tr("Getting authorization to access %1 services.").arg(m_authorizationKind); } -void XboxAuthorizationStep::rehydrate() { +void XboxAuthorizationStep::rehydrate() +{ // FIXME: check if the tokens are good? } -void XboxAuthorizationStep::perform() { +void XboxAuthorizationStep::perform() +{ QString xbox_auth_template = R"XXX( { "Properties": { @@ -41,129 +40,98 @@ void XboxAuthorizationStep::perform() { } )XXX"; auto xbox_auth_data = xbox_auth_template.arg(m_data->userToken.token, m_relyingParty); -// http://xboxlive.com + // http://xboxlive.com QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &XboxAuthorizationStep::onRequestDone); requestor->post(request, xbox_auth_data.toUtf8()); qDebug() << "Getting authorization token for " << m_relyingParty; } -void XboxAuthorizationStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers -) { - auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); +void XboxAuthorizationStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers) +{ + auto requestor = qobject_cast<AuthRequest*>(QObject::sender()); requestor->deleteLater(); qCDebug(authCredentials()) << data; if (error != QNetworkReply::NoError) { qWarning() << "Reply error:" << error; if (Net::isApplicationError(error)) { - if(!processSTSError(error, data, headers)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, error) - ); - } - else { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Unknown STS error for %1 services: %2").arg(m_authorizationKind, requestor->errorString_) - ); + if (!processSTSError(error, data, headers)) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, error)); + } else { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Unknown STS error for %1 services: %2").arg(m_authorizationKind, requestor->errorString_)); } - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("Failed to get authorization for %1 services: %2").arg(m_authorizationKind, requestor->errorString_) - ); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, + tr("Failed to get authorization for %1 services: %2").arg(m_authorizationKind, requestor->errorString_)); } return; } Katabasis::Token temp; - if(!Parsers::parseXTokenResponse(data, temp, m_authorizationKind)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Could not parse authorization response for access to %1 services.").arg(m_authorizationKind) - ); + if (!Parsers::parseXTokenResponse(data, temp, m_authorizationKind)) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Could not parse authorization response for access to %1 services.").arg(m_authorizationKind)); return; } - if(temp.extra["uhs"] != m_data->userToken.extra["uhs"]) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Server has changed %1 authorization user hash in the reply. Something is wrong.").arg(m_authorizationKind) - ); + if (temp.extra["uhs"] != m_data->userToken.extra["uhs"]) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Server has changed %1 authorization user hash in the reply. Something is wrong.").arg(m_authorizationKind)); return; } - auto & token = *m_token; + auto& token = *m_token; token = temp; emit finished(AccountTaskState::STATE_WORKING, tr("Got authorization to access %1").arg(m_relyingParty)); } - -bool XboxAuthorizationStep::processSTSError( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers -) { - if(error == QNetworkReply::AuthenticationRequiredError) { +bool XboxAuthorizationStep::processSTSError(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers) +{ + if (error == QNetworkReply::AuthenticationRequiredError) { QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Cannot parse error XSTS response as JSON: " << jsonError.errorString(); - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Cannot parse %1 authorization error response as JSON: %2").arg(m_authorizationKind, jsonError.errorString()) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Cannot parse %1 authorization error response as JSON: %2").arg(m_authorizationKind, jsonError.errorString())); return true; } int64_t errorCode = -1; auto obj = doc.object(); - if(!Parsers::getNumber(obj.value("XErr"), errorCode)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("XErr element is missing from %1 authorization error response.").arg(m_authorizationKind) - ); + if (!Parsers::getNumber(obj.value("XErr"), errorCode)) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("XErr element is missing from %1 authorization error response.").arg(m_authorizationKind)); return true; } - switch(errorCode) { - case 2148916233:{ - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("This Microsoft account does not have an XBox Live profile. Buy the game on %1 first.") - .arg("<a href=\"https://www.minecraft.net/en-us/store/minecraft-java-edition\">minecraft.net</a>") - ); + switch (errorCode) { + case 2148916233: { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("This Microsoft account does not have an XBox Live profile. Buy the game on %1 first.") + .arg("<a href=\"https://www.minecraft.net/en-us/store/minecraft-java-edition\">minecraft.net</a>")); return true; } case 2148916235: { // NOTE: this is the Grulovia error - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("XBox Live is not available in your country. You've been blocked.") - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox Live is not available in your country. You've been blocked.")); return true; } case 2148916238: { emit finished( AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account is underaged and is not linked to a family.\n\nPlease set up your account according to %1.") - .arg("<a href=\"https://help.minecraft.net/hc/en-us/articles/4403181904525\">help.minecraft.net</a>") - ); + .arg("<a href=\"https://help.minecraft.net/hc/en-us/articles/4403181904525\">help.minecraft.net</a>")); return true; } default: { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("XSTS authentication ended with unrecognized error(s):\n\n%1").arg(errorCode) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("XSTS authentication ended with unrecognized error(s):\n\n%1").arg(errorCode)); return true; } } diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.h b/launcher/minecraft/auth/steps/XboxAuthorizationStep.h index 31e43bf0..dee24c95 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.h +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class XboxAuthorizationStep : public AuthStep { Q_OBJECT -public: - explicit XboxAuthorizationStep(AccountData *data, Katabasis::Token *token, QString relyingParty, QString authorizationKind); + public: + explicit XboxAuthorizationStep(AccountData* data, Katabasis::Token* token, QString relyingParty, QString authorizationKind); virtual ~XboxAuthorizationStep() noexcept; void perform() override; @@ -17,18 +16,14 @@ public: QString describe() override; -private: - bool processSTSError( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers - ); + private: + bool processSTSError(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers); -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>); -private: - Katabasis::Token *m_token; + private: + Katabasis::Token* m_token; QString m_relyingParty; QString m_authorizationKind; }; diff --git a/launcher/minecraft/auth/steps/XboxProfileStep.cpp b/launcher/minecraft/auth/steps/XboxProfileStep.cpp index 644c419b..fd2b32cc 100644 --- a/launcher/minecraft/auth/steps/XboxProfileStep.cpp +++ b/launcher/minecraft/auth/steps/XboxProfileStep.cpp @@ -8,66 +8,56 @@ #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -XboxProfileStep::XboxProfileStep(AccountData* data) : AuthStep(data) { - -} +XboxProfileStep::XboxProfileStep(AccountData* data) : AuthStep(data) {} XboxProfileStep::~XboxProfileStep() noexcept = default; -QString XboxProfileStep::describe() { +QString XboxProfileStep::describe() +{ return tr("Fetching Xbox profile."); } -void XboxProfileStep::rehydrate() { +void XboxProfileStep::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void XboxProfileStep::perform() { +void XboxProfileStep::perform() +{ auto url = QUrl("https://profile.xboxlive.com/users/me/profile/settings"); QUrlQuery q; - q.addQueryItem( - "settings", - "GameDisplayName,AppDisplayName,AppDisplayPicRaw,GameDisplayPicRaw," - "PublicGamerpic,ShowUserAsAvatar,Gamerscore,Gamertag,ModernGamertag,ModernGamertagSuffix," - "UniqueModernGamertag,AccountTier,TenureLevel,XboxOneRep," - "PreferredColor,Location,Bio,Watermarks," - "RealName,RealNameOverride,IsQuarantined" - ); + q.addQueryItem("settings", + "GameDisplayName,AppDisplayName,AppDisplayPicRaw,GameDisplayPicRaw," + "PublicGamerpic,ShowUserAsAvatar,Gamerscore,Gamertag,ModernGamertag,ModernGamertagSuffix," + "UniqueModernGamertag,AccountTier,TenureLevel,XboxOneRep," + "PreferredColor,Location,Bio,Watermarks," + "RealName,RealNameOverride,IsQuarantined"); url.setQuery(q); QNetworkRequest request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); request.setRawHeader("x-xbl-contract-version", "3"); - request.setRawHeader("Authorization", QString("XBL3.0 x=%1;%2").arg(m_data->userToken.extra["uhs"].toString(), m_data->xboxApiToken.token).toUtf8()); - AuthRequest *requestor = new AuthRequest(this); + request.setRawHeader("Authorization", + QString("XBL3.0 x=%1;%2").arg(m_data->userToken.extra["uhs"].toString(), m_data->xboxApiToken.token).toUtf8()); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &XboxProfileStep::onRequestDone); requestor->get(request); qDebug() << "Getting Xbox profile..."; } -void XboxProfileStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers -) { - auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); +void XboxProfileStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers) +{ + auto requestor = qobject_cast<AuthRequest*>(QObject::sender()); requestor->deleteLater(); if (error != QNetworkReply::NoError) { qWarning() << "Reply error:" << error; qCDebug(authCredentials()) << data; if (Net::isApplicationError(error)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_) - ); - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_)); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_)); } return; } diff --git a/launcher/minecraft/auth/steps/XboxProfileStep.h b/launcher/minecraft/auth/steps/XboxProfileStep.h index 7a0c5873..b8494b6e 100644 --- a/launcher/minecraft/auth/steps/XboxProfileStep.h +++ b/launcher/minecraft/auth/steps/XboxProfileStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class XboxProfileStep : public AuthStep { Q_OBJECT -public: - explicit XboxProfileStep(AccountData *data); + public: + explicit XboxProfileStep(AccountData* data); virtual ~XboxProfileStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>); }; diff --git a/launcher/minecraft/auth/steps/XboxUserStep.cpp b/launcher/minecraft/auth/steps/XboxUserStep.cpp index 842eb60f..61c33a18 100644 --- a/launcher/minecraft/auth/steps/XboxUserStep.cpp +++ b/launcher/minecraft/auth/steps/XboxUserStep.cpp @@ -6,22 +6,22 @@ #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -XboxUserStep::XboxUserStep(AccountData* data) : AuthStep(data) { - -} +XboxUserStep::XboxUserStep(AccountData* data) : AuthStep(data) {} XboxUserStep::~XboxUserStep() noexcept = default; -QString XboxUserStep::describe() { +QString XboxUserStep::describe() +{ return tr("Logging in as an Xbox user."); } - -void XboxUserStep::rehydrate() { +void XboxUserStep::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void XboxUserStep::perform() { +void XboxUserStep::perform() +{ QString xbox_auth_template = R"XXX( { "Properties": { @@ -40,40 +40,31 @@ void XboxUserStep::perform() { request.setRawHeader("Accept", "application/json"); // set contract-verison header (prevent err 400 bad-request?) // https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/live/rest/additional/httpstandardheaders - request.setRawHeader("x-xbl-contract-version", "1"); + request.setRawHeader("x-xbl-contract-version", "1"); - auto *requestor = new AuthRequest(this); + auto* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &XboxUserStep::onRequestDone); requestor->post(request, xbox_auth_data.toUtf8()); qDebug() << "First layer of XBox auth ... commencing."; } -void XboxUserStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers -) { - auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); +void XboxUserStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers) +{ + auto requestor = qobject_cast<AuthRequest*>(QObject::sender()); requestor->deleteLater(); if (error != QNetworkReply::NoError) { qWarning() << "Reply error:" << error; if (Net::isApplicationError(error)) { - emit finished(AccountTaskState::STATE_FAILED_SOFT, - tr("XBox user authentication failed: %1").arg(requestor->errorString_) - ); - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("XBox user authentication failed: %1").arg(requestor->errorString_) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication failed: %1").arg(requestor->errorString_)); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, tr("XBox user authentication failed: %1").arg(requestor->errorString_)); } return; } Katabasis::Token temp; - if(!Parsers::parseXTokenResponse(data, temp, "UToken")) { + if (!Parsers::parseXTokenResponse(data, temp, "UToken")) { qWarning() << "Could not parse user authentication response..."; emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication response could not be understood.")); return; diff --git a/launcher/minecraft/auth/steps/XboxUserStep.h b/launcher/minecraft/auth/steps/XboxUserStep.h index 83e9405f..e92727a4 100644 --- a/launcher/minecraft/auth/steps/XboxUserStep.h +++ b/launcher/minecraft/auth/steps/XboxUserStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class XboxUserStep : public AuthStep { Q_OBJECT -public: - explicit XboxUserStep(AccountData *data); + public: + explicit XboxUserStep(AccountData* data); virtual ~XboxUserStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>); }; diff --git a/launcher/minecraft/auth/steps/YggdrasilStep.cpp b/launcher/minecraft/auth/steps/YggdrasilStep.cpp index e1d33172..fdcaa0d6 100644 --- a/launcher/minecraft/auth/steps/YggdrasilStep.cpp +++ b/launcher/minecraft/auth/steps/YggdrasilStep.cpp @@ -4,7 +4,8 @@ #include "minecraft/auth/Parsers.h" #include "minecraft/auth/Yggdrasil.h" -YggdrasilStep::YggdrasilStep(AccountData* data, QString password) : AuthStep(data), m_password(password) { +YggdrasilStep::YggdrasilStep(AccountData* data, QString password) : AuthStep(data), m_password(password) +{ m_yggdrasil = new Yggdrasil(m_data, this); connect(m_yggdrasil, &Task::failed, this, &YggdrasilStep::onAuthFailed); @@ -14,28 +15,32 @@ YggdrasilStep::YggdrasilStep(AccountData* data, QString password) : AuthStep(dat YggdrasilStep::~YggdrasilStep() noexcept = default; -QString YggdrasilStep::describe() { +QString YggdrasilStep::describe() +{ return tr("Logging in with Mojang account."); } -void YggdrasilStep::rehydrate() { +void YggdrasilStep::rehydrate() +{ // NOOP, for now. } -void YggdrasilStep::perform() { - if(m_password.size()) { +void YggdrasilStep::perform() +{ + if (m_password.size()) { m_yggdrasil->login(m_password); - } - else { + } else { m_yggdrasil->refresh(); } } -void YggdrasilStep::onAuthSucceeded() { +void YggdrasilStep::onAuthSucceeded() +{ emit finished(AccountTaskState::STATE_WORKING, tr("Logged in with Mojang")); } -void YggdrasilStep::onAuthFailed() { +void YggdrasilStep::onAuthFailed() +{ // TODO: hook these in again, expand to MSA // m_error = m_yggdrasil->m_error; // m_aborted = m_yggdrasil->m_aborted; @@ -44,7 +49,7 @@ void YggdrasilStep::onAuthFailed() { QString errorMessage = tr("Mojang user authentication failed."); // NOTE: soft error in the first step means 'offline' - if(state == AccountTaskState::STATE_FAILED_SOFT) { + if (state == AccountTaskState::STATE_FAILED_SOFT) { state = AccountTaskState::STATE_OFFLINE; errorMessage = tr("Mojang user authentication ended with a network error."); } diff --git a/launcher/minecraft/auth/steps/YggdrasilStep.h b/launcher/minecraft/auth/steps/YggdrasilStep.h index ebafb8e5..ef31f34d 100644 --- a/launcher/minecraft/auth/steps/YggdrasilStep.h +++ b/launcher/minecraft/auth/steps/YggdrasilStep.h @@ -9,8 +9,8 @@ class Yggdrasil; class YggdrasilStep : public AuthStep { Q_OBJECT -public: - explicit YggdrasilStep(AccountData *data, QString password); + public: + explicit YggdrasilStep(AccountData* data, QString password); virtual ~YggdrasilStep() noexcept; void perform() override; @@ -18,11 +18,11 @@ public: QString describe() override; -private slots: + private slots: void onAuthSucceeded(); void onAuthFailed(); -private: - Yggdrasil *m_yggdrasil = nullptr; + private: + Yggdrasil* m_yggdrasil = nullptr; QString m_password; }; |