diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4f936b48db231d06ff9d25bec5f28e9372dc8f05..2f25d2db6c59c680f127c218a77f697940a01ba1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -245,7 +245,7 @@ set(COMMON_HEADERS
   ${APP_SRC_DIR}/avatarimageprovider.h
   ${APP_SRC_DIR}/networkmanager.h
   ${APP_SRC_DIR}/smartlistmodel.h
-  ${APP_SRC_DIR}/updatemanager.h
+  ${APP_SRC_DIR}/appversionmanager.h
   ${APP_SRC_DIR}/utils.h
   ${APP_SRC_DIR}/bannedlistmodel.h
   ${APP_SRC_DIR}/version.h
@@ -336,7 +336,7 @@ if(MSVC)
 
   list(APPEND COMMON_SOURCES
     ${APP_SRC_DIR}/connectivitymonitor.cpp
-    ${APP_SRC_DIR}/updatemanager.cpp)
+    ${APP_SRC_DIR}/appversionmanager.cpp)
   # preprocessor defines
   add_definitions(-DUNICODE -DQT_NO_DEBUG -DNDEBUG)
 
@@ -388,7 +388,7 @@ elseif (NOT APPLE)
     ${APP_SRC_DIR}/xrectsel.c
     ${APP_SRC_DIR}/connectivitymonitor.cpp
     ${APP_SRC_DIR}/dbuserrorhandler.cpp
-    ${APP_SRC_DIR}/updatemanager.cpp)
+    ${APP_SRC_DIR}/appversionmanager.cpp)
   list(APPEND COMMON_HEADERS
     ${APP_SRC_DIR}/xrectsel.h
     ${APP_SRC_DIR}/dbuserrorhandler.h)
