diff options
| author | Silarn <jrim@rimpo.org> | 2018-08-20 18:34:31 -0500 |
|---|---|---|
| committer | Silarn <jrim@rimpo.org> | 2019-02-18 21:26:44 -0600 |
| commit | 9fe2f6126dc7b9396d188b57ccb097f0035f57b7 (patch) | |
| tree | dfb6038767f55238506122682b644ac86415050b /src/nxmaccessmanager.cpp | |
| parent | bd9cc6254c8ded2f726e2b001a4ecf61ac0e5a1d (diff) | |
Initial Nexus API changes:
- Switch to SSO with WebSocket
- Update endpoints (all but version checking)
Diffstat (limited to 'src/nxmaccessmanager.cpp')
| -rw-r--r-- | src/nxmaccessmanager.cpp | 615 |
1 files changed, 264 insertions, 351 deletions
diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 912eab30..dc1eb2cb 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -1,351 +1,264 @@ -/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "nxmaccessmanager.h"
-
-#include "iplugingame.h"
-#include "nxmurl.h"
-#include "report.h"
-#include "utility.h"
-#include "selfupdater.h"
-#include "persistentcookiejar.h"
-#include "settings.h"
-#include <QMessageBox>
-#include <QPushButton>
-#include <QNetworkProxy>
-#include <QNetworkRequest>
-#include <QNetworkCookie>
-#include <QNetworkCookieJar>
-#include <QCoreApplication>
-#include <QDir>
-#include <QUrlQuery>
-#include <QThread>
-#include <QJsonDocument>
-#include <QJsonArray>
-
-using namespace MOBase;
-
-namespace {
- QString const Nexus_Management_URL("https://legacy-api.nexusmods.com");
-}
-
-// unfortunately Nexus doesn't seem to document these states, all I know is all these listed
-// are considered premium (27 should be lifetime premium)
-const std::set<int> NXMAccessManager::s_PremiumAccountStates { 4, 6, 13, 27, 31, 32 };
-
-
-NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion)
- : QNetworkAccessManager(parent)
- , m_LoginReply(nullptr)
- , m_MOVersion(moVersion)
-{
- m_LoginTimeout.setSingleShot(true);
- m_LoginTimeout.setInterval(30000);
- setCookieJar(new PersistentCookieJar(
- QDir::fromNativeSeparators(Settings::instance().getCacheDirectory() + "/nexus_cookies.dat")));
-
- if (networkAccessible() == QNetworkAccessManager::UnknownAccessibility) {
- // why is this necessary all of a sudden?
- setNetworkAccessible(QNetworkAccessManager::Accessible);
- }
-}
-
-NXMAccessManager::~NXMAccessManager()
-{
- if (m_LoginReply != nullptr) {
- m_LoginReply->deleteLater();
- m_LoginReply = nullptr;
- }
-}
-
-void NXMAccessManager::setNMMVersion(const QString &nmmVersion)
-{
- m_NMMVersion = nmmVersion;
-}
-
-QNetworkReply *NXMAccessManager::createRequest(
- QNetworkAccessManager::Operation operation, const QNetworkRequest &request,
- QIODevice *device)
-{
- if (request.url().scheme() != "nxm") {
- return QNetworkAccessManager::createRequest(operation, request, device);
- }
- if (operation == GetOperation) {
- emit requestNXMDownload(request.url().toString());
-
- // eat the request, everything else will be done by the download manager
- return QNetworkAccessManager::createRequest(QNetworkAccessManager::GetOperation,
- QNetworkRequest(QUrl()));
- } else if (operation == PostOperation) {
- return QNetworkAccessManager::createRequest(operation, request, device);;
- } else {
- return QNetworkAccessManager::createRequest(operation, request, device);
- }
-}
-
-
-void NXMAccessManager::showCookies() const
-{
- QUrl url(Nexus_Management_URL + "/");
- for (const QNetworkCookie &cookie : cookieJar()->cookiesForUrl(url)) {
- qDebug("%s - %s (expires: %s)",
- cookie.name().constData(), cookie.value().constData(),
- qUtf8Printable(cookie.expirationDate().toString()));
- }
-}
-
-void NXMAccessManager::clearCookies()
-{
- PersistentCookieJar *jar = qobject_cast<PersistentCookieJar*>(cookieJar());
- if (jar != nullptr) {
- jar->clear();
- } else {
- qWarning("failed to clear cookies, invalid cookie jar");
- }
-}
-
-void NXMAccessManager::startLoginCheck()
-{
- if (hasLoginCookies()) {
- qDebug("validating login cookies");
- QNetworkRequest request(Nexus_Management_URL + "/Sessions/?Validate");
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
- request.setRawHeader("User-Agent", userAgent().toUtf8());
-
- m_LoginReply = get(request);
- m_LoginTimeout.start();
- m_LoginState = LOGIN_CHECKING;
- connect(m_LoginReply, SIGNAL(finished()), this, SLOT(loginChecked()));
- connect(m_LoginReply, SIGNAL(error(QNetworkReply::NetworkError)),
- this, SLOT(loginError(QNetworkReply::NetworkError)));
- }
-}
-
-
-void NXMAccessManager::retrieveCredentials()
-{
- qDebug("retrieving credentials");
-
- QNetworkRequest request(Nexus_Management_URL + "/Core/Libs/Flamework/Entities/User?GetCredentials");
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
- request.setRawHeader("User-Agent", userAgent().toUtf8());
-
- QNetworkReply *reply = get(request);
- QTimer timeout;
- connect(&timeout, &QTimer::timeout, [reply] () {
- reply->deleteLater();
- });
- timeout.start();
-
- connect(reply, &QNetworkReply::finished, [reply, this] () {
- QJsonDocument jdoc = QJsonDocument::fromJson(reply->readAll());
- QJsonArray credentialsData = jdoc.array();
- emit credentialsReceived(credentialsData.at(2).toString(),
- s_PremiumAccountStates.find(credentialsData.at(1).toInt())
- != s_PremiumAccountStates.end());
- reply->deleteLater();
- });
-
- connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
- [=] (QNetworkReply::NetworkError) {
- qDebug("failed to retrieve account credentials: %s", qUtf8Printable(reply->errorString()));
- reply->deleteLater();
- });
-}
-
-
-bool NXMAccessManager::loggedIn() const
-{
- if (m_LoginState == LOGIN_CHECKING) {
- QProgressDialog progress;
- progress.setLabelText(tr("Verifying Nexus login"));
- progress.show();
- while (m_LoginState == LOGIN_CHECKING) {
- QCoreApplication::processEvents();
- QThread::msleep(100);
- }
- progress.hide();
- }
-
- return m_LoginState == LOGIN_VALID;
-}
-
-
-void NXMAccessManager::refuseLogin()
-{
- m_LoginState = LOGIN_REFUSED;
-}
-
-
-bool NXMAccessManager::loginAttempted() const
-{
- return m_LoginState != LOGIN_NOT_CHECKED;
-}
-
-
-bool NXMAccessManager::loginWaiting() const
-{
- return m_LoginReply != nullptr;
-}
-
-
-void NXMAccessManager::login(const QString &username, const QString &password)
-{
- if (m_LoginReply != nullptr) {
- return;
- }
-
- if (m_LoginState == LOGIN_VALID) {
- emit loginSuccessful(false);
- return;
- }
- m_Username = username;
- m_Password = password;
- pageLogin();
-}
-
-
-QString NXMAccessManager::userAgent(const QString &subModule) const
-{
- QStringList comments;
- comments << "Nexus Client v" + m_NMMVersion;
- if (!subModule.isEmpty()) {
- comments << "module: " + subModule;
- }
-
- return QString("Mod Organizer/%1 (%2)").arg(m_MOVersion, comments.join("; "));
-}
-
-
-void NXMAccessManager::pageLogin()
-{
- qDebug("logging %s in on Nexus", qUtf8Printable(m_Username));
- QString requestString = (Nexus_Management_URL + "/Sessions/?Login&uri=%1")
- .arg(QString(QUrl::toPercentEncoding(Nexus_Management_URL)));
-
- QNetworkRequest request(requestString);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
-
- QByteArray postDataQuery;
- QUrlQuery postData;
- postData.addQueryItem("username", m_Username);
- postData.addQueryItem("password", QUrl::toPercentEncoding(m_Password));
- postDataQuery = postData.query(QUrl::EncodeReserved).toUtf8();
-
- request.setRawHeader("User-Agent", userAgent().toUtf8());
-
- m_ProgressDialog = new QProgressDialog(nullptr);
- m_ProgressDialog->setLabelText(tr("Logging into Nexus"));
- QList<QPushButton*> buttons = m_ProgressDialog->findChildren<QPushButton*>();
- buttons.at(0)->setEnabled(false);
- m_ProgressDialog->show();
- QCoreApplication::processEvents(); // for some reason the whole app hangs during the login. This way the user has at least a little feedback
-
- m_LoginReply = post(request, postDataQuery);
- m_LoginTimeout.start();
- m_LoginState = LOGIN_CHECKING;
- connect(m_LoginReply, SIGNAL(finished()), this, SLOT(loginFinished()));
- connect(m_LoginReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(loginError(QNetworkReply::NetworkError)));
-}
-
-
-void NXMAccessManager::loginTimeout()
-{
- m_LoginReply->deleteLater();
- m_LoginReply = nullptr;
- m_LoginAttempted = false; // this usually means we might have success later
- m_Username.clear();
- m_Password.clear();
- m_LoginState = LOGIN_NOT_VALID;
-
- emit loginFailed(tr("timeout"));
-}
-
-
-void NXMAccessManager::loginError(QNetworkReply::NetworkError)
-{
- qDebug("login error");
- if (m_ProgressDialog != nullptr) {
- m_ProgressDialog->deleteLater();
- m_ProgressDialog = nullptr;
- }
- m_Username.clear();
- m_Password.clear();
- m_LoginState = LOGIN_NOT_VALID;
-
- if (m_LoginReply != nullptr) {
- emit loginFailed(m_LoginReply->errorString());
- m_LoginReply->deleteLater();
- m_LoginReply = nullptr;
- } else {
- emit loginFailed(tr("Unknown error"));
- }
-}
-
-
-bool NXMAccessManager::hasLoginCookies() const
-{
- QUrl url(Nexus_Management_URL + "/");
- QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(url);
- for (const QNetworkCookie &cookie : cookies) {
- if (cookie.name() == "sid") {
- return true;
- }
- }
- return false;
-}
-
-
-void NXMAccessManager::loginFinished()
-{
- if (m_ProgressDialog != nullptr) {
- m_ProgressDialog->deleteLater();
- m_ProgressDialog = nullptr;
- }
-
- m_LoginReply->deleteLater();
- m_LoginReply = nullptr;
- m_Username.clear();
- m_Password.clear();
-
- if (hasLoginCookies()) {
- m_LoginState = LOGIN_VALID;
- retrieveCredentials();
- emit loginSuccessful(true);
- } else {
- m_LoginState = LOGIN_NOT_VALID;
- emit loginFailed(tr("Please check your password"));
- }
-}
-
-
-void NXMAccessManager::loginChecked()
-{
- QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
- QByteArray data = reply->readAll();
- m_LoginState = data == "null" ? LOGIN_NOT_VALID
- : LOGIN_VALID;
- if (m_LoginState == LOGIN_VALID) {
- retrieveCredentials();
- } else {
- qDebug("cookies seem to be invalid");
- }
- m_LoginReply->deleteLater();
- m_LoginReply = nullptr;
-}
+/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "nxmaccessmanager.h" + +#include "iplugingame.h" +#include "nxmurl.h" +#include "report.h" +#include "utility.h" +#include "selfupdater.h" +#include "persistentcookiejar.h" +#include "settings.h" +#include <QMessageBox> +#include <QPushButton> +#include <QNetworkProxy> +#include <QNetworkRequest> +#include <QNetworkCookie> +#include <QNetworkCookieJar> +#include <QCoreApplication> +#include <QDir> +#include <QUrlQuery> +#include <QThread> +#include <QJsonDocument> +#include <QJsonArray> + +using namespace MOBase; + +namespace { + QString const nexusBaseUrl("https://api.nexusmods.com/v1"); +} + +NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) + : QNetworkAccessManager(parent) + , m_ValidateReply(nullptr) + , m_MOVersion(moVersion) +{ + m_ValidateTimeout.setSingleShot(true); + m_ValidateTimeout.setInterval(30000); + setCookieJar(new PersistentCookieJar( + QDir::fromNativeSeparators(Settings::instance().getCacheDirectory() + "/nexus_cookies.dat"))); + + if (networkAccessible() == QNetworkAccessManager::UnknownAccessibility) { + // why is this necessary all of a sudden? + setNetworkAccessible(QNetworkAccessManager::Accessible); + } +} + +NXMAccessManager::~NXMAccessManager() +{ + if (m_ValidateReply != nullptr) { + m_ValidateReply->deleteLater(); + m_ValidateReply = nullptr; + } +} + +void NXMAccessManager::setNMMVersion(const QString &nmmVersion) +{ + m_NMMVersion = nmmVersion; +} + +QNetworkReply *NXMAccessManager::createRequest( + QNetworkAccessManager::Operation operation, const QNetworkRequest &request, + QIODevice *device) +{ + if (request.url().scheme() != "nxm") { + return QNetworkAccessManager::createRequest(operation, request, device); + } + if (operation == GetOperation) { + emit requestNXMDownload(request.url().toString()); + + // eat the request, everything else will be done by the download manager + return QNetworkAccessManager::createRequest(QNetworkAccessManager::GetOperation, + QNetworkRequest(QUrl())); + } else if (operation == PostOperation) { + return QNetworkAccessManager::createRequest(operation, request, device);; + } else { + return QNetworkAccessManager::createRequest(operation, request, device); + } +} + + +void NXMAccessManager::showCookies() const +{ + QUrl url(nexusBaseUrl + "/"); + for (const QNetworkCookie &cookie : cookieJar()->cookiesForUrl(url)) { + qDebug("%s - %s (expires: %s)", + cookie.name().constData(), cookie.value().constData(), + qUtf8Printable(cookie.expirationDate().toString())); + } +} + +void NXMAccessManager::clearCookies() +{ + PersistentCookieJar *jar = qobject_cast<PersistentCookieJar*>(cookieJar()); + if (jar != nullptr) { + jar->clear(); + } else { + qWarning("failed to clear cookies, invalid cookie jar"); + } +} + +void NXMAccessManager::startValidationCheck() +{ + qDebug("Checking Nexus API Key..."); + QString requestString = nexusBaseUrl + "/users/validate"; + + QNetworkRequest request(requestString); + request.setRawHeader("APIKEY", m_ApiKey.toUtf8()); + request.setRawHeader("User-Agent", userAgent().toUtf8()); + + m_ProgressDialog = new QProgressDialog(nullptr); + m_ProgressDialog->setLabelText(tr("Validating Nexus Connection")); + QList<QPushButton*> buttons = m_ProgressDialog->findChildren<QPushButton*>(); + buttons.at(0)->setEnabled(false); + m_ProgressDialog->show(); + QCoreApplication::processEvents(); // for some reason the whole app hangs during the login. This way the user has at least a little feedback + + m_ValidateReply = get(request); + m_ValidateTimeout.start(); + m_ValidateState = VALIDATE_CHECKING; + connect(m_ValidateReply, SIGNAL(finished()), this, SLOT(validateFinished())); + connect(m_ValidateReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(validateError(QNetworkReply::NetworkError))); +} + + +bool NXMAccessManager::validated() const +{ + if (m_ValidateState == VALIDATE_CHECKING) { + QProgressDialog progress; + progress.setLabelText(tr("Verifying Nexus login")); + progress.show(); + while (m_ValidateState == VALIDATE_CHECKING) { + QCoreApplication::processEvents(); + QThread::msleep(100); + } + progress.hide(); + } + + return m_ValidateState == VALIDATE_VALID; +} + + +void NXMAccessManager::refuseValidation() +{ + m_ValidateState = VALIDATE_REFUSED; +} + + +bool NXMAccessManager::validateAttempted() const +{ + return m_ValidateState != VALIDATE_NOT_CHECKED; +} + + +bool NXMAccessManager::validateWaiting() const +{ + return m_ValidateReply != nullptr; +} + + +void NXMAccessManager::apiCheck(const QString &apiKey) +{ + if (m_ValidateReply != nullptr) { + return; + } + + if (m_ValidateState == VALIDATE_VALID) { + emit validateSuccessful(false); + return; + } + m_ApiKey = apiKey; + startValidationCheck(); +} + + +QString NXMAccessManager::userAgent(const QString &subModule) const +{ + QStringList comments; + comments << "Nexus Client v" + m_NMMVersion; + if (!subModule.isEmpty()) { + comments << "module: " + subModule; + } + + return QString("Mod Organizer/%1 (%2)").arg(m_MOVersion, comments.join("; ")); +} + + +QString NXMAccessManager::apiKey() const +{ + return m_ApiKey; +} + + +void NXMAccessManager::validateTimeout() +{ + m_ValidateReply->deleteLater(); + m_ValidateReply = nullptr; + m_ValidateAttempted = false; // this usually means we might have success later + m_ApiKey.clear(); + m_ValidateState = VALIDATE_NOT_VALID; + + emit validateFailed(tr("timeout")); +} + + +void NXMAccessManager::validateError(QNetworkReply::NetworkError) +{ + qDebug("login error"); + if (m_ProgressDialog != nullptr) { + m_ProgressDialog->deleteLater(); + m_ProgressDialog = nullptr; + } + m_ApiKey.clear(); + m_ValidateState = VALIDATE_NOT_VALID; + + if (m_ValidateReply != nullptr) { + emit validateFailed(m_ValidateReply->errorString()); + m_ValidateReply->deleteLater(); + m_ValidateReply = nullptr; + } else { + emit validateFailed(tr("Unknown error")); + } +} + + +void NXMAccessManager::validateFinished() +{ + if (m_ProgressDialog != nullptr) { + m_ProgressDialog->deleteLater(); + m_ProgressDialog = nullptr; + } + + if (m_ValidateReply != nullptr) { + QJsonDocument jdoc = QJsonDocument::fromJson(m_ValidateReply->readAll()); + QJsonObject credentialsData = jdoc.object(); + QString test = jdoc.toJson(); + QString name = credentialsData.value("name").toString(); + bool premium = credentialsData.value("is_premium?").toBool(); + emit credentialsReceived(credentialsData.value("name").toString(), + credentialsData.value("is_premium?").toBool()); + + m_ValidateReply->deleteLater(); + m_ValidateReply = nullptr; + + m_ValidateState = VALIDATE_VALID; + emit validateSuccessful(true); + } +} |
