diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.cpp | 29 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 80 | ||||
| -rw-r--r-- | src/mainwindow.h | 2 | ||||
| -rw-r--r-- | src/modlist.cpp | 11 | ||||
| -rw-r--r-- | src/modlist.h | 1 | ||||
| -rw-r--r-- | src/nexusinterface.cpp | 14 | ||||
| -rw-r--r-- | src/nxmaccessmanager.cpp | 178 | ||||
| -rw-r--r-- | src/nxmaccessmanager.h | 40 | ||||
| -rw-r--r-- | src/organizercore.cpp | 91 | ||||
| -rw-r--r-- | src/organizercore.h | 8 |
10 files changed, 334 insertions, 120 deletions
diff --git a/src/main.cpp b/src/main.cpp index ae3bbb4c..76474fd6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -62,6 +62,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "selectiondialog.h"
#include "moapplication.h"
#include "tutorialmanager.h"
+#include "nxmaccessmanager.h"
#include <iostream>
#include <QMessageBox>
#include <QSharedMemory>
@@ -206,22 +207,28 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *except FuncMiniDumpWriteDump funcDump = (FuncMiniDumpWriteDump)::GetProcAddress(dbgDLL, "MiniDumpWriteDump");
if (funcDump) {
if (QMessageBox::question(nullptr, QObject::tr("Woops"),
- QObject::tr("ModOrganizer has crashed! Should a diagnostic file be created? If you send me this file "
- "(%1) to sherb@gmx.net, the bug is a lot more likely to be fixed. "
- "Please include a short description of what you were doing when the crash happened").arg(qApp->applicationFilePath().append(".dmp")),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+ QObject::tr("ModOrganizer has crashed! "
+ "Should a diagnostic file be created? "
+ "If you send me this file (%1) to sherb@gmx.net, "
+ "the bug is a lot more likely to be fixed. "
+ "Please include a short description of what you were "
+ "doing when the crash happened"
+ ).arg(qApp->applicationFilePath().append(".dmp")),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
std::wstring dumpName = ToWString(qApp->applicationFilePath().append(".dmp"));
HANDLE dumpFile = ::CreateFile(dumpName.c_str(),
- GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+ GENERIC_WRITE, FILE_SHARE_WRITE, nullptr,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (dumpFile != INVALID_HANDLE_VALUE) {
_MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
exceptionInfo.ThreadId = ::GetCurrentThreadId();
exceptionInfo.ExceptionPointers = exceptionPtrs;
exceptionInfo.ClientPointers = false;
- BOOL success = funcDump(::GetCurrentProcess(), ::GetCurrentProcessId(), dumpFile, MiniDumpNormal, &exceptionInfo, nullptr, nullptr);
+ BOOL success = funcDump(::GetCurrentProcess(), ::GetCurrentProcessId(), dumpFile,
+ MiniDumpNormal, &exceptionInfo, nullptr, nullptr);
::FlushFileBuffers(dumpFile);
::CloseHandle(dumpFile);
@@ -336,13 +343,9 @@ int main(int argc, char *argv[]) QString dataPath =
instanceID.isEmpty() ? application.applicationDirPath()
: QDir::fromNativeSeparators(
-#if QT_VERSION >= 0x050000
- QStandardPaths::writableLocation(QStandardPaths::DataLocation)
-#else
- QDesktopServices::storageLocation(QDesktopServices::DataLocation)
-#endif
+ QStandardPaths::writableLocation(QStandardPaths::DataLocation)
+ "/" + instanceID
- );
+ );
application.setProperty("dataPath", dataPath);
if (!QDir(dataPath).exists()) {
@@ -534,6 +537,8 @@ int main(int argc, char *argv[]) }
}
+ NexusInterface::instance()->getAccessManager()->startLoginCheck();
+
qDebug("initializing tutorials");
TutorialManager::init(qApp->applicationDirPath() + "/" + QString::fromStdWString(AppConfig::tutorialsPath()) + "/",
&organizer);
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f0e576fe..abf91d8f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -165,7 +165,7 @@ MainWindow::MainWindow(const QString &exeName , m_ArchiveListWriter(std::bind(&MainWindow::saveArchiveList, this))
{
ui->setupUi(this);
- this->setWindowTitle(ToQString(GameInfo::instance().getGameName()) + " Mod Organizer v" + m_OrganizerCore.getVersion().displayString());
+ updateWindowTitle(QString(), false);
languageChange(m_OrganizerCore.settings().language());
ui->logList->setModel(LogBuffer::instance());
@@ -175,8 +175,10 @@ MainWindow::MainWindow(const QString &exeName ui->logList->addAction(ui->actionCopy_Log_to_Clipboard);
int splitterSize = this->size().height(); // actually total window size, but the splitter doesn't seem to return the true value
ui->topLevelSplitter->setSizes(QList<int>() << splitterSize - 100 << 100);
- connect(ui->logList->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), ui->logList, SLOT(scrollToBottom()));
- connect(ui->logList->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), ui->logList, SLOT(scrollToBottom()));
+ connect(ui->logList->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ ui->logList, SLOT(scrollToBottom()));
+ connect(ui->logList->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ ui->logList, SLOT(scrollToBottom()));
m_RefreshProgress = new QProgressBar(statusBar());
m_RefreshProgress->setTextVisible(true);
@@ -286,6 +288,11 @@ MainWindow::MainWindow(const QString &exeName connect(NexusInterface::instance(), SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(int,int,QVariant,QVariant,int)));
connect(NexusInterface::instance(), SIGNAL(needLogin()), &m_OrganizerCore, SLOT(nexusLogin()));
connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString)));
+ connect(NexusInterface::instance()->getAccessManager(), &NXMAccessManager::credentialsReceived,
+ [this] (const QString &accountName, bool premium) {
+ qDebug("user %s premium", premium ? "is" : "is not");
+ updateWindowTitle(accountName, premium);
+ });
connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString)));
connect(ui->tabWidget, SIGNAL(currentChanged(int)), &TutorialManager::instance(), SIGNAL(tabChanged(int)));
@@ -349,6 +356,22 @@ MainWindow::~MainWindow() }
+void MainWindow::updateWindowTitle(const QString &accountName, bool premium)
+{
+ QString title = QString("%1 Mod Organizer v%2").arg(
+ ToQString(GameInfo::instance().getGameName()),
+ m_OrganizerCore.getVersion().displayString());
+
+ if (accountName.isEmpty()) {
+ title.append(" (not logged in)");
+ } else {
+ title.append(QString(" (%1%2)").arg(accountName, premium ? "*" : ""));
+ }
+
+ this->setWindowTitle(title);
+}
+
+
void MainWindow::disconnectPlugins()
{
if (ui->actionTool->menu() != nullptr) {
@@ -908,7 +931,8 @@ void MainWindow::modPagePluginInvoke() m_IntegratedBrowser.setWindowTitle(plugin->displayName());
m_IntegratedBrowser.openUrl(plugin->pageURL());
} else {
- ::ShellExecuteW(nullptr, L"open", ToWString(plugin->pageURL().toString()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
+ ::ShellExecuteW(nullptr, L"open", ToWString(plugin->pageURL().toString()).c_str(),
+ nullptr, nullptr, SW_SHOWNORMAL);
}
}
}
@@ -2307,8 +2331,9 @@ void MainWindow::unendorse_clicked() }
}
-void MainWindow::loginFailed(const QString&)
+void MainWindow::loginFailed(const QString &error)
{
+ qDebug("login failed: %s", qPrintable(error));
statusBar()->hide();
}
@@ -3426,36 +3451,26 @@ void MainWindow::languageChange(const QString &newLanguage) void MainWindow::writeDataToFile(QFile &file, const QString &directory, const DirectoryEntry &directoryEntry)
{
- { // list files
-// std::set<FileEntry>::const_iterator current, end;
-// directoryEntry.getFiles(current, end);
-// for (; current != end; ++current) {
-
- std::vector<FileEntry::Ptr> files = directoryEntry.getFiles();
- for (auto iter = files.begin(); iter != files.end(); ++iter) {
- FileEntry::Ptr current = *iter;
- bool isArchive = false;
- int origin = current->getOrigin(isArchive);
- if (isArchive) {
- // TODO: don't list files from archives. maybe make this an option?
- continue;
- }
- QString fullName = directory;
- fullName.append("\\").append(ToQString(current->getName()));
- file.write(fullName.toUtf8());
-
- file.write("\t(");
- file.write(ToQString(m_OrganizerCore.directoryStructure()->getOriginByID(origin).getName()).toUtf8());
- file.write(")\r\n");
+ for (FileEntry::Ptr current : directoryEntry.getFiles()) {
+ bool isArchive = false;
+ int origin = current->getOrigin(isArchive);
+ if (isArchive) {
+ // TODO: don't list files from archives. maybe make this an option?
+ continue;
}
+ QString fullName = directory + "\\" + ToQString(current->getName());
+ file.write(fullName.toUtf8());
+
+ file.write("\t(");
+ file.write(ToQString(m_OrganizerCore.directoryStructure()->getOriginByID(origin).getName()).toUtf8());
+ file.write(")\r\n");
}
- { // recurse into subdirectories
- std::vector<DirectoryEntry*>::const_iterator current, end;
- directoryEntry.getSubDirectories(current, end);
- for (; current != end; ++current) {
- writeDataToFile(file, directory.mid(0).append("\\").append(ToQString((*current)->getName())), **current);
- }
+ // recurse into subdirectories
+ std::vector<DirectoryEntry*>::const_iterator current, end;
+ directoryEntry.getSubDirectories(current, end);
+ for (; current != end; ++current) {
+ writeDataToFile(file, directory + "\\" + ToQString((*current)->getName()), **current);
}
}
@@ -4735,3 +4750,4 @@ void MainWindow::dropEvent(QDropEvent *event) }
event->accept();
}
+
diff --git a/src/mainwindow.h b/src/mainwindow.h index d16719e3..55ae40b9 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -121,6 +121,7 @@ public: virtual MOBase::DelayedFileWriterBase &archivesWriter() override { return m_ArchiveListWriter; }
+ void updateWindowTitle(const QString &accountName, bool premium);
public slots:
void displayColumnSelection(const QPoint &pos);
@@ -533,7 +534,6 @@ private slots: // ui slots void on_categoriesOrBtn_toggled(bool checked);
void on_managedArchiveLabel_linkHovered(const QString &link);
void on_manageArchivesBox_toggled(bool checked);
-
};
diff --git a/src/modlist.cpp b/src/modlist.cpp index 59bd8979..fa34004d 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -587,6 +587,12 @@ QStringList ModList::mimeTypes() const return result;
}
+QMimeData *ModList::mimeData(const QModelIndexList &indexes) const
+{
+ QMimeData *result = QAbstractItemModel::mimeData(indexes);
+ result->setData("text/plain", "mod");
+ return result;
+}
void ModList::changeModPriority(std::vector<int> sourceIndices, int newPriority)
{
@@ -824,7 +830,6 @@ bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &pa return true;
}
-
bool ModList::dropMod(const QMimeData *mimeData, int row, const QModelIndex &parent)
{
@@ -877,9 +882,10 @@ bool ModList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int }
if (m_Profile == nullptr) return false;
+
if (mimeData->hasUrls()) {
return dropURLs(mimeData, row, parent);
- } else {
+ } else if (mimeData->hasText()) {
return dropMod(mimeData, row, parent);
}
}
@@ -1141,3 +1147,4 @@ bool ModList::eventFilter(QObject *obj, QEvent *event) }
return QAbstractItemModel::eventFilter(obj, event);
}
+
diff --git a/src/modlist.h b/src/modlist.h index ed52a3ea..358fd583 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -150,6 +150,7 @@ public: // implementation of virtual functions of QAbstractItemModel virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const;
virtual Qt::DropActions supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; }
virtual QStringList mimeTypes() const;
+ virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
virtual bool removeRows(int row, int count, const QModelIndex &parent);
diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 5e29e8ee..dc027be4 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -391,8 +391,7 @@ void NexusInterface::nextRequest() return;
}
- if (!getAccessManager()->loggedIn()
- && requiresLogin(m_RequestQueue.head())) {
+ if (requiresLogin(m_RequestQueue.head()) && !getAccessManager()->loggedIn()) {
if (!getAccessManager()->loginAttempted()) {
emit needLogin();
return;
@@ -438,16 +437,7 @@ void NexusInterface::nextRequest() }
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml");
- QStringList comments;
- comments << "compatible to Nexus Client v" + m_NMMVersion;
- if (!info.m_SubModule.isEmpty()) {
- comments << "module: " + info.m_SubModule;
- }
-
- QString userAgent = QString("Mod Organizer v%1 (%2)")
- .arg(m_MOVersion.displayString())
- .arg(comments.join("; "));
- request.setRawHeader("User-Agent", userAgent.toUtf8());
+ request.setRawHeader("User-Agent", m_AccessManager->userAgent(info.m_SubModule).toUtf8());
info.m_Reply = m_AccessManager->get(request);
diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index c99d2d8d..e6c11695 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -24,6 +24,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "selfupdater.h"
#include "persistentcookiejar.h"
#include "settings.h"
+#include <gameinfo.h>
+#include <json.h>
#include <QMessageBox>
#include <QPushButton>
#include <QNetworkProxy>
@@ -32,24 +34,29 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <QNetworkCookieJar>
#include <QCoreApplication>
#include <QDir>
-#include <gameinfo.h>
-
-#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QUrlQuery>
-#endif
+#include <QThread>
+#include <QJsonDocument>
+#include <QJsonArray>
+
using namespace MOBase;
using namespace MOShared;
+// 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_ProgressDialog()
, m_MOVersion(moVersion)
- , m_LoginAttempted(false)
{
-
+ m_LoginTimeout.setSingleShot(true);
+ m_LoginTimeout.setInterval(30000);
setCookieJar(new PersistentCookieJar(
QDir::fromNativeSeparators(Settings::instance().getCacheDirectory() + "/nexus_cookies.dat")));
}
@@ -88,20 +95,96 @@ QNetworkReply *NXMAccessManager::createRequest( }
-void NXMAccessManager::showCookies()
+void NXMAccessManager::showCookies() const
+{
+ QUrl url(ToQString(GameInfo::instance().getNexusPage()) + "/");
+
+ for (const QNetworkCookie &cookie : cookieJar()->cookiesForUrl(url)) {
+ qDebug("%s - %s (expires: %s)",
+ cookie.name().constData(), cookie.value().constData(),
+ qPrintable(cookie.expirationDate().toString()));
+ }
+}
+
+
+void NXMAccessManager::startLoginCheck()
{
- QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage()) + "/"));
- foreach (QNetworkCookie cookie, cookies) {
- qDebug("%s - %s", cookie.name().constData(), cookie.value().constData());
+ if (hasLoginCookies()) {
+ QNetworkRequest request(ToQString(GameInfo::instance().getNexusPage()) + "/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()
+{
+ QNetworkRequest request(ToQString(GameInfo::instance().getNexusPage())
+ + QString("/Core/Libs/Flamework/Entities/User?GetCredentials&game_id=%1"
+ ).arg(GameInfo::instance().getNexusGameID()));
+ 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", qPrintable(reply->errorString()));
+ reply->deleteLater();
+ });
+}
+
+
bool NXMAccessManager::loggedIn() const
{
- return hasLoginCookies();
+ 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;
@@ -114,19 +197,29 @@ void NXMAccessManager::login(const QString &username, const QString &password) return;
}
- if (hasLoginCookies()) {
+ if (m_LoginState == LOGIN_VALID) {
emit loginSuccessful(false);
return;
}
- m_LoginAttempted = true;
-
m_Username = username;
m_Password = password;
pageLogin();
}
+QString NXMAccessManager::userAgent(const QString &subModule) const
+{
+ QStringList comments;
+ comments << "compatible to Nexus Client v" + m_NMMVersion;
+ if (!subModule.isEmpty()) {
+ comments << "module: " + subModule;
+ }
+
+ return QString("Mod Organizer v%1 (%2)").arg(m_MOVersion, comments.join("; "));
+}
+
+
void NXMAccessManager::pageLogin()
{
QString requestString = (ToQString(GameInfo::instance().getNexusPage()) + "/Sessions/?Login&uri=%1")
@@ -136,20 +229,12 @@ void NXMAccessManager::pageLogin() request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QByteArray postDataQuery;
-#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
QUrlQuery postData;
postData.addQueryItem("username", m_Username);
postData.addQueryItem("password", m_Password);
postDataQuery = postData.query(QUrl::FullyEncoded).toUtf8();
-#else
- QUrl postData;
- postData.addQueryItem("username", m_Username);
- postData.addQueryItem("password", m_Password);
- postDataQuery = postData.encodedQuery();
-#endif
- QString userAgent = QString("Mod Organizer v%1 (compatible to Nexus Client v%2)").arg(m_MOVersion).arg(m_NMMVersion);
- request.setRawHeader("User-Agent", userAgent.toUtf8());
+ request.setRawHeader("User-Agent", userAgent().toUtf8());
m_ProgressDialog.setLabelText(tr("Logging into Nexus"));
QList<QPushButton*> buttons = m_ProgressDialog.findChildren<QPushButton*>();
@@ -159,6 +244,7 @@ void NXMAccessManager::pageLogin() 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)));
}
@@ -166,37 +252,41 @@ void NXMAccessManager::pageLogin() void NXMAccessManager::loginTimeout()
{
- emit loginFailed(tr("timeout"));
m_LoginReply->deleteLater();
m_LoginReply = nullptr;
m_LoginAttempted = false; // this usually means we might have success later
- m_LoginTimeout.stop();
m_Username.clear();
m_Password.clear();
+ m_LoginState = LOGIN_NOT_VALID;
+
+ emit loginFailed(tr("timeout"));
}
void NXMAccessManager::loginError(QNetworkReply::NetworkError)
{
+ qDebug("login error");
m_ProgressDialog.hide();
- m_LoginTimeout.stop();
+ 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;
+ emit loginFailed(m_LoginReply->errorString());
} else {
emit loginFailed(tr("Unknown error"));
}
- m_Username.clear();
- m_Password.clear();
}
bool NXMAccessManager::hasLoginCookies() const
{
bool sidCookie = false;
- QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage()) + "/"));
- foreach (QNetworkCookie cookie, cookies) {
+ QUrl url(ToQString(GameInfo::instance().getNexusPage()) + "/");
+ QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(url);
+ for (const QNetworkCookie &cookie : cookies) {
if (cookie.name() == "sid") {
sidCookie = true;
}
@@ -208,15 +298,35 @@ bool NXMAccessManager::hasLoginCookies() const void NXMAccessManager::loginFinished()
{
m_ProgressDialog.hide();
+
+ 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"));
}
+}
+
- m_LoginTimeout.stop();
+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;
- m_Username.clear();
- m_Password.clear();
}
+
diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h index 9d692bba..2b615cdc 100644 --- a/src/nxmaccessmanager.h +++ b/src/nxmaccessmanager.h @@ -25,6 +25,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <QTimer>
#include <QNetworkReply>
#include <QProgressDialog>
+#include <set>
/**
@@ -43,12 +44,18 @@ public: bool loggedIn() const;
- bool loginAttempted() const { return m_LoginAttempted; }
+ bool loginAttempted() const;
bool loginWaiting() const;
void login(const QString &username, const QString &password);
- void showCookies();
+ void showCookies() const;
+
+ QString userAgent(const QString &subModule = QString()) const;
+
+ void startLoginCheck();
+
+ void refuseLogin();
signals:
@@ -68,11 +75,14 @@ signals: void loginFailed(const QString &message);
- private slots:
+ void credentialsReceived(const QString &userName, bool premium);
+
+private slots:
- void loginFinished();
- void loginError(QNetworkReply::NetworkError errorCode);
- void loginTimeout();
+ void loginChecked();
+ void loginFinished();
+ void loginError(QNetworkReply::NetworkError errorCode);
+ void loginTimeout();
public slots:
@@ -84,10 +94,16 @@ protected: private:
- void pageLogin();
+ void pageLogin();
// void dlLogin();
- bool hasLoginCookies() const;
+ bool hasLoginCookies() const;
+
+ void retrieveCredentials();
+
+private:
+
+ static const std::set<int> s_PremiumAccountStates;
private:
@@ -102,6 +118,14 @@ private: QString m_Password;
bool m_LoginAttempted;
+ enum {
+ LOGIN_NOT_CHECKED,
+ LOGIN_CHECKING,
+ LOGIN_NOT_VALID,
+ LOGIN_ATTEMPT_FAILED,
+ LOGIN_REFUSED,
+ LOGIN_VALID
+ } m_LoginState = LOGIN_NOT_CHECKED;
};
diff --git a/src/organizercore.cpp b/src/organizercore.cpp index a4746ddd..3d996b01 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -373,11 +373,13 @@ void OrganizerCore::setUserInterface(IUserInterface *userInterface, QWidget *wid m_InstallationManager.setParentWidget(widget);
m_Updater.setUserInterface(widget);
- // this currently wouldn't work reliably if the ui isn't initialized yet to display the result
- if (isOnline() && !m_Settings.offlineMode()) {
- m_Updater.testForUpdate();
- } else {
- qDebug("user doesn't seem to be connected to the internet");
+ if (userInterface != nullptr) {
+ // this currently wouldn't work reliably if the ui isn't initialized yet to display the result
+ if (isOnline() && !m_Settings.offlineMode()) {
+ m_Updater.testForUpdate();
+ } else {
+ qDebug("user doesn't seem to be connected to the internet");
+ }
}
}
@@ -421,21 +423,28 @@ Settings &OrganizerCore::settings() return m_Settings;
}
-bool OrganizerCore::nexusLogin()
+bool OrganizerCore::nexusLogin(bool retry)
{
- QString username, password;
-
NXMAccessManager *accessManager = NexusInterface::instance()->getAccessManager();
- if (!accessManager->loginAttempted()
- && !accessManager->loggedIn()
- && (m_Settings.getNexusLogin(username, password)
- || (m_AskForNexusPW
- && queryLogin(username, password)))) {
- accessManager->login(username, password);
- return true;
- } else {
+ if ((accessManager->loginAttempted()
+ || accessManager->loggedIn())
+ && !retry) {
+ // previous attempt, maybe even successful
return false;
+ } else {
+ QString username, password;
+ if ((!retry && m_Settings.getNexusLogin(username, password))
+ || (m_AskForNexusPW && queryLogin(username, password))) {
+ // credentials stored or user entered them manually
+ qDebug("attempt login with username %s", qPrintable(username));
+ accessManager->login(username, password);
+ return true;
+ } else {
+ // no credentials stored and user didn't enter them
+ accessManager->refuseLogin();
+ return false;
+ }
}
}
@@ -1433,9 +1442,15 @@ void OrganizerCore::loginSuccessfulUpdate(bool necessary) void OrganizerCore::loginFailed(const QString &message)
{
+ if (QMessageBox::question(qApp->activeWindow(), tr("Login failed"), tr("Login failed, try again?")) == QMessageBox::Yes) {
+ if (nexusLogin(true)) {
+ return;
+ }
+ }
+
if (!m_PendingDownloads.isEmpty()) {
- MessageDialog::showMessage(tr("login failed: %1. Trying to download anyway").arg(message), qApp->activeWindow());
- foreach (QString url, m_PendingDownloads) {
+ MessageDialog::showMessage(tr("login failed: %1. Download will not be associated with an account").arg(message), qApp->activeWindow());
+ for (QString url : m_PendingDownloads) {
downloadRequestedNXM(url);
}
m_PendingDownloads.clear();
@@ -1555,3 +1570,43 @@ void OrganizerCore::prepareStart() { storeSettings();
}
+std::vector<std::pair<QString, QString>> OrganizerCore::fileMapping()
+{
+ IPluginGame *game = qApp->property("managed_game").value<IPluginGame*>();
+ return fileMapping(game->dataDirectory().absolutePath(),
+ directoryStructure(),
+ directoryStructure());
+}
+
+
+std::vector<std::pair<QString, QString>> OrganizerCore::fileMapping(
+ const QString &dataPath,
+ const DirectoryEntry *base,
+ const DirectoryEntry *directoryEntry)
+{
+ std::vector<std::pair<QString, QString>> result;
+
+ for (FileEntry::Ptr current : directoryEntry->getFiles()) {
+ bool isArchive = false;
+ int origin = current->getOrigin(isArchive);
+ if (isArchive || (origin == 0)) {
+ continue;
+ }
+
+ QString fileName = ToQString(current->getRelativePath());
+ QString source = ToQString(base->getOriginByID(origin).getPath()) + fileName;
+ QString target = QDir::toNativeSeparators(dataPath) + fileName;
+ result.push_back(std::make_pair(source, target));
+ }
+
+ // recurse into subdirectories
+ std::vector<DirectoryEntry*>::const_iterator current, end;
+ directoryEntry->getSubDirectories(current, end);
+ for (; current != end; ++current) {
+ std::vector<std::pair<QString, QString>> subRes = fileMapping(dataPath, base, *current);
+ result.insert(result.end(), subRes.begin(), subRes.end());
+ }
+ return result;
+}
+
+
diff --git a/src/organizercore.h b/src/organizercore.h index d60ce2ab..85f0e0c4 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -157,6 +157,8 @@ public: bool onFinishedRun(const std::function<void (const QString &, unsigned int)> &func);
void refreshModList(bool saveChanges = true);
+ std::vector<std::pair<QString, QString> > fileMapping();
+
public: // IPluginDiagnose interface
virtual std::vector<unsigned int> activeProblems() const;
@@ -182,7 +184,7 @@ public slots: void requestDownload(const QUrl &url, QNetworkReply *reply);
void downloadRequestedNXM(const QString &url);
- bool nexusLogin();
+ bool nexusLogin(bool retry = false);
signals:
@@ -208,6 +210,10 @@ private: bool testForSteam();
+ std::vector<std::pair<QString, QString>> fileMapping(const QString &dataPath,
+ const MOShared::DirectoryEntry *base,
+ const MOShared::DirectoryEntry *directoryEntry);
+
private slots:
void directory_refreshed();
|
