From 7d6a080b30c5c7c8e104027eb769dab01b3f21b1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 11 Apr 2024 17:16:28 +0200 Subject: [PATCH 1/7] Introduce the `OCC::NetworkInformation` class --- src/gui/CMakeLists.txt | 1 + src/gui/accountsettings.cpp | 3 +- src/gui/accountstate.cpp | 65 +++++++++++----------- src/gui/application.cpp | 2 - src/gui/guiutility.cpp | 10 ---- src/gui/guiutility.h | 2 - src/gui/main.cpp | 26 +-------- src/gui/networkinformation.cpp | 82 ++++++++++++++++++++++++++++ src/gui/networkinformation.h | 47 ++++++++++++++++ src/gui/networksettings.cpp | 14 ++--- src/gui/scheduling/syncscheduler.cpp | 5 +- 11 files changed, 174 insertions(+), 83 deletions(-) create mode 100644 src/gui/networkinformation.cpp create mode 100644 src/gui/networkinformation.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 6155a427320..2aab0dbc8a4 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -42,6 +42,7 @@ set(client_SRCS ignorelisteditor.cpp lockwatcher.cpp logbrowser.cpp + networkinformation.cpp networksettings.cpp ocssharejob.cpp openfilemanager.cpp diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 2b7b451c782..59ae1eb3b0a 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -30,6 +30,7 @@ #include "folderwizard/folderwizard.h" #include "gui/accountmodalwidget.h" #include "gui/models/models.h" +#include "gui/networkinformation.h" #include "gui/qmlutils.h" #include "gui/selectivesyncwidget.h" #include "gui/spaces/spaceimageprovider.h" @@ -412,7 +413,7 @@ void AccountSettings::slotEnableCurrentFolder(Folder *folder, bool terminate) void AccountSettings::slotForceSyncCurrentFolder(Folder *folder) { - if (Utility::internetConnectionIsMetered() && ConfigFile().pauseSyncWhenMetered()) { + if (NetworkInformation::instance()->isMetered() && ConfigFile().pauseSyncWhenMetered()) { auto messageBox = new QMessageBox(QMessageBox::Question, tr("Internet connection is metered"), tr("Synchronization is paused because the Internet connection is a metered connection" "

Do you really want to force a Synchronization now?"), diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index f8ab56a2080..8318c7c9f5e 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -23,6 +23,7 @@ #include "libsync/creds/abstractcredentials.h" #include "libsync/creds/httpcredentials.h" +#include "gui/networkinformation.h" #include "gui/quotainfo.h" #include "gui/settingsdialog.h" #include "gui/spacemigration.h" @@ -34,7 +35,6 @@ #include "theme.h" #include -#include #include #include #include @@ -113,40 +113,39 @@ AccountState::AccountState(AccountPtr account) }, Qt::QueuedConnection); - if (QNetworkInformation *qNetInfo = QNetworkInformation::instance()) { - connect(qNetInfo, &QNetworkInformation::reachabilityChanged, this, [this](QNetworkInformation::Reachability reachability) { - switch (reachability) { - case QNetworkInformation::Reachability::Online: - [[fallthrough]]; - case QNetworkInformation::Reachability::Site: - [[fallthrough]]; - case QNetworkInformation::Reachability::Unknown: - // the connection might not yet be established - QTimer::singleShot(0, this, [this] { checkConnectivity(false); }); - break; - case QNetworkInformation::Reachability::Disconnected: - // explicitly set disconnected, this way a successful checkConnectivity call above will trigger a local discover - if (state() != State::SignedOut) { - setState(State::Disconnected); - } - [[fallthrough]]; - case QNetworkInformation::Reachability::Local: - break; + connect(NetworkInformation::instance(), &NetworkInformation::reachabilityChanged, this, [this](NetworkInformation::Reachability reachability) { + switch (reachability) { + case NetworkInformation::Reachability::Online: + [[fallthrough]]; + case NetworkInformation::Reachability::Site: + [[fallthrough]]; + case NetworkInformation::Reachability::Unknown: + // the connection might not yet be established + QTimer::singleShot(0, this, [this] { checkConnectivity(false); }); + break; + case NetworkInformation::Reachability::Disconnected: + // explicitly set disconnected, this way a successful checkConnectivity call above will trigger a local discover + if (state() != State::SignedOut) { + setState(State::Disconnected); } - }); + [[fallthrough]]; + case NetworkInformation::Reachability::Local: + break; + } + }); - connect(qNetInfo, &QNetworkInformation::isMeteredChanged, this, [this](bool isMetered) { - if (ConfigFile().pauseSyncWhenMetered()) { - if (state() == State::Connected && isMetered) { - qCInfo(lcAccountState) << "Network switched to a metered connection, setting account state to PausedDueToMetered"; - setState(State::PausedDueToMetered); - } else if (state() == State::PausedDueToMetered && !isMetered) { - qCInfo(lcAccountState) << "Network switched to a NON-metered connection, setting account state to Connected"; - setState(State::Connected); - } + connect(NetworkInformation::instance(), &NetworkInformation::isMeteredChanged, this, [this](bool isMetered) { + if (ConfigFile().pauseSyncWhenMetered()) { + if (state() == State::Connected && isMetered) { + qCInfo(lcAccountState) << "Network switched to a metered connection, setting account state to PausedDueToMetered"; + setState(State::PausedDueToMetered); + } else if (state() == State::PausedDueToMetered && !isMetered) { + qCInfo(lcAccountState) << "Network switched to a NON-metered connection, setting account state to Connected"; + setState(State::Connected); } - }); - } + } + }); + // as a fallback and to recover after server issues we also poll auto timer = new QTimer(this); timer->setInterval(ConnectionValidator::DefaultCallingInterval); @@ -240,7 +239,7 @@ void AccountState::setState(State state) _connectionValidator->deleteLater(); _connectionValidator.clear(); checkConnectivity(); - } else if (_state == Connected && Utility::internetConnectionIsMetered() && ConfigFile().pauseSyncWhenMetered()) { + } else if (_state == Connected && NetworkInformation::instance()->isMetered() && ConfigFile().pauseSyncWhenMetered()) { _state = PausedDueToMetered; } } diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 0d8d726b59d..748fd4b1c7d 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -48,8 +48,6 @@ #include #include -#include - namespace OCC { Q_LOGGING_CATEGORY(lcApplication, "gui.application", QtInfoMsg) diff --git a/src/gui/guiutility.cpp b/src/gui/guiutility.cpp index 2c9c83a7cb0..e995b68c95e 100644 --- a/src/gui/guiutility.cpp +++ b/src/gui/guiutility.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -96,15 +95,6 @@ QString Utility::vfsFreeSpaceActionText() return QCoreApplication::translate("utility", "Free up local space"); } -bool Utility::internetConnectionIsMetered() -{ - if (auto *qNetInfo = QNetworkInformation::instance()) { - return qNetInfo->isMetered(); - } - - return false; -} - void Utility::markDirectoryAsSyncRoot(const QString &path, const QUuid &accountUuid) { Q_ASSERT(getDirectorySyncRootMarkings(path).first.isEmpty()); diff --git a/src/gui/guiutility.h b/src/gui/guiutility.h index 82d75a9ae88..6638e8e612c 100644 --- a/src/gui/guiutility.h +++ b/src/gui/guiutility.h @@ -52,8 +52,6 @@ namespace Utility { QString socketApiSocketPath(); - bool internetConnectionIsMetered(); - OWNCLOUDGUI_EXPORT void markDirectoryAsSyncRoot(const QString &path, const QUuid &accountUuid); std::pair getDirectorySyncRootMarkings(const QString &path); void unmarkDirectoryAsSyncRoot(const QString &path); diff --git a/src/gui/main.cpp b/src/gui/main.cpp index d4a7244fcc7..7205bff7771 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -19,6 +19,7 @@ #include "common/utility.h" #include "gui/application.h" #include "gui/logbrowser.h" +#include "gui/networkinformation.h" #include "libsync/configfile.h" #include "libsync/platform.h" #include "libsync/theme.h" @@ -39,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -249,28 +249,6 @@ void setupLogging(const CommandLineOptions &options) qCInfo(lcMain) << "Arguments:" << qApp->arguments(); } -void loadQNetworkInformationBackend() -{ - if (!QNetworkInformation::loadDefaultBackend()) { - qCWarning(lcMain) << "Failed to load default backend of QNetworkInformation."; - if (!QNetworkInformation::loadBackendByFeatures(QNetworkInformation::Feature::Metered)) { - qCWarning(lcMain) << "Failed to load backend of QNetworkInformation by metered feature."; - if (!QNetworkInformation::loadBackendByFeatures(QNetworkInformation::Feature::Reachability)) { - qCWarning(lcMain) << "Failed to load backend of QNetworkInformation by reachability feature."; - qCWarning(lcMain) << "Available backends:" << QNetworkInformation::availableBackends().join(QStringLiteral(", ")); - return; - } - } - } - qCDebug(lcMain) << "Loaded network information backend:" << QNetworkInformation::instance()->backendName(); - qCDebug(lcMain) << "Supported features:" << QNetworkInformation::instance()->supportedFeatures(); - qCDebug(lcMain) << "Available backends:" << QNetworkInformation::availableBackends().join(QStringLiteral(", ")); - if (auto qni = QNetworkInformation::instance()) { - QObject::connect(qni, &QNetworkInformation::reachabilityChanged, - [](QNetworkInformation::Reachability reachability) { qCInfo(lcMain) << "Connection Status changed to:" << reachability; }); - } -} - QString setupTranslations(QApplication *app) { const auto trPath = Translations::translationsDirectoryPath(); @@ -480,7 +458,7 @@ int main(int argc, char **argv) } setupLogging(options); - loadQNetworkInformationBackend(); + NetworkInformation::initialize(); platform->setApplication(&app); diff --git a/src/gui/networkinformation.cpp b/src/gui/networkinformation.cpp new file mode 100644 index 00000000000..d39766bc567 --- /dev/null +++ b/src/gui/networkinformation.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) by Erik Verbruggen + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#include "networkinformation.h" + +#include + +using namespace OCC; + +Q_LOGGING_CATEGORY(lcNetInfo, "gui.netinfo", QtInfoMsg) + +NetworkInformation *NetworkInformation::_instance; + +static void loadQNetworkInformationBackend() +{ + if (!QNetworkInformation::loadDefaultBackend()) { + qCWarning(lcNetInfo) << "Failed to load default backend of QNetworkInformation."; + if (!QNetworkInformation::loadBackendByFeatures(QNetworkInformation::Feature::Metered)) { + qCWarning(lcNetInfo) << "Failed to load backend of QNetworkInformation by metered feature."; + if (!QNetworkInformation::loadBackendByFeatures(QNetworkInformation::Feature::Reachability)) { + qCWarning(lcNetInfo) << "Failed to load backend of QNetworkInformation by reachability feature."; + qCWarning(lcNetInfo) << "Available backends:" << QNetworkInformation::availableBackends().join(QStringLiteral(", ")); + return; + } + } + } + qCDebug(lcNetInfo) << "Loaded network information backend:" << QNetworkInformation::instance()->backendName(); + qCDebug(lcNetInfo) << "Supported features:" << QNetworkInformation::instance()->supportedFeatures(); + qCDebug(lcNetInfo) << "Available backends:" << QNetworkInformation::availableBackends().join(QStringLiteral(", ")); + if (auto qni = QNetworkInformation::instance()) { + QObject::connect(qni, &QNetworkInformation::reachabilityChanged, + [](QNetworkInformation::Reachability reachability) { qCInfo(lcNetInfo) << "Connection Status changed to:" << reachability; }); + } +} + +void NetworkInformation::initialize() +{ + Q_ASSERT(!_instance); + + _instance = new NetworkInformation; + + loadQNetworkInformationBackend(); + + if (auto qni = QNetworkInformation::instance()) { + connect(qni, &QNetworkInformation::isMeteredChanged, _instance, &NetworkInformation::isMeteredChanged); + connect(qni, &QNetworkInformation::reachabilityChanged, _instance, &NetworkInformation::reachabilityChanged); + } +} + +NetworkInformation *NetworkInformation::instance() +{ + return _instance; +} + +bool NetworkInformation::isMetered() +{ + if (auto *qNetInfo = QNetworkInformation::instance()) { + return qNetInfo->isMetered(); + } + + return false; +} + +bool NetworkInformation::supports(Features features) const +{ + if (auto *qNetInfo = QNetworkInformation::instance()) { + return qNetInfo->supports(features); + } + + return false; +} diff --git a/src/gui/networkinformation.h b/src/gui/networkinformation.h new file mode 100644 index 00000000000..14ccdb07918 --- /dev/null +++ b/src/gui/networkinformation.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) by Erik Verbruggen + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#pragma once + +#include "gui/owncloudguilib.h" + +#include + +namespace OCC { + +class OWNCLOUDGUI_EXPORT NetworkInformation : public QObject +{ + Q_OBJECT + +public: + static void initialize(); + static NetworkInformation *instance(); + + bool isMetered(); + + using Feature = QNetworkInformation::Feature; + using Features = QNetworkInformation::Features; + using Reachability = QNetworkInformation::Reachability; + + bool supports(Features features) const; + +Q_SIGNALS: + void isMeteredChanged(bool isMetered); + void reachabilityChanged(NetworkInformation::Reachability reachability); + +private: + static NetworkInformation *_instance; +}; + +} diff --git a/src/gui/networksettings.cpp b/src/gui/networksettings.cpp index 6774be81e80..99ccaa85d04 100644 --- a/src/gui/networksettings.cpp +++ b/src/gui/networksettings.cpp @@ -13,6 +13,7 @@ */ #include "networksettings.h" +#include "networkinformation.h" #include "ui_networksettings.h" #include "accountmanager.h" @@ -22,7 +23,6 @@ #include "theme.h" #include -#include #include #include #include @@ -186,13 +186,11 @@ void NetworkSettings::loadBWLimitSettings() void NetworkSettings::loadMeteredSettings() { - if (QNetworkInformation *qNetInfo = QNetworkInformation::instance()) { - if (Utility::isWindows() // The backend implements the metered feature, but does not report it as supported. - // See https://bugreports.qt.io/browse/QTBUG-118741 - || qNetInfo->supports(QNetworkInformation::Feature::Metered)) { - _ui->pauseSyncWhenMeteredCheckbox->setChecked(ConfigFile().pauseSyncWhenMetered()); - return; - } + if (Utility::isWindows() // The backend implements the metered feature, but does not report it as supported. + // See https://bugreports.qt.io/browse/QTBUG-118741 + || NetworkInformation::instance()->supports(NetworkInformation::Feature::Metered)) { + _ui->pauseSyncWhenMeteredCheckbox->setChecked(ConfigFile().pauseSyncWhenMetered()); + return; } _ui->pauseSyncWhenMeteredCheckbox->setVisible(false); diff --git a/src/gui/scheduling/syncscheduler.cpp b/src/gui/scheduling/syncscheduler.cpp index 42cf5b492a9..0b5b219cb7c 100644 --- a/src/gui/scheduling/syncscheduler.cpp +++ b/src/gui/scheduling/syncscheduler.cpp @@ -15,14 +15,13 @@ #include "gui/scheduling/syncscheduler.h" #include "gui/folderman.h" +#include "gui/networkinformation.h" #include "gui/scheduling/etagwatcher.h" #include "libsync/configfile.h" #include "libsync/syncengine.h" #include "guiutility.h" -#include - using namespace std::chrono_literals; using namespace OCC; @@ -168,7 +167,7 @@ void SyncScheduler::startNext() auto syncPriority = nextSync.second; if (!_currentSync.isNull()) { - if (_pauseSyncWhenMetered && Utility::internetConnectionIsMetered()) { + if (_pauseSyncWhenMetered && NetworkInformation::instance()->isMetered()) { if (syncPriority == Priority::High) { qCInfo(lcSyncScheduler) << "Scheduler is paused due to metered internet connection, BUT next sync is HIGH priority, so allow sync to start"; } else { From 0f10580ab35489229800027743a15dc97455c769 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 11 Apr 2024 17:20:18 +0200 Subject: [PATCH 2/7] Rename `crashMenu` to `debugMenu` --- src/gui/owncloudgui.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index 71f5b87edc5..db544751d4c 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -639,17 +639,17 @@ void ownCloudGui::updateContextMenu() _contextMenu->addSeparator(); if (_app->debugMode()) { - auto *crashMenu = _contextMenu->addMenu(QStringLiteral("Debug actions")); - crashMenu->addAction(QStringLiteral("Crash if asserts enabled - OC_ENSURE"), _app, [] { + auto *debugMenu = _contextMenu->addMenu(QStringLiteral("Debug actions")); + debugMenu->addAction(QStringLiteral("Crash if asserts enabled - OC_ENSURE"), _app, [] { if (OC_ENSURE(false)) { Q_UNREACHABLE(); } }); - crashMenu->addAction(QStringLiteral("Crash if asserts enabled - Q_ASSERT"), _app, [] { Q_ASSERT(false); }); - crashMenu->addAction(QStringLiteral("Crash now - Utility::crash()"), _app, [] { Utility::crash(); }); - crashMenu->addAction(QStringLiteral("Crash now - OC_ENFORCE()"), _app, [] { OC_ENFORCE(false); }); - crashMenu->addAction(QStringLiteral("Crash now - qFatal"), _app, [] { qFatal("la Qt fatale"); }); - crashMenu->addAction(QStringLiteral("Restart now"), _app, [] { RestartManager::requestRestart(); }); + debugMenu->addAction(QStringLiteral("Crash if asserts enabled - Q_ASSERT"), _app, [] { Q_ASSERT(false); }); + debugMenu->addAction(QStringLiteral("Crash now - Utility::crash()"), _app, [] { Utility::crash(); }); + debugMenu->addAction(QStringLiteral("Crash now - OC_ENFORCE()"), _app, [] { OC_ENFORCE(false); }); + debugMenu->addAction(QStringLiteral("Crash now - qFatal"), _app, [] { qFatal("la Qt fatale"); }); + debugMenu->addAction(QStringLiteral("Restart now"), _app, [] { RestartManager::requestRestart(); }); } _contextMenu->addSeparator(); From f825cedde9f6dc7d7909ae8e621a7e0149bc1f09 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 11 Apr 2024 17:39:35 +0200 Subject: [PATCH 3/7] Stop synchronisation when behind a captive portal Fixes: #11533 --- src/gui/accountsettings.cpp | 6 ++++- src/gui/accountstate.cpp | 30 +++++++++++++++++++++ src/gui/connectionvalidator.cpp | 5 ++-- src/gui/connectionvalidator.h | 5 ++-- src/gui/networkinformation.cpp | 48 ++++++++++++++++++++++++++++++++- src/gui/networkinformation.h | 16 +++++++++++ src/gui/owncloudgui.cpp | 6 +++++ test/testfoldermigration.cpp | 2 ++ test/testutils/testutils.cpp | 3 +++ 9 files changed, 115 insertions(+), 6 deletions(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 59ae1eb3b0a..0b0c77f289f 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -496,7 +496,11 @@ void AccountSettings::slotAccountStateChanged() break; } case AccountState::Connecting: - showConnectionLabel(tr("Connecting to: %1.").arg(server)); + if (NetworkInformation::instance()->isBehindCaptivePortal()) { + showConnectionLabel(tr("Captive portal prevents connections to %1.").arg(server)); + } else { + showConnectionLabel(tr("Connecting to: %1.").arg(server)); + } break; case AccountState::ConfigurationError: showConnectionLabel(tr("Server configuration error: %1.") diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index 8318c7c9f5e..d39d610cd71 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -146,6 +146,12 @@ AccountState::AccountState(AccountPtr account) } }); + connect(NetworkInformation::instance(), &NetworkInformation::isBehindCaptivePortalChanged, this, [this](bool) { + // A direct connect is not possible, because then the state parameter of `isBehindCaptivePortalChanged` + // would become the `verifyServerState` argument to `checkConnectivity`. + QTimer::singleShot(0, this, [this] { checkConnectivity(false); }); + }); + // as a fallback and to recover after server issues we also poll auto timer = new QTimer(this); timer->setInterval(ConnectionValidator::DefaultCallingInterval); @@ -166,6 +172,22 @@ AccountState::AccountState(AccountPtr account) ownCloudGui::raise(); msgBox->open(); }); + + + connect(NetworkInformation::instance(), &NetworkInformation::isBehindCaptivePortalChanged, this, [this](bool onoff) { + if (onoff) { + _queueGuard.block(); + } else { + // TODO: empty queue? + _queueGuard.unblock(); + } + }); + if (NetworkInformation::instance()->isBehindCaptivePortal()) { + _queueGuard.block(); + } else { + // TODO: empty queue? + _queueGuard.unblock(); + } } AccountState::~AccountState() { } @@ -363,6 +385,9 @@ void AccountState::checkConnectivity(bool blockJobs) this, &AccountState::slotConnectionValidatorResult); connect(_connectionValidator, &ConnectionValidator::sslErrors, this, [blockJobs, this](const QList &errors) { + if (NetworkInformation::instance()->isBehindCaptivePortal()) { + return; + } if (!_tlsDialog) { // ignore errors for already accepted certificates auto filteredErrors = _account->accessManager()->filterSslErrors(errors); @@ -478,6 +503,7 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta setState(Connected); break; case ConnectionValidator::Undefined: + [[fallthrough]]; case ConnectionValidator::NotConfigured: setState(Disconnected); break; @@ -493,6 +519,7 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta setState(NetworkError); break; case ConnectionValidator::CredentialsWrong: + [[fallthrough]]; case ConnectionValidator::CredentialsNotReady: slotInvalidCredentials(); break; @@ -510,6 +537,9 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta case ConnectionValidator::Timeout: setState(NetworkError); break; + case ConnectionValidator::CaptivePortal: + setState(Connecting); + break; } } diff --git a/src/gui/connectionvalidator.cpp b/src/gui/connectionvalidator.cpp index 3ce3018b44b..f178be3dc87 100644 --- a/src/gui/connectionvalidator.cpp +++ b/src/gui/connectionvalidator.cpp @@ -14,6 +14,7 @@ #include "gui/connectionvalidator.h" #include "gui/clientproxy.h" #include "gui/fetchserversettings.h" +#include "gui/networkinformation.h" #include "gui/tlserrordialog.h" #include "libsync/account.h" #include "libsync/cookiejar.h" @@ -132,7 +133,7 @@ void ConnectionValidator::slotCheckServerAndAuth() reportResult(Timeout); return; case QNetworkReply::SslHandshakeFailedError: - reportResult(SslError); + reportResult(NetworkInformation::instance()->isBehindCaptivePortal() ? CaptivePortal : SslError); return; case QNetworkReply::TooManyRedirectsError: reportResult(MaintenanceMode); @@ -214,7 +215,7 @@ void ConnectionValidator::slotAuthFailed() if (job->reply()->error() == QNetworkReply::SslHandshakeFailedError) { _errors << job->errorStringParsingBody(); - stat = SslError; + stat = NetworkInformation::instance()->isBehindCaptivePortal() ? CaptivePortal : SslError; } else if (job->reply()->error() == QNetworkReply::AuthenticationRequiredError || !_account->credentials()->stillValid(job->reply())) { qCWarning(lcConnectionValidator) << "******** Password is wrong!" << job->reply()->error() << job; diff --git a/src/gui/connectionvalidator.h b/src/gui/connectionvalidator.h index bf421c57969..7c2ca7999bc 100644 --- a/src/gui/connectionvalidator.h +++ b/src/gui/connectionvalidator.h @@ -101,9 +101,10 @@ class OWNCLOUDGUI_EXPORT ConnectionValidator : public QObject ServiceUnavailable, // 503 on authed request MaintenanceMode, // maintenance enabled in status.php Timeout, // actually also used for other errors on the authed request - ClientUnsupported // The server blocks us as an unsupported client + ClientUnsupported, // The server blocks us as an unsupported client + CaptivePortal, // We're stuck behind a captive portal and (will) get SSL certificate problems }; - Q_ENUM(Status); + Q_ENUM(Status) // How often should the Application ask this object to check for the connection? static constexpr auto DefaultCallingInterval = std::chrono::seconds(62); diff --git a/src/gui/networkinformation.cpp b/src/gui/networkinformation.cpp index d39766bc567..af548338069 100644 --- a/src/gui/networkinformation.cpp +++ b/src/gui/networkinformation.cpp @@ -46,7 +46,9 @@ static void loadQNetworkInformationBackend() void NetworkInformation::initialize() { - Q_ASSERT(!_instance); + if (_instance) { + return; + } _instance = new NetworkInformation; @@ -55,6 +57,7 @@ void NetworkInformation::initialize() if (auto qni = QNetworkInformation::instance()) { connect(qni, &QNetworkInformation::isMeteredChanged, _instance, &NetworkInformation::isMeteredChanged); connect(qni, &QNetworkInformation::reachabilityChanged, _instance, &NetworkInformation::reachabilityChanged); + connect(qni, &QNetworkInformation::isBehindCaptivePortalChanged, _instance, &NetworkInformation::slotIsBehindCaptivePortalChanged); } } @@ -80,3 +83,46 @@ bool NetworkInformation::supports(Features features) const return false; } + +bool NetworkInformation::isForcedCaptivePortal() const +{ + return _forcedCaptivePortal; +} + +void NetworkInformation::setForcedCaptivePortal(bool onoff) +{ + if (_forcedCaptivePortal != onoff) { + _forcedCaptivePortal = onoff; + qCDebug(lcNetInfo) << "Switching forced captive portal to" << onoff; + + bool behindCaptivePortal = false; + + if (auto *qNetInfo = QNetworkInformation::instance()) { + behindCaptivePortal = qNetInfo->isBehindCaptivePortal(); + } + + if (behindCaptivePortal != _forcedCaptivePortal) { + Q_EMIT isBehindCaptivePortalChanged(_forcedCaptivePortal); + } + } +} + +bool NetworkInformation::isBehindCaptivePortal() const +{ + if (_forcedCaptivePortal) { + return true; + } + + if (auto *qNetInfo = QNetworkInformation::instance()) { + return qNetInfo->isBehindCaptivePortal(); + } + + return false; +} + +void NetworkInformation::slotIsBehindCaptivePortalChanged(bool state) +{ + if (!_forcedCaptivePortal) { + Q_EMIT isBehindCaptivePortalChanged(state); + } +} diff --git a/src/gui/networkinformation.h b/src/gui/networkinformation.h index 14ccdb07918..7b34217d386 100644 --- a/src/gui/networkinformation.h +++ b/src/gui/networkinformation.h @@ -20,6 +20,12 @@ namespace OCC { +/** + * @brief Wrapper class for QNetworkInformation + * + * This class is used instead of QNetworkInformation so we do not need to check for an instance, + * and to facilitate debugging by being able to force certain network states (i.e. captive portal). + */ class OWNCLOUDGUI_EXPORT NetworkInformation : public QObject { Q_OBJECT @@ -36,12 +42,22 @@ class OWNCLOUDGUI_EXPORT NetworkInformation : public QObject bool supports(Features features) const; + bool isForcedCaptivePortal() const; + void setForcedCaptivePortal(bool onoff); + bool isBehindCaptivePortal() const; + Q_SIGNALS: void isMeteredChanged(bool isMetered); void reachabilityChanged(NetworkInformation::Reachability reachability); + void isBehindCaptivePortalChanged(bool state); + +private Q_SLOTS: + void slotIsBehindCaptivePortalChanged(bool state); private: static NetworkInformation *_instance; + + bool _forcedCaptivePortal = false; }; } diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index db544751d4c..6d8f425695d 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -26,6 +26,7 @@ #include "folderwizard/folderwizard.h" #include "gui/accountsettings.h" #include "gui/commonstrings.h" +#include "gui/networkinformation.h" #include "guiutility.h" #include "libsync/theme.h" #include "logbrowser.h" @@ -650,6 +651,11 @@ void ownCloudGui::updateContextMenu() debugMenu->addAction(QStringLiteral("Crash now - OC_ENFORCE()"), _app, [] { OC_ENFORCE(false); }); debugMenu->addAction(QStringLiteral("Crash now - qFatal"), _app, [] { qFatal("la Qt fatale"); }); debugMenu->addAction(QStringLiteral("Restart now"), _app, [] { RestartManager::requestRestart(); }); + debugMenu->addSeparator(); + auto captivePortalCheckbox = debugMenu->addAction(QStringLiteral("Behind Captive Portal")); + captivePortalCheckbox->setCheckable(true); + captivePortalCheckbox->setChecked(NetworkInformation::instance()->isForcedCaptivePortal()); + connect(captivePortalCheckbox, &QAction::triggered, [](bool checked) { NetworkInformation::instance()->setForcedCaptivePortal(checked); }); } _contextMenu->addSeparator(); diff --git a/test/testfoldermigration.cpp b/test/testfoldermigration.cpp index 6bd36b37330..9e80677e699 100644 --- a/test/testfoldermigration.cpp +++ b/test/testfoldermigration.cpp @@ -11,6 +11,7 @@ #include "common/utility.h" #include "configfile.h" #include "folderman.h" +#include "gui/networkinformation.h" #include "testutils/testutils.h" using namespace OCC; @@ -91,6 +92,7 @@ private Q_SLOTS: // init folder man std::ignore = TestUtils::folderMan(); + NetworkInformation::initialize(); AccountManager::instance()->restore(); settings->beginGroup(QStringLiteral("0/Folders")); diff --git a/test/testutils/testutils.cpp b/test/testutils/testutils.cpp index ff45b069661..3c94e19709b 100644 --- a/test/testutils/testutils.cpp +++ b/test/testutils/testutils.cpp @@ -2,6 +2,7 @@ #include "common/checksums.h" #include "gui/accountmanager.h" +#include "gui/networkinformation.h" #include "libsync/creds/httpcredentials.h" #include @@ -27,6 +28,8 @@ namespace OCC { namespace TestUtils { TestUtilsPrivate::AccountStateRaii createDummyAccount() { + // ensure we have an instance of NetworkInformation + NetworkInformation::initialize(); // ensure we have an instance of folder man std::ignore = folderMan(); // don't use the account manager to create the account, it would try to use widgets From 2495ffba5e89fa987f0871c8763b4113d901453e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 15 Apr 2024 16:46:46 +0200 Subject: [PATCH 4/7] Remove `PausedDueToMetered` state and use `Connecting` state Like we do for when the client is behind a captive portal. --- src/gui/accountsettings.cpp | 5 ++- src/gui/accountstate.cpp | 50 +++++++++++++++++------------- src/gui/accountstate.h | 5 --- src/gui/folderman.cpp | 5 ++- src/gui/folderstatusmodel.cpp | 3 +- src/gui/main.cpp | 2 +- src/gui/networkinformation.cpp | 33 ++++++++++---------- src/gui/networkinformation.h | 3 +- src/libsync/jobqueue.h | 7 +++-- test/testfoldermigration.cpp | 1 - test/testutils/testutils.cpp | 3 -- test/testutils/testutilsloader.cpp | 4 +++ 12 files changed, 64 insertions(+), 57 deletions(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 0b0c77f289f..46b7f4e2732 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -471,9 +471,6 @@ void AccountSettings::slotAccountStateChanged() .arg(Utility::escape(safeUrl.toString())); switch (state) { - case AccountState::PausedDueToMetered: - showConnectionLabel(tr("Sync to %1 is paused due to metered internet connection.").arg(server)); - break; case AccountState::Connected: { QStringList errors; if (account->serverSupportLevel() != Account::ServerSupportLevel::Supported) { @@ -498,6 +495,8 @@ void AccountSettings::slotAccountStateChanged() case AccountState::Connecting: if (NetworkInformation::instance()->isBehindCaptivePortal()) { showConnectionLabel(tr("Captive portal prevents connections to %1.").arg(server)); + } else if (NetworkInformation::instance()->isMetered()) { + showConnectionLabel(tr("Sync to %1 is paused due to metered internet connection.").arg(server)); } else { showConnectionLabel(tr("Connecting to: %1.").arg(server)); } diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index d39d610cd71..69abb0a0b81 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -138,19 +138,38 @@ AccountState::AccountState(AccountPtr account) if (ConfigFile().pauseSyncWhenMetered()) { if (state() == State::Connected && isMetered) { qCInfo(lcAccountState) << "Network switched to a metered connection, setting account state to PausedDueToMetered"; - setState(State::PausedDueToMetered); - } else if (state() == State::PausedDueToMetered && !isMetered) { + setState(State::Connecting); + } else if (state() == State::Connecting && !isMetered) { qCInfo(lcAccountState) << "Network switched to a NON-metered connection, setting account state to Connected"; setState(State::Connected); } } }); - connect(NetworkInformation::instance(), &NetworkInformation::isBehindCaptivePortalChanged, this, [this](bool) { + connect(NetworkInformation::instance(), &NetworkInformation::isBehindCaptivePortalChanged, this, [this](bool onoff) { + if (onoff) { + // Block jobs from starting: they will fail because of the captive portal. + // Note: this includes the `Drives` jobs started periodically by the `SpacesManager`. + _queueGuard.block(); + } else { + // Empty the jobs queue before unblocking it. The client might have been behind a captive + // portal for hours, so a whole bunch of jobs might have queued up. If we wouldn't + // clear the queue, unleashing all those jobs might look like a DoS attack. Most of them + // are also not very useful anymore (e.g. `Drives` jobs), and the important ones (PUT jobs) + // will be rescheduled by a directory scan. + _account->jobQueue()->clear(); + _queueGuard.unblock(); + } + // A direct connect is not possible, because then the state parameter of `isBehindCaptivePortalChanged` // would become the `verifyServerState` argument to `checkConnectivity`. + // The call is also made for when we "go behind" a captive portal. That ensures that not + // only the status is set to `Connecting`, but also makes the UI show that syncing is paused. QTimer::singleShot(0, this, [this] { checkConnectivity(false); }); }); + if (NetworkInformation::instance()->isBehindCaptivePortal()) { + _queueGuard.block(); + } // as a fallback and to recover after server issues we also poll auto timer = new QTimer(this); @@ -172,22 +191,6 @@ AccountState::AccountState(AccountPtr account) ownCloudGui::raise(); msgBox->open(); }); - - - connect(NetworkInformation::instance(), &NetworkInformation::isBehindCaptivePortalChanged, this, [this](bool onoff) { - if (onoff) { - _queueGuard.block(); - } else { - // TODO: empty queue? - _queueGuard.unblock(); - } - }); - if (NetworkInformation::instance()->isBehindCaptivePortal()) { - _queueGuard.block(); - } else { - // TODO: empty queue? - _queueGuard.unblock(); - } } AccountState::~AccountState() { } @@ -261,8 +264,11 @@ void AccountState::setState(State state) _connectionValidator->deleteLater(); _connectionValidator.clear(); checkConnectivity(); - } else if (_state == Connected && NetworkInformation::instance()->isMetered() && ConfigFile().pauseSyncWhenMetered()) { - _state = PausedDueToMetered; + } else if (_state == Connected) { + if ((NetworkInformation::instance()->isMetered() && ConfigFile().pauseSyncWhenMetered()) + || NetworkInformation::instance()->isBehindCaptivePortal()) { + _state = Connecting; + } } } @@ -323,7 +329,7 @@ void AccountState::signIn() bool AccountState::isConnected() const { - return _state == Connected || _state == PausedDueToMetered; + return _state == Connected; } void AccountState::tagLastSuccessfullETagRequest(const QDateTime &tp) diff --git a/src/gui/accountstate.h b/src/gui/accountstate.h index fcbe0675d00..a3802c1342a 100644 --- a/src/gui/accountstate.h +++ b/src/gui/accountstate.h @@ -82,11 +82,6 @@ class OWNCLOUDGUI_EXPORT AccountState : public QObject /// We are currently asking the user for credentials AskingCredentials, - /// We are on a metered internet connection, and the user preference - /// is to pause syncing in this case. This state is entered from and - /// left to a `Connected` state. - PausedDueToMetered, - Connecting }; Q_ENUM(State) diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index ee8e1dd09ec..2cae66cf940 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -20,6 +20,7 @@ #include "common/asserts.h" #include "configfile.h" #include "folder.h" +#include "gui/networkinformation.h" #include "guiutility.h" #include "libsync/syncengine.h" #include "lockwatcher.h" @@ -73,7 +74,9 @@ void TrayOverallStatusResult::addResult(Folder *f) lastSyncDone = time; } - auto status = f->syncPaused() || f->accountState()->state() == AccountState::PausedDueToMetered ? SyncResult::Paused : f->syncResult().status(); + auto status = f->syncPaused() || NetworkInformation::instance()->isBehindCaptivePortal() || NetworkInformation::instance()->isMetered() + ? SyncResult::Paused + : f->syncResult().status(); if (status == SyncResult::Undefined) { status = SyncResult::Problem; } diff --git a/src/gui/folderstatusmodel.cpp b/src/gui/folderstatusmodel.cpp index 0ab1b053f01..50f37dcc7a5 100644 --- a/src/gui/folderstatusmodel.cpp +++ b/src/gui/folderstatusmodel.cpp @@ -16,6 +16,7 @@ #include "account.h" #include "accountstate.h" #include "folderman.h" +#include "gui/networkinformation.h" #include "gui/quotainfo.h" #include "theme.h" @@ -49,7 +50,7 @@ namespace { auto status = f->syncResult(); if (!f->accountState()->isConnected()) { status.setStatus(SyncResult::Status::Offline); - } else if (f->syncPaused() || f->accountState()->state() == AccountState::PausedDueToMetered) { + } else if (f->syncPaused() || NetworkInformation::instance()->isBehindCaptivePortal() || NetworkInformation::instance()->isMetered()) { status.setStatus(SyncResult::Status::Paused); } return QStringLiteral("states/%1").arg(Theme::instance()->syncStateIconName(status)); diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 7205bff7771..2066e7f692c 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -458,7 +458,7 @@ int main(int argc, char **argv) } setupLogging(options); - NetworkInformation::initialize(); + NetworkInformation::instance(); // platform->setApplication(&app); diff --git a/src/gui/networkinformation.cpp b/src/gui/networkinformation.cpp index af548338069..a4243d6da64 100644 --- a/src/gui/networkinformation.cpp +++ b/src/gui/networkinformation.cpp @@ -20,9 +20,11 @@ using namespace OCC; Q_LOGGING_CATEGORY(lcNetInfo, "gui.netinfo", QtInfoMsg) -NetworkInformation *NetworkInformation::_instance; +NetworkInformation *NetworkInformation::_instance = nullptr; -static void loadQNetworkInformationBackend() +namespace { + +void loadQNetworkInformationBackend() { if (!QNetworkInformation::loadDefaultBackend()) { qCWarning(lcNetInfo) << "Failed to load default backend of QNetworkInformation."; @@ -44,25 +46,24 @@ static void loadQNetworkInformationBackend() } } -void NetworkInformation::initialize() -{ - if (_instance) { - return; - } +} // anonymous namespace - _instance = new NetworkInformation; +NetworkInformation::NetworkInformation() { } - loadQNetworkInformationBackend(); +NetworkInformation *NetworkInformation::instance() +{ + if (!_instance) { + _instance = new NetworkInformation; - if (auto qni = QNetworkInformation::instance()) { - connect(qni, &QNetworkInformation::isMeteredChanged, _instance, &NetworkInformation::isMeteredChanged); - connect(qni, &QNetworkInformation::reachabilityChanged, _instance, &NetworkInformation::reachabilityChanged); - connect(qni, &QNetworkInformation::isBehindCaptivePortalChanged, _instance, &NetworkInformation::slotIsBehindCaptivePortalChanged); + loadQNetworkInformationBackend(); + + if (auto qni = QNetworkInformation::instance()) { + connect(qni, &QNetworkInformation::isMeteredChanged, _instance, &NetworkInformation::isMeteredChanged); + connect(qni, &QNetworkInformation::reachabilityChanged, _instance, &NetworkInformation::reachabilityChanged); + connect(qni, &QNetworkInformation::isBehindCaptivePortalChanged, _instance, &NetworkInformation::slotIsBehindCaptivePortalChanged); + } } -} -NetworkInformation *NetworkInformation::instance() -{ return _instance; } diff --git a/src/gui/networkinformation.h b/src/gui/networkinformation.h index 7b34217d386..075fad6a784 100644 --- a/src/gui/networkinformation.h +++ b/src/gui/networkinformation.h @@ -31,7 +31,6 @@ class OWNCLOUDGUI_EXPORT NetworkInformation : public QObject Q_OBJECT public: - static void initialize(); static NetworkInformation *instance(); bool isMetered(); @@ -55,6 +54,8 @@ private Q_SLOTS: void slotIsBehindCaptivePortalChanged(bool state); private: + NetworkInformation(); + static NetworkInformation *_instance; bool _forcedCaptivePortal = false; diff --git a/src/libsync/jobqueue.h b/src/libsync/jobqueue.h index 70a24b65abd..438849c1c14 100644 --- a/src/libsync/jobqueue.h +++ b/src/libsync/jobqueue.h @@ -47,14 +47,15 @@ class OWNCLOUDSYNC_EXPORT JobQueue size_t size() const; -private: - void block(); - void unblock(); /** * Clear the queue and abort all jobs */ void clear(); +private: + void block(); + void unblock(); + Account *_account; uint _blocked = 0; std::vector> _jobs; diff --git a/test/testfoldermigration.cpp b/test/testfoldermigration.cpp index 9e80677e699..643ca329fab 100644 --- a/test/testfoldermigration.cpp +++ b/test/testfoldermigration.cpp @@ -92,7 +92,6 @@ private Q_SLOTS: // init folder man std::ignore = TestUtils::folderMan(); - NetworkInformation::initialize(); AccountManager::instance()->restore(); settings->beginGroup(QStringLiteral("0/Folders")); diff --git a/test/testutils/testutils.cpp b/test/testutils/testutils.cpp index 3c94e19709b..ff45b069661 100644 --- a/test/testutils/testutils.cpp +++ b/test/testutils/testutils.cpp @@ -2,7 +2,6 @@ #include "common/checksums.h" #include "gui/accountmanager.h" -#include "gui/networkinformation.h" #include "libsync/creds/httpcredentials.h" #include @@ -28,8 +27,6 @@ namespace OCC { namespace TestUtils { TestUtilsPrivate::AccountStateRaii createDummyAccount() { - // ensure we have an instance of NetworkInformation - NetworkInformation::initialize(); // ensure we have an instance of folder man std::ignore = folderMan(); // don't use the account manager to create the account, it would try to use widgets diff --git a/test/testutils/testutilsloader.cpp b/test/testutils/testutilsloader.cpp index f5b137e22c7..016ed935eb2 100644 --- a/test/testutils/testutilsloader.cpp +++ b/test/testutils/testutilsloader.cpp @@ -1,4 +1,5 @@ #include "configfile.h" +#include "gui/networkinformation.h" #include "logger.h" #include "resources/loadresources.h" #include "testutils.h" @@ -19,6 +20,9 @@ void setUpTests() OCC::Logger::instance()->setLogDebug(true); OCC::Account::setCommonCacheDirectory(QStringLiteral("%1/cache").arg(dir.path())); + + // ensure we have an instance of NetworkInformation + OCC::NetworkInformation::instance(); } Q_COREAPP_STARTUP_FUNCTION(setUpTests) } From ce34bad69036614a654fdbeb71affcdde5ceba9a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 12 Jun 2024 16:04:57 +0200 Subject: [PATCH 5/7] Add changelog entry --- changelog/unreleased/11533 | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 changelog/unreleased/11533 diff --git a/changelog/unreleased/11533 b/changelog/unreleased/11533 new file mode 100644 index 00000000000..1e0a45aa212 --- /dev/null +++ b/changelog/unreleased/11533 @@ -0,0 +1,9 @@ +Enhancement: Pause sync when behind a captive portal + +When the operating system detects a captive portal, stop all outgoing +network requests. These requests will fail with an SSL certificate +mismatch error. When the captive portal is "gone", synchronisation will +be restarted. + +https://github.com/owncloud/client/issues/11533 +https://github.com/owncloud/client/pull/11567 From 52f31321ce51f0c0f4ad8d245d97592d534270b2 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 21 Jun 2024 16:08:36 +0200 Subject: [PATCH 6/7] Add more logging for network status changes --- src/gui/accountsettings.cpp | 3 ++- src/gui/networkinformation.cpp | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 46b7f4e2732..a70d70e97b1 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -458,6 +458,7 @@ void AccountSettings::slotAccountStateChanged() { const AccountState::State state = _accountState->state(); const AccountPtr account = _accountState->account(); + qCDebug(lcAccountSettings) << "Account state changed to" << state << "for account" << account; // in 2023 there should never be credentials encoded in the url, but we never know... const auto safeUrl = account->url().adjusted(QUrl::RemoveUserInfo); @@ -495,7 +496,7 @@ void AccountSettings::slotAccountStateChanged() case AccountState::Connecting: if (NetworkInformation::instance()->isBehindCaptivePortal()) { showConnectionLabel(tr("Captive portal prevents connections to %1.").arg(server)); - } else if (NetworkInformation::instance()->isMetered()) { + } else if (NetworkInformation::instance()->isMetered() && ConfigFile().pauseSyncWhenMetered()) { showConnectionLabel(tr("Sync to %1 is paused due to metered internet connection.").arg(server)); } else { showConnectionLabel(tr("Connecting to: %1.").arg(server)); diff --git a/src/gui/networkinformation.cpp b/src/gui/networkinformation.cpp index a4243d6da64..b1309fdd877 100644 --- a/src/gui/networkinformation.cpp +++ b/src/gui/networkinformation.cpp @@ -40,9 +40,11 @@ void loadQNetworkInformationBackend() qCDebug(lcNetInfo) << "Loaded network information backend:" << QNetworkInformation::instance()->backendName(); qCDebug(lcNetInfo) << "Supported features:" << QNetworkInformation::instance()->supportedFeatures(); qCDebug(lcNetInfo) << "Available backends:" << QNetworkInformation::availableBackends().join(QStringLiteral(", ")); + if (auto qni = QNetworkInformation::instance()) { - QObject::connect(qni, &QNetworkInformation::reachabilityChanged, - [](QNetworkInformation::Reachability reachability) { qCInfo(lcNetInfo) << "Connection Status changed to:" << reachability; }); + QObject::connect(qni, &QNetworkInformation::reachabilityChanged, [qni](QNetworkInformation::Reachability reachability) { + qCInfo(lcNetInfo) << "Connection Status changed to:" << reachability << "captive portal status:" << qni->isBehindCaptivePortal(); + }); } } @@ -123,6 +125,8 @@ bool NetworkInformation::isBehindCaptivePortal() const void NetworkInformation::slotIsBehindCaptivePortalChanged(bool state) { + qCDebug(lcNetInfo) << "OS signals behind captive portal changed to" << state << "forced captive portal flag:" << _forcedCaptivePortal; + if (!_forcedCaptivePortal) { Q_EMIT isBehindCaptivePortalChanged(state); } From ca65721ff82cb7734e19e654c2a7006f23bddd3e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 16 Aug 2024 14:25:16 +0200 Subject: [PATCH 7/7] Add changelog entry for captive portal handling --- changelog/unreleased/11567 | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/unreleased/11567 diff --git a/changelog/unreleased/11567 b/changelog/unreleased/11567 new file mode 100644 index 00000000000..3870c61396e --- /dev/null +++ b/changelog/unreleased/11567 @@ -0,0 +1,6 @@ +Enhancement: Pause synchronization when a captive portal is detected + +An encrypted connection to a captive portal will use an unknown certificate, which in turn will caused a dialog to be shown by the client. +This is now changed: when a captive portal is detected by the OS, the client now shows in the account status that it cannot connect to the server because of a captive portal. + +Supported OSses: Windows, Linux (with some network managers)