diff options
| author | Trial97 <alexandru.tripon97@gmail.com> | 2023-05-31 20:12:12 +0300 | 
|---|---|---|
| committer | Trial97 <alexandru.tripon97@gmail.com> | 2023-05-31 20:12:12 +0300 | 
| commit | 29c3dc40ef7f5b1fce5ab5970a39613d0f7f5089 (patch) | |
| tree | ce92f8a86d08531879105a16194a14391c0ae2ea /launcher/net | |
| parent | e8ee4497f77a571b305a48b70f84c8729b800859 (diff) | |
| parent | 954d4d701a136e79c25b58f9680d26a555a6e6fe (diff) | |
| download | PrismLauncher-29c3dc40ef7f5b1fce5ab5970a39613d0f7f5089.tar.gz PrismLauncher-29c3dc40ef7f5b1fce5ab5970a39613d0f7f5089.tar.bz2 PrismLauncher-29c3dc40ef7f5b1fce5ab5970a39613d0f7f5089.zip | |
Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into logdir
Diffstat (limited to 'launcher/net')
| -rw-r--r-- | launcher/net/Download.cpp | 122 | ||||
| -rw-r--r-- | launcher/net/Download.h | 15 | ||||
| -rw-r--r-- | launcher/net/FileSink.cpp | 12 | ||||
| -rw-r--r-- | launcher/net/FileSink.h | 2 | ||||
| -rw-r--r-- | launcher/net/HttpMetaCache.cpp | 20 | ||||
| -rw-r--r-- | launcher/net/HttpMetaCache.h | 2 | ||||
| -rw-r--r-- | launcher/net/Logging.cpp | 26 | ||||
| -rw-r--r-- | launcher/net/Logging.h | 28 | ||||
| -rw-r--r-- | launcher/net/MetaCacheSink.cpp | 13 | ||||
| -rw-r--r-- | launcher/net/MetaCacheSink.h | 2 | ||||
| -rw-r--r-- | launcher/net/NetAction.h | 22 | ||||
| -rw-r--r-- | launcher/net/NetJob.cpp | 8 | ||||
| -rw-r--r-- | launcher/net/NetJob.h | 3 | ||||
| -rw-r--r-- | launcher/net/PasteUpload.cpp | 26 | ||||
| -rw-r--r-- | launcher/net/PasteUpload.h | 2 | ||||
| -rw-r--r-- | launcher/net/Upload.cpp | 64 | ||||
| -rw-r--r-- | launcher/net/Upload.h | 7 | 
17 files changed, 257 insertions, 117 deletions
| diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index fd3dbedc..7f8d3a06 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -1,8 +1,10 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> + *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -35,29 +37,31 @@   */  #include "Download.h" +#include <QUrl>  #include <QDateTime>  #include <QFileInfo>  #include "ByteArraySink.h"  #include "ChecksumValidator.h" -#include "FileSystem.h"  #include "MetaCacheSink.h" -#include "BuildConfig.h"  #include "Application.h" +#include "BuildConfig.h" -namespace Net { +#include "net/Logging.h" +#include "net/NetAction.h" -Download::Download() : NetAction() -{ -    m_state = State::Inactive; -} +#include "MMCTime.h" +#include "StringUtils.h" + +namespace Net {  auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr  { -    auto* dl = new Download(); +    auto dl = makeShared<Download>();      dl->m_url = url; +    dl->setObjectName(QString("CACHE:") + url.toString());      dl->m_options = options;      auto md5Node = new ChecksumValidator(QCryptographicHash::Md5);      auto cachedNode = new MetaCacheSink(entry, md5Node, options.testFlag(Option::MakeEternal)); @@ -67,8 +71,9 @@ auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Down  auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> Download::Ptr  { -    auto* dl = new Download(); +    auto dl = makeShared<Download>();      dl->m_url = url; +    dl->setObjectName(QString("BYTES:") + url.toString());      dl->m_options = options;      dl->m_sink.reset(new ByteArraySink(output));      return dl; @@ -76,8 +81,9 @@ auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> D  auto Download::makeFile(QUrl url, QString path, Options options) -> Download::Ptr  { -    auto* dl = new Download(); +    auto dl = makeShared<Download>();      dl->m_url = url; +    dl->setObjectName(QString("FILE:") + url.toString());      dl->m_options = options;      dl->m_sink.reset(new FileSink(path));      return dl; @@ -90,10 +96,10 @@ void Download::addValidator(Validator* v)  void Download::executeTask()  { -    setStatus(tr("Downloading %1").arg(m_url.toString())); +    setStatus(tr("Downloading %1").arg(StringUtils::truncateUrlHumanFriendly(m_url, 80)));      if (getState() == Task::State::AbortedByUser) { -        qWarning() << "Attempt to start an aborted Download:" << m_url.toString(); +        qCWarning(taskDownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString();          emitAborted();          return;      } @@ -103,10 +109,10 @@ void Download::executeTask()      switch (m_state) {          case State::Succeeded:              emit succeeded(); -            qDebug() << "Download cache hit " << m_url.toString(); +            qCDebug(taskDownloadLogC) << getUid().toString() << "Download cache hit " << m_url.toString();              return;          case State::Running: -            qDebug() << "Downloading " << m_url.toString(); +            qCDebug(taskDownloadLogC) << getUid().toString() << "Downloading " << m_url.toString();              break;          case State::Inactive:          case State::Failed: @@ -118,20 +124,31 @@ void Download::executeTask()      }      request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); -    if (APPLICATION->capabilities() & Application::SupportsFlame -            && request.url().host().contains("api.curseforge.com")) { +    // TODO remove duplication +    if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) {          request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); -    }; +    } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || +               request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { +        QString token = APPLICATION->getModrinthAPIToken(); +        if (!token.isNull()) +            request.setRawHeader("Authorization", token.toUtf8()); +    } -    QNetworkReply* rep = m_network->get(request); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) +    request.setTransferTimeout(); +#endif +    m_last_progress_time = m_clock.now(); +    m_last_progress_bytes = 0; + +    QNetworkReply* rep = m_network->get(request);      m_reply.reset(rep);      connect(rep, &QNetworkReply::downloadProgress, this, &Download::downloadProgress);      connect(rep, &QNetworkReply::finished, this, &Download::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -    connect(rep, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +    connect(rep, &QNetworkReply::errorOccurred, this, &Download::downloadError);  #else -    connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); +    connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Download::downloadError);  #endif      connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors);      connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead); @@ -139,13 +156,39 @@ void Download::executeTask()  void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)  { +    auto now = m_clock.now(); +    auto elapsed = now - m_last_progress_time; + +    // use milliseconds for speed precision +    auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed); +    auto bytes_received_since = bytesReceived - m_last_progress_bytes; +    auto dl_speed_bps = (double)bytes_received_since / elapsed_ms.count() * 1000; +    auto remaing_time_s = (bytesTotal - bytesReceived) / dl_speed_bps; + +    //: Current amount of bytes downloaded, out of the total amount of bytes in the download +    QString dl_progress = +        tr("%1 / %2").arg(StringUtils::humanReadableFileSize(bytesReceived)).arg(StringUtils::humanReadableFileSize(bytesTotal)); + +    QString dl_speed_str; +    if (elapsed_ms.count() > 0) { +        auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaing_time_s) : tr("unknown"); +        //: Download speed, in bytes per second (remaining download time in parenthesis) +        dl_speed_str = +            tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta); +    } else { +        //: Download speed at 0 bytes per second +        dl_speed_str = tr("0 B/s"); +    } + +    setDetails(dl_progress + "\n" + dl_speed_str); +      setProgress(bytesReceived, bytesTotal);  }  void Download::downloadError(QNetworkReply::NetworkError error)  {      if (error == QNetworkReply::OperationCanceledError) { -        qCritical() << "Aborted " << m_url.toString(); +        qCCritical(taskDownloadLogC) << getUid().toString() << "Aborted " << m_url.toString();          m_state = State::AbortedByUser;      } else {          if (m_options & Option::AcceptLocalFiles) { @@ -155,7 +198,7 @@ void Download::downloadError(QNetworkReply::NetworkError error)              }          }          // error happened during download. -        qCritical() << "Failed " << m_url.toString() << " with reason " << error; +        qCCritical(taskDownloadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;          m_state = State::Failed;      }  } @@ -164,9 +207,10 @@ void Download::sslErrors(const QList<QSslError>& errors)  {      int i = 1;      for (auto error : errors) { -        qCritical() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString(); +        qCCritical(taskDownloadLogC) << getUid().toString() << "Download" << m_url.toString() << "SSL Error #" << i << " : " +                                     << error.errorString();          auto cert = error.certificate(); -        qCritical() << "Certificate in question:\n" << cert.toText(); +        qCCritical(taskDownloadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();          i++;      }  } @@ -209,17 +253,17 @@ auto Download::handleRedirect() -> bool           */          redirect = QUrl(redirectStr, QUrl::TolerantMode);          if (!redirect.isValid()) { -            qWarning() << "Failed to parse redirect URL:" << redirectStr; +            qCWarning(taskDownloadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;              downloadError(QNetworkReply::ProtocolFailure);              return false;          } -        qDebug() << "Fixed location header:" << redirect; +        qCDebug(taskDownloadLogC) << getUid().toString() << "Fixed location header:" << redirect;      } else { -        qDebug() << "Location header:" << redirect; +        qCDebug(taskDownloadLogC) << getUid().toString() << "Location header:" << redirect;      }      m_url = QUrl(redirect.toString()); -    qDebug() << "Following redirect to " << m_url.toString(); +    qCDebug(taskDownloadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();      startAction(m_network);      return true; @@ -229,26 +273,26 @@ void Download::downloadFinished()  {      // handle HTTP redirection first      if (handleRedirect()) { -        qDebug() << "Download redirected:" << m_url.toString(); +        qCDebug(taskDownloadLogC) << getUid().toString() << "Download redirected:" << m_url.toString();          return;      }      // if the download failed before this point ...      if (m_state == State::Succeeded)  // pretend to succeed so we continue processing :)      { -        qDebug() << "Download failed but we are allowed to proceed:" << m_url.toString(); +        qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed but we are allowed to proceed:" << m_url.toString();          m_sink->abort();          m_reply.reset();          emit succeeded();          return;      } else if (m_state == State::Failed) { -        qDebug() << "Download failed in previous step:" << m_url.toString(); +        qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed in previous step:" << m_url.toString();          m_sink->abort();          m_reply.reset();          emit failed("");          return;      } else if (m_state == State::AbortedByUser) { -        qDebug() << "Download aborted in previous step:" << m_url.toString(); +        qCDebug(taskDownloadLogC) << getUid().toString() << "Download aborted in previous step:" << m_url.toString();          m_sink->abort();          m_reply.reset();          emit aborted(); @@ -258,14 +302,14 @@ void Download::downloadFinished()      // make sure we got all the remaining data, if any      auto data = m_reply->readAll();      if (data.size()) { -        qDebug() << "Writing extra" << data.size() << "bytes"; +        qCDebug(taskDownloadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";          m_state = m_sink->write(data);      }      // otherwise, finalize the whole graph      m_state = m_sink->finalize(*m_reply.get());      if (m_state != State::Succeeded) { -        qDebug() << "Download failed to finalize:" << m_url.toString(); +        qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed to finalize:" << m_url.toString();          m_sink->abort();          m_reply.reset();          emit failed(""); @@ -273,7 +317,7 @@ void Download::downloadFinished()      }      m_reply.reset(); -    qDebug() << "Download succeeded:" << m_url.toString(); +    qCDebug(taskDownloadLogC) << getUid().toString() << "Download succeeded:" << m_url.toString();      emit succeeded();  } @@ -283,11 +327,11 @@ void Download::downloadReadyRead()          auto data = m_reply->readAll();          m_state = m_sink->write(data);          if (m_state == State::Failed) { -            qCritical() << "Failed to process response chunk"; +            qCCritical(taskDownloadLogC) << getUid().toString() << "Failed to process response chunk";          }          // qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes";      } else { -        qCritical() << "Cannot write download data! illegal status " << m_status; +        qCCritical(taskDownloadLogC) << getUid().toString() << "Cannot write download data! illegal status " << m_status;      }  } diff --git a/launcher/net/Download.h b/launcher/net/Download.h index 3faa5db5..920164a3 100644 --- a/launcher/net/Download.h +++ b/launcher/net/Download.h @@ -1,8 +1,9 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -22,6 +23,7 @@   *      Copyright 2013-2021 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   * @@ -36,6 +38,8 @@  #pragma once +#include <chrono> +  #include "HttpMetaCache.h"  #include "NetAction.h"  #include "Sink.h" @@ -52,9 +56,6 @@ class Download : public NetAction {      enum class Option { NoOptions = 0, AcceptLocalFiles = 1, MakeEternal = 2 };      Q_DECLARE_FLAGS(Options, Option) -   protected: -    explicit Download(); -     public:      ~Download() override = default; @@ -73,7 +74,7 @@ class Download : public NetAction {     protected slots:      void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;      void downloadError(QNetworkReply::NetworkError error) override; -    void sslErrors(const QList<QSslError>& errors); +    void sslErrors(const QList<QSslError>& errors) override;      void downloadFinished() override;      void downloadReadyRead() override; @@ -83,6 +84,10 @@ class Download : public NetAction {     private:      std::unique_ptr<Sink> m_sink;      Options m_options; + +    std::chrono::steady_clock m_clock; +    std::chrono::time_point<std::chrono::steady_clock> m_last_progress_time; +    qint64 m_last_progress_bytes;  };  }  // namespace Net diff --git a/launcher/net/FileSink.cpp b/launcher/net/FileSink.cpp index ba0caf6c..1ecb21fd 100644 --- a/launcher/net/FileSink.cpp +++ b/launcher/net/FileSink.cpp @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *   *  This program is free software: you can redistribute it and/or modify @@ -37,6 +37,8 @@  #include "FileSystem.h" +#include "net/Logging.h" +  namespace Net {  Task::State FileSink::init(QNetworkRequest& request) @@ -48,14 +50,14 @@ Task::State FileSink::init(QNetworkRequest& request)      // create a new save file and open it for writing      if (!FS::ensureFilePathExists(m_filename)) { -        qCritical() << "Could not create folder for " + m_filename; +        qCCritical(taskNetLogC) << "Could not create folder for " + m_filename;          return Task::State::Failed;      }      wroteAnyData = false;      m_output_file.reset(new QSaveFile(m_filename));      if (!m_output_file->open(QIODevice::WriteOnly)) { -        qCritical() << "Could not open " + m_filename + " for writing"; +        qCCritical(taskNetLogC) << "Could not open " + m_filename + " for writing";          return Task::State::Failed;      } @@ -67,7 +69,7 @@ Task::State FileSink::init(QNetworkRequest& request)  Task::State FileSink::write(QByteArray& data)  {      if (!writeAllValidators(data) || m_output_file->write(data) != data.size()) { -        qCritical() << "Failed writing into " + m_filename; +        qCCritical(taskNetLogC) << "Failed writing into " + m_filename;          m_output_file->cancelWriting();          m_output_file.reset();          wroteAnyData = false; @@ -106,7 +108,7 @@ Task::State FileSink::finalize(QNetworkReply& reply)          // nothing went wrong...          if (!m_output_file->commit()) { -            qCritical() << "Failed to commit changes to " << m_filename; +            qCCritical(taskNetLogC) << "Failed to commit changes to " << m_filename;              m_output_file->cancelWriting();              return Task::State::Failed;          } diff --git a/launcher/net/FileSink.h b/launcher/net/FileSink.h index dffbdca6..40134b5f 100644 --- a/launcher/net/FileSink.h +++ b/launcher/net/FileSink.h @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *   *  This program is free software: you can redistribute it and/or modify diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp index 0d7ca769..689dbac9 100644 --- a/launcher/net/HttpMetaCache.cpp +++ b/launcher/net/HttpMetaCache.cpp @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *   *  This program is free software: you can redistribute it and/or modify @@ -44,6 +44,8 @@  #include <QDebug> +#include "net/Logging.h" +  auto MetaEntry::getFullPath() -> QString  {      // FIXME: make local? @@ -55,7 +57,7 @@ HttpMetaCache::HttpMetaCache(QString path) : QObject(), m_index_file(path)      saveBatchingTimer.setSingleShot(true);      saveBatchingTimer.setTimerType(Qt::VeryCoarseTimer); -    connect(&saveBatchingTimer, SIGNAL(timeout()), SLOT(SaveNow())); +    connect(&saveBatchingTimer, &QTimer::timeout, this, &HttpMetaCache::SaveNow);  }  HttpMetaCache::~HttpMetaCache() @@ -124,7 +126,7 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex      // Get rid of old entries, to prevent cache problems      auto current_time = QDateTime::currentSecsSinceEpoch();      if (entry->isExpired(current_time - ( file_last_changed / 1000 ))) { -        qWarning() << "Removing cache entry because of old age!"; +        qCWarning(taskNetLogC) << "[HttpMetaCache]" << "Removing cache entry because of old age!";          selected_base.entry_list.remove(resource_path);          return staleEntry(base, resource_path);      } @@ -137,12 +139,12 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex  auto HttpMetaCache::updateEntry(MetaEntryPtr stale_entry) -> bool  {      if (!m_entries.contains(stale_entry->m_baseId)) { -        qCritical() << "Cannot add entry with unknown base: " << stale_entry->m_baseId.toLocal8Bit(); +        qCCritical(taskHttpMetaCacheLogC) << "Cannot add entry with unknown base: " << stale_entry->m_baseId.toLocal8Bit();          return false;      }      if (stale_entry->m_stale) { -        qCritical() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit(); +        qCCritical(taskHttpMetaCacheLogC) << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit();          return false;      } @@ -166,10 +168,10 @@ void HttpMetaCache::evictAll()  {      for (QString& base : m_entries.keys()) {          EntryMap& map = m_entries[base]; -        qDebug() << "Evicting base" << base; +        qCDebug(taskHttpMetaCacheLogC) << "Evicting base" << base;          for (MetaEntryPtr entry : map.entry_list) {              if (!evictEntry(entry)) -                qWarning() << "Unexpected missing cache entry" << entry->m_basePath; +                qCWarning(taskHttpMetaCacheLogC) << "Unexpected missing cache entry" << entry->m_basePath;          }      }  } @@ -267,7 +269,7 @@ void HttpMetaCache::SaveNow()      if (m_index_file.isNull())          return; -    qDebug() << "[HttpMetaCache]" << "Saving metacache with" << m_entries.size() << "entries"; +    qCDebug(taskHttpMetaCacheLogC) << "Saving metacache with" << m_entries.size() << "entries";      QJsonObject toplevel;      Json::writeString(toplevel, "version", "1"); @@ -302,6 +304,6 @@ void HttpMetaCache::SaveNow()      try {          Json::write(toplevel, m_index_file);      } catch (const Exception& e) { -        qWarning() << e.what(); +        qCWarning(taskHttpMetaCacheLogC) << "Error writing cache:" << e.what();      }  } diff --git a/launcher/net/HttpMetaCache.h b/launcher/net/HttpMetaCache.h index 37f4b49a..0dcb5668 100644 --- a/launcher/net/HttpMetaCache.h +++ b/launcher/net/HttpMetaCache.h @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *   *  This program is free software: you can redistribute it and/or modify diff --git a/launcher/net/Logging.cpp b/launcher/net/Logging.cpp new file mode 100644 index 00000000..a9b9db7c --- /dev/null +++ b/launcher/net/Logging.cpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + *  Prism Launcher - Minecraft Launcher + *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + *  This program 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, version 3. + * + *  This program 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 this program.  If not, see <https://www.gnu.org/licenses/>. + * + */ + +#include "net/Logging.h" + +Q_LOGGING_CATEGORY(taskNetLogC, "launcher.task.net") +Q_LOGGING_CATEGORY(taskDownloadLogC, "launcher.task.net.download") +Q_LOGGING_CATEGORY(taskUploadLogC, "launcher.task.net.upload") +Q_LOGGING_CATEGORY(taskMetaCacheLogC, "launcher.task.net.metacache") +Q_LOGGING_CATEGORY(taskHttpMetaCacheLogC, "launcher.task.net.metacache.http") diff --git a/launcher/net/Logging.h b/launcher/net/Logging.h new file mode 100644 index 00000000..b692e707 --- /dev/null +++ b/launcher/net/Logging.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + *  Prism Launcher - Minecraft Launcher + *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + *  This program 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, version 3. + * + *  This program 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 this program.  If not, see <https://www.gnu.org/licenses/>. + * + */ +  +#pragma  once + +#include <QLoggingCategory> + +Q_DECLARE_LOGGING_CATEGORY(taskNetLogC) +Q_DECLARE_LOGGING_CATEGORY(taskDownloadLogC) +Q_DECLARE_LOGGING_CATEGORY(taskUploadLogC) +Q_DECLARE_LOGGING_CATEGORY(taskMetaCacheLogC) +Q_DECLARE_LOGGING_CATEGORY(taskHttpMetaCacheLogC) diff --git a/launcher/net/MetaCacheSink.cpp b/launcher/net/MetaCacheSink.cpp index 5ae53c1c..e203bc06 100644 --- a/launcher/net/MetaCacheSink.cpp +++ b/launcher/net/MetaCacheSink.cpp @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *   *  This program is free software: you can redistribute it and/or modify @@ -36,8 +36,11 @@  #include "MetaCacheSink.h"  #include <QFile>  #include <QFileInfo> +#include <QRegularExpression>  #include "Application.h" +#include "net/Logging.h" +  namespace Net {  /** Maximum time to hold a cache entry @@ -96,11 +99,11 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply)      { // Cache lifetime          if (m_is_eternal) { -            qDebug() << "[MetaCache] Adding eternal cache entry:" << m_entry->getFullPath(); +            qCDebug(taskMetaCacheLogC) << "Adding eternal cache entry:" << m_entry->getFullPath();              m_entry->makeEternal(true);          } else if (reply.hasRawHeader("Cache-Control")) {              auto cache_control_header = reply.rawHeader("Cache-Control"); -            // qDebug() << "[MetaCache] Parsing 'Cache-Control' header with" << cache_control_header; +            qCDebug(taskMetaCacheLogC) << "Parsing 'Cache-Control' header with" << cache_control_header;              QRegularExpression max_age_expr("max-age=([0-9]+)");              qint64 max_age = max_age_expr.match(cache_control_header).captured(1).toLongLong(); @@ -108,7 +111,7 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply)          } else if (reply.hasRawHeader("Expires")) {              auto expires_header = reply.rawHeader("Expires"); -            // qDebug() << "[MetaCache] Parsing 'Expires' header with" << expires_header; +            qCDebug(taskMetaCacheLogC) << "Parsing 'Expires' header with" << expires_header;              qint64 max_age = QDateTime::fromString(expires_header).toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch();              m_entry->setMaximumAge(max_age); @@ -118,7 +121,7 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply)          if (reply.hasRawHeader("Age")) {              auto age_header = reply.rawHeader("Age"); -            // qDebug() << "[MetaCache] Parsing 'Age' header with" << age_header; +            qCDebug(taskMetaCacheLogC) << "Parsing 'Age' header with" << age_header;              qint64 current_age = age_header.toLongLong();              m_entry->setCurrentAge(current_age); diff --git a/launcher/net/MetaCacheSink.h b/launcher/net/MetaCacheSink.h index f5948085..f9f7d233 100644 --- a/launcher/net/MetaCacheSink.h +++ b/launcher/net/MetaCacheSink.h @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *   *  This program is free software: you can redistribute it and/or modify diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index d9c4fadc..ab9322c2 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -1,7 +1,8 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com> + *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -44,7 +45,7 @@  class NetAction : public Task {      Q_OBJECT     protected: -    explicit NetAction() : Task() {}; +    explicit NetAction() : Task(){};     public:      using Ptr = shared_qobject_ptr<NetAction>; @@ -52,7 +53,6 @@ class NetAction : public Task {      virtual ~NetAction() = default;      QUrl url() { return m_url; } -    auto index() -> int { return m_index_within_job; }      void setNetwork(shared_qobject_ptr<QNetworkAccessManager> network) { m_network = network; } @@ -62,6 +62,17 @@ class NetAction : public Task {      virtual void downloadFinished() = 0;      virtual void downloadReadyRead() = 0; +    virtual void sslErrors(const QList<QSslError>& errors) { +        int i = 1; +        for (auto error : errors) { +            qCritical() << "Network SSL Error #" << i << " : " << error.errorString(); +            auto cert = error.certificate(); +            qCritical() << "Certificate in question:\n" << cert.toText(); +            i++; +        } + +    }; +     public slots:      void startAction(shared_qobject_ptr<QNetworkAccessManager> network)      { @@ -70,14 +81,11 @@ class NetAction : public Task {      }     protected: -    void executeTask() override {}; +    void executeTask() override{};     public:      shared_qobject_ptr<QNetworkAccessManager> m_network; -    /// index within the parent job, FIXME: nuke -    int m_index_within_job = 0; -      /// the network reply      unique_qobject_ptr<QNetworkReply> m_reply; diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index 9b5d4f1b..3869316e 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -1,8 +1,9 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -38,11 +39,10 @@  auto NetJob::addNetAction(NetAction::Ptr action) -> bool  { -    action->m_index_within_job = m_queue.size(); -    m_queue.append(action); -      action->setNetwork(m_network); +    addTask(action); +      return true;  } diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index cd5d5e48..764cec18 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -1,7 +1,8 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com> + *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by diff --git a/launcher/net/PasteUpload.cpp b/launcher/net/PasteUpload.cpp index 76b86743..595279a3 100644 --- a/launcher/net/PasteUpload.cpp +++ b/launcher/net/PasteUpload.cpp @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>   *  Copyright (C) 2022 Swirl <swurl@swurl.xyz>   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> @@ -41,9 +41,13 @@  #include <QDebug>  #include <QJsonObject> +#include <QHttpPart>  #include <QJsonArray>  #include <QJsonDocument>  #include <QFile> +#include <QUrlQuery> + +#include "net/Logging.h"  std::array<PasteUpload::PasteTypeInfo, 4> PasteUpload::PasteTypes = {      {{"0x0.st", "https://0x0.st", ""}, @@ -145,7 +149,7 @@ void PasteUpload::executeTask()  void PasteUpload::downloadError(QNetworkReply::NetworkError error)  {      // error happened during download. -    qCritical() << "Network error: " << error; +    qCCritical(taskUploadLogC) << getUid().toString() << "Network error: " << error;      emitFailed(m_reply->errorString());  } @@ -164,7 +168,7 @@ void PasteUpload::downloadFinished()      {          QString reasonPhrase = m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();          emitFailed(tr("Error: %1 returned unexpected status code %2 %3").arg(m_uploadUrl).arg(statusCode).arg(reasonPhrase)); -        qCritical() << m_uploadUrl << " returned unexpected status code " << statusCode << " with body: " << data; +        qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned unexpected status code " << statusCode << " with body: " << data;          m_reply.reset();          return;      } @@ -185,7 +189,7 @@ void PasteUpload::downloadFinished()          else          {              emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); -            qCritical() << m_uploadUrl << " returned malformed response body: " << data; +            qCCritical(taskUploadLogC) << getUid().toString() << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data;              return;          }          break; @@ -204,15 +208,15 @@ void PasteUpload::downloadFinished()              {                  QString error = jsonObj["error"].toString();                  emitFailed(tr("Error: %1 returned an error: %2").arg(m_uploadUrl, error)); -                qCritical() << m_uploadUrl << " returned error: " << error; -                qCritical() << "Response body: " << data; +                qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned error: " << error; +                qCCritical(taskUploadLogC) << getUid().toString() << "Response body: " << data;                  return;              }          }          else          {              emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); -            qCritical() << m_uploadUrl << " returned malformed response body: " << data; +            qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data;              return;          }          break; @@ -232,16 +236,16 @@ void PasteUpload::downloadFinished()                  QString error = jsonObj["error"].toString();                  QString message = (jsonObj.contains("message") && jsonObj["message"].isString()) ? jsonObj["message"].toString() : "none";                  emitFailed(tr("Error: %1 returned an error code: %2\nError message: %3").arg(m_uploadUrl, error, message)); -                qCritical() << m_uploadUrl << " returned error: " << error; -                qCritical() << "Error message: " << message; -                qCritical() << "Response body: " << data; +                qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned error: " << error; +                qCCritical(taskUploadLogC) << getUid().toString() << "Error message: " << message; +                qCCritical(taskUploadLogC) << getUid().toString() << "Response body: " << data;                  return;              }          }          else          {              emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); -            qCritical() << m_uploadUrl << " returned malformed response body: " << data; +            qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data;              return;          }          break; diff --git a/launcher/net/PasteUpload.h b/launcher/net/PasteUpload.h index eb315c2b..b72ab5b0 100644 --- a/launcher/net/PasteUpload.h +++ b/launcher/net/PasteUpload.h @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>   *   *  This program is free software: you can redistribute it and/or modify diff --git a/launcher/net/Upload.cpp b/launcher/net/Upload.cpp index f3b19022..4f9553ed 100644 --- a/launcher/net/Upload.cpp +++ b/launcher/net/Upload.cpp @@ -1,8 +1,10 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> + *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -41,6 +43,8 @@  #include "BuildConfig.h"  #include "Application.h" +#include "net/Logging.h" +  namespace Net {      bool Upload::abort() @@ -59,11 +63,11 @@ namespace Net {      void Upload::downloadError(QNetworkReply::NetworkError error) {          if (error == QNetworkReply::OperationCanceledError) { -            qCritical() << "Aborted " << m_url.toString(); +            qCCritical(taskUploadLogC) << getUid().toString() << "Aborted " << m_url.toString();              m_state = State::AbortedByUser;          } else {              // error happened during download. -            qCritical() << "Failed " << m_url.toString() << " with reason " << error; +            qCCritical(taskUploadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;              m_state = State::Failed;          }      } @@ -71,9 +75,9 @@ namespace Net {      void Upload::sslErrors(const QList<QSslError> &errors) {          int i = 1;          for (const auto& error : errors) { -            qCritical() << "Upload" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString(); +            qCCritical(taskUploadLogC) << getUid().toString() << "Upload" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();              auto cert = error.certificate(); -            qCritical() << "Certificate in question:\n" << cert.toText(); +            qCCritical(taskUploadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();              i++;          }      } @@ -116,17 +120,17 @@ namespace Net {               */              redirect = QUrl(redirectStr, QUrl::TolerantMode);              if (!redirect.isValid()) { -                qWarning() << "Failed to parse redirect URL:" << redirectStr; +                qCWarning(taskUploadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;                  downloadError(QNetworkReply::ProtocolFailure);                  return false;              } -            qDebug() << "Fixed location header:" << redirect; +            qCDebug(taskUploadLogC) << getUid().toString() << "Fixed location header:" << redirect;          } else { -            qDebug() << "Location header:" << redirect; +            qCDebug(taskUploadLogC) << getUid().toString() << "Location header:" << redirect;          }          m_url = QUrl(redirect.toString()); -        qDebug() << "Following redirect to " << m_url.toString(); +        qCDebug(taskUploadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();          startAction(m_network);          return true;      } @@ -135,25 +139,25 @@ namespace Net {          // handle HTTP redirection first          // very unlikely for post requests, still can happen          if (handleRedirect()) { -            qDebug() << "Upload redirected:" << m_url.toString(); +            qCDebug(taskUploadLogC) << getUid().toString() << "Upload redirected:" << m_url.toString();              return;          }          // if the download failed before this point ...          if (m_state == State::Succeeded) { -            qDebug() << "Upload failed but we are allowed to proceed:" << m_url.toString(); +            qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed but we are allowed to proceed:" << m_url.toString();              m_sink->abort();              m_reply.reset();              emit succeeded();              return;          } else if (m_state == State::Failed) { -            qDebug() << "Upload failed in previous step:" << m_url.toString(); +            qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed in previous step:" << m_url.toString();              m_sink->abort();              m_reply.reset();              emit failed("");              return;          } else if (m_state == State::AbortedByUser) { -            qDebug() << "Upload aborted in previous step:" << m_url.toString(); +            qCDebug(taskUploadLogC) << getUid().toString() << "Upload aborted in previous step:" << m_url.toString();              m_sink->abort();              m_reply.reset();              emit aborted(); @@ -163,21 +167,21 @@ namespace Net {          // make sure we got all the remaining data, if any          auto data = m_reply->readAll();          if (data.size()) { -            qDebug() << "Writing extra" << data.size() << "bytes"; +            qCDebug(taskUploadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";              m_state = m_sink->write(data);          }          // otherwise, finalize the whole graph          m_state = m_sink->finalize(*m_reply.get());          if (m_state != State::Succeeded) { -            qDebug() << "Upload failed to finalize:" << m_url.toString(); +            qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed to finalize:" << m_url.toString();              m_sink->abort();              m_reply.reset();              emit failed("");              return;          }          m_reply.reset(); -        qDebug() << "Upload succeeded:" << m_url.toString(); +        qCDebug(taskUploadLogC) << getUid().toString() << "Upload succeeded:" << m_url.toString();          emit succeeded();      } @@ -192,7 +196,7 @@ namespace Net {          setStatus(tr("Uploading %1").arg(m_url.toString()));          if (m_state == State::AbortedByUser) { -            qWarning() << "Attempt to start an aborted Upload:" << m_url.toString(); +            qCWarning(taskUploadLogC) << getUid().toString() << "Attempt to start an aborted Upload:" << m_url.toString();              emit aborted();              return;          } @@ -201,10 +205,10 @@ namespace Net {          switch (m_state) {              case State::Succeeded:                  emitSucceeded(); -                qDebug() << "Upload cache hit " << m_url.toString(); +                qCDebug(taskUploadLogC) << getUid().toString() << "Upload cache hit " << m_url.toString();                  return;              case State::Running: -                qDebug() << "Uploading " << m_url.toString(); +                qCDebug(taskUploadLogC) << getUid().toString() << "Uploading " << m_url.toString();                  break;              case State::Inactive:              case State::Failed: @@ -216,24 +220,34 @@ namespace Net {          }          request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); -        if (APPLICATION->capabilities() & Application::SupportsFlame -                && request.url().host().contains("api.curseforge.com")) { +        // TODO remove duplication +        if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) {              request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); +        } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || +                   request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { +            QString token = APPLICATION->getModrinthAPIToken(); +            if (!token.isNull()) +                request.setRawHeader("Authorization", token.toUtf8());          } +          //TODO other types of post requests ?          request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");          QNetworkReply* rep = m_network->post(request, m_post_data);          m_reply.reset(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, &QNetworkReply::downloadProgress, this,  &Upload::downloadProgress); +        connect(rep, &QNetworkReply::finished, this, &Upload::downloadFinished); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +        connect(rep, &QNetworkReply::errorOccurred, this, &Upload::downloadError); +#else +        connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Upload::downloadError); +#endif          connect(rep, &QNetworkReply::sslErrors, this, &Upload::sslErrors);          connect(rep, &QNetworkReply::readyRead, this, &Upload::downloadReadyRead);      }      Upload::Ptr Upload::makeByteArray(QUrl url, QByteArray *output, QByteArray m_post_data) { -        auto* up = new Upload(); +        auto up = makeShared<Upload>();          up->m_url = std::move(url);          up->m_sink.reset(new ByteArraySink(output));          up->m_post_data = std::move(m_post_data); diff --git a/launcher/net/Upload.h b/launcher/net/Upload.h index 7c194bbc..e8f0ea40 100644 --- a/launcher/net/Upload.h +++ b/launcher/net/Upload.h @@ -1,8 +1,9 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com>   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -45,6 +46,8 @@ namespace Net {          Q_OBJECT      public: +        using Ptr = shared_qobject_ptr<Upload>; +          static Upload::Ptr makeByteArray(QUrl url, QByteArray *output, QByteArray m_post_data);          auto abort() -> bool override;          auto canAbort() const -> bool override { return true; }; @@ -52,7 +55,7 @@ namespace Net {      protected slots:          void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;          void downloadError(QNetworkReply::NetworkError error) override; -        void sslErrors(const QList<QSslError> & errors); +        void sslErrors(const QList<QSslError> & errors) override;          void downloadFinished() override;          void downloadReadyRead() override; | 
