diff options
Diffstat (limited to 'logic/auth')
-rw-r--r-- | logic/auth/MojangAccount.cpp | 22 | ||||
-rw-r--r-- | logic/auth/MojangAccount.h | 3 | ||||
-rw-r--r-- | logic/auth/YggdrasilTask.cpp | 166 | ||||
-rw-r--r-- | logic/auth/YggdrasilTask.h | 55 | ||||
-rw-r--r-- | logic/auth/flows/InvalidateTask.cpp | 0 | ||||
-rw-r--r-- | logic/auth/flows/InvalidateTask.h | 0 | ||||
-rw-r--r-- | logic/auth/flows/RefreshTask.cpp | 4 | ||||
-rw-r--r-- | logic/auth/flows/ValidateTask.h | 4 |
8 files changed, 130 insertions, 124 deletions
diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp index 9ab71077..b1acfb25 100644 --- a/logic/auth/MojangAccount.cpp +++ b/logic/auth/MojangAccount.cpp @@ -134,22 +134,21 @@ AccountStatus MojangAccount::accountStatus() const return Online; } -bool MojangAccount::login(QString password) +std::shared_ptr<Task> MojangAccount::login(QString password) { if(m_currentTask) - return false; + return m_currentTask; if(password.isEmpty()) { - m_currentTask.reset(new RefreshTask(this, this)); + m_currentTask.reset(new RefreshTask(this)); } else { - m_currentTask.reset(new AuthenticateTask(this, password, this)); + 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))); - m_currentTask->start(); - return true; + return m_currentTask; } void MojangAccount::authSucceeded() @@ -161,8 +160,13 @@ void MojangAccount::authSucceeded() void MojangAccount::authFailed(QString reason) { - m_online = false; - m_accessToken = QString(); + // 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(); - emit changed(); } diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h index 751aafe5..8c44ce58 100644 --- a/logic/auth/MojangAccount.h +++ b/logic/auth/MojangAccount.h @@ -23,6 +23,7 @@ #include <memory> +class Task; class YggdrasilTask; class MojangAccount; @@ -94,7 +95,7 @@ public: /* manipulation */ * Attempt to login. Empty password means we use the token. * If the attempt fails because we already are performing some task, it returns false. */ - bool login(QString password = QString()); + std::shared_ptr<Task> login(QString password = QString()); public: /* queries */ const QString &username() const diff --git a/logic/auth/YggdrasilTask.cpp b/logic/auth/YggdrasilTask.cpp index e06ae182..6b938ea7 100644 --- a/logic/auth/YggdrasilTask.cpp +++ b/logic/auth/YggdrasilTask.cpp @@ -30,12 +30,6 @@ YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent) { } -YggdrasilTask::~YggdrasilTask() -{ - if (m_error) - delete m_error; -} - void YggdrasilTask::executeTask() { setStatus(getStateMessage(STATE_SENDING_REQUEST)); @@ -44,107 +38,124 @@ 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()); 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(); +} + +void YggdrasilTask::processReply() +{ + setStatus(getStateMessage(STATE_PROCESSING_RESPONSE)); - if (reply->error() == QNetworkReply::OperationCanceledError) + if (m_netReply->error() == QNetworkReply::OperationCanceledError) { + // 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())) { - 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 - { - 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 { @@ -165,8 +176,3 @@ QString YggdrasilTask::getStateMessage(const YggdrasilTask::State state) const return tr("Processing. Please wait."); } } - -YggdrasilTask::Error *YggdrasilTask::getError() const -{ - return this->m_error; -} diff --git a/logic/auth/YggdrasilTask.h b/logic/auth/YggdrasilTask.h index 18d3dc61..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" @@ -32,39 +33,17 @@ class YggdrasilTask : public Task Q_OBJECT public: explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0); - ~YggdrasilTask(); /** * 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; }; - /** - * 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. @@ -115,13 +94,25 @@ protected: */ virtual QString getStateMessage(const State state) const; - MojangAccount *m_account = nullptr; - - QNetworkReply *m_netReply; - - Error *m_error = nullptr; - 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/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 39fb493f..bd38eb10 100644 --- a/logic/auth/flows/RefreshTask.cpp +++ b/logic/auth/flows/RefreshTask.cpp @@ -143,9 +143,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/ValidateTask.h b/logic/auth/flows/ValidateTask.h index 9788f20b..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> |