Add abstract base class for Yggdrasil tasks.
This commit is contained in:
		| @@ -270,6 +270,8 @@ logic/net/S3ListBucket.cpp | ||||
| # Yggdrasil login stuff | ||||
| logic/auth/MojangAccount.h | ||||
| logic/auth/MojangAccount.cpp | ||||
| logic/auth/YggdrasilTask.h | ||||
| logic/auth/YggdrasilTask.cpp | ||||
|  | ||||
|  | ||||
| # legacy instances | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| /* Copyright 2013 Andrew Okin | ||||
| /* 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. | ||||
|   | ||||
							
								
								
									
										170
									
								
								logic/auth/YggdrasilTask.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								logic/auth/YggdrasilTask.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,170 @@ | ||||
| /* 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/YggdrasilTask.h> | ||||
|  | ||||
| #include <QObject> | ||||
| #include <QString> | ||||
| #include <QJsonObject> | ||||
| #include <QJsonDocument> | ||||
| #include <QNetworkReply> | ||||
|  | ||||
| #include <MultiMC.h> | ||||
| #include <logic/auth/MojangAccount.h> | ||||
|  | ||||
| YggdrasilTask::YggdrasilTask(MojangAccount* account, QObject* parent) : Task(parent) | ||||
| { | ||||
| 	m_account = account; | ||||
| } | ||||
|  | ||||
|  | ||||
| YggdrasilTask::~YggdrasilTask() | ||||
| { | ||||
| 	if (m_error) | ||||
| 		delete m_error; | ||||
| } | ||||
|  | ||||
| void YggdrasilTask::executeTask() | ||||
| { | ||||
| 	setStatus(getStateMessage(STATE_SENDING_REQUEST)); | ||||
|  | ||||
| 	// Get the content of the request we're going to send to the server. | ||||
| 	QJsonDocument doc(getRequestContent()); | ||||
|  | ||||
| 	auto worker = MMC->qnam(); | ||||
| 	connect(worker.get(), SIGNAL(finished(QNetworkReply*)), this, | ||||
| 			SLOT(processResponse(QNetworkReply*))); | ||||
|  | ||||
| 	QUrl reqUrl("https://authserver.mojang.com/" + getEndpoint()); | ||||
| 	QNetworkRequest netRequest(reqUrl); | ||||
| 	netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); | ||||
| 	 | ||||
| 	m_netReply = worker->post(netRequest, doc.toJson()); | ||||
| } | ||||
|  | ||||
| void YggdrasilTask::processReply(QNetworkReply* reply) | ||||
| { | ||||
| 	setStatus(getStateMessage(STATE_PROCESSING_RESPONSE)); | ||||
|  | ||||
| 	if (m_netReply != reply) | ||||
| 		// Wrong reply for some reason... | ||||
| 		return; | ||||
|  | ||||
| 	// Check for errors. | ||||
| 	switch (reply->error()) | ||||
| 	{ | ||||
| 		case QNetworkReply::NoError: | ||||
| 		{ | ||||
| 			// 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); | ||||
|  | ||||
| 			// Check the response code. | ||||
| 			int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); | ||||
|  | ||||
| 			switch (responseCode) | ||||
| 			{ | ||||
| 				case 200: | ||||
| 				{ | ||||
| 					// If the response code was 200, then there shouldn't be an error. Make sure anyways. | ||||
| 					switch (jsonError.error) | ||||
| 					{ | ||||
| 						case QJsonParseError::NoError: | ||||
| 							if (!processResponse(doc.object())) | ||||
| 							{ | ||||
| 								YggdrasilTask::Error* err = getError(); | ||||
| 								if (err) | ||||
| 									emitFailed(err->getErrorMessage()); | ||||
| 								else | ||||
| 									emitFailed(tr("An unknown error occurred when processing the response from the authentication server.")); | ||||
| 							} | ||||
| 						break; | ||||
|  | ||||
| 						default: | ||||
| 						emitFailed(tr("Failed to parse Yggdrasil JSON response: \"%1\".").arg(jsonError.errorString())); | ||||
| 						break; | ||||
| 					} | ||||
| 					break; | ||||
| 				} | ||||
|  | ||||
| 				default: | ||||
| 					// If the response code was something else, 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. | ||||
| 					switch (jsonError.error) | ||||
| 					{ | ||||
| 						case 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. | ||||
| 							processError(doc.object()); | ||||
| 							break; | ||||
|  | ||||
| 						default: | ||||
| 							// The server didn't say anything regarding the error. Give the user an unknown error. | ||||
| 							emitFailed(tr("Login failed: Unknown HTTP code %1 encountered.").arg(responseCode)); | ||||
| 							break; | ||||
| 					} | ||||
| 					break; | ||||
| 			} | ||||
|  | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		case QNetworkReply::OperationCanceledError: | ||||
| 			emitFailed(tr("Login canceled.")); | ||||
| 			break; | ||||
|  | ||||
| 		default: | ||||
| 			emitFailed(tr("An unknown error occurred when trying to communicate with the authentication server.")); | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| QString YggdrasilTask::processError(QJsonObject responseData) | ||||
| { | ||||
| 	QJsonValue errorVal = responseData.value("error"); | ||||
| 	QJsonValue msgVal = responseData.value("errorMessage"); | ||||
| 	QJsonValue causeVal = responseData.value("cause"); | ||||
|  | ||||
| 	if (errorVal.isString() && msgVal.isString() && causeVal.isString()) | ||||
| 	{ | ||||
| 		m_error = new Error(errorVal.toString(""), msgVal.toString(""), causeVal.toString("")); | ||||
| 		return m_error->getDisplayMessage(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		// Error is not in standard format. Don't set m_error and return unknown error. | ||||
| 		return tr("An unknown Yggdrasil error occurred."); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| QString YggdrasilTask::getStateMessage(const YggdrasilTask::State state) | ||||
| { | ||||
| 	switch (state) | ||||
| 	{ | ||||
| 	case STATE_SENDING_REQUEST: | ||||
| 		return tr("Sending request to auth servers."); | ||||
| 	case STATE_PROCESSING_RESPONSE: | ||||
| 		return tr("Processing response from servers."); | ||||
| 	default: | ||||
| 		return tr("Processing. Please wait."); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| YggdrasilTask::Error *YggdrasilTask::getError() const | ||||
| { | ||||
| 	return this->m_error; | ||||
| } | ||||
|  | ||||
							
								
								
									
										124
									
								
								logic/auth/YggdrasilTask.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								logic/auth/YggdrasilTask.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| /* 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 <logic/tasks/Task.h> | ||||
|  | ||||
| #include <QString> | ||||
| #include <QJsonObject> | ||||
|  | ||||
|  | ||||
| class MojangAccount; | ||||
|  | ||||
| class QNetworkReply; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * A Yggdrasil task is a task that performs an operation on a given mojang account. | ||||
|  */ | ||||
| class YggdrasilTask : public Task | ||||
| { | ||||
| Q_OBJECT | ||||
| public: | ||||
| 	explicit YggdrasilTask(MojangAccount* account, QObject* parent=0); | ||||
| 	~YggdrasilTask(); | ||||
|  | ||||
| protected: | ||||
| 	/** | ||||
| 	 * Class describing a Yggdrasil error response. | ||||
| 	 */ | ||||
| 	class 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_cause; | ||||
| 	}; | ||||
|  | ||||
| 	/** | ||||
| 	 * Enum for describing the state of the current task. | ||||
| 	 * Used by the getStateMessage function to determine what the status message should be. | ||||
| 	 */ | ||||
| 	enum State | ||||
| 	{ | ||||
| 		STATE_SENDING_REQUEST, | ||||
| 		STATE_PROCESSING_RESPONSE, | ||||
| 		STATE_OTHER, | ||||
| 	}; | ||||
|  | ||||
| 	virtual void executeTask(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the JSON object that will be sent to the authentication server. | ||||
| 	 * Should be overridden by subclasses. | ||||
| 	 */ | ||||
| 	virtual QJsonObject getRequestContent() const = 0; | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the endpoint to POST to. | ||||
| 	 * No leading slash. | ||||
| 	 */ | ||||
| 	virtual QString getEndpoint() const = 0; | ||||
|  | ||||
| 	/** | ||||
| 	 * Processes the response received from the server. | ||||
| 	 * If an error occurred, this should emit a failed signal and return false. | ||||
| 	 * If Yggdrasil gave an error response, it should call setError() first, and then return false. | ||||
| 	 * Otherwise, it should return true. | ||||
| 	 */ | ||||
| 	virtual bool processResponse(QJsonObject responseData) = 0; | ||||
|  | ||||
| 	/** | ||||
| 	 * Processes an error response received from the server. | ||||
| 	 * The default implementation will read data from Yggdrasil's standard error response format and set it as this task's Error. | ||||
| 	 * \returns a QString error message that will be passed to emitFailed. | ||||
| 	 */ | ||||
| 	virtual QString processError(QJsonObject responseData); | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns the state message for the given state. | ||||
| 	 * 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); | ||||
|  | ||||
| 	/** | ||||
| 	 * 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; | ||||
|  | ||||
| 	MojangAccount* m_account; | ||||
|  | ||||
| 	QNetworkReply* m_netReply; | ||||
|  | ||||
| 	Error* m_error; | ||||
|  | ||||
| protected slots: | ||||
| 	void processReply(QNetworkReply* reply); | ||||
| }; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user