summaryrefslogtreecommitdiff
path: root/src/nexusinterface.cpp
diff options
context:
space:
mode:
authorTannin <devnull@localhost>2014-07-17 22:02:15 +0200
committerTannin <devnull@localhost>2014-07-17 22:02:15 +0200
commitcc9827cdda5776a4fd540a2ab0fb8a08ec9f75a7 (patch)
tree570a4a3bea9457d82de2cf44aa440e4a4cec4271 /src/nexusinterface.cpp
parent4e5a67fc063243c4c10e04b5f601108dd01357f3 (diff)
normalized eol style (all files should now have windows line endings)
Diffstat (limited to 'src/nexusinterface.cpp')
-rw-r--r--src/nexusinterface.cpp1244
1 files changed, 622 insertions, 622 deletions
diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp
index 48426834..c7e2df9e 100644
--- a/src/nexusinterface.cpp
+++ b/src/nexusinterface.cpp
@@ -1,622 +1,622 @@
-/*
-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 "nexusinterface.h"
-#include "nxmaccessmanager.h"
-#include "utility.h"
-#include <json.h>
-#include "selectiondialog.h"
-#include <QApplication>
-#include <utility.h>
-#include <regex>
-#include <util.h>
-
-
-using namespace MOBase;
-using namespace MOShared;
-
-
-NexusBridge::NexusBridge(const QString &subModule)
- : m_Interface(NexusInterface::instance())
- , m_Url(MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()))
- , m_SubModule(subModule)
-{
-}
-
-
-void NexusBridge::requestDescription(int modID, QVariant userData)
-{
- m_RequestIDs.insert(m_Interface->requestDescription(modID, this, userData, m_SubModule, m_Url));
-}
-
-void NexusBridge::requestFiles(int modID, QVariant userData)
-{
- m_RequestIDs.insert(m_Interface->requestFiles(modID, this, userData, m_SubModule, m_Url));
-}
-
-void NexusBridge::requestFileInfo(int modID, int fileID, QVariant userData)
-{
- m_RequestIDs.insert(m_Interface->requestFileInfo(modID, fileID, this, userData, m_SubModule, m_Url));
-}
-
-void NexusBridge::requestDownloadURL(int modID, int fileID, QVariant userData)
-{
- m_RequestIDs.insert(m_Interface->requestDownloadURL(modID, fileID, this, userData, m_SubModule, m_Url));
-}
-
-void NexusBridge::requestToggleEndorsement(int modID, bool endorse, QVariant userData)
-{
- m_RequestIDs.insert(m_Interface->requestToggleEndorsement(modID, endorse, this, userData, m_SubModule, m_Url));
-}
-
-void NexusBridge::nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID)
-{
- std::set<int>::iterator iter = m_RequestIDs.find(requestID);
- if (iter != m_RequestIDs.end()) {
- m_RequestIDs.erase(iter);
-
- emit descriptionAvailable(modID, userData, resultData);
- }
-}
-
-void NexusBridge::nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID)
-{
- std::set<int>::iterator iter = m_RequestIDs.find(requestID);
- if (iter != m_RequestIDs.end()) {
- m_RequestIDs.erase(iter);
-
- QList<ModRepositoryFileInfo> fileInfoList;
-
- QVariantList resultList = resultData.toList();
-
- foreach(QVariant file, resultList) {
- ModRepositoryFileInfo temp;
- QVariantMap fileInfo = file.toMap();
- temp.uri = fileInfo["uri"].toString();
- temp.name = fileInfo["name"].toString();
- temp.description = fileInfo["description"].toString();
- temp.version = VersionInfo(fileInfo["version"].toString());
- temp.categoryID = fileInfo["category_id"].toInt();
- temp.fileID = fileInfo["id"].toInt();
- temp.fileSize = fileInfo["size"].toInt();
- fileInfoList.append(temp);
- }
-
- emit filesAvailable(modID, userData, fileInfoList);
- }
-}
-
-void NexusBridge::nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID)
-{
- std::set<int>::iterator iter = m_RequestIDs.find(requestID);
- if (iter != m_RequestIDs.end()) {
- m_RequestIDs.erase(iter);
- emit fileInfoAvailable(modID, fileID, userData, resultData);
- }
-}
-
-void NexusBridge::nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID)
-{
- std::set<int>::iterator iter = m_RequestIDs.find(requestID);
- if (iter != m_RequestIDs.end()) {
- m_RequestIDs.erase(iter);
- emit downloadURLsAvailable(modID, fileID, userData, resultData);
- }
-}
-
-void NexusBridge::nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID)
-{
- std::set<int>::iterator iter = m_RequestIDs.find(requestID);
- if (iter != m_RequestIDs.end()) {
- m_RequestIDs.erase(iter);
- emit endorsementToggled(modID, userData, resultData);
- }
-}
-
-void NexusBridge::nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage)
-{
- std::set<int>::iterator iter = m_RequestIDs.find(requestID);
- if (iter != m_RequestIDs.end()) {
- m_RequestIDs.erase(iter);
- emit requestFailed(modID, fileID, userData, errorMessage);
- }
-}
-
-
-QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0);
-
-
-NexusInterface::NexusInterface()
- : m_NMMVersion()
-{
- atexit(&cleanup);
- m_AccessManager = new NXMAccessManager(this);
-
- m_DiskCache = new QNetworkDiskCache(this);
- connect(m_AccessManager, SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString)));
-
-
- VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath()));
-
- m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16,
- version.dwFileVersionMS & 0xFFFF,
- version.dwFileVersionLS >> 16);
-}
-
-
-void NexusInterface::cleanup()
-{
-}
-
-
-NXMAccessManager *NexusInterface::getAccessManager()
-{
- return m_AccessManager;
-}
-
-
-NexusInterface *NexusInterface::instance()
-{
- static NexusInterface s_Instance;
- return &s_Instance;
-}
-
-
-void NexusInterface::setCacheDirectory(const QString &directory)
-{
- m_DiskCache->setCacheDirectory(directory);
- m_AccessManager->setCache(m_DiskCache);
-}
-
-
-void NexusInterface::setNMMVersion(const QString &nmmVersion)
-{
- m_NMMVersion = nmmVersion;
-}
-
-void NexusInterface::loginCompleted()
-{
- nextRequest();
-}
-
-
-void NexusInterface::interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query)
-{
- static std::tr1::regex exp("^([a-zA-Z0-9_\\- ]*?)([-_ ][VvRr]?[0-9_]+)?-([1-9][0-9]+).*");
- static std::tr1::regex simpleexp("^([a-zA-Z0-9_]+)");
-
- QByteArray fileNameUTF8 = fileName.toUtf8();
- std::tr1::cmatch result;
- if (std::tr1::regex_search(fileNameUTF8.constData(), result, exp)) {
- modName = QString::fromUtf8(result[1].str().c_str());
- modName = modName.replace('_', ' ').trimmed();
-
- std::string candidate = result[3].str();
- std::string candidate2 = result[2].str();
- if (candidate2.length() != 0 && (candidate2.find_last_of("VvRr") == std::string::npos)) {
- // well, that second match might be an id too...
- unsigned offset = strspn(candidate2.c_str(), "-_ ");
- if (offset < candidate2.length() && query) {
- SelectionDialog selection(tr("Failed to guess mod id for \"%1\", please pick the correct one").arg(fileName));
- QString r2Highlight(fileName);
- r2Highlight.insert(result.position(2) + result.length(2), "* ").insert(result.position(2) + offset, " *");
- QString r3Highlight(fileName);
- r3Highlight.insert(result.position(3) + result.length(3), "* ").insert(result.position(3), " *");
-
- selection.addChoice(candidate.c_str(), r3Highlight, strtol(candidate.c_str(), NULL, 10));
- selection.addChoice(candidate2.c_str() + offset, r2Highlight, abs(strtol(candidate2.c_str() + offset, NULL, 10)));
- if (selection.exec() == QDialog::Accepted) {
- modID = selection.getChoiceData().toInt();
- } else {
- modID = -1;
- }
- } else {
- modID = -1;
- }
- } else {
- modID = strtol(candidate.c_str(), NULL, 10);
- }
- qDebug("mod id guessed: %s -> %d", qPrintable(fileName), modID);
- } else if (std::tr1::regex_search(fileNameUTF8.constData(), result, simpleexp)) {
- qDebug("simple expression matched, using name only");
- modName = QString::fromUtf8(result[1].str().c_str());
- modName = modName.replace('_', ' ').trimmed();
-
- modID = -1;
- } else {
- qDebug("no expression matched!");
- modName.clear();
- modID = -1;
- }
-}
-
-
-int NexusInterface::requestDescription(int modID, QObject *receiver, QVariant userData,
- const QString &subModule, const QString &url, int nexusGameId)
-{
- NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_DESCRIPTION, userData, subModule, url,
- nexusGameId == -1 ? GameInfo::instance().getNexusGameID() : nexusGameId);
- m_RequestQueue.enqueue(requestInfo);
-
- connect(this, SIGNAL(nxmDescriptionAvailable(int,QVariant,QVariant,int)),
- receiver, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant,int)), Qt::UniqueConnection);
-
- connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
- receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
-
- nextRequest();
- return requestInfo.m_ID;
-}
-
-
-int NexusInterface::requestUpdates(const std::vector<int> &modIDs, QObject *receiver, QVariant userData,
- const QString &subModule, const QString &url)
-{
- NXMRequestInfo requestInfo(modIDs, NXMRequestInfo::TYPE_GETUPDATES, userData, subModule, url, GameInfo::instance().getNexusGameID());
- m_RequestQueue.enqueue(requestInfo);
-
- connect(this, SIGNAL(nxmUpdatesAvailable(std::vector<int>,QVariant,QVariant,int)),
- receiver, SLOT(nxmUpdatesAvailable(std::vector<int>,QVariant,QVariant,int)), Qt::UniqueConnection);
-
- connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
- receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
-
- nextRequest();
- return requestInfo.m_ID;
-}
-
-
-void NexusInterface::fakeFiles()
-{
- static int id = 42;
-
- QVariantList result;
- QVariantMap fileMap;
- fileMap["uri"] = "fakeURI";
- fileMap["name"] = "fakeName";
- fileMap["description"] = "fakeDescription";
- fileMap["version"] = "1.0.0";
- fileMap["category_id"] = "1";
- fileMap["id"] = "1";
- fileMap["size"] = "512";
- result.append(fileMap);
-
- emit nxmFilesAvailable(1234, "fake", result, id++);
-}
-
-
-int NexusInterface::requestFiles(int modID, QObject *receiver, QVariant userData,
- const QString &subModule, const QString &url)
-{
- NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_FILES, userData, subModule, url, GameInfo::instance().getNexusGameID());
- m_RequestQueue.enqueue(requestInfo);
- connect(this, SIGNAL(nxmFilesAvailable(int,QVariant,QVariant,int)),
- receiver, SLOT(nxmFilesAvailable(int,QVariant,QVariant,int)), Qt::UniqueConnection);
-
- connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
- receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
-
-// QTimer::singleShot(1000, this, SLOT(fakeFiles()));
-// static int fID = 42;
-// return fID++;
-
- nextRequest();
- return requestInfo.m_ID;
-}
-
-
-int NexusInterface::requestFileInfo(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule,
- const QString &url)
-{
- NXMRequestInfo requestInfo(modID, fileID, NXMRequestInfo::TYPE_FILEINFO, userData, subModule, url, GameInfo::instance().getNexusGameID());
- m_RequestQueue.enqueue(requestInfo);
-
- connect(this, SIGNAL(nxmFileInfoAvailable(int,int,QVariant,QVariant,int)),
- receiver, SLOT(nxmFileInfoAvailable(int,int,QVariant,QVariant,int)), Qt::UniqueConnection);
-
- connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
- receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
-
- nextRequest();
- return requestInfo.m_ID;
-}
-
-
-int NexusInterface::requestDownloadURL(int modID, int fileID, QObject *receiver, QVariant userData,
- const QString &subModule, const QString &url)
-{
- NXMRequestInfo requestInfo(modID, fileID, NXMRequestInfo::TYPE_DOWNLOADURL, userData, subModule, url, GameInfo::instance().getNexusGameID());
- m_RequestQueue.enqueue(requestInfo);
-
- connect(this, SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)),
- receiver, SLOT(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), Qt::UniqueConnection);
-
- connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
- receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
-
- nextRequest();
- return requestInfo.m_ID;
-}
-
-
-int NexusInterface::requestToggleEndorsement(int modID, bool endorse, QObject *receiver, QVariant userData,
- const QString &subModule, const QString &url)
-{
- NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_TOGGLEENDORSEMENT, userData, subModule, url, GameInfo::instance().getNexusGameID());
- requestInfo.m_Endorse = endorse;
- m_RequestQueue.enqueue(requestInfo);
-
- connect(this, SIGNAL(nxmEndorsementToggled(int,QVariant,QVariant,int)),
- receiver, SLOT(nxmEndorsementToggled(int,QVariant,QVariant,int)), Qt::UniqueConnection);
-
- connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
- receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
-
- nextRequest();
- return requestInfo.m_ID;
-}
-
-bool NexusInterface::requiresLogin(const NXMRequestInfo &info)
-{
- return (info.m_Type == NXMRequestInfo::TYPE_TOGGLEENDORSEMENT)
- || (info.m_Type == NXMRequestInfo::TYPE_DOWNLOADURL);
-}
-
-void NexusInterface::nextRequest()
-{
- if ((m_ActiveRequest.size() >= MAX_ACTIVE_DOWNLOADS)
- || m_RequestQueue.isEmpty()) {
- return;
- }
-
- if (!getAccessManager()->loggedIn()
- && requiresLogin(m_RequestQueue.head())) {
- if (!getAccessManager()->loginAttempted()) {
- emit needLogin();
- return;
- } else if (getAccessManager()->loginWaiting()) {
- return;
- }
- }
-
- NXMRequestInfo info = m_RequestQueue.dequeue();
- info.m_Timeout = new QTimer(this);
- info.m_Timeout->setInterval(60000);
-
- QString url;
- if (!info.m_Reroute) {
- bool hasParams = false;
- switch (info.m_Type) {
- case NXMRequestInfo::TYPE_DESCRIPTION: {
- url = QString("%1/Mods/%2/").arg(info.m_URL).arg(info.m_ModID);
- } break;
- case NXMRequestInfo::TYPE_FILES: {
- url = QString("%1/Files/indexfrommod/%2/").arg(info.m_URL).arg(info.m_ModID);
- } break;
- case NXMRequestInfo::TYPE_FILEINFO: {
- url = QString("%1/Files/%2/").arg(info.m_URL).arg(info.m_FileID);
- } break;
- case NXMRequestInfo::TYPE_DOWNLOADURL: {
- url = QString("%1/Files/download/%2").arg(info.m_URL).arg(info.m_FileID);
- } break;
- case NXMRequestInfo::TYPE_TOGGLEENDORSEMENT: {
- url = QString("%1/Mods/toggleendorsement/%2?lvote=%3").arg(info.m_URL).arg(info.m_ModID).arg(!info.m_Endorse);
- hasParams = true;
- } break;
- case NXMRequestInfo::TYPE_GETUPDATES: {
- QString modIDList = VectorJoin<int>(info.m_ModIDList, ",");
- modIDList = "[" + modIDList + "]";
- url = QString("%1/Mods/GetUpdates?ModList=%2").arg(info.m_URL).arg(modIDList);
- hasParams = true;
- } break;
- }
- url.append(QString("%1game_id=%2").arg(hasParams ? '&' : '?').arg(info.m_NexusGameID));
- } else {
- url = info.m_URL;
- }
- 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());
-
- info.m_Reply = m_AccessManager->get(request);
-
- connect(info.m_Reply, SIGNAL(finished()), this, SLOT(requestFinished()));
- connect(info.m_Reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError)));
- connect(info.m_Timeout, SIGNAL(timeout()), this, SLOT(requestTimeout()));
- info.m_Timeout->start();
- m_ActiveRequest.push_back(info);
-}
-
-
-void NexusInterface::downloadRequestedNXM(const QString &url)
-{
- emit requestNXMDownload(url);
-}
-
-void NexusInterface::requestFinished(std::list<NXMRequestInfo>::iterator iter)
-{
- QNetworkReply *reply = iter->m_Reply;
-
- if (reply->error() != QNetworkReply::NoError) {
- qWarning("request failed: %s", reply->errorString().toUtf8().constData());
- emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, reply->errorString());
- } else {
- int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- if (statusCode == 301) {
- // redirect request, return request to queue
- iter->m_URL = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
- iter->m_Reroute = true;
- m_RequestQueue.enqueue(*iter);
- nextRequest();
- return;
- }
- QByteArray data = reply->readAll();
- if (data.isNull() || data.isEmpty() || (strcmp(data.constData(), "null") == 0)) {
- QString nexusError(reply->rawHeader("NexusErrorInfo"));
- if (nexusError.length() == 0) {
- nexusError = tr("empty response");
- }
- qDebug("nexus error: %s", qPrintable(nexusError));
- emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, nexusError);
- } else {
- bool ok;
- QVariant result = QtJson::parse(data, ok);
- if (result.isValid() && ok) {
- switch (iter->m_Type) {
- case NXMRequestInfo::TYPE_DESCRIPTION: {
- emit nxmDescriptionAvailable(iter->m_ModID, iter->m_UserData, result, iter->m_ID);
- } break;
- case NXMRequestInfo::TYPE_FILES: {
- emit nxmFilesAvailable(iter->m_ModID, iter->m_UserData, result, iter->m_ID);
- } break;
- case NXMRequestInfo::TYPE_FILEINFO: {
- emit nxmFileInfoAvailable(iter->m_ModID, iter->m_FileID, iter->m_UserData, result, iter->m_ID);
- } break;
- case NXMRequestInfo::TYPE_DOWNLOADURL: {
- emit nxmDownloadURLsAvailable(iter->m_ModID, iter->m_FileID, iter->m_UserData, result, iter->m_ID);
- } break;
- case NXMRequestInfo::TYPE_GETUPDATES: {
- emit nxmUpdatesAvailable(iter->m_ModIDList, iter->m_UserData, result, iter->m_ID);
- } break;
- case NXMRequestInfo::TYPE_TOGGLEENDORSEMENT: {
- emit nxmEndorsementToggled(iter->m_ModID, iter->m_UserData, result, iter->m_ID);
- } break;
- }
- } else {
- emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, tr("invalid response"));
- }
- }
- }
-}
-
-
-void NexusInterface::requestFinished()
-{
- QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
- for (std::list<NXMRequestInfo>::iterator iter = m_ActiveRequest.begin(); iter != m_ActiveRequest.end(); ++iter) {
- if (iter->m_Reply == reply) {
- iter->m_Timeout->stop();
- iter->m_Timeout->deleteLater();
- requestFinished(iter);
- iter->m_Reply->deleteLater();
- m_ActiveRequest.erase(iter);
- nextRequest();
- return;
- }
- }
-}
-
-
-void NexusInterface::requestError(QNetworkReply::NetworkError)
-{
- QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
- if (reply == NULL) {
- qWarning("invalid sender type");
- return;
- }
-
- qCritical("request (%s) error: %s",
- qPrintable(reply->url().toString()), qPrintable(reply->errorString()));
-}
-
-
-void NexusInterface::requestTimeout()
-{
- QTimer *timer = qobject_cast<QTimer*>(sender());
- if (timer == NULL) {
- qWarning("invalid sender type");
- return;
- }
- for (std::list<NXMRequestInfo>::iterator iter = m_ActiveRequest.begin(); iter != m_ActiveRequest.end(); ++iter) {
- if (iter->m_Timeout == timer) {
- // this abort causes a "request failed" which cleans up the rest
- iter->m_Reply->abort();
- return;
- }
- }
-}
-
-
-NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID
- , NexusInterface::NXMRequestInfo::Type type
- , QVariant userData
- , const QString &subModule
- , const QString &url
- , int nexusGameId)
- : m_ModID(modID)
- , m_FileID(0)
- , m_Reply(NULL)
- , m_Type(type)
- , m_UserData(userData)
- , m_Timeout(NULL)
- , m_Reroute(false)
- , m_ID(s_NextID.fetchAndAddAcquire(1))
- , m_URL(url)
- , m_SubModule(subModule)
- , m_NexusGameID(nexusGameId)
-{}
-
-NexusInterface::NXMRequestInfo::NXMRequestInfo(std::vector<int> modIDList
- , NexusInterface::NXMRequestInfo::Type type
- , QVariant userData
- , const QString &subModule
- , const QString &url
- , int nexusGameId)
- : m_ModID(-1)
- , m_ModIDList(modIDList)
- , m_FileID(0)
- , m_Reply(NULL)
- , m_Type(type)
- , m_UserData(userData)
- , m_Timeout(NULL)
- , m_Reroute(false)
- , m_ID(s_NextID.fetchAndAddAcquire(1))
- , m_URL(url)
- , m_SubModule(subModule)
- , m_NexusGameID(nexusGameId)
-{}
-
-NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID
- , int fileID
- , NexusInterface::NXMRequestInfo::Type type
- , QVariant userData
- , const QString &subModule
- , const QString &url
- , int nexusGameId)
- : m_ModID(modID)
- , m_FileID(fileID)
- , m_Reply(NULL)
- , m_Type(type)
- , m_UserData(userData)
- , m_Timeout(NULL)
- , m_Reroute(false)
- , m_ID(s_NextID.fetchAndAddAcquire(1))
- , m_URL(url)
- , m_SubModule(subModule)
- , m_NexusGameID(nexusGameId)
-{}
+/*
+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 "nexusinterface.h"
+#include "nxmaccessmanager.h"
+#include "utility.h"
+#include <json.h>
+#include "selectiondialog.h"
+#include <QApplication>
+#include <utility.h>
+#include <regex>
+#include <util.h>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+NexusBridge::NexusBridge(const QString &subModule)
+ : m_Interface(NexusInterface::instance())
+ , m_Url(MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()))
+ , m_SubModule(subModule)
+{
+}
+
+
+void NexusBridge::requestDescription(int modID, QVariant userData)
+{
+ m_RequestIDs.insert(m_Interface->requestDescription(modID, this, userData, m_SubModule, m_Url));
+}
+
+void NexusBridge::requestFiles(int modID, QVariant userData)
+{
+ m_RequestIDs.insert(m_Interface->requestFiles(modID, this, userData, m_SubModule, m_Url));
+}
+
+void NexusBridge::requestFileInfo(int modID, int fileID, QVariant userData)
+{
+ m_RequestIDs.insert(m_Interface->requestFileInfo(modID, fileID, this, userData, m_SubModule, m_Url));
+}
+
+void NexusBridge::requestDownloadURL(int modID, int fileID, QVariant userData)
+{
+ m_RequestIDs.insert(m_Interface->requestDownloadURL(modID, fileID, this, userData, m_SubModule, m_Url));
+}
+
+void NexusBridge::requestToggleEndorsement(int modID, bool endorse, QVariant userData)
+{
+ m_RequestIDs.insert(m_Interface->requestToggleEndorsement(modID, endorse, this, userData, m_SubModule, m_Url));
+}
+
+void NexusBridge::nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID)
+{
+ std::set<int>::iterator iter = m_RequestIDs.find(requestID);
+ if (iter != m_RequestIDs.end()) {
+ m_RequestIDs.erase(iter);
+
+ emit descriptionAvailable(modID, userData, resultData);
+ }
+}
+
+void NexusBridge::nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID)
+{
+ std::set<int>::iterator iter = m_RequestIDs.find(requestID);
+ if (iter != m_RequestIDs.end()) {
+ m_RequestIDs.erase(iter);
+
+ QList<ModRepositoryFileInfo> fileInfoList;
+
+ QVariantList resultList = resultData.toList();
+
+ foreach(QVariant file, resultList) {
+ ModRepositoryFileInfo temp;
+ QVariantMap fileInfo = file.toMap();
+ temp.uri = fileInfo["uri"].toString();
+ temp.name = fileInfo["name"].toString();
+ temp.description = fileInfo["description"].toString();
+ temp.version = VersionInfo(fileInfo["version"].toString());
+ temp.categoryID = fileInfo["category_id"].toInt();
+ temp.fileID = fileInfo["id"].toInt();
+ temp.fileSize = fileInfo["size"].toInt();
+ fileInfoList.append(temp);
+ }
+
+ emit filesAvailable(modID, userData, fileInfoList);
+ }
+}
+
+void NexusBridge::nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID)
+{
+ std::set<int>::iterator iter = m_RequestIDs.find(requestID);
+ if (iter != m_RequestIDs.end()) {
+ m_RequestIDs.erase(iter);
+ emit fileInfoAvailable(modID, fileID, userData, resultData);
+ }
+}
+
+void NexusBridge::nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID)
+{
+ std::set<int>::iterator iter = m_RequestIDs.find(requestID);
+ if (iter != m_RequestIDs.end()) {
+ m_RequestIDs.erase(iter);
+ emit downloadURLsAvailable(modID, fileID, userData, resultData);
+ }
+}
+
+void NexusBridge::nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID)
+{
+ std::set<int>::iterator iter = m_RequestIDs.find(requestID);
+ if (iter != m_RequestIDs.end()) {
+ m_RequestIDs.erase(iter);
+ emit endorsementToggled(modID, userData, resultData);
+ }
+}
+
+void NexusBridge::nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage)
+{
+ std::set<int>::iterator iter = m_RequestIDs.find(requestID);
+ if (iter != m_RequestIDs.end()) {
+ m_RequestIDs.erase(iter);
+ emit requestFailed(modID, fileID, userData, errorMessage);
+ }
+}
+
+
+QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0);
+
+
+NexusInterface::NexusInterface()
+ : m_NMMVersion()
+{
+ atexit(&cleanup);
+ m_AccessManager = new NXMAccessManager(this);
+
+ m_DiskCache = new QNetworkDiskCache(this);
+ connect(m_AccessManager, SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString)));
+
+
+ VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath()));
+
+ m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16,
+ version.dwFileVersionMS & 0xFFFF,
+ version.dwFileVersionLS >> 16);
+}
+
+
+void NexusInterface::cleanup()
+{
+}
+
+
+NXMAccessManager *NexusInterface::getAccessManager()
+{
+ return m_AccessManager;
+}
+
+
+NexusInterface *NexusInterface::instance()
+{
+ static NexusInterface s_Instance;
+ return &s_Instance;
+}
+
+
+void NexusInterface::setCacheDirectory(const QString &directory)
+{
+ m_DiskCache->setCacheDirectory(directory);
+ m_AccessManager->setCache(m_DiskCache);
+}
+
+
+void NexusInterface::setNMMVersion(const QString &nmmVersion)
+{
+ m_NMMVersion = nmmVersion;
+}
+
+void NexusInterface::loginCompleted()
+{
+ nextRequest();
+}
+
+
+void NexusInterface::interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query)
+{
+ static std::tr1::regex exp("^([a-zA-Z0-9_\\- ]*?)([-_ ][VvRr]?[0-9_]+)?-([1-9][0-9]+).*");
+ static std::tr1::regex simpleexp("^([a-zA-Z0-9_]+)");
+
+ QByteArray fileNameUTF8 = fileName.toUtf8();
+ std::tr1::cmatch result;
+ if (std::tr1::regex_search(fileNameUTF8.constData(), result, exp)) {
+ modName = QString::fromUtf8(result[1].str().c_str());
+ modName = modName.replace('_', ' ').trimmed();
+
+ std::string candidate = result[3].str();
+ std::string candidate2 = result[2].str();
+ if (candidate2.length() != 0 && (candidate2.find_last_of("VvRr") == std::string::npos)) {
+ // well, that second match might be an id too...
+ unsigned offset = strspn(candidate2.c_str(), "-_ ");
+ if (offset < candidate2.length() && query) {
+ SelectionDialog selection(tr("Failed to guess mod id for \"%1\", please pick the correct one").arg(fileName));
+ QString r2Highlight(fileName);
+ r2Highlight.insert(result.position(2) + result.length(2), "* ").insert(result.position(2) + offset, " *");
+ QString r3Highlight(fileName);
+ r3Highlight.insert(result.position(3) + result.length(3), "* ").insert(result.position(3), " *");
+
+ selection.addChoice(candidate.c_str(), r3Highlight, strtol(candidate.c_str(), NULL, 10));
+ selection.addChoice(candidate2.c_str() + offset, r2Highlight, abs(strtol(candidate2.c_str() + offset, NULL, 10)));
+ if (selection.exec() == QDialog::Accepted) {
+ modID = selection.getChoiceData().toInt();
+ } else {
+ modID = -1;
+ }
+ } else {
+ modID = -1;
+ }
+ } else {
+ modID = strtol(candidate.c_str(), NULL, 10);
+ }
+ qDebug("mod id guessed: %s -> %d", qPrintable(fileName), modID);
+ } else if (std::tr1::regex_search(fileNameUTF8.constData(), result, simpleexp)) {
+ qDebug("simple expression matched, using name only");
+ modName = QString::fromUtf8(result[1].str().c_str());
+ modName = modName.replace('_', ' ').trimmed();
+
+ modID = -1;
+ } else {
+ qDebug("no expression matched!");
+ modName.clear();
+ modID = -1;
+ }
+}
+
+
+int NexusInterface::requestDescription(int modID, QObject *receiver, QVariant userData,
+ const QString &subModule, const QString &url, int nexusGameId)
+{
+ NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_DESCRIPTION, userData, subModule, url,
+ nexusGameId == -1 ? GameInfo::instance().getNexusGameID() : nexusGameId);
+ m_RequestQueue.enqueue(requestInfo);
+
+ connect(this, SIGNAL(nxmDescriptionAvailable(int,QVariant,QVariant,int)),
+ receiver, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant,int)), Qt::UniqueConnection);
+
+ connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
+ receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
+
+ nextRequest();
+ return requestInfo.m_ID;
+}
+
+
+int NexusInterface::requestUpdates(const std::vector<int> &modIDs, QObject *receiver, QVariant userData,
+ const QString &subModule, const QString &url)
+{
+ NXMRequestInfo requestInfo(modIDs, NXMRequestInfo::TYPE_GETUPDATES, userData, subModule, url, GameInfo::instance().getNexusGameID());
+ m_RequestQueue.enqueue(requestInfo);
+
+ connect(this, SIGNAL(nxmUpdatesAvailable(std::vector<int>,QVariant,QVariant,int)),
+ receiver, SLOT(nxmUpdatesAvailable(std::vector<int>,QVariant,QVariant,int)), Qt::UniqueConnection);
+
+ connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
+ receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
+
+ nextRequest();
+ return requestInfo.m_ID;
+}
+
+
+void NexusInterface::fakeFiles()
+{
+ static int id = 42;
+
+ QVariantList result;
+ QVariantMap fileMap;
+ fileMap["uri"] = "fakeURI";
+ fileMap["name"] = "fakeName";
+ fileMap["description"] = "fakeDescription";
+ fileMap["version"] = "1.0.0";
+ fileMap["category_id"] = "1";
+ fileMap["id"] = "1";
+ fileMap["size"] = "512";
+ result.append(fileMap);
+
+ emit nxmFilesAvailable(1234, "fake", result, id++);
+}
+
+
+int NexusInterface::requestFiles(int modID, QObject *receiver, QVariant userData,
+ const QString &subModule, const QString &url)
+{
+ NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_FILES, userData, subModule, url, GameInfo::instance().getNexusGameID());
+ m_RequestQueue.enqueue(requestInfo);
+ connect(this, SIGNAL(nxmFilesAvailable(int,QVariant,QVariant,int)),
+ receiver, SLOT(nxmFilesAvailable(int,QVariant,QVariant,int)), Qt::UniqueConnection);
+
+ connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
+ receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
+
+// QTimer::singleShot(1000, this, SLOT(fakeFiles()));
+// static int fID = 42;
+// return fID++;
+
+ nextRequest();
+ return requestInfo.m_ID;
+}
+
+
+int NexusInterface::requestFileInfo(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule,
+ const QString &url)
+{
+ NXMRequestInfo requestInfo(modID, fileID, NXMRequestInfo::TYPE_FILEINFO, userData, subModule, url, GameInfo::instance().getNexusGameID());
+ m_RequestQueue.enqueue(requestInfo);
+
+ connect(this, SIGNAL(nxmFileInfoAvailable(int,int,QVariant,QVariant,int)),
+ receiver, SLOT(nxmFileInfoAvailable(int,int,QVariant,QVariant,int)), Qt::UniqueConnection);
+
+ connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
+ receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
+
+ nextRequest();
+ return requestInfo.m_ID;
+}
+
+
+int NexusInterface::requestDownloadURL(int modID, int fileID, QObject *receiver, QVariant userData,
+ const QString &subModule, const QString &url)
+{
+ NXMRequestInfo requestInfo(modID, fileID, NXMRequestInfo::TYPE_DOWNLOADURL, userData, subModule, url, GameInfo::instance().getNexusGameID());
+ m_RequestQueue.enqueue(requestInfo);
+
+ connect(this, SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)),
+ receiver, SLOT(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), Qt::UniqueConnection);
+
+ connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
+ receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
+
+ nextRequest();
+ return requestInfo.m_ID;
+}
+
+
+int NexusInterface::requestToggleEndorsement(int modID, bool endorse, QObject *receiver, QVariant userData,
+ const QString &subModule, const QString &url)
+{
+ NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_TOGGLEENDORSEMENT, userData, subModule, url, GameInfo::instance().getNexusGameID());
+ requestInfo.m_Endorse = endorse;
+ m_RequestQueue.enqueue(requestInfo);
+
+ connect(this, SIGNAL(nxmEndorsementToggled(int,QVariant,QVariant,int)),
+ receiver, SLOT(nxmEndorsementToggled(int,QVariant,QVariant,int)), Qt::UniqueConnection);
+
+ connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)),
+ receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection);
+
+ nextRequest();
+ return requestInfo.m_ID;
+}
+
+bool NexusInterface::requiresLogin(const NXMRequestInfo &info)
+{
+ return (info.m_Type == NXMRequestInfo::TYPE_TOGGLEENDORSEMENT)
+ || (info.m_Type == NXMRequestInfo::TYPE_DOWNLOADURL);
+}
+
+void NexusInterface::nextRequest()
+{
+ if ((m_ActiveRequest.size() >= MAX_ACTIVE_DOWNLOADS)
+ || m_RequestQueue.isEmpty()) {
+ return;
+ }
+
+ if (!getAccessManager()->loggedIn()
+ && requiresLogin(m_RequestQueue.head())) {
+ if (!getAccessManager()->loginAttempted()) {
+ emit needLogin();
+ return;
+ } else if (getAccessManager()->loginWaiting()) {
+ return;
+ }
+ }
+
+ NXMRequestInfo info = m_RequestQueue.dequeue();
+ info.m_Timeout = new QTimer(this);
+ info.m_Timeout->setInterval(60000);
+
+ QString url;
+ if (!info.m_Reroute) {
+ bool hasParams = false;
+ switch (info.m_Type) {
+ case NXMRequestInfo::TYPE_DESCRIPTION: {
+ url = QString("%1/Mods/%2/").arg(info.m_URL).arg(info.m_ModID);
+ } break;
+ case NXMRequestInfo::TYPE_FILES: {
+ url = QString("%1/Files/indexfrommod/%2/").arg(info.m_URL).arg(info.m_ModID);
+ } break;
+ case NXMRequestInfo::TYPE_FILEINFO: {
+ url = QString("%1/Files/%2/").arg(info.m_URL).arg(info.m_FileID);
+ } break;
+ case NXMRequestInfo::TYPE_DOWNLOADURL: {
+ url = QString("%1/Files/download/%2").arg(info.m_URL).arg(info.m_FileID);
+ } break;
+ case NXMRequestInfo::TYPE_TOGGLEENDORSEMENT: {
+ url = QString("%1/Mods/toggleendorsement/%2?lvote=%3").arg(info.m_URL).arg(info.m_ModID).arg(!info.m_Endorse);
+ hasParams = true;
+ } break;
+ case NXMRequestInfo::TYPE_GETUPDATES: {
+ QString modIDList = VectorJoin<int>(info.m_ModIDList, ",");
+ modIDList = "[" + modIDList + "]";
+ url = QString("%1/Mods/GetUpdates?ModList=%2").arg(info.m_URL).arg(modIDList);
+ hasParams = true;
+ } break;
+ }
+ url.append(QString("%1game_id=%2").arg(hasParams ? '&' : '?').arg(info.m_NexusGameID));
+ } else {
+ url = info.m_URL;
+ }
+ 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());
+
+ info.m_Reply = m_AccessManager->get(request);
+
+ connect(info.m_Reply, SIGNAL(finished()), this, SLOT(requestFinished()));
+ connect(info.m_Reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError)));
+ connect(info.m_Timeout, SIGNAL(timeout()), this, SLOT(requestTimeout()));
+ info.m_Timeout->start();
+ m_ActiveRequest.push_back(info);
+}
+
+
+void NexusInterface::downloadRequestedNXM(const QString &url)
+{
+ emit requestNXMDownload(url);
+}
+
+void NexusInterface::requestFinished(std::list<NXMRequestInfo>::iterator iter)
+{
+ QNetworkReply *reply = iter->m_Reply;
+
+ if (reply->error() != QNetworkReply::NoError) {
+ qWarning("request failed: %s", reply->errorString().toUtf8().constData());
+ emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, reply->errorString());
+ } else {
+ int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ if (statusCode == 301) {
+ // redirect request, return request to queue
+ iter->m_URL = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
+ iter->m_Reroute = true;
+ m_RequestQueue.enqueue(*iter);
+ nextRequest();
+ return;
+ }
+ QByteArray data = reply->readAll();
+ if (data.isNull() || data.isEmpty() || (strcmp(data.constData(), "null") == 0)) {
+ QString nexusError(reply->rawHeader("NexusErrorInfo"));
+ if (nexusError.length() == 0) {
+ nexusError = tr("empty response");
+ }
+ qDebug("nexus error: %s", qPrintable(nexusError));
+ emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, nexusError);
+ } else {
+ bool ok;
+ QVariant result = QtJson::parse(data, ok);
+ if (result.isValid() && ok) {
+ switch (iter->m_Type) {
+ case NXMRequestInfo::TYPE_DESCRIPTION: {
+ emit nxmDescriptionAvailable(iter->m_ModID, iter->m_UserData, result, iter->m_ID);
+ } break;
+ case NXMRequestInfo::TYPE_FILES: {
+ emit nxmFilesAvailable(iter->m_ModID, iter->m_UserData, result, iter->m_ID);
+ } break;
+ case NXMRequestInfo::TYPE_FILEINFO: {
+ emit nxmFileInfoAvailable(iter->m_ModID, iter->m_FileID, iter->m_UserData, result, iter->m_ID);
+ } break;
+ case NXMRequestInfo::TYPE_DOWNLOADURL: {
+ emit nxmDownloadURLsAvailable(iter->m_ModID, iter->m_FileID, iter->m_UserData, result, iter->m_ID);
+ } break;
+ case NXMRequestInfo::TYPE_GETUPDATES: {
+ emit nxmUpdatesAvailable(iter->m_ModIDList, iter->m_UserData, result, iter->m_ID);
+ } break;
+ case NXMRequestInfo::TYPE_TOGGLEENDORSEMENT: {
+ emit nxmEndorsementToggled(iter->m_ModID, iter->m_UserData, result, iter->m_ID);
+ } break;
+ }
+ } else {
+ emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, tr("invalid response"));
+ }
+ }
+ }
+}
+
+
+void NexusInterface::requestFinished()
+{
+ QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
+ for (std::list<NXMRequestInfo>::iterator iter = m_ActiveRequest.begin(); iter != m_ActiveRequest.end(); ++iter) {
+ if (iter->m_Reply == reply) {
+ iter->m_Timeout->stop();
+ iter->m_Timeout->deleteLater();
+ requestFinished(iter);
+ iter->m_Reply->deleteLater();
+ m_ActiveRequest.erase(iter);
+ nextRequest();
+ return;
+ }
+ }
+}
+
+
+void NexusInterface::requestError(QNetworkReply::NetworkError)
+{
+ QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
+ if (reply == NULL) {
+ qWarning("invalid sender type");
+ return;
+ }
+
+ qCritical("request (%s) error: %s",
+ qPrintable(reply->url().toString()), qPrintable(reply->errorString()));
+}
+
+
+void NexusInterface::requestTimeout()
+{
+ QTimer *timer = qobject_cast<QTimer*>(sender());
+ if (timer == NULL) {
+ qWarning("invalid sender type");
+ return;
+ }
+ for (std::list<NXMRequestInfo>::iterator iter = m_ActiveRequest.begin(); iter != m_ActiveRequest.end(); ++iter) {
+ if (iter->m_Timeout == timer) {
+ // this abort causes a "request failed" which cleans up the rest
+ iter->m_Reply->abort();
+ return;
+ }
+ }
+}
+
+
+NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID
+ , NexusInterface::NXMRequestInfo::Type type
+ , QVariant userData
+ , const QString &subModule
+ , const QString &url
+ , int nexusGameId)
+ : m_ModID(modID)
+ , m_FileID(0)
+ , m_Reply(NULL)
+ , m_Type(type)
+ , m_UserData(userData)
+ , m_Timeout(NULL)
+ , m_Reroute(false)
+ , m_ID(s_NextID.fetchAndAddAcquire(1))
+ , m_URL(url)
+ , m_SubModule(subModule)
+ , m_NexusGameID(nexusGameId)
+{}
+
+NexusInterface::NXMRequestInfo::NXMRequestInfo(std::vector<int> modIDList
+ , NexusInterface::NXMRequestInfo::Type type
+ , QVariant userData
+ , const QString &subModule
+ , const QString &url
+ , int nexusGameId)
+ : m_ModID(-1)
+ , m_ModIDList(modIDList)
+ , m_FileID(0)
+ , m_Reply(NULL)
+ , m_Type(type)
+ , m_UserData(userData)
+ , m_Timeout(NULL)
+ , m_Reroute(false)
+ , m_ID(s_NextID.fetchAndAddAcquire(1))
+ , m_URL(url)
+ , m_SubModule(subModule)
+ , m_NexusGameID(nexusGameId)
+{}
+
+NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID
+ , int fileID
+ , NexusInterface::NXMRequestInfo::Type type
+ , QVariant userData
+ , const QString &subModule
+ , const QString &url
+ , int nexusGameId)
+ : m_ModID(modID)
+ , m_FileID(fileID)
+ , m_Reply(NULL)
+ , m_Type(type)
+ , m_UserData(userData)
+ , m_Timeout(NULL)
+ , m_Reroute(false)
+ , m_ID(s_NextID.fetchAndAddAcquire(1))
+ , m_URL(url)
+ , m_SubModule(subModule)
+ , m_NexusGameID(nexusGameId)
+{}