@@ -440,7 +440,7 @@ elseif (NOT APPLE)
   find_library(X11 X11)
 else() # APPLE
   list(APPEND COMMON_SOURCES
-    ${APP_SRC_DIR}/os/macos/updatemanager.mm
+    ${APP_SRC_DIR}/os/macos/appversionmanager.mm
     ${APP_SRC_DIR}/os/macos/connectivitymonitor.mm
     ${APP_SRC_DIR}/os/macos/macutils.mm)
   list(APPEND COMMON_HEADERS
diff --git a/src/app/MainApplicationWindow.qml b/src/app/MainApplicationWindow.qml
index 6410f6a54483f7d443fc377ae87b51e6b56ce37a..f75732477fed88dbc78bd380e267272bd5be6adf 100644
--- a/src/app/MainApplicationWindow.qml
+++ b/src/app/MainApplicationWindow.qml
@@ -205,8 +205,8 @@ ApplicationWindow {
             // Quiet check for updates on start if set to.
             if (Qt.platform.os.toString() === "windows") {
                 if (UtilsAdapter.getAppValue(Settings.AutoUpdate)) {
-                    UpdateManager.checkForUpdates(true);
-                    UpdateManager.setAutoUpdateCheck(true);
+                    AppVersionManager.checkForUpdates(true);
+                    AppVersionManager.setAutoUpdateCheck(true);
                 }
             }
 
@@ -268,7 +268,7 @@ ApplicationWindow {
     }
 
     Connections {
-        target: UpdateManager
+        target: AppVersionManager
 
         function onDownloadStarted() {
             viewCoordinator.presentDialog(appWindow, "settingsview/components/UpdateDownloadDialog.qml", {
@@ -290,7 +290,7 @@ ApplicationWindow {
                         "buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater],
                         "buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue],
                         "buttonCallBacks": [function () {
-                                UpdateManager.applyUpdates();
+                                AppVersionManager.applyUpdates();
                             }]
                     });
             }
diff --git a/src/app/updatemanager.cpp b/src/app/appversionmanager.cpp
similarity index 52%
rename from src/app/updatemanager.cpp
rename to src/app/appversionmanager.cpp
index ca300d336b1d69ad8f5e392123978240292ead10..c3ac06c9c57595b54f227e29aaea33240026eebb 100644
--- a/src/app/updatemanager.cpp
+++ b/src/app/appversionmanager.cpp
@@ -15,7 +15,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "updatemanager.h"
+#include "appversionmanager.h"
 
 #include "lrcinstance.h"
 #include "version.h"
@@ -37,9 +37,9 @@ static constexpr char betaVersionSubUrl[] = "/beta/version";
 static constexpr char msiSubUrl[] = "/jami.release.x64.msi";
 static constexpr char betaMsiSubUrl[] = "/beta/jami.beta.x64.msi";
 
-struct UpdateManager::Impl : public QObject
+struct AppVersionManager::Impl : public QObject
 {
-    Impl(const QString& url, LRCInstance* instance, UpdateManager& parent)
+    Impl(const QString& url, LRCInstance* instance, AppVersionManager& parent)
         : QObject(nullptr)
         , parent_(parent)
         , lrcInstance_(instance)
@@ -60,9 +60,9 @@ struct UpdateManager::Impl : public QObject
         // Fail without UI if this is a programmatic check.
         if (!quiet)
             connect(&parent_,
-                    &NetworkManager::errorOccured,
+                    &NetworkManager::errorOccurred,
                     &parent_,
-                    &UpdateManager::updateErrorOccurred);
+                    &AppVersionManager::updateErrorOccurred);
 
         cleanUpdateFiles();
         QUrl versionUrl {isBeta ? QUrl::fromUserInput(baseUrlString_ + betaVersionSubUrl)
@@ -92,31 +92,21 @@ struct UpdateManager::Impl : public QObject
     {
         parent_.disconnect();
         connect(&parent_,
-                &NetworkManager::errorOccured,
+                &NetworkManager::errorOccurred,
                 &parent_,
-                &UpdateManager::updateErrorOccurred);
-        connect(&parent_, &UpdateManager::statusChanged, this, [this](Status status) {
-            switch (status) {
-            case Status::STARTED:
-                Q_EMIT parent_.updateDownloadStarted();
-                break;
-            case Status::FINISHED:
-                Q_EMIT parent_.updateDownloadFinished();
-                break;
-            default:
-                break;
-            }
-        });
+                &AppVersionManager::updateErrorOccurred);
 
-        QUrl downloadUrl {(beta || isBeta) ? QUrl::fromUserInput(baseUrlString_ + betaMsiSubUrl)
-                                           : QUrl::fromUserInput(baseUrlString_ + msiSubUrl)};
+        const QUrl downloadUrl {(beta || isBeta)
+                                    ? QUrl::fromUserInput(baseUrlString_ + betaMsiSubUrl)
+                                    : QUrl::fromUserInput(baseUrlString_ + msiSubUrl)};
 
-        parent_.downloadFile(
+        int uuid = parent_.downloadFile(
             downloadUrl,
+            *(parent_.replyId_),
             [this, downloadUrl](bool success, const QString& errorMessage) {
                 Q_UNUSED(success)
                 Q_UNUSED(errorMessage)
-                QProcess process;
+                const QProcess process;
                 auto basePath = tempPath_ + QDir::separator();
                 auto msiPath = QDir::toNativeSeparators(basePath + downloadUrl.fileName());
                 auto logPath = QDir::toNativeSeparators(basePath + "jami_x64_install.log");
@@ -127,11 +117,12 @@ struct UpdateManager::Impl : public QObject
                                                     << "/L*V" << logPath);
             },
             tempPath_);
+        parent_.replyId_.reset(&uuid);
     };
 
     void cancelUpdate()
     {
-        parent_.cancelDownload();
+        parent_.cancelDownload(*(parent_.replyId_));
     };
 
     void setAutoUpdateCheck(bool state)
@@ -162,7 +153,7 @@ struct UpdateManager::Impl : public QObject
         }
     };
 
-    UpdateManager& parent_;
+    AppVersionManager& parent_;
 
     LRCInstance* lrcInstance_ {nullptr};
     QString baseUrlString_;
@@ -170,51 +161,52 @@ struct UpdateManager::Impl : public QObject
     QTimer* updateTimer_;
 };
 
-UpdateManager::UpdateManager(const QString& url,
-                             ConnectivityMonitor* cm,
-                             LRCInstance* instance,
-                             QObject* parent)
+AppVersionManager::AppVersionManager(const QString& url,
+                                     ConnectivityMonitor* cm,
+                                     LRCInstance* instance,
+                                     QObject* parent)
     : NetworkManager(cm, parent)
     , pimpl_(std::make_unique<Impl>(url, instance, *this))
+    , replyId_(new int(0))
 {}
 
-UpdateManager::~UpdateManager()
+AppVersionManager::~AppVersionManager()
 {
-    cancelDownload();
+    cancelDownload(*replyId_);
 }
 
 void
-UpdateManager::checkForUpdates(bool quiet)
+AppVersionManager::checkForUpdates(bool quiet)
 {
     pimpl_->checkForUpdates(quiet);
 }
 
 void
-UpdateManager::applyUpdates(bool beta)
+AppVersionManager::applyUpdates(bool beta)
 {
     pimpl_->applyUpdates(beta);
 }
 
 void
-UpdateManager::cancelUpdate()
+AppVersionManager::cancelUpdate()
 {
     pimpl_->cancelUpdate();
 }
 
 void
-UpdateManager::setAutoUpdateCheck(bool state)
+AppVersionManager::setAutoUpdateCheck(bool state)
 {
     pimpl_->setAutoUpdateCheck(state);
 }
 
 bool
-UpdateManager::isCurrentVersionBeta()
+AppVersionManager::isCurrentVersionBeta()
 {
     return isBeta;
 }
 
 bool
-UpdateManager::isUpdaterEnabled()
+AppVersionManager::isUpdaterEnabled()
 {
 #ifdef Q_OS_WIN
     return true;
@@ -223,116 +215,7 @@ UpdateManager::isUpdaterEnabled()
 }
 
 bool
-UpdateManager::isAutoUpdaterEnabled()
+AppVersionManager::isAutoUpdaterEnabled()
 {
     return false;
 }
-
-void
-UpdateManager::cancelDownload()
-{
-    if (downloadReply_) {
-        Q_EMIT errorOccured(GetError::CANCELED);
-        downloadReply_->abort();
-        resetDownload();
-    }
-}
-
-void
-UpdateManager::downloadFile(const QUrl& url,
-                            std::function<void(bool, const QString&)> onDoneCallback,
-                            const QString& filePath)
-{
-    // If there is already a download in progress, return.
-    if (downloadReply_ && downloadReply_->isRunning()) {
-        qWarning() << Q_FUNC_INFO << "Download already in progress";
-        return;
-    }
-
-    // Clean up any previous download.
-    resetDownload();
-
-    // If the url is invalid, return.
-    if (!url.isValid()) {
-        Q_EMIT errorOccured(GetError::NETWORK_ERROR, "Invalid url");
-        return;
-    }
-
-    // If the file path is empty, return.
-    if (filePath.isEmpty()) {
-        Q_EMIT errorOccured(GetError::NETWORK_ERROR, "Invalid file path");
-        return;
-    }
-
-    // Create the file. Return if it cannot be created.
-    QFileInfo fileInfo(url.path());
-    QString fileName = fileInfo.fileName();
-    file_.reset(new QFile(filePath + "/" + fileName));
-    if (!file_->open(QIODevice::WriteOnly)) {
-        Q_EMIT errorOccured(GetError::ACCESS_DENIED);
-        file_.reset();
-        qWarning() << Q_FUNC_INFO << "Could not open file for writing";
-        return;
-    }
-
-    // Start the download.
-    QNetworkRequest request(url);
-    downloadReply_ = manager_->get(request);
-
-    connect(downloadReply_, &QNetworkReply::readyRead, this, [=]() {
-        if (file_ && file_->isOpen()) {
-            file_->write(downloadReply_->readAll());
-        }
-    });
-
-    connect(downloadReply_,
-            &QNetworkReply::downloadProgress,
-            this,
-            [=](qint64 bytesReceived, qint64 bytesTotal) {
-                Q_EMIT downloadProgressChanged(bytesReceived, bytesTotal);
-            });
-
-    connect(downloadReply_,
-            QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred),
-            this,
-            [this](QNetworkReply::NetworkError error) {
-                downloadReply_->disconnect();
-                resetDownload();
-                qWarning() << Q_FUNC_INFO
-                           << QMetaEnum::fromType<QNetworkReply::NetworkError>().valueToKey(error);
-                Q_EMIT errorOccured(GetError::NETWORK_ERROR);
-            });
-
-    connect(downloadReply_, &QNetworkReply::finished, this, [this, onDoneCallback]() {
-        bool success = false;
-        QString errorMessage;
-        if (downloadReply_->error() == QNetworkReply::NoError) {
-            resetDownload();
-            success = true;
-        } else {
-            errorMessage = downloadReply_->errorString();
-            resetDownload();
-        }
-        onDoneCallback(success, errorMessage);
-        Q_EMIT statusChanged(Status::FINISHED);
-    });
-
-    Q_EMIT statusChanged(Status::STARTED);
-}
-
-void
-UpdateManager::resetDownload()
-{
-    if (downloadReply_) {
-        downloadReply_->deleteLater();
-        downloadReply_ = nullptr;
-    }
-    if (file_) {
-        if (file_->isOpen()) {
-            file_->flush();
-            file_->close();
-        }
-        file_->deleteLater();
-        file_.reset();
-    }
-}
diff --git a/src/app/updatemanager.h b/src/app/appversionmanager.h
similarity index 59%
rename from src/app/updatemanager.h
rename to src/app/appversionmanager.h
index 373c97fceb97feac2f899af1ae1685e0d156ff85..55fa94c5ff756e085024d3f67ab36b34828ae9c2 100644
--- a/src/app/updatemanager.h
+++ b/src/app/appversionmanager.h
@@ -1,6 +1,5 @@
 /*
  * Copyright (C) 2020-2023 Savoir-faire Linux Inc.
- * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.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
@@ -25,51 +24,35 @@
 class LRCInstance;
 class ConnectivityMonitor;
 
-class UpdateManager final : public NetworkManager
+class AppVersionManager final : public NetworkManager
 {
     Q_OBJECT
-    Q_DISABLE_COPY(UpdateManager)
+    Q_DISABLE_COPY(AppVersionManager)
 public:
-    explicit UpdateManager(const QString& url,
-                           ConnectivityMonitor* cm,
-                           LRCInstance* instance = nullptr,
-                           QObject* parent = nullptr);
-    ~UpdateManager();
-
-    enum Status { STARTED, FINISHED };
-    Q_ENUM(Status)
+    explicit AppVersionManager(const QString& url,
+                               ConnectivityMonitor* cm,
+                               LRCInstance* instance = nullptr,
+                               QObject* parent = nullptr);
+    ~AppVersionManager();
 
     Q_INVOKABLE void checkForUpdates(bool quiet = false);
     Q_INVOKABLE void applyUpdates(bool beta = false);
-    Q_INVOKABLE void cancelUpdate();
-    Q_INVOKABLE void setAutoUpdateCheck(bool state);
-    Q_INVOKABLE bool isCurrentVersionBeta();
     Q_INVOKABLE bool isUpdaterEnabled();
     Q_INVOKABLE bool isAutoUpdaterEnabled();
-    Q_INVOKABLE void cancelDownload();
-
-    void downloadFile(const QUrl& url,
-                      std::function<void(bool, const QString&)> onDoneCallback,
-                      const QString& filePath);
+    Q_INVOKABLE void setAutoUpdateCheck(bool state);
+    Q_INVOKABLE void cancelUpdate();
+    Q_INVOKABLE bool isCurrentVersionBeta();
 
 Q_SIGNALS:
-    void statusChanged(UpdateManager::Status status);
-    void downloadProgressChanged(qint64 bytesRead, qint64 totalBytes);
-
+    void appCloseRequested();
     void updateCheckReplyReceived(bool ok, bool found = false);
-    void updateErrorOccurred(const NetworkManager::GetError& error);
-    void updateDownloadStarted();
     void updateDownloadProgressChanged(qint64 bytesRead, qint64 totalBytes);
-    void updateDownloadFinished();
-    void appCloseRequested();
-
-private:
-    void resetDownload();
-    QNetworkReply* downloadReply_ {nullptr};
-    QScopedPointer<QFile> file_;
+    void updateErrorOccurred(const NetworkManager::GetError& error);
 
 private:
+    QScopedPointer<int> replyId_;
     struct Impl;
+    friend struct Impl;
     std::unique_ptr<Impl> pimpl_;
 };
-Q_DECLARE_METATYPE(UpdateManager*)
+Q_DECLARE_METATYPE(AppVersionManager*)
diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml
index c04dc8030fe8d56018337af17596887a677b3977..c62fbd884d2b3b8cf1cff07b9f018a274006a10d 100644
--- a/src/app/constant/JamiStrings.qml
+++ b/src/app/constant/JamiStrings.qml
@@ -46,7 +46,7 @@ Item {
     property string reconnectTry: qsTr("Trying to reconnect to the Jami daemon (jamid)…")
 
     // AboutPopUp
-    property string version: qsTr("Version") + (UpdateManager.isCurrentVersionBeta() ? " (Beta)" : "")
+    property string version: qsTr("Version") + (AppVersionManager.isCurrentVersionBeta() ? " (Beta)" : "")
     property string companyDeclarationYear: declarationYear + " " + companyName
     property string declarationYear: "© 2015-2023"
     property string companyName: "Savoir-faire Linux Inc."
diff --git a/src/app/imagedownloader.cpp b/src/app/imagedownloader.cpp
index 945a4e6078fd46a124186501c38758f4e15c3957..9eb0784d50d64877c558223795dedbbb285c98b1 100644
--- a/src/app/imagedownloader.cpp
+++ b/src/app/imagedownloader.cpp
@@ -26,7 +26,7 @@ ImageDownloader::ImageDownloader(ConnectivityMonitor* cm, QObject* parent)
 void
 ImageDownloader::downloadImage(const QUrl& url, const QString& localPath)
 {
-    Utils::oneShotConnect(this, &NetworkManager::errorOccured, this, [this, localPath]() {
+    Utils::oneShotConnect(this, &NetworkManager::errorOccurred, this, [this, localPath]() {
         onDownloadImageFinished({}, localPath);
     });
 
diff --git a/src/app/lrcinstance.cpp b/src/app/lrcinstance.cpp
index 7e7162d8ee61a26d5d7b0db795b74eadfd26fbea..323cab0b54daeac9df070b6580fb68ca01db50fe 100644
--- a/src/app/lrcinstance.cpp
+++ b/src/app/lrcinstance.cpp
@@ -34,7 +34,7 @@ LRCInstance::LRCInstance(migrateCallback willMigrateCb,
                          bool debugMode,
                          bool muteDaemon)
     : lrc_(std::make_unique<Lrc>(willMigrateCb, didMigrateCb, !debugMode || muteDaemon))
-    , updateManager_(std::make_unique<UpdateManager>(updateUrl, connectivityMonitor, this))
+    , updateManager_(std::make_unique<AppVersionManager>(updateUrl, connectivityMonitor, this))
     , threadPool_(new QThreadPool(this))
 {
     debugMode_ = debugMode;
@@ -75,8 +75,8 @@ LRCInstance::LRCInstance(migrateCallback willMigrateCb,
     }
 };
 
-UpdateManager*
-LRCInstance::getUpdateManager()
+AppVersionManager*
+LRCInstance::getAppVersionManager()
 {
     return updateManager_.get();
 }
diff --git a/src/app/lrcinstance.h b/src/app/lrcinstance.h
index 4acf94884e7a6ccc421975f6235a8ad9ed132534..0da5abee14ca57e9b0b248988ac094fa3a63c329 100644
--- a/src/app/lrcinstance.h
+++ b/src/app/lrcinstance.h
@@ -24,7 +24,7 @@
 #undef ERROR
 #endif
 
-#include "updatemanager.h"
+#include "appversionmanager.h"
 #include "qtutils.h"
 #include "utils.h"
 
@@ -71,7 +71,7 @@ public:
 
     void finish();
 
-    UpdateManager* getUpdateManager();
+    AppVersionManager* getAppVersionManager();
 
     AccountModel& accountModel();
     ConversationModel* getCurrentConversationModel();
@@ -145,7 +145,7 @@ Q_SIGNALS:
 
 private:
     std::unique_ptr<Lrc> lrc_;
-    std::unique_ptr<UpdateManager> updateManager_;
+    std::unique_ptr<AppVersionManager> updateManager_;
 
     QString selectedConvUid_;
     MapStringString contentDrafts_;
diff --git a/src/app/networkmanager.cpp b/src/app/networkmanager.cpp
index 15cec11caa6fd735377284f4290e063ee72c56d0..0af5f216e8e5e1a87eecd431aedaf0100f2efa15 100644
--- a/src/app/networkmanager.cpp
+++ b/src/app/networkmanager.cpp
@@ -19,14 +19,18 @@
 
 #include "connectivitymonitor.h"
 
+#include <QMap>
+#include <QDir>
 #include <QMetaEnum>
 #include <QtNetwork>
+#include <QScopedPointer>
 
 NetworkManager::NetworkManager(ConnectivityMonitor* cm, QObject* parent)
     : QObject(parent)
     , manager_(new QNetworkAccessManager(this))
     , connectivityMonitor_(cm)
     , lastConnectionState_(cm->isOnline())
+    , rng_(std::random_device {}())
 {
 #if QT_CONFIG(ssl)
     connect(manager_,
@@ -36,7 +40,7 @@ NetworkManager::NetworkManager(ConnectivityMonitor* cm, QObject* parent)
                 Q_UNUSED(reply);
                 Q_FOREACH (const QSslError& error, errors) {
                     qWarning() << Q_FUNC_INFO << error.errorString();
-                    Q_EMIT errorOccured(GetError::SSL_ERROR, error.errorString());
+                    Q_EMIT errorOccurred(GetError::SSL_ERROR, error.errorString());
                 }
             });
 #endif
@@ -53,15 +57,127 @@ NetworkManager::NetworkManager(ConnectivityMonitor* cm, QObject* parent)
 
 void
 NetworkManager::sendGetRequest(const QUrl& url,
-                               std::function<void(const QByteArray&)> onDoneCallback)
+                               std::function<void(const QByteArray&)>&& onDoneCallback)
 {
-    auto reply = manager_->get(QNetworkRequest(url));
+    auto* const reply = manager_->get(QNetworkRequest(url));
     QObject::connect(reply, &QNetworkReply::finished, this, [reply, onDoneCallback, this]() {
         if (reply->error() == QNetworkReply::NoError) {
             onDoneCallback(reply->readAll());
-        } else{
-            Q_EMIT errorOccured(GetError::NETWORK_ERROR, reply->errorString());
-        }      
+        } else {
+            Q_EMIT errorOccurred(GetError::NETWORK_ERROR, reply->errorString());
+        }
         reply->deleteLater();
     });
 }
+
+int
+NetworkManager::downloadFile(const QUrl& url,
+                             unsigned int replyId,
+                             std::function<void(bool, const QString&)>&& onDoneCallback,
+                             const QString& filePath)
+{
+    // If there is already a download in progress, return.
+    if ((downloadReplies_.value(replyId) != NULL || !(replyId == 0))
+        && downloadReplies_[replyId]->isRunning()) {
+        qWarning() << Q_FUNC_INFO << "Download already in progress";
+        return replyId;
+    }
+
+    // Clean up any previous download.
+    resetDownload(replyId);
+
+    // If the url is invalid, return.
+    if (!url.isValid()) {
+        Q_EMIT errorOccurred(GetError::NETWORK_ERROR, "Invalid url");
+        return 0;
+    }
+
+    // If the file path is empty, return.
+    if (filePath.isEmpty()) {
+        Q_EMIT errorOccurred(GetError::NETWORK_ERROR, "Invalid file path");
+        return 0;
+    }
+
+    // set the id for the request
+    std::uniform_int_distribution<int> dist(1, std::numeric_limits<int>::max());
+    auto uuid = dist(rng_);
+
+    const QDir dir;
+    if (!dir.exists(filePath)) {
+        dir.mkpath(filePath);
+    }
+
+    // Create the file. Return if it cannot be created.
+    const QFileInfo fileInfo(url.path());
+    const QString fileName = fileInfo.fileName();
+    auto& file = files_[uuid];
+    file = new QFile(filePath + fileName + ".jpl");
+    if (!file->open(QIODevice::WriteOnly)) {
+        Q_EMIT errorOccurred(GetError::ACCESS_DENIED);
+        files_.remove(uuid);
+        qWarning() << Q_FUNC_INFO << "Could not open file for writing";
+        return 0;
+    }
+
+    // Start the download.
+    const QNetworkRequest request(url);
+
+    downloadReplies_[uuid] = manager_->get(request);
+    auto* const reply = downloadReplies_[uuid];
+    connect(reply, &QNetworkReply::readyRead, this, [file, reply]() {
+        if (file && file->isOpen()) {
+            file->write(reply->readAll());
+        }
+    });
+
+    connect(reply,
+            &QNetworkReply::downloadProgress,
+            this,
+            [this](qint64 bytesReceived, qint64 bytesTotal) {
+                Q_EMIT downloadProgressChanged(bytesReceived, bytesTotal);
+            });
+
+    connect(reply,
+            QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred),
+            this,
+            [this, uuid, reply](QNetworkReply::NetworkError error) {
+                reply->disconnect();
+                resetDownload(uuid);
+                qWarning() << Q_FUNC_INFO
+                           << QMetaEnum::fromType<QNetworkReply::NetworkError>().valueToKey(error);
+                Q_EMIT errorOccurred(GetError::NETWORK_ERROR);
+            });
+
+    connect(reply, &QNetworkReply::finished, this, [this, uuid, onDoneCallback, reply]() {
+        bool success = false;
+        QString errorMessage;
+        if (reply->error() == QNetworkReply::NoError) {
+            resetDownload(uuid);
+            success = true;
+        } else {
+            errorMessage = reply->errorString();
+            resetDownload(uuid);
+        }
+        onDoneCallback(success, errorMessage);
+        Q_EMIT downloadFinished(uuid);
+    });
+    Q_EMIT downloadStarted(uuid);
+    return uuid;
+}
+
+void
+NetworkManager::cancelDownload(int replyId)
+{
+    if (downloadReplies_.value(replyId) != NULL) {
+        Q_EMIT errorOccurred(GetError::CANCELED);
+        downloadReplies_[replyId]->abort();
+        resetDownload(replyId);
+    }
+}
+
+void
+NetworkManager::resetDownload(int replyId)
+{
+    files_.remove(replyId);
+    downloadReplies_.remove(replyId);
+}
diff --git a/src/app/networkmanager.h b/src/app/networkmanager.h
index bf80b0d95c0c2cdb7ac374063292218eee6dd397..67dacb237dbf14bdf820f5c1e04ac03e5cc5f2ef 100644
--- a/src/app/networkmanager.h
+++ b/src/app/networkmanager.h
@@ -20,7 +20,10 @@
 #include <QObject>
 #include <QFile>
 #include <QSslError>
+#include <QMap>
+#include <QString>
 #include <QNetworkReply>
+#include <random>
 
 class QNetworkAccessManager;
 class ConnectivityMonitor;
@@ -35,10 +38,19 @@ public:
     enum GetError { DISCONNECTED, NETWORK_ERROR, ACCESS_DENIED, SSL_ERROR, CANCELED };
     Q_ENUM(GetError)
 
-    void sendGetRequest(const QUrl& url, std::function<void(const QByteArray&)> onDoneCallback);
+    void sendGetRequest(const QUrl& url, std::function<void(const QByteArray&)>&& onDoneCallback);
 
+    int downloadFile(const QUrl& url,
+                     unsigned int replyId,
+                     std::function<void(bool, const QString&)>&& onDoneCallback,
+                     const QString& filePath);
+    void resetDownload(int replyId);
+    void cancelDownload(int replyId);
 Q_SIGNALS:
-    void errorOccured(GetError error, const QString& msg = {});
+    void errorOccurred(GetError error, const QString& msg = {});
+    void downloadProgressChanged(qint64 bytesRead, qint64 totalBytes);
+    void downloadFinished(int replyId);
+    void downloadStarted(int replyId);
 
 protected:
     QNetworkAccessManager* manager_;
@@ -46,5 +58,8 @@ protected:
 private:
     ConnectivityMonitor* connectivityMonitor_;
     bool lastConnectionState_;
+    QMap<int, QNetworkReply*> downloadReplies_ {};
+    QMap<int, QFile*> files_ {};
+    std::mt19937 rng_;
 };
 Q_DECLARE_METATYPE(NetworkManager*)
diff --git a/src/app/os/macos/updatemanager.mm b/src/app/os/macos/appversionmanager.mm
similarity index 83%
rename from src/app/os/macos/updatemanager.mm
rename to src/app/os/macos/appversionmanager.mm
index ecbac015222a72e0d2123cb07c3929f45b0c24fd..1933ab842132a0c1bdbd5b3404ff958fca9531c5 100644
--- a/src/app/os/macos/updatemanager.mm
+++ b/src/app/os/macos/appversionmanager.mm
@@ -16,7 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "updatemanager.h"
+#include "appversionmanager.h"
 
 #ifdef ENABLE_SPARKLE
 #include <Sparkle/Sparkle.h>
@@ -29,7 +29,7 @@ static constexpr bool isBeta = false;
 #endif
 
 #ifdef ENABLE_SPARKLE
- struct UpdateManager::Impl
+ struct AppVersionManager::Impl
 {
 
     Impl()
@@ -63,7 +63,7 @@ static constexpr bool isBeta = false;
 
 };
 #else
-struct UpdateManager::Impl
+struct AppVersionManager::Impl
 {
     void checkForUpdates() {};
 
@@ -80,7 +80,7 @@ struct UpdateManager::Impl
 };
 #endif
 
-UpdateManager::UpdateManager(const QString& url,
+AppVersionManager::AppVersionManager(const QString& url,
                              ConnectivityMonitor* cm,
                              LRCInstance* instance,
                              QObject* parent)
@@ -88,51 +88,46 @@ UpdateManager::UpdateManager(const QString& url,
     , pimpl_(std::make_unique<Impl>())
 {}
 
-UpdateManager::~UpdateManager()
+AppVersionManager::~AppVersionManager()
 {}
 
 void
-UpdateManager::checkForUpdates(bool quiet)
+AppVersionManager::checkForUpdates(bool quiet)
 {
     Q_UNUSED(quiet)
     pimpl_->checkForUpdates();
 }
 
 void
-UpdateManager::applyUpdates(bool beta)
+AppVersionManager::applyUpdates(bool beta)
 {
     Q_UNUSED(beta)
 }
 
 void
-UpdateManager::cancelUpdate()
-{}
-
-
-void
-UpdateManager::cancelDownload()
+AppVersionManager::cancelUpdate()
 {}
 
 void
-UpdateManager::setAutoUpdateCheck(bool state)
+AppVersionManager::setAutoUpdateCheck(bool state)
 {
     pimpl_->setAutoUpdateCheck(state);
 }
 
 bool
-UpdateManager::isCurrentVersionBeta()
+AppVersionManager::isCurrentVersionBeta()
 {
     return isBeta;
 }
 
 bool
-UpdateManager::isUpdaterEnabled()
+AppVersionManager::isUpdaterEnabled()
 {
     return pimpl_->isUpdaterEnabled();
 }
 
 bool
-UpdateManager::isAutoUpdaterEnabled()
+AppVersionManager::isAutoUpdaterEnabled()
 {
     return pimpl_->isAutoUpdaterEnabled();
 }
diff --git a/src/app/pluginadapter.cpp b/src/app/pluginadapter.cpp
index bde8370a46b1a1ef1a70c74288139e98f48758fe..6c5a97cf08cab2d3d17f576af08bcc8578dc7a58 100644
--- a/src/app/pluginadapter.cpp
+++ b/src/app/pluginadapter.cpp
@@ -18,10 +18,12 @@
 
 #include "pluginadapter.h"
 
+#include "networkmanager.h"
 #include "lrcinstance.h"
 
 PluginAdapter::PluginAdapter(LRCInstance* instance, QObject* parent)
     : QmlAdapterBase(instance, parent)
+
 {
     set_isEnabled(lrcInstance_->pluginModel().getPluginsEnabled());
     updateHandlersListCount();
diff --git a/src/app/qmlregister.cpp b/src/app/qmlregister.cpp
index b199012643735481d35781eac020749e3fe2ed1c..5c5d677b1d212e5526bc7c871590dbccbc1082a5 100644
--- a/src/app/qmlregister.cpp
+++ b/src/app/qmlregister.cpp
@@ -55,8 +55,8 @@
 #include "appsettingsmanager.h"
 #include "mainapplication.h"
 #include "namedirectory.h"
-#include "updatemanager.h"
 #include "pluginlistmodel.h"
+#include "appversionmanager.h"
 #include "pluginlistpreferencemodel.h"
 #include "preferenceitemlistmodel.h"
 #include "wizardviewstepmodel.h"
@@ -156,7 +156,7 @@ registerTypes(QQmlEngine* engine,
     // TODO: remove these
     QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, AVModel, &lrcInstance->avModel())
     QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, PluginModel, &lrcInstance->pluginModel())
-    QML_REGISTERSINGLETONTYPE_CUSTOM(NS_HELPERS, UpdateManager, lrcInstance->getUpdateManager())
+    QML_REGISTERSINGLETONTYPE_CUSTOM(NS_HELPERS, AppVersionManager, lrcInstance->getAppVersionManager())
 
     // Hack for QtCreator autocomplete (part 2)
     // https://bugreports.qt.io/browse/QTCREATORBUG-20569
@@ -244,7 +244,7 @@ registerTypes(QQmlEngine* engine,
 
     engine->setObjectOwnership(&lrcInstance->avModel(), QQmlEngine::CppOwnership);
     engine->setObjectOwnership(&lrcInstance->pluginModel(), QQmlEngine::CppOwnership);
-    engine->setObjectOwnership(lrcInstance->getUpdateManager(), QQmlEngine::CppOwnership);
+    engine->setObjectOwnership(lrcInstance->getAppVersionManager(), QQmlEngine::CppOwnership);
     engine->setObjectOwnership(&NameDirectory::instance(), QQmlEngine::CppOwnership);
 }
 // clang-format on
diff --git a/src/app/settingsview/SettingsSidePanel.qml b/src/app/settingsview/SettingsSidePanel.qml
index 887a3dfb357a7961a75bf6e6a72b60224a9a04be..92511de41e6b3d4eeb76f704f239a33bfe38f14b 100644
--- a/src/app/settingsview/SettingsSidePanel.qml
+++ b/src/app/settingsview/SettingsSidePanel.qml
@@ -83,7 +83,7 @@ SidePanelBase {
                     }, {
                         "id": 11,
                         "title": JamiStrings.updatesTitle,
-                        "visible": UpdateManager.isUpdaterEnabled()
+                        "visible": AppVersionManager.isUpdaterEnabled()
                     }]
             }, {
                 "title": JamiStrings.audioVideoSettingsTitle,
diff --git a/src/app/settingsview/components/PluginItemDelegate.qml b/src/app/settingsview/components/PluginItemDelegate.qml
index e273d8772beaf2313ce764924c544109798ea64c..578808bb8307516122f744582f2ed5658d2bfcfb 100644
--- a/src/app/settingsview/components/PluginItemDelegate.qml
+++ b/src/app/settingsview/components/PluginItemDelegate.qml
@@ -77,7 +77,21 @@ ItemDelegate {
                 text: pluginName === "" ? pluginId : pluginName
                 verticalAlignment: Text.AlignVCenter
             }
-
+            MaterialButton {
+                id: update
+                TextMetrics {
+                    id: updateTextSize
+                    font.weight: Font.Bold
+                    font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
+                    font.capitalization: Font.AllUppercase
+                    text: JamiStrings.updatePlugin
+                }
+                visible: false
+                secondary: true
+                preferredWidth: updateTextSize.width
+                text: JamiStrings.updatePlugin
+                fontSize: 15
+            }
             ToggleSwitch {
                 id: loadSwitch
                 Layout.fillHeight: true
diff --git a/src/app/settingsview/components/PluginSettingsPage.qml b/src/app/settingsview/components/PluginSettingsPage.qml
index f1350b315c564b3f74a2f54d9a30e184babd66be..6b177e94bb5a3a82cc8b8cd356978dca766c2513 100644
--- a/src/app/settingsview/components/PluginSettingsPage.qml
+++ b/src/app/settingsview/components/PluginSettingsPage.qml
@@ -25,7 +25,7 @@ import "../../commoncomponents"
 
 SettingsPageBase {
     id: root
-
+    contentFlickableWidth: Math.min(root.width, root.width - 2 * JamiTheme.preferredSettingsMarginSize)
     title: JamiStrings.pluginSettingsTitle
 
     flickableContent: ColumnLayout {
@@ -38,34 +38,19 @@ SettingsPageBase {
 
         ColumnLayout {
             id: generalSettings
-
-            width: parent.width
+            Layout.preferredWidth: root.width
             spacing: JamiTheme.settingsCategorySpacing
+        }
+        // View of installed plugins
+        PluginListView {
+            id: pluginListView
 
-            ToggleSwitch {
-                id: enabledplugin
-
-                checked: PluginAdapter.isEnabled
-                Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
-                Layout.fillWidth: true
-                labelText: JamiStrings.enable
-
-                onSwitchToggled: {
-                    PluginModel.setPluginsEnabled(checked);
-                    PluginAdapter.isEnabled = checked;
-                }
-            }
-
-            PluginListView {
-                id: pluginListView
-
-                visible: PluginAdapter.isEnabled
+            visible: PluginAdapter.isEnabled
 
-                Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
-                Layout.preferredWidth: parent.width
-                Layout.minimumHeight: 0
-                Layout.preferredHeight: childrenRect.height
-            }
+            Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
+            Layout.preferredWidth: parent.width
+            Layout.minimumHeight: 0
+            Layout.preferredHeight: childrenRect.height
         }
     }
 }
diff --git a/src/app/settingsview/components/UpdateDownloadDialog.qml b/src/app/settingsview/components/UpdateDownloadDialog.qml
index d4827c223dd6885438e8e120ff51a4b09919026c..f183bd845d078a4a5e8e2f4fda1b81fc189b6775 100644
--- a/src/app/settingsview/components/UpdateDownloadDialog.qml
+++ b/src/app/settingsview/components/UpdateDownloadDialog.qml
@@ -34,9 +34,10 @@ SimpleMessageDialog {
     property alias progressBarValue: progressBar.value
 
     Connections {
-        target: UpdateManager
+        target: AppVersionManager
 
-        function onUpdateErrorOccurred(error) {
+        function onErrorOccurred(error, msg) {
+            console.warn("Error while downloading update: " + error + " - " + msg);
             downloadDialog.close();
         }
 
@@ -44,7 +45,7 @@ SimpleMessageDialog {
             downloadDialog.setDownloadProgress(bytesRead, totalBytes);
         }
 
-        function onUpdateDownloadFinished() {
+        function onDownloadFinished() {
             downloadDialog.close();
         }
     }
@@ -98,10 +99,10 @@ SimpleMessageDialog {
     buttonTitles: [JamiStrings.optionCancel]
     buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue]
     buttonCallBacks: [function () {
-            UpdateManager.cancelDownload();
+            AppVersionManager.cancelUpdate();
         }]
     onVisibleChanged: {
         if (!visible)
-            UpdateManager.cancelDownload();
+            AppVersionManager.cancelUpdate();
     }
 }
diff --git a/src/app/settingsview/components/UpdateSettingsPage.qml b/src/app/settingsview/components/UpdateSettingsPage.qml
index 186cf0c46d5577ae8e3e29f95face85b033c6730..e2a02e5468c02aba7913fc3baea46c2d2abb8e83 100644
--- a/src/app/settingsview/components/UpdateSettingsPage.qml
+++ b/src/app/settingsview/components/UpdateSettingsPage.qml
@@ -49,7 +49,7 @@ SettingsPageBase {
                 "buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater],
                 "buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue],
                 "buttonCallBacks": [function () {
-                        UpdateManager.applyUpdates(beta);
+                        AppVersionManager.applyUpdates(beta);
                     }]
             });
     }
@@ -66,14 +66,14 @@ SettingsPageBase {
 
             Layout.fillWidth: true
 
-            checked: Qt.platform.os.toString() === "windows" ? UtilsAdapter.getAppValue(Settings.Key.AutoUpdate) : UpdateManager.isAutoUpdaterEnabled()
+            checked: Qt.platform.os.toString() === "windows" ? UtilsAdapter.getAppValue(Settings.Key.AutoUpdate) : AppVersionManager.isAutoUpdaterEnabled()
 
             labelText: JamiStrings.update
             tooltipText: JamiStrings.enableAutoUpdates
 
             onSwitchToggled: {
                 UtilsAdapter.setAppValue(Settings.Key.AutoUpdate, checked);
-                UpdateManager.setAutoUpdateCheck(checked);
+                AppVersionManager.setAutoUpdateCheck(checked);
             }
         }
 
@@ -98,13 +98,13 @@ SettingsPageBase {
             toolTipText: JamiStrings.checkForUpdates
             text: JamiStrings.checkForUpdates
 
-            onClicked: UpdateManager.checkForUpdates()
+            onClicked: AppVersionManager.checkForUpdates()
         }
 
         MaterialButton {
             id: installBetaButton
 
-            visible: !UpdateManager.isCurrentVersionBeta() && Qt.platform.os.toString() === "windows"
+            visible: !AppVersionManager.isCurrentVersionBeta() && Qt.platform.os.toString() === "windows"
 
             Layout.alignment: Qt.AlignHCenter
 
@@ -125,7 +125,7 @@ SettingsPageBase {
                     "buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater],
                     "buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue],
                     "buttonCallBacks": [function () {
-                            UpdateManager.applyUpdates(true);
+                            AppVersionManager.applyUpdates(true);
                         }]
                 })
         }
diff --git a/src/app/wizardview/WizardView.qml b/src/app/wizardview/WizardView.qml
index 461feb4718ff208fbf6ed2592946fe6cc74d94b6..036e695e8c51962d64bcd3c3eab5d7905ff75820 100644
--- a/src/app/wizardview/WizardView.qml
+++ b/src/app/wizardview/WizardView.qml
@@ -43,7 +43,7 @@ BaseView {
             var errorMessage = JamiStrings.errorCreateAccount;
             for (var i = 0; i < controlPanelStackView.children.length; i++) {
                 if (i === controlPanelStackView.currentIndex) {
-                    controlPanelStackView.children[i].errorOccured(errorMessage);
+                    controlPanelStackView.children[i].errorOccurred(errorMessage);
                     return;
                 }
             }
diff --git a/src/app/wizardview/components/ConnectToAccountManagerPage.qml b/src/app/wizardview/components/ConnectToAccountManagerPage.qml
index 0f8848376f705f72a246ad6ee476810b09b97399..2564779a4488dca5207d979f60afc60ccdc682d5 100644
--- a/src/app/wizardview/components/ConnectToAccountManagerPage.qml
+++ b/src/app/wizardview/components/ConnectToAccountManagerPage.qml
@@ -36,7 +36,7 @@ Rectangle {
         errorText = "";
     }
 
-    function errorOccured(errorMessage) {
+    function errorOccurred(errorMessage) {
         connectBtn.spinnerTriggered = false;
         errorText = errorMessage;
     }
diff --git a/src/app/wizardview/components/ImportFromBackupPage.qml b/src/app/wizardview/components/ImportFromBackupPage.qml
index b4fed5433c7dbaee2c380df281e1522434040b1f..9cd94783be0eafa42c865fb2e9872b74faebb650 100644
--- a/src/app/wizardview/components/ImportFromBackupPage.qml
+++ b/src/app/wizardview/components/ImportFromBackupPage.qml
@@ -43,7 +43,7 @@ Rectangle {
         fileImportBtnText = JamiStrings.selectArchiveFile;
     }
 
-    function errorOccured(errorMessage) {
+    function errorOccurred(errorMessage) {
         errorText = errorMessage;
         connectBtn.spinnerTriggered = false;
     }
diff --git a/src/app/wizardview/components/ImportFromDevicePage.qml b/src/app/wizardview/components/ImportFromDevicePage.qml
index e12295ffcfaf8b939e871cfe36d5c099708e85ec..d377cfff69736d21dee88102f1a1a59469db23e1 100644
--- a/src/app/wizardview/components/ImportFromDevicePage.qml
+++ b/src/app/wizardview/components/ImportFromDevicePage.qml
@@ -39,7 +39,7 @@ Rectangle {
         connectBtn.spinnerTriggered = false;
     }
 
-    function errorOccured(errorMessage) {
+    function errorOccurred(errorMessage) {
         errorText = errorMessage;
         connectBtn.spinnerTriggered = false;
     }