summaryrefslogtreecommitdiffstats
path: root/logic/auth
diff options
context:
space:
mode:
Diffstat (limited to 'logic/auth')
-rw-r--r--logic/auth/MojangAccount.cpp236
-rw-r--r--logic/auth/MojangAccount.h210
-rw-r--r--logic/auth/MojangAccountList.cpp413
-rw-r--r--logic/auth/MojangAccountList.h194
-rw-r--r--logic/auth/YggdrasilTask.cpp171
-rw-r--r--logic/auth/YggdrasilTask.h62
-rw-r--r--logic/auth/flows/AuthenticateTask.cpp37
-rw-r--r--logic/auth/flows/AuthenticateTask.h2
-rw-r--r--logic/auth/flows/InvalidateTask.cpp0
-rw-r--r--logic/auth/flows/InvalidateTask.h0
-rw-r--r--logic/auth/flows/RefreshTask.cpp32
-rw-r--r--logic/auth/flows/RefreshTask.h2
-rw-r--r--logic/auth/flows/ValidateTask.cpp4
-rw-r--r--logic/auth/flows/ValidateTask.h6
14 files changed, 987 insertions, 382 deletions
diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp
index 4a61cf19..bc6af98f 100644
--- a/logic/auth/MojangAccount.cpp
+++ b/logic/auth/MojangAccount.cpp
@@ -16,113 +16,17 @@
*/
#include "MojangAccount.h"
+#include "flows/RefreshTask.h"
+#include "flows/AuthenticateTask.h"
#include <QUuid>
#include <QJsonObject>
#include <QJsonArray>
+#include <QRegExp>
+#include <QStringList>
#include <logger/QsLog.h>
-MojangAccount::MojangAccount(const QString &username, QObject *parent) : QObject(parent)
-{
- // Generate a client token.
- m_clientToken = QUuid::createUuid().toString();
-
- m_username = username;
-
- m_currentProfile = -1;
-}
-
-MojangAccount::MojangAccount(const QString &username, const QString &clientToken,
- const QString &accessToken, QObject *parent)
- : QObject(parent)
-{
- m_username = username;
- m_clientToken = clientToken;
- m_accessToken = accessToken;
-
- m_currentProfile = -1;
-}
-
-MojangAccount::MojangAccount(const MojangAccount &other, QObject *parent)
-{
- m_username = other.username();
- m_clientToken = other.clientToken();
- m_accessToken = other.accessToken();
-
- m_profiles = other.m_profiles;
- m_currentProfile = other.m_currentProfile;
-}
-
-QString MojangAccount::username() const
-{
- return m_username;
-}
-
-QString MojangAccount::clientToken() const
-{
- return m_clientToken;
-}
-
-void MojangAccount::setClientToken(const QString &clientToken)
-{
- m_clientToken = clientToken;
-}
-
-QString MojangAccount::accessToken() const
-{
- return m_accessToken;
-}
-
-void MojangAccount::setAccessToken(const QString &accessToken)
-{
- m_accessToken = accessToken;
-}
-
-QString MojangAccount::sessionId() const
-{
- return "token:" + m_accessToken + ":" + currentProfile()->id();
-}
-
-const QList<AccountProfile> MojangAccount::profiles() const
-{
- return m_profiles;
-}
-
-const AccountProfile *MojangAccount::currentProfile() const
-{
- if (m_currentProfile < 0)
- {
- if (m_profiles.length() > 0)
- return &m_profiles.at(0);
- else
- return nullptr;
- }
- else
- return &m_profiles.at(m_currentProfile);
-}
-
-bool MojangAccount::setProfile(const QString &profileId)
-{
- const QList<AccountProfile> &profiles = this->profiles();
- for (int i = 0; i < profiles.length(); i++)
- {
- if (profiles.at(i).id() == profileId)
- {
- m_currentProfile = i;
- return true;
- }
- }
- return false;
-}
-
-void MojangAccount::loadProfiles(const ProfileList &profiles)
-{
- m_profiles.clear();
- for (auto profile : profiles)
- m_profiles.append(profile);
-}
-
MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
{
// The JSON object must at least have a username for it to be valid.
@@ -143,78 +47,160 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
return nullptr;
}
- ProfileList profiles;
+ QList<AccountProfile> profiles;
for (QJsonValue profileVal : profileArray)
{
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())
{
QLOG_WARN() << "Unable to load a profile because it was missing an ID or a name.";
continue;
}
- profiles.append(AccountProfile(id, name));
+ profiles.append({id, name, legacy});
}
- MojangAccountPtr account(new MojangAccount(username, clientToken, accessToken));
- account->loadProfiles(profiles);
+ MojangAccountPtr account(new MojangAccount());
+ if(object.value("user").isObject())
+ {
+ User u;
+ QJsonObject userStructure = object.value("user").toObject();
+ u.id = userStructure.value("id").toString();
+ /*
+ QJsonObject propMap = userStructure.value("properties").toObject();
+ for(auto key: propMap.keys())
+ {
+ auto values = propMap.operator[](key).toArray();
+ for(auto value: values)
+ u.properties.insert(key, value.toString());
+ }
+ */
+ account->m_user = u;
+ }
+ account->m_username = username;
+ account->m_clientToken = clientToken;
+ account->m_accessToken = accessToken;
+ account->m_profiles = profiles;
// Get the currently selected profile.
QString currentProfile = object.value("activeProfile").toString("");
if (!currentProfile.isEmpty())
- account->setProfile(currentProfile);
+ account->setCurrentProfile(currentProfile);
+
+ return account;
+}
+MojangAccountPtr MojangAccount::createFromUsername(const QString& username)
+{
+ MojangAccountPtr account(new MojangAccount());
+ account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ account->m_username = username;
return account;
}
-QJsonObject MojangAccount::saveToJson()
+QJsonObject MojangAccount::saveToJson() const
{
QJsonObject json;
- json.insert("username", username());
- json.insert("clientToken", clientToken());
- json.insert("accessToken", accessToken());
+ json.insert("username", m_username);
+ json.insert("clientToken", m_clientToken);
+ json.insert("accessToken", m_accessToken);
QJsonArray profileArray;
for (AccountProfile profile : m_profiles)
{
QJsonObject profileObj;
- profileObj.insert("id", profile.id());
- profileObj.insert("name", profile.name());
+ profileObj.insert("id", profile.id);
+ profileObj.insert("name", profile.name);
+ profileObj.insert("legacy", profile.legacy);
profileArray.append(profileObj);
}
json.insert("profiles", profileArray);
- if (currentProfile() != nullptr)
- json.insert("activeProfile", currentProfile()->id());
+ QJsonObject userStructure;
+ {
+ userStructure.insert("id", m_user.id);
+ /*
+ QJsonObject userAttrs;
+ for(auto key: m_user.properties.keys())
+ {
+ auto array = QJsonArray::fromStringList(m_user.properties.values(key));
+ userAttrs.insert(key, array);
+ }
+ userStructure.insert("properties", userAttrs);
+ */
+ }
+ json.insert("user", userStructure);
+
+ if (m_currentProfile != -1)
+ json.insert("activeProfile", currentProfile()->id);
return json;
}
-
-AccountProfile::AccountProfile(const QString& id, const QString& name)
+bool MojangAccount::setCurrentProfile(const QString &profileId)
{
- m_id = id;
- m_name = name;
+ for (int i = 0; i < m_profiles.length(); i++)
+ {
+ if (m_profiles[i].id == profileId)
+ {
+ m_currentProfile = i;
+ return true;
+ }
+ }
+ return false;
}
-AccountProfile::AccountProfile(const AccountProfile &other)
+const AccountProfile* MojangAccount::currentProfile() const
{
- m_id = other.m_id;
- m_name = other.m_name;
+ if(m_currentProfile == -1)
+ return nullptr;
+ return &m_profiles[m_currentProfile];
}
-QString AccountProfile::id() const
+AccountStatus MojangAccount::accountStatus() const
{
- return m_id;
+ if(m_accessToken.isEmpty())
+ return NotVerified;
+ if(!m_online)
+ return Verified;
+ return Online;
}
-QString AccountProfile::name() const
+std::shared_ptr<Task> MojangAccount::login(QString password)
{
- return m_name;
+ if(m_currentTask)
+ return m_currentTask;
+ if(password.isEmpty())
+ {
+ m_currentTask.reset(new RefreshTask(this));
+ }
+ else
+ {
+ m_currentTask.reset(new AuthenticateTask(this, password));
+ }
+ connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
+ connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
+ return m_currentTask;
}
-void MojangAccount::propagateChange()
+void MojangAccount::authSucceeded()
{
+ m_online = true;
+ m_currentTask.reset();
emit changed();
}
+
+void MojangAccount::authFailed(QString reason)
+{
+ // This is emitted when the yggdrasil tasks time out or are cancelled.
+ // -> we treat the error as no-op
+ if(reason != "Yggdrasil task cancelled.")
+ {
+ m_online = false;
+ m_accessToken = QString();
+ emit changed();
+ }
+ m_currentTask.reset();
+}
diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h
index 25a85790..325aa826 100644
--- a/logic/auth/MojangAccount.h
+++ b/logic/auth/MojangAccount.h
@@ -20,43 +20,42 @@
#include <QList>
#include <QJsonObject>
#include <QPair>
+#include <QMap>
#include <memory>
+class Task;
+class YggdrasilTask;
class MojangAccount;
typedef std::shared_ptr<MojangAccount> MojangAccountPtr;
Q_DECLARE_METATYPE(MojangAccountPtr)
/**
- * Class that represents a profile within someone's Mojang account.
+ * A profile within someone's Mojang account.
*
* Currently, the profile system has not been implemented by Mojang yet,
* but we might as well add some things for it in MultiMC right now so
* we don't have to rip the code to pieces to add it later.
*/
-class AccountProfile
+struct AccountProfile
{
-public:
- AccountProfile(const QString &id, const QString &name);
- AccountProfile(const AccountProfile &other);
-
- QString id() const;
- QString name() const;
-
-protected:
- QString m_id;
- QString m_name;
+ QString id;
+ QString name;
+ bool legacy;
};
-typedef QList<AccountProfile> ProfileList;
-
struct User
{
QString id;
- // pair of key:value
- // we don't know if the keys:value mapping is 1:1, so a list is used.
- QList<QPair<QString, QString>> properties;
+ QMultiMap<QString,QString> properties;
+};
+
+enum AccountStatus
+{
+ NotVerified,
+ Verified,
+ Online
};
/**
@@ -68,106 +67,121 @@ struct User
class MojangAccount : public QObject
{
Q_OBJECT
-public:
- /**
- * Constructs a new MojangAccount with the given username.
- * The client token will be generated automatically and the access token will be blank.
- */
- explicit MojangAccount(const QString &username, QObject *parent = 0);
+public: /* construction */
+ //! Do not copy accounts. ever.
+ explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete;
- /**
- * Constructs a new MojangAccount with the given username, client token, and access token.
- */
- explicit MojangAccount(const QString &username, const QString &clientToken,
- const QString &accessToken, QObject *parent = 0);
+ //! Default constructor
+ explicit MojangAccount(QObject *parent = 0) : QObject(parent) {};
- /**
- * Constructs a new MojangAccount matching the given account.
- */
- MojangAccount(const MojangAccount &other, QObject *parent);
+ //! Creates an empty account for the specified user name.
+ static MojangAccountPtr createFromUsername(const QString &username);
- /**
- * Loads a MojangAccount from the given JSON object.
- */
+ //! Loads a MojangAccount from the given JSON object.
static MojangAccountPtr loadFromJson(const QJsonObject &json);
- /**
- * Saves a MojangAccount to a JSON object and returns it.
- */
- QJsonObject saveToJson();
+ //! Saves a MojangAccount to a JSON object and returns it.
+ QJsonObject saveToJson() const;
+public: /* manipulation */
/**
- * Update the account on disk and lists (it changed, for whatever reason)
- * This is called by various Yggdrasil tasks.
- */
- void propagateChange();
+ * Sets the currently selected profile to the profile with the given ID string.
+ * If profileId is not in the list of available profiles, the function will simply return
+ * false.
+ */
+ bool setCurrentProfile(const QString &profileId);
+
+ /**
+ * Attempt to login. Empty password means we use the token.
+ * If the attempt fails because we already are performing some task, it returns false.
+ */
+ std::shared_ptr<Task> login(QString password = QString());
+
+ void downgrade()
+ {
+ m_online = false;
+ }
+public: /* queries */
+ const QString &username() const
+ {
+ return m_username;
+ }
+
+ const QString &clientToken() const
+ {
+ return m_clientToken;
+ }
+
+ const QString &accessToken() const
+ {
+ return m_accessToken;
+ }
+
+ const QList<AccountProfile> &profiles() const
+ {
+ return m_profiles;
+ }
+
+ const User & user()
+ {
+ return m_user;
+ }
+
+ //! Get the session ID required for legacy Minecraft versions
+ QString sessionId() const
+ {
+ if (m_currentProfile != -1 && !m_accessToken.isEmpty())
+ return "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id;
+ return "-";
+ }
+
+ //! Returns the currently selected profile (if none, returns nullptr)
+ const AccountProfile *currentProfile() const;
- /**
- * This MojangAccount's username. May be an email address if the account is migrated.
- */
- QString username() const;
+ //! Returns whether the account is NotVerified, Verified or Online
+ AccountStatus accountStatus() const;
+signals:
/**
- * This MojangAccount's client token. This is a UUID used by Mojang's auth servers to identify this client.
- * This is unique for each MojangAccount.
+ * This signal is emitted when the account changes
*/
- QString clientToken() const;
+ void changed();
- /**
- * Sets the MojangAccount's client token to the given value.
- */
- void setClientToken(const QString &token);
+ // TODO: better signalling for the various possible state changes - especially errors
- /**
- * This MojangAccount's access token.
- * If the user has not chosen to stay logged in, this will be an empty string.
- */
- QString accessToken() const;
+protected: /* variables */
+ QString m_username;
- /**
- * Changes this MojangAccount's access token to the given value.
- */
- void setAccessToken(const QString &token);
+ // Used to identify the client - the user can have multiple clients for the same account
+ // Think: different launchers, all connecting to the same account/profile
+ QString m_clientToken;
- /**
- * Get full session ID
- */
- QString sessionId() const;
+ // Blank if not logged in.
+ QString m_accessToken;
- /**
- * Returns a list of the available account profiles.
- */
- const ProfileList profiles() const;
+ // Index of the selected profile within the list of available
+ // profiles. -1 if nothing is selected.
+ int m_currentProfile = -1;
- /**
- * Returns a pointer to the currently selected profile.
- * If no profile is selected, returns the first profile in the profile list or nullptr if there are none.
- */
- const AccountProfile *currentProfile() const;
+ // List of available profiles.
+ QList<AccountProfile> m_profiles;
- /**
- * Sets the currently selected profile to the profile with the given ID string.
- * If profileId is not in the list of available profiles, the function will simply return false.
- */
- bool setProfile(const QString &profileId);
+ // the user structure, whatever it is.
+ User m_user;
- /**
- * Clears the current account profile list and replaces it with the given profile list.
- */
- void loadProfiles(const ProfileList &profiles);
+ // true when the account is verified
+ bool m_online = false;
-signals:
- /**
- * This isgnal is emitted whrn the account changes
- */
- void changed();
+ // current task we are executing here
+ std::shared_ptr<YggdrasilTask> m_currentTask;
-protected:
- QString m_username;
- QString m_clientToken;
- QString m_accessToken; // Blank if not logged in.
- int m_currentProfile; // Index of the selected profile within the list of available
- // profiles. -1 if nothing is selected.
- ProfileList m_profiles; // List of available profiles.
- User m_user; // the user structure, whatever it is.
+private slots:
+ void authSucceeded();
+ void authFailed(QString reason);
+
+public:
+ friend class YggdrasilTask;
+ friend class AuthenticateTask;
+ friend class ValidateTask;
+ friend class RefreshTask;
};
diff --git a/logic/auth/MojangAccountList.cpp b/logic/auth/MojangAccountList.cpp
new file mode 100644
index 00000000..33990662
--- /dev/null
+++ b/logic/auth/MojangAccountList.cpp
@@ -0,0 +1,413 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logic/auth/MojangAccountList.h"
+
+#include <QIODevice>
+#include <QFile>
+#include <QTextStream>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonParseError>
+#include <QDir>
+
+#include "logger/QsLog.h"
+
+#include "logic/auth/MojangAccount.h"
+
+#define ACCOUNT_LIST_FORMAT_VERSION 2
+
+MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(parent)
+{
+}
+
+MojangAccountPtr MojangAccountList::findAccount(const QString &username) const
+{
+ for (int i = 0; i < count(); i++)
+ {
+ MojangAccountPtr account = at(i);
+ if (account->username() == username)
+ return account;
+ }
+ return nullptr;
+}
+
+const MojangAccountPtr MojangAccountList::at(int i) const
+{
+ return MojangAccountPtr(m_accounts.at(i));
+}
+
+void MojangAccountList::addAccount(const MojangAccountPtr account)
+{
+ beginResetModel();
+ connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
+ m_accounts.append(account);
+ endResetModel();
+ onListChanged();
+}
+
+void MojangAccountList::removeAccount(const QString &username)
+{
+ beginResetModel();
+ for (auto account : m_accounts)
+ {
+ if (account->username() == username)
+ {
+ m_accounts.removeOne(account);
+ return;
+ }
+ }
+ endResetModel();
+ onListChanged();
+}
+
+void MojangAccountList::removeAccount(QModelIndex index)
+{
+ beginResetModel();
+ m_accounts.removeAt(index.row());
+ endResetModel();
+ onListChanged();
+}
+
+MojangAccountPtr MojangAccountList::activeAccount() const
+{
+ return m_activeAccount;
+}
+
+void MojangAccountList::setActiveAccount(const QString &username)
+{
+ beginResetModel();
+ if (username.isEmpty())
+ {
+ m_activeAccount = nullptr;
+ }
+ else
+ {
+ for (MojangAccountPtr account : m_accounts)
+ {
+ if (account->username() == username)
+ m_activeAccount = account;
+ }
+ }
+ endResetModel();
+ onActiveChanged();
+}
+
+void MojangAccountList::accountChanged()
+{
+ // the list changed. there is no doubt.
+ onListChanged();
+}
+
+void MojangAccountList::onListChanged()
+{
+ if (m_autosave)
+ // TODO: Alert the user if this fails.
+ saveList();
+
+ emit listChanged();
+}
+
+void MojangAccountList::onActiveChanged()
+{
+ if (m_autosave)
+ saveList();
+
+ emit activeAccountChanged();
+}
+
+int MojangAccountList::count() const
+{
+ return m_accounts.count();
+}
+
+QVariant MojangAccountList::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (index.row() > count())
+ return QVariant();
+
+ MojangAccountPtr account = at(index.row());
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (index.column())
+ {
+ case NameColumn:
+ return account->username();
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ return account->username();
+
+ case PointerRole:
+ return qVariantFromValue(account);
+
+ case Qt::CheckStateRole:
+ switch (index.column())
+ {
+ case ActiveColumn:
+ return account == m_activeAccount;
+ }
+
+ default:
+ return QVariant();
+ }
+}
+
+QVariant MojangAccountList::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case ActiveColumn:
+ return "Active?";
+
+ case NameColumn:
+ return "Name";
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case NameColumn:
+ return "The name of the version.";
+
+ default:
+ return QVariant();
+ }
+
+ default:
+ return QVariant();
+ }
+}
+
+int MojangAccountList::rowCount(const QModelIndex &parent) const
+{
+ // Return count
+ return count();
+}
+
+int MojangAccountList::columnCount(const QModelIndex &parent) const
+{
+ return 2;
+}
+
+Qt::ItemFlags MojangAccountList::flags(const QModelIndex &index) const
+{
+ if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
+ {
+ return Qt::NoItemFlags;
+ }
+
+ return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+bool MojangAccountList::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
+ {
+ return false;
+ }
+
+ if(role == Qt::CheckStateRole)
+ {
+ if(value == Qt::Checked)
+ {
+ MojangAccountPtr account = this->at(index.row());
+ this->setActiveAccount(account->username());
+ }
+ }
+
+ emit dataChanged(index, index);
+ return true;
+}
+
+void MojangAccountList::updateListData(QList<MojangAccountPtr> versions)
+{
+ beginResetModel();
+ m_accounts = versions;
+ endResetModel();
+}
+
+bool MojangAccountList::loadList(const QString &filePath)
+{
+ QString path = filePath;
+ if (path.isEmpty())
+ path = m_listFilePath;
+ if (path.isEmpty())
+ {
+ QLOG_ERROR() << "Can't load Mojang account list. No file path given and no default set.";
+ return false;
+ }
+
+ if (!QDir::current().exists(path))
+ {
+ QDir::current().mkpath(path);
+ }
+
+ QFile file(path);
+
+ // 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))
+ {
+ QLOG_ERROR() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
+ return false;
+ }
+
+ // Read the file and close it.
+ QByteArray jsonData = file.readAll();
+ file.close();
+
+ QJsonParseError parseError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
+
+ // Fail if the JSON is invalid.
+ if (parseError.error != QJsonParseError::NoError)
+ {
+ QLOG_ERROR() << QString("Failed to parse account list file: %1 at offset %2")
+ .arg(parseError.errorString(), QString::number(parseError.offset))
+ .toUtf8();
+ return false;
+ }
+
+ // Make sure the root is an object.
+ if (!jsonDoc.isObject())
+ {
+ QLOG_ERROR() << "Invalid account list JSON: Root should be an array.";
+ return false;
+ }
+
+ QJsonObject root = jsonDoc.object();
+
+ // Make sure the format version matches.
+ if (root.value("formatVersion").toVariant().toInt() != ACCOUNT_LIST_FORMAT_VERSION)
+ {
+ QString newName = "accounts-old.json";
+ QLOG_WARN() << "Format version mismatch when loading account list. Existing one will be renamed to"
+ << newName;
+
+ // Attempt to rename the old version.
+ file.rename(newName);
+ return false;
+ }
+
+ // Now, load the accounts array.
+ beginResetModel();
+ QJsonArray accounts = root.value("accounts").toArray();
+ for (QJsonValue accountVal : accounts)
+ {
+ QJsonObject accountObj = accountVal.toObject();
+ MojangAccountPtr account = MojangAccount::loadFromJson(accountObj);
+ if (account.get() != nullptr)
+ {
+ connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
+ m_accounts.append(account);
+ }
+ else
+ {
+ QLOG_WARN() << "Failed to load an account.";
+ }
+ }
+ // Load the active account.
+ m_activeAccount = findAccount(root.value("activeAccount").toString(""));
+ endResetModel();
+ return true;
+}
+
+bool MojangAccountList::saveList(const QString &filePath)
+{
+ QString path(filePath);
+ if (path.isEmpty())
+ path = m_listFilePath;
+ if (path.isEmpty())
+ {
+ QLOG_ERROR() << "Can't save Mojang account list. No file path given and no default set.";
+ return false;
+ }
+
+ if (!QDir::current().exists(path))
+ {
+ QDir::current().mkpath(path);
+ }
+
+ QLOG_INFO() << "Writing account list to" << path;
+
+ QLOG_DEBUG() << "Building JSON data structure.";
+ // Build the JSON document to write to the list file.
+ QJsonObject root;
+
+ root.insert("formatVersion", ACCOUNT_LIST_FORMAT_VERSION);
+
+ // Build a list of accounts.
+ QLOG_DEBUG() << "Building account array.";
+ QJsonArray accounts;
+ for (MojangAccountPtr account : m_accounts)
+ {
+ QJsonObject accountObj = account->saveToJson();
+ accounts.append(accountObj);
+ }
+
+ // Insert the account list into the root object.
+ root.insert("accounts", accounts);
+
+ if(m_activeAccount)
+ {
+ // Save the active account.
+ root.insert("activeAccount", m_activeAccount->username());
+ }
+
+ // Create a JSON document object to convert our JSON to bytes.
+ QJsonDocument doc(root);
+
+ // Now that we're done building the JSON object, we can write it to the file.
+ QLOG_DEBUG() << "Writing account list to file.";
+ QFile file(path);
+
+ // 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))
+ {
+ QLOG_ERROR() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
+ return false;
+ }
+
+ // Write the JSON to the file.
+ file.write(doc.toJson());
+ file.close();
+
+ QLOG_INFO() << "Saved account list to" << path;
+
+ return true;
+}
+
+void MojangAccountList::setListFilePath(QString path, bool autosave)
+{
+ m_listFilePath = path;
+ m_autosave = autosave;
+}
diff --git a/logic/auth/MojangAccountList.h b/logic/auth/MojangAccountList.h
new file mode 100644
index 00000000..c7e30958
--- /dev/null
+++ b/logic/auth/MojangAccountList.h
@@ -0,0 +1,194 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QVariant>
+#include <QAbstractListModel>
+#include <QSharedPointer>
+
+#include "logic/auth/MojangAccount.h"
+
+/*!
+ * \brief List of available Mojang accounts.
+ * This should be loaded in the background by MultiMC on startup.
+ *
+ * This class also inherits from QAbstractListModel. Methods from that
+ * class determine how this list shows up in a list view. Said methods
+ * all have a default implementation, but they can be overridden by subclasses to
+ * change the behavior of the list.
+ */
+class MojangAccountList : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ enum ModelRoles
+ {
+ PointerRole = 0x34B1CB48
+ };
+
+ enum VListColumns
+ {
+ // TODO: Add icon column.
+
+ // First column - Active?
+ ActiveColumn = 0,
+
+ // Second column - Name
+ NameColumn,
+ };
+
+ explicit MojangAccountList(QObject *parent = 0);
+
+ //! Gets the account at the given index.
+ virtual const MojangAccountPtr at(int i) const;
+
+ //! Returns the number of accounts in the list.
+ virtual int count() const;
+
+ //////// List Model Functions ////////
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ virtual int rowCount(const QModelIndex &parent) const;
+ virtual int columnCount(const QModelIndex &parent) const;
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
+
+ /*!
+ * Adds a the given Mojang account to the account list.
+ */
+ virtual void addAccount(const MojangAccountPtr account);
+
+ /*!
+ * Removes the mojang account with the given username from the account list.
+ */
+ virtual void removeAccount(const QString &username);
+
+ /*!
+ * Removes the account at the given QModelIndex.
+ */
+ virtual void removeAccount(QModelIndex index);
+
+ /*!
+ * \brief Finds an account by its username.
+ * \param The username of the account to find.
+ * \return A const pointer to the account with the given username. NULL if
+ * one doesn't exist.
+ */
+ virtual MojangAccountPtr findAccount(const QString &username) const;
+
+ /*!
+ * Sets the default path to save the list file to.
+ * If autosave is true, this list will automatically save to the given path whenever it changes.
+ * THIS FUNCTION DOES NOT LOAD THE LIST. If you set autosave, be sure to call loadList() immediately
+ * after calling this function to ensure an autosaved change doesn't overwrite the list you intended
+ * to load.
+ */
+ virtual void setListFilePath(QString path, bool autosave = false);
+
+ /*!
+ * \brief Loads the account list from the given file path.
+ * If the given file is an empty string (default), will load from the default account list file.
+ * \return True if successful, otherwise false.
+ */
+ virtual bool loadList(const QString &file = "");
+
+ /*!
+ * \brief Saves the account list to the given file.
+ * If the given file is an empty string (default), will save from the default account list file.
+ * \return True if successful, otherwise false.
+ */
+ virtual bool saveList(const QString &file = "");
+
+ /*!
+ * \brief Gets a pointer to the account that the user has selected as their "active" account.
+ * Which account is active can be overridden on a per-instance basis, but this will return the one that
+ * is set as active globally.
+ * \return The currently active MojangAccount. If there isn't an active account, returns a null pointer.
+ */
+ virtual MojangAccountPtr activeAccount() const;
+
+ /*!
+ * Sets the given account as the current active account.
+ * If the username given is an empty string, sets the active account to nothing.
+ */
+ virtual void setActiveAccount(const QString &username);
+
+signals:
+ /*!
+ * Signal emitted to indicate that the account list has changed.
+ * This will also fire if the value of an element in the list changes (will be implemented
+ * later).
+ */
+ void listChanged();
+
+ /*!
+ * Signal emitted to indicate that the active account has changed.
+ */
+ void activeAccountChanged();
+
+public
+slots:
+ /**
+ * This is called when one of the accounts changes and the list needs to be updated
+ */
+ void accountChanged();
+
+protected:
+ /*!
+ * Called whenever the list changes.
+ * This emits the listChanged() signal and autosaves the list (if autosave is enabled).
+ */
+ void onListChanged();
+
+ /*!
+ * Called whenever the active account changes.
+ * Emits the activeAccountChanged() signal and autosaves the list if enabled.
+ */
+ void onActiveChanged();
+
+ QList<MojangAccountPtr> m_accounts;
+
+ /*!
+ * Account that is currently active.
+ */
+ MojangAccountPtr m_activeAccount;
+
+ //! Path to the account list file. Empty string if there isn't one.
+ QString m_listFilePath;
+
+ /*!
+ * If true, the account list will automatically save to the account list path when it changes.
+ * Ignored if m_listFilePath is blank.
+ */
+ bool m_autosave = false;
+
+protected
+slots:
+ /*!
+ * Updates this list with the given list of accounts.
+ * This is done by copying each account in the given list and inserting it
+ * into this one.
+ * We need to do this so that we can set the parents of the accounts are set to this
+ * account list. This can't be done in the load task, because the accounts the load
+ * task creates are on the load task's thread and Qt won't allow their parents
+ * to be set to something created on another thread.
+ * To get around that problem, we invoke this method on the GUI thread, which
+ * then copies the accounts and sets their parents correctly.
+ * \param accounts List of accounts whose parents should be set.
+ */
+ virtual void updateListData(QList<MojangAccountPtr> versions);
+};
diff --git a/logic/auth/YggdrasilTask.cpp b/logic/auth/YggdrasilTask.cpp
index 797e84cd..088e1fc0 100644
--- a/logic/auth/YggdrasilTask.cpp
+++ b/logic/auth/YggdrasilTask.cpp
@@ -24,17 +24,11 @@
#include <MultiMC.h>
#include <logic/auth/MojangAccount.h>
+#include <logic/net/URLConstants.h>
-YggdrasilTask::YggdrasilTask(MojangAccountPtr account, QObject *parent) : Task(parent)
+YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent)
+ : Task(parent), m_account(account)
{
- m_error = nullptr;
- m_account = account;
-}
-
-YggdrasilTask::~YggdrasilTask()
-{
- if (m_error)
- delete m_error;
}
void YggdrasilTask::executeTask()
@@ -45,97 +39,126 @@ void YggdrasilTask::executeTask()
QJsonDocument doc(getRequestContent());
auto worker = MMC->qnam();
- connect(worker.get(), SIGNAL(finished(QNetworkReply *)), this,
- SLOT(processReply(QNetworkReply *)));
-
- QUrl reqUrl("https://authserver.mojang.com/" + getEndpoint());
+ QUrl reqUrl("https://" + URLConstants::AUTH_BASE + getEndpoint());
QNetworkRequest netRequest(reqUrl);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QByteArray requestData = doc.toJson();
m_netReply = worker->post(netRequest, requestData);
+ connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply);
+ connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers);
+ connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers);
+ timeout_keeper.setSingleShot(true);
+ timeout_keeper.start(timeout_max);
+ counter.setSingleShot(false);
+ counter.start(time_step);
+ progress(0, timeout_max);
+ connect(&timeout_keeper, &QTimer::timeout, this, &YggdrasilTask::abort);
+ connect(&counter, &QTimer::timeout, this, &YggdrasilTask::heartbeat);
}
-void YggdrasilTask::processReply(QNetworkReply *reply)
+void YggdrasilTask::refreshTimers(qint64, qint64)
{
- setStatus(getStateMessage(STATE_PROCESSING_RESPONSE));
+ timeout_keeper.stop();
+ timeout_keeper.start(timeout_max);
+ progress(count = 0, timeout_max);
+}
+void YggdrasilTask::heartbeat()
+{
+ count += time_step;
+ progress(count, timeout_max);
+}
- if (m_netReply != reply)
- // Wrong reply for some reason...
- return;
+void YggdrasilTask::abort()
+{
+ progress(timeout_max, timeout_max);
+ m_netReply->abort();
+}
- if (reply->error() == QNetworkReply::OperationCanceledError)
+void YggdrasilTask::processReply()
+{
+ setStatus(getStateMessage(STATE_PROCESSING_RESPONSE));
+
+ // any network errors lead to offline mode right now
+ if (m_netReply->error() >= QNetworkReply::ConnectionRefusedError &&
+ m_netReply->error() <= QNetworkReply::UnknownNetworkError)
{
+ // WARNING/FIXME: the value here is used in MojangAccount to detect the cancel/timeout
emitFailed("Yggdrasil task cancelled.");
return;
}
- else
+
+ // Try to parse the response regardless of the response code.
+ // Sometimes the auth server will give more information and an error code.
+ QJsonParseError jsonError;
+ QByteArray replyData = m_netReply->readAll();
+ QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError);
+ // Check the response code.
+ int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ if (responseCode == 200)
{
- // Try to parse the response regardless of the response code.
- // Sometimes the auth server will give more information and an error code.
- QJsonParseError jsonError;
- QByteArray replyData = reply->readAll();
- QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError);
- // Check the response code.
- int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
- if (responseCode == 200)
+ // If the response code was 200, then there shouldn't be an error. Make sure
+ // anyways.
+ // Also, sometimes an empty reply indicates success. If there was no data received,
+ // pass an empty json object to the processResponse function.
+ if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0)
{
- // If the response code was 200, then there shouldn't be an error. Make sure anyways.
- // Also, sometimes an empty reply indicates success. If there was no data received,
- // pass an empty json object to the processResponse function.
- if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0)
- {
- if (!processResponse(replyData.size() > 0 ? doc.object() : QJsonObject()))
- {
- YggdrasilTask::Error *err = getError();
- if (err)
- emitFailed(err->getErrorMessage());
- else
- emitFailed(tr("An unknown error occurred when processing the response "
- "from the authentication server."));
- }
- else
- {
- emitSucceeded();
- }
- }
- else
+ if (processResponse(replyData.size() > 0 ? doc.object() : QJsonObject()))
{
- emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.").arg(jsonError.errorString()).arg(jsonError.offset));
+ emitSucceeded();
+ return;
}
+
+ // errors happened anyway?
+ emitFailed(m_error ? m_error->m_errorMessageVerbose
+ : tr("An unknown error occurred when processing the response "
+ "from the authentication server."));
}
else
{
- // If the response code was not 200, then Yggdrasil may have given us information about the error.
- // If we can parse the response, then get information from it. Otherwise just say there was an unknown error.
- if (jsonError.error == QJsonParseError::NoError)
- {
- // We were able to parse the server's response. Woo!
- // Call processError. If a subclass has overridden it then they'll handle their stuff there.
- QLOG_DEBUG() << "The request failed, but the server gave us an error message. Processing error.";
- emitFailed(processError(doc.object()));
- }
- else
- {
- // The server didn't say anything regarding the error. Give the user an unknown error.
- QLOG_DEBUG() << "The request failed and the server gave no error message. Unknown error.";
- emitFailed(tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(reply->errorString()));
- }
+ emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.")
+ .arg(jsonError.errorString())
+ .arg(jsonError.offset));
}
+ return;
+ }
+
+ // If the response code was not 200, then Yggdrasil may have given us information
+ // about the error.
+ // If we can parse the response, then get information from it. Otherwise just say
+ // there was an unknown error.
+ if (jsonError.error == QJsonParseError::NoError)
+ {
+ // We were able to parse the server's response. Woo!
+ // Call processError. If a subclass has overridden it then they'll handle their
+ // stuff there.
+ QLOG_DEBUG() << "The request failed, but the server gave us an error message. "
+ "Processing error.";
+ emitFailed(processError(doc.object()));
+ }
+ else
+ {
+ // The server didn't say anything regarding the error. Give the user an unknown
+ // error.
+ QLOG_DEBUG() << "The request failed and the server gave no error message. "
+ "Unknown error.";
+ emitFailed(tr("An unknown error occurred when trying to communicate with the "
+ "authentication server: %1").arg(m_netReply->errorString()));
}
}
QString YggdrasilTask::processError(QJsonObject responseData)
{
QJsonValue errorVal = responseData.value("error");
- QJsonValue msgVal = responseData.value("errorMessage");
+ QJsonValue errorMessageValue = responseData.value("errorMessage");
QJsonValue causeVal = responseData.value("cause");
- if (errorVal.isString() && msgVal.isString())
+ if (errorVal.isString() && errorMessageValue.isString())
{
- m_error = new Error(errorVal.toString(""), msgVal.toString(""), causeVal.toString(""));
- return m_error->getDisplayMessage();
+ m_error = std::shared_ptr<Error>(new Error{
+ errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")});
+ return m_error->m_errorMessageVerbose;
}
else
{
@@ -156,13 +179,3 @@ QString YggdrasilTask::getStateMessage(const YggdrasilTask::State state) const
return tr("Processing. Please wait.");
}
}
-
-YggdrasilTask::Error *YggdrasilTask::getError() const
-{
- return this->m_error;
-}
-
-MojangAccountPtr YggdrasilTask::getMojangAccount() const
-{
- return this->m_account;
-}
diff --git a/logic/auth/YggdrasilTask.h b/logic/auth/YggdrasilTask.h
index 62638c9d..1f81a2d0 100644
--- a/logic/auth/YggdrasilTask.h
+++ b/logic/auth/YggdrasilTask.h
@@ -19,6 +19,7 @@
#include <QString>
#include <QJsonObject>
+#include <QTimer>
#include "logic/auth/MojangAccount.h"
@@ -31,45 +32,18 @@ class YggdrasilTask : public Task
{
Q_OBJECT
public:
- explicit YggdrasilTask(MojangAccountPtr account, QObject *parent = 0);
- ~YggdrasilTask();
+ explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0);
/**
* Class describing a Yggdrasil error response.
*/
- class Error
+ struct Error
{
- public:
- Error(const QString& shortError, const QString& errorMessage, const QString& cause) :
- m_shortError(shortError), m_errorMessage(errorMessage), m_cause(cause) {}
-
- QString getShortError() const { return m_shortError; }
- QString getErrorMessage() const { return m_errorMessage; }
- QString getCause() const { return m_cause; }
-
- /// Gets the string to display in the GUI for describing this error.
- QString getDisplayMessage()
- {
- return getErrorMessage();
- }
-
- protected:
- QString m_shortError;
- QString m_errorMessage;
+ QString m_errorMessageShort;
+ QString m_errorMessageVerbose;
QString m_cause;
};
- /**
- * Gets the Mojang account that this task is operating on.
- */
- virtual MojangAccountPtr getMojangAccount() const;
-
- /**
- * Returns a pointer to a YggdrasilTask::Error object if an error has occurred.
- * If no error has occurred, returns a null pointer.
- */
- virtual Error *getError() const;
-
protected:
/**
* Enum for describing the state of the current task.
@@ -120,13 +94,25 @@ protected:
*/
virtual QString getStateMessage(const State state) const;
- MojangAccountPtr m_account;
-
- QNetworkReply *m_netReply;
-
- Error *m_error;
-
protected
slots:
- void processReply(QNetworkReply *reply);
+ void processReply();
+ void refreshTimers(qint64, qint64);
+ void heartbeat();
+
+public
+slots:
+ virtual void abort() override;
+
+protected:
+ // FIXME: segfault disaster waiting to happen
+ MojangAccount *m_account = nullptr;
+ QNetworkReply *m_netReply = nullptr;
+ std::shared_ptr<Error> m_error;
+ QTimer timeout_keeper;
+ QTimer counter;
+ int count = 0; // num msec since time reset
+
+ const int timeout_max = 10000;
+ const int time_step = 50;
};
diff --git a/logic/auth/flows/AuthenticateTask.cpp b/logic/auth/flows/AuthenticateTask.cpp
index ec2004d6..f60be35d 100644
--- a/logic/auth/flows/AuthenticateTask.cpp
+++ b/logic/auth/flows/AuthenticateTask.cpp
@@ -26,7 +26,7 @@
#include "logger/QsLog.h"
-AuthenticateTask::AuthenticateTask(MojangAccountPtr account, const QString &password,
+AuthenticateTask::AuthenticateTask(MojangAccount * account, const QString &password,
QObject *parent)
: YggdrasilTask(account, parent), m_password(password)
{
@@ -59,14 +59,14 @@ QJsonObject AuthenticateTask::getRequestContent() const
req.insert("agent", agent);
}
- req.insert("username", getMojangAccount()->username());
+ req.insert("username", m_account->username());
req.insert("password", m_password);
req.insert("requestUser", true);
// If we already have a client token, give it to the server.
// Otherwise, let the server give us one.
- if (!getMojangAccount()->clientToken().isEmpty())
- req.insert("clientToken", getMojangAccount()->clientToken());
+ if (!m_account->m_clientToken.isEmpty())
+ req.insert("clientToken", m_account->m_clientToken);
return req;
}
@@ -76,7 +76,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
// Read the response data. We need to get the client token, access token, and the selected
// profile.
QLOG_DEBUG() << "Processing authentication response.";
-
+ // QLOG_DEBUG() << responseData;
// If we already have a client token, make sure the one the server gave us matches our
// existing one.
QLOG_DEBUG() << "Getting client token.";
@@ -88,8 +88,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
QLOG_ERROR() << "Server didn't send a client token.";
return false;
}
- if (!getMojangAccount()->clientToken().isEmpty() &&
- clientToken != getMojangAccount()->clientToken())
+ if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
{
// The server changed our client token! Obey its wishes, but complain. That's what I do
// for my parents, so...
@@ -97,7 +96,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
<< "'. This shouldn't happen, but it isn't really a big deal.";
}
// Set the client token.
- getMojangAccount()->setClientToken(clientToken);
+ m_account->m_clientToken = clientToken;
// Now, we set the access token.
QLOG_DEBUG() << "Getting access token.";
@@ -109,7 +108,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
QLOG_ERROR() << "Server didn't send an access token.";
}
// Set the access token.
- getMojangAccount()->setAccessToken(accessToken);
+ m_account->m_accessToken = accessToken;
// Now we load the list of available profiles.
// Mojang hasn't yet implemented the profile system,
@@ -117,13 +116,14 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
// don't have trouble implementing it later.
QLOG_DEBUG() << "Loading profile list.";
QJsonArray availableProfiles = responseData.value("availableProfiles").toArray();
- ProfileList loadedProfiles;
+ QList<AccountProfile> loadedProfiles;
for (auto iter : availableProfiles)
{
QJsonObject profile = iter.toObject();
// Profiles are easy, we just need their ID and name.
QString id = profile.value("id").toString("");
QString name = profile.value("name").toString("");
+ bool legacy = profile.value("legacy").toBool(false);
if (id.isEmpty() || name.isEmpty())
{
@@ -135,10 +135,10 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
}
// Now, add a new AccountProfile entry to the list.
- loadedProfiles.append(AccountProfile(id, name));
+ loadedProfiles.append({id, name, legacy});
}
// Put the list of profiles we loaded into the MojangAccount object.
- getMojangAccount()->loadProfiles(loadedProfiles);
+ m_account->m_profiles = loadedProfiles;
// Finally, we set the current profile to the correct value. This is pretty simple.
// We do need to make sure that the current profile that the server gave us
@@ -153,7 +153,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
QLOG_ERROR() << "Server didn't specify a currently selected profile.";
return false;
}
- if (!getMojangAccount()->setProfile(currentProfileId))
+ if (!m_account->setCurrentProfile(currentProfileId))
{
// TODO: Set an error to display to the user.
QLOG_ERROR() << "Server specified a selected profile that wasn't in the available "
@@ -162,23 +162,20 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
}
// this is what the vanilla launcher passes to the userProperties launch param
- // doesn't seem to be used for anything so far? I don't get any of this data on my account
- // (peterixxx)
- // is it a good idea to log this?
if (responseData.contains("user"))
{
+ User u;
auto obj = responseData.value("user").toObject();
- auto userId = obj.value("id").toString();
+ u.id = obj.value("id").toString();
auto propArray = obj.value("properties").toArray();
- QLOG_DEBUG() << "User ID: " << userId;
- QLOG_DEBUG() << "User Properties: ";
for (auto prop : propArray)
{
auto propTuple = prop.toObject();
auto name = propTuple.value("name").toString();
auto value = propTuple.value("value").toString();
- QLOG_DEBUG() << name << " : " << value;
+ u.properties.insert(name, value);
}
+ m_account->m_user = u;
}
// We've made it through the minefield of possible errors. Return true to indicate that
diff --git a/logic/auth/flows/AuthenticateTask.h b/logic/auth/flows/AuthenticateTask.h
index 3b99caad..b6564657 100644
--- a/logic/auth/flows/AuthenticateTask.h
+++ b/logic/auth/flows/AuthenticateTask.h
@@ -30,7 +30,7 @@ class AuthenticateTask : public YggdrasilTask
{
Q_OBJECT
public:
- AuthenticateTask(MojangAccountPtr account, const QString &password, QObject *parent = 0);
+ AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0);
protected:
virtual QJsonObject getRequestContent() const;
diff --git a/logic/auth/flows/InvalidateTask.cpp b/logic/auth/flows/InvalidateTask.cpp
deleted file mode 100644
index e69de29b..00000000
--- a/logic/auth/flows/InvalidateTask.cpp
+++ /dev/null
diff --git a/logic/auth/flows/InvalidateTask.h b/logic/auth/flows/InvalidateTask.h
deleted file mode 100644
index e69de29b..00000000
--- a/logic/auth/flows/InvalidateTask.h
+++ /dev/null
diff --git a/logic/auth/flows/RefreshTask.cpp b/logic/auth/flows/RefreshTask.cpp
index b56ed9bc..5f68ccc7 100644
--- a/logic/auth/flows/RefreshTask.cpp
+++ b/logic/auth/flows/RefreshTask.cpp
@@ -25,7 +25,7 @@
#include "logger/QsLog.h"
-RefreshTask::RefreshTask(MojangAccountPtr account, QObject *parent)
+RefreshTask::RefreshTask(MojangAccount *account, QObject *parent)
: YggdrasilTask(account, parent)
{
}
@@ -44,13 +44,12 @@ QJsonObject RefreshTask::getRequestContent() const
* "requestUser": true/false // request the user structure
* }
*/
- auto account = getMojangAccount();
QJsonObject req;
- req.insert("clientToken", account->clientToken());
- req.insert("accessToken", account->accessToken());
+ req.insert("clientToken", m_account->m_clientToken);
+ req.insert("accessToken", m_account->m_accessToken);
/*
{
- auto currentProfile = account->currentProfile();
+ auto currentProfile = m_account->currentProfile();
QJsonObject profile;
profile.insert("id", currentProfile->id());
profile.insert("name", currentProfile->name());
@@ -64,12 +63,11 @@ QJsonObject RefreshTask::getRequestContent() const
bool RefreshTask::processResponse(QJsonObject responseData)
{
- auto account = getMojangAccount();
-
// Read the response data. We need to get the client token, access token, and the selected
// profile.
QLOG_DEBUG() << "Processing authentication response.";
+ // QLOG_DEBUG() << responseData;
// If we already have a client token, make sure the one the server gave us matches our
// existing one.
QString clientToken = responseData.value("clientToken").toString("");
@@ -80,7 +78,7 @@ bool RefreshTask::processResponse(QJsonObject responseData)
QLOG_ERROR() << "Server didn't send a client token.";
return false;
}
- if (!account->clientToken().isEmpty() && clientToken != account->clientToken())
+ if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
{
// The server changed our client token! Obey its wishes, but complain. That's what I do
// for my parents, so...
@@ -104,7 +102,7 @@ bool RefreshTask::processResponse(QJsonObject responseData)
// profile)
QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
QString currentProfileId = currentProfile.value("id").toString("");
- if (account->currentProfile()->id() != currentProfileId)
+ if (m_account->currentProfile()->id != currentProfileId)
{
// TODO: Set an error to display to the user.
QLOG_ERROR() << "Server didn't specify the same selected profile as ours.";
@@ -114,26 +112,26 @@ bool RefreshTask::processResponse(QJsonObject responseData)
// this is what the vanilla launcher passes to the userProperties launch param
if (responseData.contains("user"))
{
+ User u;
auto obj = responseData.value("user").toObject();
- auto userId = obj.value("id").toString();
+ u.id = obj.value("id").toString();
auto propArray = obj.value("properties").toArray();
- QLOG_DEBUG() << "User ID: " << userId;
- QLOG_DEBUG() << "User Properties: ";
for (auto prop : propArray)
{
auto propTuple = prop.toObject();
auto name = propTuple.value("name").toString();
auto value = propTuple.value("value").toString();
- QLOG_DEBUG() << name << " : " << value;
+ u.properties.insert(name, value);
}
+ m_account->m_user = u;
}
+
// We've made it through the minefield of possible errors. Return true to indicate that
// we've succeeded.
QLOG_DEBUG() << "Finished reading refresh response.";
// Reset the access token.
- account->setAccessToken(accessToken);
- account->propagateChange();
+ m_account->m_accessToken = accessToken;
return true;
}
@@ -147,9 +145,9 @@ QString RefreshTask::getStateMessage(const YggdrasilTask::State state) const
switch (state)
{
case STATE_SENDING_REQUEST:
- return tr("Refreshing: Sending request.");
+ return tr("Refreshing login token.");
case STATE_PROCESSING_RESPONSE:
- return tr("Refreshing: Processing response.");
+ return tr("Refreshing login token: Processing response.");
default:
return YggdrasilTask::getStateMessage(state);
}
diff --git a/logic/auth/flows/RefreshTask.h b/logic/auth/flows/RefreshTask.h
index 2596f6c7..2fd50c60 100644
--- a/logic/auth/flows/RefreshTask.h
+++ b/logic/auth/flows/RefreshTask.h
@@ -30,7 +30,7 @@ class RefreshTask : public YggdrasilTask
{
Q_OBJECT
public:
- RefreshTask(MojangAccountPtr account, QObject *parent = 0);
+ RefreshTask(MojangAccount * account, QObject *parent = 0);
protected:
virtual QJsonObject getRequestContent() const;
diff --git a/logic/auth/flows/ValidateTask.cpp b/logic/auth/flows/ValidateTask.cpp
index d9e0e46b..84d5e703 100644
--- a/logic/auth/flows/ValidateTask.cpp
+++ b/logic/auth/flows/ValidateTask.cpp
@@ -26,7 +26,7 @@
#include "logger/QsLog.h"
-ValidateTask::ValidateTask(MojangAccountPtr account, QObject *parent)
+ValidateTask::ValidateTask(MojangAccount * account, QObject *parent)
: YggdrasilTask(account, parent)
{
}
@@ -34,7 +34,7 @@ ValidateTask::ValidateTask(MojangAccountPtr account, QObject *parent)
QJsonObject ValidateTask::getRequestContent() const
{
QJsonObject req;
- req.insert("accessToken", getMojangAccount()->accessToken());
+ req.insert("accessToken", m_account->m_accessToken);
return req;
}
diff --git a/logic/auth/flows/ValidateTask.h b/logic/auth/flows/ValidateTask.h
index 3ff78c6a..0e34f0c3 100644
--- a/logic/auth/flows/ValidateTask.h
+++ b/logic/auth/flows/ValidateTask.h
@@ -13,6 +13,10 @@
* limitations under the License.
*/
+/*
+ * :FIXME: DEAD CODE, DEAD CODE, DEAD CODE! :FIXME:
+ */
+
#pragma once
#include <logic/auth/YggdrasilTask.h>
@@ -28,7 +32,7 @@ class ValidateTask : public YggdrasilTask
{
Q_OBJECT
public:
- ValidateTask(MojangAccountPtr account, QObject *parent = 0);
+ ValidateTask(MojangAccount *account, QObject *parent = 0);
protected:
virtual QJsonObject getRequestContent() const;