summaryrefslogtreecommitdiffstats
path: root/logic
diff options
context:
space:
mode:
Diffstat (limited to 'logic')
-rw-r--r--logic/auth/AuthenticateTask.cpp115
-rw-r--r--logic/auth/AuthenticateTask.h2
-rw-r--r--logic/auth/MojangAccount.cpp74
-rw-r--r--logic/auth/MojangAccount.h58
-rw-r--r--logic/auth/YggdrasilTask.cpp13
-rw-r--r--logic/auth/YggdrasilTask.h2
6 files changed, 256 insertions, 8 deletions
diff --git a/logic/auth/AuthenticateTask.cpp b/logic/auth/AuthenticateTask.cpp
index edfdafdb..db4b0d92 100644
--- a/logic/auth/AuthenticateTask.cpp
+++ b/logic/auth/AuthenticateTask.cpp
@@ -17,10 +17,15 @@
#include <logic/auth/AuthenticateTask.h>
#include <logic/auth/MojangAccount.h>
+
#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
#include <QVariant>
#include <QDebug>
+#include "logger/QsLog.h"
+
AuthenticateTask::AuthenticateTask(MojangAccount* account, const QString& password, QObject* parent) :
YggdrasilTask(account, parent), m_password(password)
{
@@ -53,15 +58,106 @@ QJsonObject AuthenticateTask::getRequestContent() const
req.insert("username", getMojangAccount()->username());
req.insert("password", m_password);
- req.insert("clientToken", getMojangAccount()->clientToken());
+
+ // 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());
return req;
}
bool AuthenticateTask::processResponse(QJsonObject responseData)
{
- qDebug() << QJsonDocument(responseData).toVariant().toString();
- return false;
+ // Read the response data. We need to get the client token, access token, and the selected profile.
+ QLOG_DEBUG() << "Processing authentication response.";
+
+ // If we already have a client token, make sure the one the server gave us matches our existing one.
+ QLOG_DEBUG() << "Getting client token.";
+ QString clientToken = responseData.value("clientToken").toString("");
+ if (clientToken.isEmpty())
+ {
+ // Fail if the server gave us an empty client token
+ // TODO: Set an error properly to display to the user.
+ QLOG_ERROR() << "Server didn't send a client token.";
+ return false;
+ }
+ if (!getMojangAccount()->clientToken().isEmpty() && clientToken != getMojangAccount()->clientToken())
+ {
+ // The server changed our client token! Obey its wishes, but complain. That's what I do for my parents, so...
+ QLOG_WARN() << "Server changed our client token to '" << clientToken
+ << "'. This shouldn't happen, but it isn't really a big deal.";
+ }
+ // Set the client token.
+ getMojangAccount()->setClientToken(clientToken);
+
+
+ // Now, we set the access token.
+ QLOG_DEBUG() << "Getting access token.";
+ QString accessToken = responseData.value("accessToken").toString("");
+ if (accessToken.isEmpty())
+ {
+ // Fail if the server didn't give us an access token.
+ // TODO: Set an error properly to display to the user.
+ QLOG_ERROR() << "Server didn't send an access token.";
+ }
+ // Set the access token.
+ getMojangAccount()->setAccessToken(accessToken);
+
+
+ // Now we load the list of available profiles.
+ // Mojang hasn't yet implemented the profile system,
+ // but we might as well support what's there so we
+ // don't have trouble implementing it later.
+ QLOG_DEBUG() << "Loading profile list.";
+ QJsonArray availableProfiles = responseData.value("availableProfiles").toArray();
+ ProfileList 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("");
+
+ if (id.isEmpty() || name.isEmpty())
+ {
+ // This should never happen, but we might as well
+ // warn about it if it does so we can debug it easily.
+ // You never know when Mojang might do something truly derpy.
+ QLOG_WARN() << "Found entry in available profiles list with missing ID or name field. Ignoring it.";
+ }
+
+ // Now, add a new AccountProfile entry to the list.
+ loadedProfiles.append(AccountProfile(id, name));
+ }
+ // Put the list of profiles we loaded into the MojangAccount object.
+ getMojangAccount()->loadProfiles(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
+ // is actually in the available profiles list.
+ // If it isn't, we'll just fail horribly (*shouldn't* ever happen, but you never know).
+ QLOG_DEBUG() << "Setting current profile.";
+ QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
+ QString currentProfileId = currentProfile.value("id").toString("");
+ if (currentProfileId.isEmpty())
+ {
+ // TODO: Set an error to display to the user.
+ QLOG_ERROR() << "Server didn't specify a currently selected profile.";
+ return false;
+ }
+ if (!getMojangAccount()->setProfile(currentProfileId))
+ {
+ // TODO: Set an error to display to the user.
+ QLOG_ERROR() << "Server specified a selected profile that wasn't in the available profiles list.";
+ return false;
+ }
+
+
+ // We've made it through the minefield of possible errors. Return true to indicate that we've succeeded.
+ QLOG_DEBUG() << "Finished reading authentication response.";
+ return true;
}
QString AuthenticateTask::getEndpoint() const
@@ -69,3 +165,16 @@ QString AuthenticateTask::getEndpoint() const
return "authenticate";
}
+QString AuthenticateTask::getStateMessage(const YggdrasilTask::State state) const
+{
+ switch (state)
+ {
+ case STATE_SENDING_REQUEST:
+ return tr("Authenticating: Sending request.");
+ case STATE_PROCESSING_RESPONSE:
+ return tr("Authenticating: Processing response.");
+ default:
+ return YggdrasilTask::getStateMessage(state);
+ }
+}
+
diff --git a/logic/auth/AuthenticateTask.h b/logic/auth/AuthenticateTask.h
index 8c142535..4169ad5f 100644
--- a/logic/auth/AuthenticateTask.h
+++ b/logic/auth/AuthenticateTask.h
@@ -37,6 +37,8 @@ protected:
virtual QString getEndpoint() const;
virtual bool processResponse(QJsonObject responseData);
+
+ QString getStateMessage(const YggdrasilTask::State state) const;
private:
QString m_password;
diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp
index f796794e..8856047e 100644
--- a/logic/auth/MojangAccount.cpp
+++ b/logic/auth/MojangAccount.cpp
@@ -26,6 +26,8 @@ MojangAccount::MojangAccount(const QString& username, QObject* parent) :
m_clientToken = QUuid::createUuid().toString();
m_username = username;
+
+ m_currentProfile = -1;
}
MojangAccount::MojangAccount(const QString& username, const QString& clientToken,
@@ -35,6 +37,8 @@ MojangAccount::MojangAccount(const QString& username, const QString& clientToken
m_username = username;
m_clientToken = clientToken;
m_accessToken = accessToken;
+
+ m_currentProfile = -1;
}
@@ -48,8 +52,78 @@ 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;
+}
+
+
+const QList<AccountProfile> MojangAccount::profiles() const
+{
+ return m_profiles;
+}
+
+const AccountProfile* MojangAccount::currentProfile() const
+{
+ if (m_currentProfile < 0)
+ 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);
+}
+
+
+AccountProfile::AccountProfile(const QString& id, const QString& name)
+{
+ m_id = id;
+ m_name = name;
+}
+
+AccountProfile::AccountProfile(const AccountProfile& other)
+{
+ m_id = other.m_id;
+ m_name = other.m_name;
+}
+
+QString AccountProfile::id() const
+{
+ return m_id;
+}
+
+QString AccountProfile::name() const
+{
+ return m_name;
+}
+
+
diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h
index d4b8dfb1..c5a26736 100644
--- a/logic/auth/MojangAccount.h
+++ b/logic/auth/MojangAccount.h
@@ -17,6 +17,32 @@
#include <QObject>
#include <QString>
+#include <QList>
+
+
+/**
+ * Class that represents 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
+{
+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;
+};
+
+
+typedef QList<AccountProfile> ProfileList;
+
/**
* Object that stores information about a certain Mojang account.
@@ -52,6 +78,11 @@ public:
QString clientToken() const;
/**
+ * Sets the MojangAccount's client token to the given value.
+ */
+ void setClientToken(const QString& token);
+
+ /**
* This MojangAccount's access token.
* If the user has not chosen to stay logged in, this will be an empty string.
*/
@@ -60,11 +91,36 @@ public:
/**
* Changes this MojangAccount's access token to the given value.
*/
- QString setAccessToken(const QString& token);
+ void setAccessToken(const QString& token);
+
+ /**
+ * Returns a list of the available account profiles.
+ */
+ const ProfileList profiles() const;
+
+ /**
+ * Returns a pointer to the currently selected profile.
+ * If no profile is selected, returns nullptr.
+ */
+ const AccountProfile* currentProfile() const;
+
+ /**
+ * 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);
+
+ /**
+ * Clears the current account profile list and replaces it with the given profile list.
+ */
+ void loadProfiles(const ProfileList& profiles);
+
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.
};
diff --git a/logic/auth/YggdrasilTask.cpp b/logic/auth/YggdrasilTask.cpp
index efc6ec96..a023b07e 100644
--- a/logic/auth/YggdrasilTask.cpp
+++ b/logic/auth/YggdrasilTask.cpp
@@ -20,12 +20,14 @@
#include <QJsonObject>
#include <QJsonDocument>
#include <QNetworkReply>
+#include <QByteArray>
#include <MultiMC.h>
#include <logic/auth/MojangAccount.h>
YggdrasilTask::YggdrasilTask(MojangAccount* account, QObject* parent) : Task(parent)
{
+ m_error = nullptr;
m_account = account;
}
@@ -45,7 +47,7 @@ void YggdrasilTask::executeTask()
auto worker = MMC->qnam();
connect(worker.get(), SIGNAL(finished(QNetworkReply*)), this,
- SLOT(processResponse(QNetworkReply*)));
+ SLOT(processReply(QNetworkReply*)));
QUrl reqUrl("https://authserver.mojang.com/" + getEndpoint());
QNetworkRequest netRequest(reqUrl);
@@ -70,7 +72,8 @@ void YggdrasilTask::processReply(QNetworkReply* reply)
// Try to parse the response regardless of the response code.
// Sometimes the auth server will give more information and an error code.
QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
+ QByteArray replyData = reply->readAll();
+ QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError);
// Check the response code.
int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
@@ -91,6 +94,10 @@ void YggdrasilTask::processReply(QNetworkReply* reply)
else
emitFailed(tr("An unknown error occurred when processing the response from the authentication server."));
}
+ else
+ {
+ emitSucceeded();
+ }
break;
default:
@@ -150,7 +157,7 @@ QString YggdrasilTask::processError(QJsonObject responseData)
}
}
-QString YggdrasilTask::getStateMessage(const YggdrasilTask::State state)
+QString YggdrasilTask::getStateMessage(const YggdrasilTask::State state) const
{
switch (state)
{
diff --git a/logic/auth/YggdrasilTask.h b/logic/auth/YggdrasilTask.h
index 57bbede9..64632d44 100644
--- a/logic/auth/YggdrasilTask.h
+++ b/logic/auth/YggdrasilTask.h
@@ -115,7 +115,7 @@ protected:
* Used to set the status message for the task.
* Should be overridden by subclasses that want to change messages for a given state.
*/
- virtual QString getStateMessage(const State state);
+ virtual QString getStateMessage(const State state) const;
MojangAccount* m_account;