diff options
| author | Petr Mrázek <peterix@gmail.com> | 2016-04-10 15:53:05 +0200 |
|---|---|---|
| committer | Petr Mrázek <peterix@gmail.com> | 2016-05-01 00:00:14 +0200 |
| commit | b6d455a02bd338e9dc0faa09d4d8177ecd8d569a (patch) | |
| tree | 41982bca1ede50049f2f8c7109dd18edeefde6d0 /api/logic/minecraft/auth | |
| parent | 47e37635f50c09b4f9a9ee7699e3120bab3e4088 (diff) | |
| download | PrismLauncher-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.tar.gz PrismLauncher-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.tar.bz2 PrismLauncher-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.zip | |
NOISSUE reorganize and document libraries
Diffstat (limited to 'api/logic/minecraft/auth')
| -rw-r--r-- | api/logic/minecraft/auth/AuthSession.cpp | 30 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/AuthSession.h | 51 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/MojangAccount.cpp | 278 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/MojangAccount.h | 173 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/MojangAccountList.cpp | 427 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/MojangAccountList.h | 201 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/YggdrasilTask.cpp | 255 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/YggdrasilTask.h | 150 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/AuthenticateTask.cpp | 202 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/AuthenticateTask.h | 46 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/RefreshTask.cpp | 144 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/RefreshTask.h | 44 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/ValidateTask.cpp | 61 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/ValidateTask.h | 47 |
14 files changed, 2109 insertions, 0 deletions
diff --git a/api/logic/minecraft/auth/AuthSession.cpp b/api/logic/minecraft/auth/AuthSession.cpp new file mode 100644 index 00000000..8758bfbd --- /dev/null +++ b/api/logic/minecraft/auth/AuthSession.cpp @@ -0,0 +1,30 @@ +#include "AuthSession.h" +#include <QJsonObject> +#include <QJsonArray> +#include <QJsonDocument> +#include <QStringList> + +QString AuthSession::serializeUserProperties() +{ + QJsonObject userAttrs; + for (auto key : u.properties.keys()) + { + auto array = QJsonArray::fromStringList(u.properties.values(key)); + userAttrs.insert(key, array); + } + QJsonDocument value(userAttrs); + return value.toJson(QJsonDocument::Compact); + +} + +bool AuthSession::MakeOffline(QString offline_playername) +{ + if (status != PlayableOffline && status != PlayableOnline) + { + return false; + } + session = "-"; + player_name = offline_playername; + status = PlayableOffline; + return true; +} diff --git a/api/logic/minecraft/auth/AuthSession.h b/api/logic/minecraft/auth/AuthSession.h new file mode 100644 index 00000000..dede90a9 --- /dev/null +++ b/api/logic/minecraft/auth/AuthSession.h @@ -0,0 +1,51 @@ +#pragma once + +#include <QString> +#include <QMultiMap> +#include <memory> + +#include "multimc_logic_export.h" + +struct User +{ + QString id; + QMultiMap<QString, QString> properties; +}; + +struct MULTIMC_LOGIC_EXPORT AuthSession +{ + bool MakeOffline(QString offline_playername); + + QString serializeUserProperties(); + + enum Status + { + Undetermined, + RequiresPassword, + PlayableOffline, + PlayableOnline + } status = Undetermined; + + User u; + + // client token + QString client_token; + // account user name + QString username; + // combined session ID + QString session; + // volatile auth token + QString access_token; + // profile name + QString player_name; + // profile ID + QString uuid; + // 'legacy' or 'mojang', depending on account type + QString user_type; + // Did the auth server reply? + bool auth_server_online = false; + // Did the user request online mode? + bool wants_online = true; +}; + +typedef std::shared_ptr<AuthSession> AuthSessionPtr; diff --git a/api/logic/minecraft/auth/MojangAccount.cpp b/api/logic/minecraft/auth/MojangAccount.cpp new file mode 100644 index 00000000..69a24c09 --- /dev/null +++ b/api/logic/minecraft/auth/MojangAccount.cpp @@ -0,0 +1,278 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Authors: Orochimarufan <orochimarufan.x3@gmail.com> + * + * 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 "MojangAccount.h" +#include "flows/RefreshTask.h" +#include "flows/AuthenticateTask.h" + +#include <QUuid> +#include <QJsonObject> +#include <QJsonArray> +#include <QRegExp> +#include <QStringList> +#include <QJsonDocument> + +#include <QDebug> + +MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) +{ + // The JSON object must at least have a username for it to be valid. + if (!object.value("username").isString()) + { + qCritical() << "Can't load Mojang account info from JSON object. Username field is " + "missing or of the wrong type."; + return nullptr; + } + + QString username = object.value("username").toString(""); + QString clientToken = object.value("clientToken").toString(""); + QString accessToken = object.value("accessToken").toString(""); + + QJsonArray profileArray = object.value("profiles").toArray(); + if (profileArray.size() < 1) + { + qCritical() << "Can't load Mojang account with username \"" << username + << "\". No profiles found."; + return nullptr; + } + + 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()) + { + qWarning() << "Unable to load a profile because it was missing an ID or a name."; + continue; + } + profiles.append({id, name, legacy}); + } + + 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->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() const +{ + QJsonObject json; + 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("legacy", profile.legacy); + profileArray.append(profileObj); + } + json.insert("profiles", profileArray); + + 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; +} + +bool MojangAccount::setCurrentProfile(const QString &profileId) +{ + for (int i = 0; i < m_profiles.length(); i++) + { + if (m_profiles[i].id == profileId) + { + m_currentProfile = i; + return true; + } + } + return false; +} + +const AccountProfile *MojangAccount::currentProfile() const +{ + if (m_currentProfile == -1) + return nullptr; + return &m_profiles[m_currentProfile]; +} + +AccountStatus MojangAccount::accountStatus() const +{ + if (m_accessToken.isEmpty()) + return NotVerified; + else + return Verified; +} + +std::shared_ptr<YggdrasilTask> MojangAccount::login(AuthSessionPtr session, + QString password) +{ + Q_ASSERT(m_currentTask.get() == nullptr); + + // take care of the true offline status + if (accountStatus() == NotVerified && password.isEmpty()) + { + if (session) + { + session->status = AuthSession::RequiresPassword; + fillSession(session); + } + return nullptr; + } + + if (password.isEmpty()) + { + m_currentTask.reset(new RefreshTask(this)); + } + else + { + m_currentTask.reset(new AuthenticateTask(this, password)); + } + m_currentTask->assignSession(session); + + connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); + connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + return m_currentTask; +} + +void MojangAccount::authSucceeded() +{ + auto session = m_currentTask->getAssignedSession(); + if (session) + { + session->status = + session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline; + fillSession(session); + session->auth_server_online = true; + } + m_currentTask.reset(); + emit changed(); +} + +void MojangAccount::authFailed(QString reason) +{ + auto session = m_currentTask->getAssignedSession(); + // This is emitted when the yggdrasil tasks time out or are cancelled. + // -> we treat the error as no-op + if (m_currentTask->state() == YggdrasilTask::STATE_FAILED_SOFT) + { + if (session) + { + session->status = accountStatus() == Verified ? AuthSession::PlayableOffline + : AuthSession::RequiresPassword; + session->auth_server_online = false; + fillSession(session); + } + } + else + { + m_accessToken = QString(); + emit changed(); + if (session) + { + session->status = AuthSession::RequiresPassword; + session->auth_server_online = true; + fillSession(session); + } + } + m_currentTask.reset(); +} + +void MojangAccount::fillSession(AuthSessionPtr session) +{ + // the user name. you have to have an user name + session->username = m_username; + // volatile auth token + session->access_token = m_accessToken; + // the semi-permanent client token + session->client_token = m_clientToken; + if (currentProfile()) + { + // profile name + session->player_name = currentProfile()->name; + // profile ID + session->uuid = currentProfile()->id; + // 'legacy' or 'mojang', depending on account type + session->user_type = currentProfile()->legacy ? "legacy" : "mojang"; + if (!session->access_token.isEmpty()) + { + session->session = "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id; + } + else + { + session->session = "-"; + } + } + else + { + session->player_name = "Player"; + session->session = "-"; + } + session->u = user(); +} diff --git a/api/logic/minecraft/auth/MojangAccount.h b/api/logic/minecraft/auth/MojangAccount.h new file mode 100644 index 00000000..2de0c19c --- /dev/null +++ b/api/logic/minecraft/auth/MojangAccount.h @@ -0,0 +1,173 @@ +/* Copyright 2013-2015 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 <QString> +#include <QList> +#include <QJsonObject> +#include <QPair> +#include <QMap> + +#include <memory> +#include "AuthSession.h" + +#include "multimc_logic_export.h" + +class Task; +class YggdrasilTask; +class MojangAccount; + +typedef std::shared_ptr<MojangAccount> MojangAccountPtr; +Q_DECLARE_METATYPE(MojangAccountPtr) + +/** + * 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. + */ +struct AccountProfile +{ + QString id; + QString name; + bool legacy; +}; + +enum AccountStatus +{ + NotVerified, + Verified +}; + +/** + * Object that stores information about a certain Mojang account. + * + * 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 MULTIMC_LOGIC_EXPORT MojangAccount : public QObject +{ + Q_OBJECT +public: /* construction */ + //! Do not copy accounts. ever. + explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete; + + //! Default constructor + explicit MojangAccount(QObject *parent = 0) : 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. + static MojangAccountPtr loadFromJson(const QJsonObject &json); + + //! Saves a MojangAccount to a JSON object and returns it. + QJsonObject saveToJson() const; + +public: /* manipulation */ + /** + * 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<YggdrasilTask> login(AuthSessionPtr session, + QString password = QString()); + +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; + } + + //! Returns the currently selected profile (if none, returns nullptr) + const AccountProfile *currentProfile() const; + + //! Returns whether the account is NotVerified, Verified or Online + AccountStatus accountStatus() const; + +signals: + /** + * This signal is emitted when the account changes + */ + void changed(); + + // TODO: better signalling for the various possible state changes - especially errors + +protected: /* variables */ + QString m_username; + + // 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; + + // Blank if not logged in. + QString m_accessToken; + + // Index of the selected profile within the list of available + // profiles. -1 if nothing is selected. + int m_currentProfile = -1; + + // List of available profiles. + QList<AccountProfile> m_profiles; + + // the user structure, whatever it is. + User m_user; + + // current task we are executing here + std::shared_ptr<YggdrasilTask> m_currentTask; + +private +slots: + void authSucceeded(); + void authFailed(QString reason); + +private: + void fillSession(AuthSessionPtr session); + +public: + friend class YggdrasilTask; + friend class AuthenticateTask; + friend class ValidateTask; + friend class RefreshTask; +}; diff --git a/api/logic/minecraft/auth/MojangAccountList.cpp b/api/logic/minecraft/auth/MojangAccountList.cpp new file mode 100644 index 00000000..26cbc81a --- /dev/null +++ b/api/logic/minecraft/auth/MojangAccountList.cpp @@ -0,0 +1,427 @@ +/* Copyright 2013-2015 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 "MojangAccountList.h" +#include "MojangAccount.h" + +#include <QIODevice> +#include <QFile> +#include <QTextStream> +#include <QJsonDocument> +#include <QJsonArray> +#include <QJsonObject> +#include <QJsonParseError> +#include <QDir> + +#include <QDebug> + +#include <FileSystem.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 tr("Active?"); + + case NameColumn: + return tr("Name"); + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + switch (section) + { + case NameColumn: + return tr("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()) + { + qCritical() << "Can't load Mojang account list. No file path given and no default set."; + return false; + } + + 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)) + { + qCritical() << 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) + { + qCritical() << 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()) + { + qCritical() << "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"; + qWarning() << "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 + { + qWarning() << "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()) + { + 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(path)) + return false; + + // make sure the file wasn't overwritten with a folder before (fixes a bug) + QFileInfo finfo(path); + if(finfo.isDir()) + { + QDir badDir(path); + badDir.removeRecursively(); + } + + qDebug() << "Writing account list to" << path; + + qDebug() << "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. + qDebug() << "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. + qDebug() << "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)) + { + qCritical() << 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.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser); + file.close(); + + qDebug() << "Saved account list to" << path; + + return true; +} + +void MojangAccountList::setListFilePath(QString path, bool autosave) +{ + m_listFilePath = path; + m_autosave = autosave; +} + +bool MojangAccountList::anyAccountIsValid() +{ + for(auto account:m_accounts) + { + if(account->accountStatus() != NotVerified) + return true; + } + return false; +} diff --git a/api/logic/minecraft/auth/MojangAccountList.h b/api/logic/minecraft/auth/MojangAccountList.h new file mode 100644 index 00000000..c40fa6a3 --- /dev/null +++ b/api/logic/minecraft/auth/MojangAccountList.h @@ -0,0 +1,201 @@ +/* Copyright 2013-2015 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 "MojangAccount.h" + +#include <QObject> +#include <QVariant> +#include <QAbstractListModel> +#include <QSharedPointer> + +#include "multimc_logic_export.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 MULTIMC_LOGIC_EXPORT 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); + + /*! |
