summaryrefslogtreecommitdiffstats
path: root/api/logic/net
diff options
context:
space:
mode:
Diffstat (limited to 'api/logic/net')
-rw-r--r--api/logic/net/Download.cpp74
-rw-r--r--api/logic/net/Download.h3
-rw-r--r--api/logic/net/HttpMetaCache.cpp2
-rw-r--r--api/logic/net/HttpMetaCache.h2
-rw-r--r--api/logic/net/Mode.h10
-rw-r--r--api/logic/net/NetAction.h73
-rw-r--r--api/logic/net/NetJob.cpp84
-rw-r--r--api/logic/net/NetJob.h41
-rw-r--r--api/logic/net/PasteUpload.cpp43
-rw-r--r--api/logic/net/PasteUpload.h4
-rw-r--r--api/logic/net/URLConstants.h2
11 files changed, 207 insertions, 131 deletions
diff --git a/api/logic/net/Download.cpp b/api/logic/net/Download.cpp
index 12c1b201..631d3076 100644
--- a/api/logic/net/Download.cpp
+++ b/api/logic/net/Download.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2017 MultiMC Contributors
+/* Copyright 2013-2018 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -102,7 +102,8 @@ void Download::start()
connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64)));
connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError)));
- connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
+ connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors);
+ connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead);
}
void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
@@ -135,31 +136,68 @@ void Download::downloadError(QNetworkReply::NetworkError error)
}
}
-bool Download::handleRedirect()
+void Download::sslErrors(const QList<QSslError> & errors)
{
- QVariant redirect = m_reply->header(QNetworkRequest::LocationHeader);
- QString redirectURL;
- if(redirect.isValid())
+ int i = 1;
+ for (auto error : errors)
{
- redirectURL = redirect.toString();
+ qCritical() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
+ auto cert = error.certificate();
+ qCritical() << "Certificate in question:\n" << cert.toText();
+ i++;
}
- // FIXME: This is a hack for https://bugreports.qt-project.org/browse/QTBUG-41061
- else if(m_reply->hasRawHeader("Location"))
+}
+
+bool Download::handleRedirect()
+{
+ QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl();
+ if(!redirect.isValid())
{
- auto data = m_reply->rawHeader("Location");
- if(data.size() > 2 && data[0] == '/' && data[1] == '/')
+ if(!m_reply->hasRawHeader("Location"))
+ {
+ // no redirect -> it's fine to continue
+ return false;
+ }
+ // there is a Location header, but it's not correct. we need to apply some workarounds...
+ QByteArray redirectBA = m_reply->rawHeader("Location");
+ if(redirectBA.size() == 0)
+ {
+ // empty, yet present redirect header? WTF?
+ return false;
+ }
+ QString redirectStr = QString::fromUtf8(redirectBA);
+
+ /*
+ * IF the URL begins with //, we need to insert the URL scheme.
+ * See: https://bugreports.qt-project.org/browse/QTBUG-41061
+ */
+ if(redirectStr.startsWith("//"))
+ {
+ redirectStr = m_reply->url().scheme() + ":" + redirectStr;
+ }
+
+ /*
+ * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues.
+ * FIXME: report Qt bug for this
+ */
+ redirect = QUrl(redirectStr, QUrl::TolerantMode);
+ if(!redirect.isValid())
{
- redirectURL = m_reply->url().scheme() + ":" + data;
+ qWarning() << "Failed to parse redirect URL:" << redirectStr;
+ downloadError(QNetworkReply::ProtocolFailure);
+ return false;
}
+ qDebug() << "Fixed location header:" << redirect;
}
- if (!redirectURL.isEmpty())
+ else
{
- m_url = QUrl(redirect.toString());
- qDebug() << "Following redirect to " << m_url.toString();
- start();
- return true;
+ qDebug() << "Location header:" << redirect;
}
- return false;
+
+ m_url = QUrl(redirect.toString());
+ qDebug() << "Following redirect to " << m_url.toString();
+ start();
+ return true;
}
diff --git a/api/logic/net/Download.h b/api/logic/net/Download.h
index 3347dc96..00bf108c 100644
--- a/api/logic/net/Download.h
+++ b/api/logic/net/Download.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2017 MultiMC Contributors
+/* Copyright 2013-2018 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,6 +58,7 @@ private: /* methods */
protected slots:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
void downloadError(QNetworkReply::NetworkError error) override;
+ void sslErrors(const QList<QSslError> & errors);
void downloadFinished() override;
void downloadReadyRead() override;
diff --git a/api/logic/net/HttpMetaCache.cpp b/api/logic/net/HttpMetaCache.cpp
index 3eec185b..ebcb0a27 100644
--- a/api/logic/net/HttpMetaCache.cpp
+++ b/api/logic/net/HttpMetaCache.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2017 MultiMC Contributors
+/* Copyright 2013-2018 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/api/logic/net/HttpMetaCache.h b/api/logic/net/HttpMetaCache.h
index 21c9b6c8..7ee5f735 100644
--- a/api/logic/net/HttpMetaCache.h
+++ b/api/logic/net/HttpMetaCache.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2017 MultiMC Contributors
+/* Copyright 2013-2018 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/api/logic/net/Mode.h b/api/logic/net/Mode.h
new file mode 100644
index 00000000..62e26d92
--- /dev/null
+++ b/api/logic/net/Mode.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace Net
+{
+enum class Mode
+{
+ Offline,
+ Online
+};
+}
diff --git a/api/logic/net/NetAction.h b/api/logic/net/NetAction.h
index a533c317..08e40a29 100644
--- a/api/logic/net/NetAction.h
+++ b/api/logic/net/NetAction.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2017 MultiMC Contributors
+/* Copyright 2013-2018 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,10 @@ enum JobStatus
Job_Finished,
Job_Failed,
Job_Aborted,
+ /*
+ * FIXME: @NUKE this confuses the task failing with us having a fallback in the form of local data. Clear up the confusion.
+ * Same could be true for aborted task - the presence of pre-existing result is a separate concern
+ */
Job_Failed_Proceed
};
@@ -43,18 +47,26 @@ protected:
public:
virtual ~NetAction() {};
-public:
- virtual qint64 totalProgress() const
+ bool isRunning() const
{
- return m_total_progress;
+ return m_status == Job_InProgress;
}
- virtual qint64 currentProgress() const
+ bool isFinished() const
{
- return m_progress;
+ return m_status >= Job_Finished;
}
- virtual qint64 numberOfFailures() const
+ bool wasSuccessful() const
{
- return m_failures;
+ return m_status == Job_Finished || m_status == Job_Failed_Proceed;
+ }
+
+ qint64 totalProgress() const
+ {
+ return m_total_progress;
+ }
+ qint64 currentProgress() const
+ {
+ return m_progress;
}
virtual bool abort()
{
@@ -64,25 +76,10 @@ public:
{
return false;
}
-
-public:
- /// the network reply
- unique_qobject_ptr<QNetworkReply> m_reply;
-
- /// source URL
- QUrl m_url;
-
- /// The file's status
- JobStatus m_status = Job_NotStarted;
-
- /// index within the parent job
- int m_index_within_job = 0;
-
- qint64 m_progress = 0;
- qint64 m_total_progress = 1;
-
- /// number of failures up to this point
- int m_failures = 0;
+ QUrl url()
+ {
+ return m_url;
+ }
signals:
void started(int index);
@@ -91,14 +88,28 @@ signals:
void failed(int index);
void aborted(int index);
-protected
-slots:
+protected slots:
virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0;
virtual void downloadError(QNetworkReply::NetworkError error) = 0;
virtual void downloadFinished() = 0;
virtual void downloadReadyRead() = 0;
-public
-slots:
+public slots:
virtual void start() = 0;
+
+public:
+ /// index within the parent job, FIXME: nuke
+ int m_index_within_job = 0;
+
+ /// the network reply
+ unique_qobject_ptr<QNetworkReply> m_reply;
+
+ /// source URL
+ QUrl m_url;
+
+ qint64 m_progress = 0;
+ qint64 m_total_progress = 1;
+
+protected:
+ JobStatus m_status = Job_NotStarted;
};
diff --git a/api/logic/net/NetJob.cpp b/api/logic/net/NetJob.cpp
index 275da749..304b5820 100644
--- a/api/logic/net/NetJob.cpp
+++ b/api/logic/net/NetJob.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2017 MultiMC Contributors
+/* Copyright 2013-2018 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,55 +59,74 @@ void NetJob::partAborted(int index)
void NetJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal)
{
auto &slot = parts_progress[index];
-
- current_progress -= slot.current_progress;
slot.current_progress = bytesReceived;
- current_progress += slot.current_progress;
-
- total_progress -= slot.total_progress;
slot.total_progress = bytesTotal;
- total_progress += slot.total_progress;
- setProgress(current_progress, total_progress);
+
+ int done = m_done.size();
+ int doing = m_doing.size();
+ int all = parts_progress.size();
+
+ qint64 bytesAll = 0;
+ qint64 bytesTotalAll = 0;
+ for(auto & partIdx: m_doing)
+ {
+ auto part = parts_progress[partIdx];
+ // do not count parts with unknown/nonsensical total size
+ if(part.total_progress <= 0)
+ {
+ continue;
+ }
+ bytesAll += part.current_progress;
+ bytesTotalAll += part.total_progress;
+ }
+
+ qint64 inprogress = (bytesTotalAll == 0) ? 0 : (bytesAll * 1000) / bytesTotalAll;
+ auto current = done * 1000 + doing * inprogress;
+ auto current_total = all * 1000;
+ // HACK: make sure it never jumps backwards.
+ if(m_current_progress > current)
+ {
+ current = m_current_progress;
+ }
+ m_current_progress = current;
+ setProgress(current, current_total);
}
void NetJob::executeTask()
{
- qDebug() << m_job_name.toLocal8Bit() << " started.";
- m_running = true;
- for (int i = 0; i < downloads.size(); i++)
- {
- m_todo.enqueue(i);
- }
// hack that delays early failures so they can be caught easier
QMetaObject::invokeMethod(this, "startMoreParts", Qt::QueuedConnection);
}
void NetJob::startMoreParts()
{
- // check for final conditions if there's nothing in the queue
+ if(!isRunning())
+ {
+ // this actually makes sense. You can put running downloads into a NetJob and then not start it until much later.
+ return;
+ }
+ // OK. We are actively processing tasks, proceed.
+ // Check for final conditions if there's nothing in the queue.
if(!m_todo.size())
{
if(!m_doing.size())
{
if(!m_failed.size())
{
- qDebug() << m_job_name << "succeeded.";
emitSucceeded();
}
else if(m_aborted)
{
- qDebug() << m_job_name << "aborted.";
- emitFailed(tr("Job '%1' aborted.").arg(m_job_name));
+ emitAborted();
}
else
{
- qCritical() << m_job_name << "failed.";
- emitFailed(tr("Job '%1' failed to process:\n%2").arg(m_job_name).arg(getFailedFiles().join("\n")));
+ emitFailed(tr("Job '%1' failed to process:\n%2").arg(objectName()).arg(getFailedFiles().join("\n")));
}
}
return;
}
- // otherwise try to start more parts
+ // There's work to do, try to start more parts.
while (m_doing.size() < 6)
{
if(!m_todo.size())
@@ -131,7 +150,7 @@ QStringList NetJob::getFailedFiles()
QStringList failed;
for (auto index: m_failed)
{
- failed.push_back(downloads[index]->m_url.toString());
+ failed.push_back(downloads[index]->url().toString());
}
failed.sort();
return failed;
@@ -170,3 +189,24 @@ bool NetJob::abort()
}
return fullyAborted;
}
+
+bool NetJob::addNetAction(NetActionPtr action)
+{
+ action->m_index_within_job = downloads.size();
+ downloads.append(action);
+ part_info pi;
+ parts_progress.append(pi);
+ partProgress(parts_progress.count() - 1, action->currentProgress(), action->totalProgress());
+
+ if(action->isRunning())
+ {
+ connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
+ connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
+ connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)), SLOT(partProgress(int, qint64, qint64)));
+ }
+ else
+ {
+ m_todo.append(parts_progress.size() - 1);
+ }
+ return true;
+}
diff --git a/api/logic/net/NetJob.h b/api/logic/net/NetJob.h
index ca4f5df1..be58c61a 100644
--- a/api/logic/net/NetJob.h
+++ b/api/logic/net/NetJob.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2017 MultiMC Contributors
+/* Copyright 2013-2018 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,32 +30,13 @@ class MULTIMC_LOGIC_EXPORT NetJob : public Task
{
Q_OBJECT
public:
- explicit NetJob(QString job_name) : Task(), m_job_name(job_name) {}
- virtual ~NetJob() {}
- bool addNetAction(NetActionPtr action)
+ explicit NetJob(QString job_name) : Task()
{
- action->m_index_within_job = downloads.size();
- downloads.append(action);
- part_info pi;
- {
- pi.current_progress = action->currentProgress();
- pi.total_progress = action->totalProgress();
- pi.failures = action->numberOfFailures();
- }
- parts_progress.append(pi);
- total_progress += pi.total_progress;
- // if this is already running, the action needs to be started right away!
- if (isRunning())
- {
- setProgress(current_progress, total_progress);
- connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
- connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
- connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)),
- SLOT(partProgress(int, qint64, qint64)));
- action->start();
- }
- return true;
+ setObjectName(job_name);
}
+ virtual ~NetJob() {}
+
+ bool addNetAction(NetActionPtr action);
NetActionPtr operator[](int index)
{
@@ -75,10 +56,6 @@ public:
{
return downloads.size();
}
- virtual bool isRunning() const override
- {
- return m_running;
- }
QStringList getFailedFiles();
bool canAbort() const override;
@@ -102,17 +79,13 @@ private:
qint64 current_progress = 0;
qint64 total_progress = 1;
int failures = 0;
- bool connected = false;
};
- QString m_job_name;
QList<NetActionPtr> downloads;
QList<part_info> parts_progress;
QQueue<int> m_todo;
QSet<int> m_doing;
QSet<int> m_done;
QSet<int> m_failed;
- qint64 current_progress = 0;
- qint64 total_progress = 0;
- bool m_running = false;
+ qint64 m_current_progress = 0;
bool m_aborted = false;
};
diff --git a/api/logic/net/PasteUpload.cpp b/api/logic/net/PasteUpload.cpp
index 59779b2c..d1ddf39d 100644
--- a/api/logic/net/PasteUpload.cpp
+++ b/api/logic/net/PasteUpload.cpp
@@ -2,41 +2,45 @@
#include "Env.h"
#include <QDebug>
#include <QJsonObject>
+#include <QJsonArray>
#include <QJsonDocument>
+#include <QFile>
PasteUpload::PasteUpload(QWidget *window, QString text, QString key) : m_window(window)
{
m_key = key;
QByteArray temp;
- temp = text.toUtf8();
- temp.replace('\n', "\r\n");
- m_textSize = temp.size();
- m_text = "key=" + m_key.toLatin1() + "&description=MultiMC5+Log+File&language=plain&format=json&expire=2592000&paste=" + temp.toPercentEncoding();
- buf = new QBuffer(&m_text);
+ QJsonObject topLevelObj;
+ QJsonObject sectionObject;
+ sectionObject.insert("contents", text);
+ QJsonArray sectionArray;
+ sectionArray.append(sectionObject);
+ topLevelObj.insert("description", "MultiMC Log Upload");
+ topLevelObj.insert("sections", sectionArray);
+ QJsonDocument docOut;
+ docOut.setObject(topLevelObj);
+ m_jsonContent = docOut.toJson();
}
PasteUpload::~PasteUpload()
{
- if(buf)
- {
- delete buf;
- }
}
bool PasteUpload::validateText()
{
- return m_textSize <= maxSize();
+ return m_jsonContent.size() <= maxSize();
}
void PasteUpload::executeTask()
{
- QNetworkRequest request(QUrl("https://paste.ee/api"));
+ QNetworkRequest request(QUrl("https://api.paste.ee/v1/pastes"));
request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
- request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
- request.setRawHeader("Content-Length", QByteArray::number(m_text.size()));
+ request.setRawHeader("Content-Type", "application/json");
+ request.setRawHeader("Content-Length", QByteArray::number(m_jsonContent.size()));
+ request.setRawHeader("X-Auth-Token", m_key.toStdString().c_str());
- QNetworkReply *rep = ENV.qnam().post(request, buf);
+ QNetworkReply *rep = ENV.qnam().post(request, m_jsonContent);
m_reply = std::shared_ptr<QNetworkReply>(rep);
setStatus(tr("Uploading to paste.ee"));
@@ -54,10 +58,10 @@ void PasteUpload::downloadError(QNetworkReply::NetworkError error)
void PasteUpload::downloadFinished()
{
+ QByteArray data = m_reply->readAll();
// if the download succeeded
if (m_reply->error() == QNetworkReply::NetworkError::NoError)
{
- QByteArray data = m_reply->readAll();
m_reply.reset();
QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
@@ -85,14 +89,15 @@ void PasteUpload::downloadFinished()
bool PasteUpload::parseResult(QJsonDocument doc)
{
auto object = doc.object();
- auto status = object.value("status").toString("error");
- if (status == "error")
+ auto status = object.value("success").toBool();
+ if (!status)
{
qCritical() << "paste.ee reported error:" << QString(object.value("error").toString());
return false;
}
- m_pasteLink = object.value("paste").toObject().value("link").toString();
- m_pasteID = object.value("paste").toObject().value("id").toString();
+ m_pasteLink = object.value("link").toString();
+ m_pasteID = object.value("id").toString();
+ qDebug() << m_pasteLink;
return true;
}
diff --git a/api/logic/net/PasteUpload.h b/api/logic/net/PasteUpload.h
index 78d1da8e..62d2e766 100644
--- a/api/logic/net/PasteUpload.h
+++ b/api/logic/net/PasteUpload.h
@@ -34,14 +34,12 @@ protected:
private:
bool parseResult(QJsonDocument doc);
- QByteArray m_text;
QString m_error;
QWidget *m_window;
QString m_pasteID;
QString m_pasteLink;
QString m_key;
- int m_textSize = 0;
- QBuffer * buf = nullptr;
+ QByteArray m_jsonContent;
std::shared_ptr<QNetworkReply> m_reply;
public
slots:
diff --git a/api/logic/net/URLConstants.h b/api/logic/net/URLConstants.h
index dedb1424..22d128b2 100644
--- a/api/logic/net/URLConstants.h
+++ b/api/logic/net/URLConstants.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2017 MultiMC Contributors
+/* Copyright 2013-2018 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.