summaryrefslogtreecommitdiffstats
path: root/logic
diff options
context:
space:
mode:
Diffstat (limited to 'logic')
-rw-r--r--logic/BaseInstance.cpp5
-rw-r--r--logic/BaseInstance.h2
-rw-r--r--logic/OneSixUpdate.cpp9
-rw-r--r--logic/auth/MojangAccountList.cpp27
-rw-r--r--logic/auth/MojangAccountList.h5
-rw-r--r--logic/lists/InstanceList.cpp14
-rw-r--r--logic/lists/InstanceList.h5
-rw-r--r--logic/net/ByteArrayDownload.cpp8
-rw-r--r--logic/net/CacheDownload.cpp14
-rw-r--r--logic/net/ForgeMirrors.cpp8
-rw-r--r--logic/net/ForgeXzDownload.cpp18
-rw-r--r--logic/net/MD5EtagDownload.cpp16
-rw-r--r--logic/net/NetAction.h23
-rw-r--r--logic/net/NetJob.h12
-rw-r--r--logic/net/PasteUpload.cpp2
-rw-r--r--logic/net/S3ListBucket.cpp175
-rw-r--r--logic/net/S3ListBucket.h57
-rw-r--r--logic/updater/DownloadUpdateTask.cpp172
-rw-r--r--logic/updater/DownloadUpdateTask.h34
-rw-r--r--logic/updater/UpdateChecker.cpp2
-rw-r--r--logic/updater/UpdateChecker.h5
21 files changed, 244 insertions, 369 deletions
diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp
index 6f8222b7..bc82fee1 100644
--- a/logic/BaseInstance.cpp
+++ b/logic/BaseInstance.cpp
@@ -85,11 +85,6 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
settings().registerSetting(
new OverrideSetting("PermGen", globalSettings->getSetting("PermGen")));
- // Auto login
- settings().registerSetting(new Setting("OverrideLogin", false));
- settings().registerSetting(
- new OverrideSetting("AutoLogin", globalSettings->getSetting("AutoLogin")));
-
// Console
settings().registerSetting(new Setting("OverrideConsole", false));
settings().registerSetting(
diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h
index 93e57414..5f426676 100644
--- a/logic/BaseInstance.h
+++ b/logic/BaseInstance.h
@@ -149,7 +149,7 @@ public:
*/
virtual SettingsObject &settings() const;
- /// returns a valid update task if update is needed, NULL otherwise
+ /// returns a valid update task
virtual std::shared_ptr<Task> doUpdate(bool only_prepare) = 0;
/// returns a valid minecraft process, ready for launch with the given account.
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
index 66950fc4..696eeff0 100644
--- a/logic/OneSixUpdate.cpp
+++ b/logic/OneSixUpdate.cpp
@@ -54,11 +54,9 @@ void OneSixUpdate::executeTask()
if (m_only_prepare)
{
- if (m_inst->shouldUpdate())
- {
- emitFailed("Unable to update instance in offline mode.");
- return;
- }
+ /*
+ * FIXME: in offline mode, do not proceed!
+ */
setStatus("Testing the Java installation.");
QString java_path = m_inst->settings().get("JavaPath").toString();
@@ -243,6 +241,7 @@ void OneSixUpdate::assetIndexFinished()
auto objectDL = MD5EtagDownload::make(
QUrl("http://" + URLConstants::RESOURCE_BASE + objectName),
objectFile.filePath());
+ objectDL->m_total_progress = object.size;
dls.append(objectDL);
}
}
diff --git a/logic/auth/MojangAccountList.cpp b/logic/auth/MojangAccountList.cpp
index 33990662..70bc0cf2 100644
--- a/logic/auth/MojangAccountList.cpp
+++ b/logic/auth/MojangAccountList.cpp
@@ -27,6 +27,7 @@
#include "logger/QsLog.h"
#include "logic/auth/MojangAccount.h"
+#include <pathutils.h>
#define ACCOUNT_LIST_FORMAT_VERSION 2
@@ -265,11 +266,6 @@ bool MojangAccountList::loadList(const QString &filePath)
return false;
}
- if (!QDir::current().exists(path))
- {
- QDir::current().mkpath(path);
- }
-
QFile file(path);
// Try to open the file and fail if we can't.
@@ -351,9 +347,16 @@ bool MojangAccountList::saveList(const QString &filePath)
return false;
}
- if (!QDir::current().exists(path))
+ // make sure the parent folder exists
+ if(!ensureFilePathExists(path))
+ return false;
+
+ // make sure the file wasn't overwritten with a folder before (fixes a bug)
+ QFileInfo finfo(path);
+ if(finfo.isDir())
{
- QDir::current().mkpath(path);
+ QDir badDir(path);
+ badDir.removeRecursively();
}
QLOG_INFO() << "Writing account list to" << path;
@@ -411,3 +414,13 @@ void MojangAccountList::setListFilePath(QString path, bool autosave)
m_listFilePath = path;
m_autosave = autosave;
}
+
+bool MojangAccountList::anyAccountIsValid()
+{
+ for(auto account:m_accounts)
+ {
+ if(account->accountStatus() != NotVerified)
+ return true;
+ }
+ return false;
+}
diff --git a/logic/auth/MojangAccountList.h b/logic/auth/MojangAccountList.h
index c7e30958..6f4fbb17 100644
--- a/logic/auth/MojangAccountList.h
+++ b/logic/auth/MojangAccountList.h
@@ -126,6 +126,11 @@ public:
* If the username given is an empty string, sets the active account to nothing.
*/
virtual void setActiveAccount(const QString &username);
+
+ /*!
+ * Returns true if any of the account is at least Validated
+ */
+ bool anyAccountIsValid();
signals:
/*!
diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp
index 94481fb9..15fd10ba 100644
--- a/logic/lists/InstanceList.cpp
+++ b/logic/lists/InstanceList.cpp
@@ -117,6 +117,11 @@ void InstanceList::groupChanged()
saveGroupList();
}
+QStringList InstanceList::getGroups()
+{
+ return m_groups.toList();
+}
+
void InstanceList::saveGroupList()
{
QString groupFileName = m_instDir + "/instgroups.json";
@@ -126,7 +131,7 @@ void InstanceList::saveGroupList()
if (!groupFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
// An error occurred. Ignore it.
- QLOG_ERROR() << "Failed to read instance group file.";
+ QLOG_ERROR() << "Failed to save instance group file.";
return;
}
QTextStream out(&groupFile);
@@ -137,6 +142,10 @@ void InstanceList::saveGroupList()
QString group = instance->group();
if (group.isEmpty())
continue;
+
+ // keep a list/set of groups for choosing
+ m_groups.insert(group);
+
if (!groupMap.count(group))
{
QSet<QString> set;
@@ -253,6 +262,9 @@ void InstanceList::loadGroupList(QMap<QString, QString> &groupMap)
continue;
}
+ // keep a list/set of groups for choosing
+ m_groups.insert(groupName);
+
// Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray();
diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h
index c3bb74cd..f23b7763 100644
--- a/logic/lists/InstanceList.h
+++ b/logic/lists/InstanceList.h
@@ -17,6 +17,7 @@
#include <QObject>
#include <QAbstractListModel>
+#include <QSet>
#include "categorizedsortfilterproxymodel.h"
#include <QIcon>
@@ -97,6 +98,9 @@ public:
InstancePtr getInstanceById(QString id) const;
QModelIndex getInstanceIndexById(const QString &id) const;
+
+ // FIXME: instead of iterating through all instances and forming a set, keep the set around
+ QStringList getGroups();
signals:
void dataIsInvalid();
@@ -116,6 +120,7 @@ private:
protected:
QString m_instDir;
QList<InstancePtr> m_instances;
+ QSet<QString> m_groups;
};
class InstanceProxyModel : public KCategorizedSortFilterProxyModel
diff --git a/logic/net/ByteArrayDownload.cpp b/logic/net/ByteArrayDownload.cpp
index af5af8e9..27d2a250 100644
--- a/logic/net/ByteArrayDownload.cpp
+++ b/logic/net/ByteArrayDownload.cpp
@@ -42,7 +42,9 @@ void ByteArrayDownload::start()
void ByteArrayDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
- emit progress(index_within_job, bytesReceived, bytesTotal);
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit progress(m_index_within_job, bytesReceived, bytesTotal);
}
void ByteArrayDownload::downloadError(QNetworkReply::NetworkError error)
@@ -62,14 +64,14 @@ void ByteArrayDownload::downloadFinished()
m_status = Job_Finished;
m_data = m_reply->readAll();
m_reply.reset();
- emit succeeded(index_within_job);
+ emit succeeded(m_index_within_job);
return;
}
// else the download failed
else
{
m_reply.reset();
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
}
diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp
index 873d3a2e..6eadae39 100644
--- a/logic/net/CacheDownload.cpp
+++ b/logic/net/CacheDownload.cpp
@@ -35,14 +35,14 @@ void CacheDownload::start()
{
if (!m_entry->stale)
{
- emit succeeded(index_within_job);
+ emit succeeded(m_index_within_job);
return;
}
m_output_file.setFileName(m_target_path);
// if there already is a file and md5 checking is in effect and it can be opened
if (!ensureFilePathExists(m_target_path))
{
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
QLOG_INFO() << "Downloading " << m_url.toString();
@@ -69,7 +69,9 @@ void CacheDownload::start()
void CacheDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
- emit progress(index_within_job, bytesReceived, bytesTotal);
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit progress(m_index_within_job, bytesReceived, bytesTotal);
}
void CacheDownload::downloadError(QNetworkReply::NetworkError error)
@@ -116,7 +118,7 @@ void CacheDownload::downloadFinished()
MMC->metacache()->updateEntry(m_entry);
m_reply.reset();
- emit succeeded(index_within_job);
+ emit succeeded(m_index_within_job);
return;
}
// else the download failed
@@ -125,7 +127,7 @@ void CacheDownload::downloadFinished()
m_output_file.close();
m_output_file.remove();
m_reply.reset();
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
}
@@ -140,7 +142,7 @@ void CacheDownload::downloadReadyRead()
* Can't open the file... the job failed
*/
m_reply->abort();
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
}
diff --git a/logic/net/ForgeMirrors.cpp b/logic/net/ForgeMirrors.cpp
index fd7eccca..b224306f 100644
--- a/logic/net/ForgeMirrors.cpp
+++ b/logic/net/ForgeMirrors.cpp
@@ -68,7 +68,7 @@ void ForgeMirrors::deferToFixedList()
"https://www.creeperhost.net/link.php?id=1",
"http://new.creeperrepo.net/forge/maven/"});
injectDownloads();
- emit succeeded(index_within_job);
+ emit succeeded(m_index_within_job);
}
void ForgeMirrors::parseMirrorList()
@@ -88,7 +88,7 @@ void ForgeMirrors::parseMirrorList()
if(!m_mirrors.size())
deferToFixedList();
injectDownloads();
- emit succeeded(index_within_job);
+ emit succeeded(m_index_within_job);
}
void ForgeMirrors::injectDownloads()
@@ -108,7 +108,9 @@ void ForgeMirrors::injectDownloads()
void ForgeMirrors::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
- emit progress(index_within_job, bytesReceived, bytesTotal);
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit progress(m_index_within_job, bytesReceived, bytesTotal);
}
void ForgeMirrors::downloadReadyRead()
diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp
index f119878a..1771d304 100644
--- a/logic/net/ForgeXzDownload.cpp
+++ b/logic/net/ForgeXzDownload.cpp
@@ -44,20 +44,20 @@ void ForgeXzDownload::start()
if (!m_entry->stale)
{
m_status = Job_Finished;
- emit succeeded(index_within_job);
+ emit succeeded(m_index_within_job);
return;
}
// can we actually create the real, final file?
if (!ensureFilePathExists(m_target_path))
{
m_status = Job_Failed;
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
if (m_mirrors.empty())
{
m_status = Job_Failed;
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
@@ -80,7 +80,9 @@ void ForgeXzDownload::start()
void ForgeXzDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
- emit progress(index_within_job, bytesReceived, bytesTotal);
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit progress(m_index_within_job, bytesReceived, bytesTotal);
}
void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error)
@@ -100,7 +102,7 @@ void ForgeXzDownload::failAndTryNextMirror()
m_mirror_index = next;
updateUrl();
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
}
void ForgeXzDownload::updateUrl()
@@ -148,7 +150,7 @@ void ForgeXzDownload::downloadFinished()
m_status = Job_Failed;
m_pack200_xz_file.remove();
m_reply.reset();
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
}
@@ -175,7 +177,7 @@ void ForgeXzDownload::downloadReadyRead()
* Can't open the file... the job failed
*/
m_reply->abort();
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
}
@@ -347,5 +349,5 @@ void ForgeXzDownload::decompressAndInstall()
MMC->metacache()->updateEntry(m_entry);
m_reply.reset();
- emit succeeded(index_within_job);
+ emit succeeded(m_index_within_job);
}
diff --git a/logic/net/MD5EtagDownload.cpp b/logic/net/MD5EtagDownload.cpp
index 4b9f52af..435e854e 100644
--- a/logic/net/MD5EtagDownload.cpp
+++ b/logic/net/MD5EtagDownload.cpp
@@ -44,7 +44,7 @@ void MD5EtagDownload::start()
if (m_check_md5 && hash == m_expected_md5)
{
QLOG_INFO() << "Skipping " << m_url.toString() << ": md5 match.";
- emit succeeded(index_within_job);
+ emit succeeded(m_index_within_job);
return;
}
else
@@ -54,7 +54,7 @@ void MD5EtagDownload::start()
}
if (!ensureFilePathExists(filename))
{
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
@@ -68,7 +68,7 @@ void MD5EtagDownload::start()
// Plus, this way, we don't end up starting a download for a file we can't open.
if (!m_output_file.open(QIODevice::WriteOnly))
{
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
@@ -86,7 +86,9 @@ void MD5EtagDownload::start()
void MD5EtagDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
- emit progress(index_within_job, bytesReceived, bytesTotal);
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit progress(m_index_within_job, bytesReceived, bytesTotal);
}
void MD5EtagDownload::downloadError(QNetworkReply::NetworkError error)
@@ -107,7 +109,7 @@ void MD5EtagDownload::downloadFinished()
QLOG_INFO() << "Finished " << m_url.toString() << " got " << m_reply->rawHeader("ETag").constData();
m_reply.reset();
- emit succeeded(index_within_job);
+ emit succeeded(m_index_within_job);
return;
}
// else the download failed
@@ -115,7 +117,7 @@ void MD5EtagDownload::downloadFinished()
{
m_output_file.close();
m_reply.reset();
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
}
@@ -130,7 +132,7 @@ void MD5EtagDownload::downloadReadyRead()
* Can't open the file... the job failed
*/
m_reply->abort();
- emit failed(index_within_job);
+ emit failed(m_index_within_job);
return;
}
}
diff --git a/logic/net/NetAction.h b/logic/net/NetAction.h
index c96d8f8f..97c96e5d 100644
--- a/logic/net/NetAction.h
+++ b/logic/net/NetAction.h
@@ -39,6 +39,19 @@ public:
virtual ~NetAction() {};
public:
+ virtual qint64 totalProgress() const
+ {
+ return m_total_progress;
+ }
+ virtual qint64 currentProgress() const
+ {
+ return m_progress;
+ }
+ virtual qint64 numberOfFailures() const
+ {
+ return m_failures;
+ }
+public:
/// the network reply
std::shared_ptr<QNetworkReply> m_reply;
@@ -46,10 +59,16 @@ public:
QUrl m_url;
/// The file's status
- JobStatus m_status;
+ JobStatus m_status = Job_NotStarted;
/// index within the parent job
- int index_within_job = 0;
+ 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;
signals:
void started(int index);
diff --git a/logic/net/NetJob.h b/logic/net/NetJob.h
index 6e2e7607..68c4c408 100644
--- a/logic/net/NetJob.h
+++ b/logic/net/NetJob.h
@@ -36,10 +36,16 @@ public:
template <typename T> bool addNetAction(T action)
{
NetActionPtr base = std::static_pointer_cast<NetAction>(action);
- base->index_within_job = downloads.size();
+ base->m_index_within_job = downloads.size();
downloads.append(action);
- parts_progress.append(part_info());
- total_progress++;
+ part_info pi;
+ {
+ pi.current_progress = base->currentProgress();
+ pi.total_progress = base->totalProgress();
+ pi.failures = base->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())
{
diff --git a/logic/net/PasteUpload.cpp b/logic/net/PasteUpload.cpp
index acf09291..fa54d084 100644
--- a/logic/net/PasteUpload.cpp
+++ b/logic/net/PasteUpload.cpp
@@ -78,7 +78,9 @@ bool PasteUpload::parseResult(QJsonDocument doc, QString *parseError)
parseError = new QString(object.value("error").toString());
return false;
}
+ // FIXME: not the place for GUI things.
QString pasteUrl = object.value("paste").toObject().value("link").toString();
QDesktopServices::openUrl(pasteUrl);
return true;
}
+
diff --git a/logic/net/S3ListBucket.cpp b/logic/net/S3ListBucket.cpp
deleted file mode 100644
index 439b7086..00000000
--- a/logic/net/S3ListBucket.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-/* Copyright 2013 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "S3ListBucket.h"
-#include "MultiMC.h"
-#include "logger/QsLog.h"
-#include <QUrlQuery>
-#include <qxmlstream.h>
-#include <QDomDocument>
-
-inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
-{
- QDomNodeList elementList = parent.elementsByTagName(tagname);
- if (elementList.count())
- return elementList.at(0).toElement();
- else
- return QDomElement();
-}
-
-S3ListBucket::S3ListBucket(QUrl url) : NetAction()
-{
- m_url = url;
- m_status = Job_NotStarted;
-}
-
-void S3ListBucket::start()
-{
- QUrl finalUrl = m_url;
- if (current_marker.size())
- {
- QUrlQuery query;
- query.addQueryItem("marker", current_marker);
- finalUrl.setQuery(query);
- }
- QNetworkRequest request(finalUrl);
- request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
- auto worker = MMC->qnam();
- QNetworkReply *rep = worker->get(request);
-
- m_reply = std::shared_ptr<QNetworkReply>(rep);
- 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()));
-}
-
-void S3ListBucket::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
-{
- emit progress(index_within_job, bytesSoFar + bytesReceived, bytesSoFar + bytesTotal);
-}
-
-void S3ListBucket::downloadError(QNetworkReply::NetworkError error)
-{
- // error happened during download.
- QLOG_ERROR() << "Error getting URL:" << m_url.toString().toLocal8Bit()
- << "Network error: " << error;
- m_status = Job_Failed;
-}
-
-void S3ListBucket::processValidReply()
-{
- QLOG_TRACE() << "GOT: " << m_url.toString() << " marker:" << current_marker;
- auto readContents = [&](QXmlStreamReader & xml)
- {
- QString Key, ETag, Size;
- while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "Contents"))
- {
- if (xml.tokenType() == QXmlStreamReader::StartElement)
- {
- if (xml.name() == "Key")
- {
- Key = xml.readElementText();
- }
- if (xml.name() == "ETag")
- {
- ETag = xml.readElementText();
- }
- if (xml.name() == "Size")
- {
- Size = xml.readElementText();
- }
- }
- xml.readNext();
- }
- if (xml.error() != QXmlStreamReader::NoError)
- return;
- objects.append({Key, ETag, Size.toLongLong()});
- };
-
- // nothing went wrong...
- QByteArray ba = m_reply->readAll();
-
- QString xmlErrorMsg;
-
- bool is_truncated = false;
- QXmlStreamReader xml(ba);
- while (!xml.atEnd() && !xml.hasError())
- {
- /* Read next element.*/
- QXmlStreamReader::TokenType token = xml.readNext();
- /* If token is just StartDocument, we'll go to next.*/
- if (token == QXmlStreamReader::StartDocument)
- {
- continue;
- }
- if (token == QXmlStreamReader::StartElement)
- {
- /* If it's named person, we'll dig the information from there.*/
- if (xml.name() == "Contents")
- {
- readContents(xml);
- }
- else if (xml.name() == "IsTruncated")
- {
- is_truncated = (xml.readElementText() == "true");
- }
- }
- }
- if (xml.hasError())
- {
- QLOG_ERROR() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:"
- << xml.errorString() << ba;
- emit failed(index_within_job);
- return;
- }
- if (is_truncated)
- {
- current_marker = objects.last().Key;
- bytesSoFar += m_reply->size();
- m_reply.reset();
- start();
- }
- else
- {
- m_status = Job_Finished;
- m_reply.reset();
- emit succeeded(index_within_job);
- }
- return;
-}
-
-void S3ListBucket::downloadFinished()
-{
- // if the download succeeded
- if (m_status != Job_Failed)
- {
- processValidReply();
- }
- // else the download failed
- else
- {
- m_reply.reset();
- emit failed(index_within_job);
- return;
- }
-}
-
-void S3ListBucket::downloadReadyRead()
-{
- // ~_~
-}
diff --git a/logic/net/S3ListBucket.h b/logic/net/S3ListBucket.h
deleted file mode 100644
index e7c5e05c..00000000
--- a/logic/net/S3ListBucket.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Copyright 2013 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include "NetAction.h"
-
-struct S3Object
-{
- QString Key;
- QString ETag;
- qlonglong size;
-};
-
-typedef std::shared_ptr<class S3ListBucket> S3ListBucketPtr;
-class S3ListBucket : public NetAction
-{
- Q_OBJECT
-public:
- S3ListBucket(QUrl url);
- static S3ListBucketPtr make(QUrl url)
- {
- return S3ListBucketPtr(new S3ListBucket(url));
- }
-
-public:
- QList<S3Object> objects;
-
-public
-slots:
- virtual void start() override;
-
-protected
-slots:
- virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
- virtual void downloadError(QNetworkReply::NetworkError error) override;
- virtual void downloadFinished() override;
- virtual void downloadReadyRead() override;
-
-private:
- void processValidReply();
-
-private:
- qint64 bytesSoFar = 0;
- QString current_marker;
-};
diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp
index d9aab826..d72cfcf6 100644
--- a/logic/updater/DownloadUpdateTask.cpp
+++ b/logic/updater/DownloadUpdateTask.cpp
@@ -45,55 +45,54 @@ void DownloadUpdateTask::executeTask()
findCurrentVersionInfo();
}
-void DownloadUpdateTask::findCurrentVersionInfo()
+void DownloadUpdateTask::processChannels()
{
- setStatus(tr("Finding information about the current version."));
-
auto checker = MMC->updateChecker();
- // This runs after we've tried loading the channel list.
- // If the channel list doesn't need to be loaded, this will be called immediately.
- // If the channel list does need to be loaded, this will be called when it's done.
- auto processFunc = [this, &checker] () -> void
+ // Now, check the channel list again.
+ if (!checker->hasChannels())
{
- // Now, check the channel list again.
- if (checker->hasChannels())
- {
- // We still couldn't load the channel list. Give up. Call loadVersionInfo and return.
- QLOG_INFO() << "Reloading the channel list didn't work. Giving up.";
- loadVersionInfo();
- return;
- }
+ // We still couldn't load the channel list. Give up. Call loadVersionInfo and return.
+ QLOG_INFO() << "Reloading the channel list didn't work. Giving up.";
+ loadVersionInfo();
+ return;
+ }
- QList<UpdateChecker::ChannelListEntry> channels = checker->getChannelList();
- QString channelId = MMC->version().channel;
+ QList<UpdateChecker::ChannelListEntry> channels = checker->getChannelList();
+ QString channelId = MMC->version().channel;
- // Search through the channel list for a channel with the correct ID.
- for (auto channel : channels)
+ // Search through the channel list for a channel with the correct ID.
+ for (auto channel : channels)
+ {
+ if (channel.id == channelId)
{
- if (channel.id == channelId)
- {
- QLOG_INFO() << "Found matching channel.";
- m_cRepoUrl = channel.url;
- break;
- }
+ QLOG_INFO() << "Found matching channel.";
+ m_cRepoUrl = preparePath(channel.url);
+ break;
}
+ }
- // Now that we've done that, load version info.
- loadVersionInfo();
- };
+ // Now that we've done that, load version info.
+ loadVersionInfo();
+}
+
+void DownloadUpdateTask::findCurrentVersionInfo()
+{
+ setStatus(tr("Finding information about the current version."));
- if (checker->hasChannels())
+ auto checker = MMC->updateChecker();
+
+ if (!checker->hasChannels())
{
// Load the channel list and wait for it to finish loading.
QLOG_INFO() << "No channel list entries found. Will try reloading it.";
- QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, processFunc);
+ QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, this, &DownloadUpdateTask::processChannels);
checker->updateChanList();
}
else
{
- processFunc();
+ processChannels();
}
}
@@ -152,12 +151,24 @@ void DownloadUpdateTask::parseDownloadedVersionInfo()
{
setStatus(tr("Reading file lists."));
- parseVersionInfo(NEW_VERSION, &m_nVersionFileList);
+ setStatus(tr("Reading file list for new version."));
+ QLOG_DEBUG() << "Reading file list for new version.";
+ QString error;
+ if (!parseVersionInfo(std::dynamic_pointer_cast<ByteArrayDownload>(
+ m_vinfoNetJob->first())->m_data, &m_nVersionFileList, &error))
+ {
+ emitFailed(error);
+ return;
+ }
// If there is a second entry in the network job's list, load it as the current version's info.
if (m_vinfoNetJob->size() >= 2 && m_vinfoNetJob->operator[](1)->m_status != Job_Failed)
{
- parseVersionInfo(CURRENT_VERSION, &m_cVersionFileList);
+ setStatus(tr("Reading file list for current version."));
+ QLOG_DEBUG() << "Reading file list for current version.";
+ QString error;
+ parseVersionInfo(std::dynamic_pointer_cast<ByteArrayDownload>(
+ m_vinfoNetJob->operator[](1))->m_data, &m_cVersionFileList, &error);
}
// We don't need this any more.
@@ -167,26 +178,15 @@ void DownloadUpdateTask::parseDownloadedVersionInfo()
processFileLists();
}
-void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFileList* list)
+bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileList* list, QString *error)
{
- if (vfile == CURRENT_VERSION) setStatus(tr("Reading file list for current version."));
- else if (vfile == NEW_VERSION) setStatus(tr("Reading file list for new version."));
-
- QLOG_DEBUG() << "Reading file list for" << (vfile == NEW_VERSION ? "new" : "current") << "version.";
-
- QByteArray data;
- {
- ByteArrayDownloadPtr dl = std::dynamic_pointer_cast<ByteArrayDownload>(
- vfile == NEW_VERSION ? m_vinfoNetJob->first() : m_vinfoNetJob->operator[](1));
- data = dl->m_data;
- }
-
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{
- QLOG_ERROR() << "Failed to parse version info JSON:" << jsonError.errorString() << "at" << jsonError.offset;
- return;
+ *error = QString("Failed to parse version info JSON: %1 at %2").arg(jsonError.errorString()).arg(jsonError.offset);
+ QLOG_ERROR() << error;
+ return false;
}
QJsonObject json = jsonDoc.object();
@@ -213,11 +213,11 @@ void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFile
QString type = sourceObj.value("SourceType").toString();
if (type == "http")
{
- file.sources.append(FileSource("http", sourceObj.value("Url").toString()));
+ file.sources.append(FileSource("http", preparePath(sourceObj.value("Url").toString())));
}
else if (type == "httpc")
{
- file.sources.append(FileSource("httpc", sourceObj.value("Url").toString(), sourceObj.value("CompressionType").toString()));
+ file.sources.append(FileSource("httpc", preparePath(sourceObj.value("Url").toString()), sourceObj.value("CompressionType").toString()));
}
else
{
@@ -229,18 +229,41 @@ void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFile
list->append(file);
}
+
+ return true;
}
void DownloadUpdateTask::processFileLists()
{
+ // Create a network job for downloading files.
+ NetJob* netJob = new NetJob("Update Files");
+
+ processFileLists(netJob, m_cVersionFileList, m_nVersionFileList, m_operationList);
+
+ // Add listeners to wait for the downloads to finish.
+ QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::fileDownloadFinished);
+ QObject::connect(netJob, &NetJob::progress, this, &DownloadUpdateTask::fileDownloadProgressChanged);
+ QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::fileDownloadFailed);
+
+ // Now start the download.
+ setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size())));
+ QLOG_DEBUG() << "Begin downloading update files to" << m_updateFilesDir.path();
+ m_filesNetJob.reset(netJob);
+ netJob->start();
+
+ writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml"));
+}
+
+void DownloadUpdateTask::processFileLists(NetJob *job, const VersionFileList &currentVersion, const VersionFileList &newVersion, DownloadUpdateTask::UpdateOperationList &ops)
+{
setStatus(tr("Processing file lists. Figuring out how to install the update."));
// First, if we've loaded the current version's file list, we need to iterate through it and
// delete anything in the current one version's list that isn't in the new version's list.
- for (VersionFileEntry entry : m_cVersionFileList)
+ for (VersionFileEntry entry : currentVersion)
{
bool keep = false;
- for (VersionFileEntry newEntry : m_nVersionFileList)
+ for (VersionFileEntry newEntry : newVersion)
{
if (newEntry.path == entry.path)
{
@@ -251,14 +274,11 @@ void DownloadUpdateTask::processFileLists()
}
// If the loop reaches the end and we didn't find a match, delete the file.
if(!keep)
- m_operationList.append(UpdateOperation::DeleteOp(entry.path));
+ ops.append(UpdateOperation::DeleteOp(entry.path));
}
- // Create a network job for downloading files.
- NetJob* netJob = new NetJob("Update Files");
-
// Next, check each file in MultiMC's folder and see if we need to update them.
- for (VersionFileEntry entry : m_nVersionFileList)
+ for (VersionFileEntry entry : newVersion)
{
// TODO: Let's not MD5sum a ton of files on the GUI thread. We should probably find a way to do this in the background.
QString fileMD5;
@@ -285,34 +305,24 @@ void DownloadUpdateTask::processFileLists()
// Download it to updatedir/<filepath>-<md5> where filepath is the file's path with slashes replaced by underscores.
QString dlPath = PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_"));
- // We need to download the file to the updatefiles folder and add a task to copy it to its install path.
- auto download = MD5EtagDownload::make(source.url, dlPath);
- download->m_check_md5 = true;
- download->m_expected_md5 = entry.md5;
- netJob->addNetAction(download);
+ if (job)
+ {
+ // We need to download the file to the updatefiles folder and add a task to copy it to its install path.
+ auto download = MD5EtagDownload::make(source.url, dlPath);
+ download->m_check_md5 = true;
+ download->m_expected_md5 = entry.md5;
+ job->addNetAction(download);
+ }
// Now add a copy operation to our operations list to install the file.
- m_operationList.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode));
+ ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode));
}
}
}
}
-
- // Add listeners to wait for the downloads to finish.
- QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::fileDownloadFinished);
- QObject::connect(netJob, &NetJob::progress, this, &DownloadUpdateTask::fileDownloadProgressChanged);
- QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::fileDownloadFailed);
-
- // Now start the download.
- setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size())));
- QLOG_DEBUG() << "Begin downloading update files to" << m_updateFilesDir.path();
- m_filesNetJob.reset(netJob);
- netJob->start();
-
- writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml"));
}
-void DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile)
+bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile)
{
// Build the base structure of the XML document.
QDomDocument doc;
@@ -377,7 +387,15 @@ void DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QStrin
else
{
emitFailed(tr("Failed to write update script file."));
+ return false;
}
+
+ return true;
+}
+
+QString DownloadUpdateTask::preparePath(const QString &path)
+{
+ return QString(path).replace("$PWD", qApp->applicationDirPath());
}
void DownloadUpdateTask::fileDownloadFinished()
diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h
index f5b23d12..8530be77 100644
--- a/logic/updater/DownloadUpdateTask.h
+++ b/logic/updater/DownloadUpdateTask.h
@@ -34,7 +34,8 @@ public:
*/
QString updateFilesDir();
-protected:
+public:
+
// TODO: We should probably put these data structures into a separate header...
/*!
@@ -53,7 +54,6 @@ protected:
QString url;
QString compressionType;
};
-
typedef QList<FileSource> FileSourceList;
/*!
@@ -66,10 +66,8 @@ protected:
FileSourceList sources;
QString md5;
};
-
typedef QList<VersionFileEntry> VersionFileList;
-
/*!
* Structure that describes an operation to perform when installing updates.
*/
@@ -100,9 +98,12 @@ protected:
// Yeah yeah, polymorphism blah blah inheritance, blah blah object oriented. I'm lazy, OK?
};
-
typedef QList<UpdateOperation> UpdateOperationList;
+protected:
+ friend class DownloadUpdateTaskTest;
+
+
/*!
* Used for arguments to parseVersionInfo and friends to specify which version info file to parse.
*/
@@ -120,6 +121,13 @@ protected:
virtual void findCurrentVersionInfo();
/*!
+ * This runs after we've tried loading the channel list.
+ * If the channel list doesn't need to be loaded, this will be called immediately.
+ * If the channel list does need to be loaded, this will be called when it's done.
+ */
+ void processChannels();
+
+ /*!
* Downloads the version info files from the repository.
* The files for both the current build, and the build that we're updating to need to be downloaded.
* If the current version's info file can't be found, MultiMC will not delete files that
@@ -142,20 +150,25 @@ protected:
/*!
* Loads the file list from the given version info JSON object into the given list.
*/
- virtual void parseVersionInfo(VersionInfoFileEnum vfile, VersionFileList* list);
+ virtual bool parseVersionInfo(const QByteArray &data, VersionFileList* list, QString *error);
/*!
* Takes a list of file entries for the current version's files and the new version's files
* and populates the downloadList and operationList with information about how to download and install the update.
*/
+ virtual void processFileLists(NetJob *job, const VersionFileList &currentVersion, const VersionFileList &newVersion, UpdateOperationList &ops);
+
+ /*!
+ * Calls \see processFileLists to populate the \see m_operationList and a NetJob, and then executes
+ * the NetJob to fetch all needed files
+ */
virtual void processFileLists();
/*!
* Takes the operations list and writes an install script for the updater to the update files directory.
*/
- virtual void writeInstallScript(UpdateOperationList& opsList, QString scriptFile);
+ virtual bool writeInstallScript(UpdateOperationList& opsList, QString scriptFile);
- VersionFileList m_downloadList;
UpdateOperationList m_operationList;
VersionFileList m_nVersionFileList;
@@ -181,6 +194,11 @@ protected:
*/
QTemporaryDir m_updateFilesDir;
+ /*!
+ * Substitutes $PWD for the application directory
+ */
+ static QString preparePath(const QString &path);
+
protected slots:
void vinfoDownloadFinished();
void vinfoDownloadFailed();
diff --git a/logic/updater/UpdateChecker.cpp b/logic/updater/UpdateChecker.cpp
index 5ff1898e..af56288c 100644
--- a/logic/updater/UpdateChecker.cpp
+++ b/logic/updater/UpdateChecker.cpp
@@ -44,7 +44,7 @@ QList<UpdateChecker::ChannelListEntry> UpdateChecker::getChannelList() const
bool UpdateChecker::hasChannels() const
{
- return m_channels.isEmpty();
+ return !m_channels.isEmpty();
}
void UpdateChecker::checkForUpdate()
diff --git a/logic/updater/UpdateChecker.h b/logic/updater/UpdateChecker.h
index 59fb8e47..5b7efc05 100644
--- a/logic/updater/UpdateChecker.h
+++ b/logic/updater/UpdateChecker.h
@@ -27,6 +27,9 @@ public:
UpdateChecker();
void checkForUpdate();
+ void setCurrentChannel(const QString &channel) { m_currentChannel = channel; }
+ void setChannelListUrl(const QString &url) { m_channelListUrl = url; }
+
/*!
* Causes the update checker to download the channel list from the URL specified in config.h (generated by CMake).
* If this isn't called before checkForUpdate(), it will automatically be called.
@@ -70,6 +73,8 @@ private slots:
void chanListDownloadFailed();
private:
+ friend class UpdateCheckerTest;
+
NetJobPtr indexJob;
NetJobPtr chanListJob;