From 9f0d4aaba1e63581519156eb9625c8bceb5df786 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 31 Jul 2025 23:17:46 +0800 Subject: [PATCH] refactoring: separated split tunneling and dns controllers and models --- client/core/controllers/coreController.cpp | 59 +++---- client/core/controllers/coreController.h | 21 ++- client/core/controllers/dnsController.cpp | 5 + .../core/controllers/settingsController.cpp | 18 ++- client/core/controllers/settingsController.h | 3 + .../controllers/splitTunnelingController.cpp | 104 ++++++++++++- .../controllers/splitTunnelingController.h | 13 +- ...troller.cpp => allowedDnsUIController.cpp} | 46 +++--- ...sController.h => allowedDnsUIController.h} | 18 +-- .../appSplitTunnelingController.cpp | 49 ------ .../controllers/appSplitTunnelingController.h | 31 ---- .../ui/controllers/appSplitUIController.cpp | 59 +++++++ client/ui/controllers/appSplitUIController.h | 31 ++++ .../ui/controllers/siteSplitUIController.cpp | 109 +++++++++++++ client/ui/controllers/siteSplitUIController.h | 35 +++++ client/ui/controllers/sitesController.cpp | 146 ------------------ client/ui/controllers/sitesController.h | 38 ----- client/ui/models/allowed_dns_model.cpp | 4 +- client/ui/models/allowed_dns_model.h | 4 +- client/ui/models/appSplitTunnelingModel.cpp | 4 +- client/ui/models/appSplitTunnelingModel.h | 6 +- client/ui/models/languageModel.cpp | 5 +- client/ui/models/languageModel.h | 8 +- client/ui/models/sites_model.cpp | 4 +- client/ui/models/sites_model.h | 6 +- 25 files changed, 450 insertions(+), 376 deletions(-) rename client/ui/controllers/{allowedDnsController.cpp => allowedDnsUIController.cpp} (60%) rename client/ui/controllers/{allowedDnsController.h => allowedDnsUIController.h} (51%) delete mode 100644 client/ui/controllers/appSplitTunnelingController.cpp delete mode 100644 client/ui/controllers/appSplitTunnelingController.h create mode 100644 client/ui/controllers/appSplitUIController.cpp create mode 100644 client/ui/controllers/appSplitUIController.h create mode 100644 client/ui/controllers/siteSplitUIController.cpp create mode 100644 client/ui/controllers/siteSplitUIController.h delete mode 100644 client/ui/controllers/sitesController.cpp delete mode 100644 client/ui/controllers/sitesController.h diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp index 9841eb2c0..f409fa346 100644 --- a/client/core/controllers/coreController.cpp +++ b/client/core/controllers/coreController.cpp @@ -17,8 +17,9 @@ CoreController::CoreController(const QSharedPointer &vpnConnectio QQmlApplicationEngine *engine, QObject *parent) : QObject(parent), m_vpnConnection(vpnConnection), m_settings(settings), m_engine(engine) { + initCoreControllers(); initModels(); - initControllers(); + initUIControllers(); initSignalHandlers(); initAndroidController(); @@ -42,18 +43,6 @@ void CoreController::initModels() m_serversModel.reset(new ServersModel(m_settings, this)); m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get()); - m_languageModel.reset(new LanguageModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get()); - - m_sitesModel.reset(new SitesModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get()); - - m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get()); - - m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get()); - m_openVpnConfigModel = QSharedPointer::create(this); m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get()); @@ -91,11 +80,9 @@ void CoreController::initModels() m_sftpConfigModel, m_socks5ConfigModel, this)); m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get()); - // Create ClientManagementController first auto clientManagementController = QSharedPointer::create(m_settings, this); m_clientManagementModel.reset(new ClientManagementModel(m_settings, clientManagementController, this)); - - // Create ClientManagementUIController + m_clientManagementUIController.reset(new ClientManagementUIController(clientManagementController, this)); m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get()); @@ -110,9 +97,30 @@ void CoreController::initModels() m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this)); m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get()); + + m_sitesModel.reset(new SitesModel(m_splitTunnelingController, this)); + m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get()); + + m_allowedDnsModel.reset(new AllowedDnsModel(m_dnsController, this)); + m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get()); + + m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_splitTunnelingController, this)); + m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get()); + + m_languageModel.reset(new LanguageModel(m_settingsController, this)); + m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get()); } -void CoreController::initControllers() +void CoreController::initCoreControllers() +{ + m_settingsController = QSharedPointer::create(m_settings, this); + m_dnsController = QSharedPointer::create(m_settings, this); + m_splitTunnelingController = QSharedPointer::create(m_settings, m_vpnConnection, this); + m_exportController = QSharedPointer::create(m_settings, this); + m_installController = QSharedPointer::create(m_settings, this); +} + +void CoreController::initUIControllers() { m_connectionController.reset( new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings)); @@ -124,9 +132,6 @@ void CoreController::initControllers() m_focusController.reset(new FocusController(m_engine, this)); m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get()); - m_exportController = QSharedPointer::create(m_settings, this); - m_installController = QSharedPointer::create(m_settings, this); - auto clientManagementController = m_clientManagementUIController->getClientManagementController(); m_exportUIController.reset(new ExportUIController(m_serversModel, m_containersModel, m_clientManagementModel, m_exportController, clientManagementController)); m_engine->rootContext()->setContextProperty("ExportController", m_exportUIController.get()); @@ -140,19 +145,18 @@ void CoreController::initControllers() m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings)); m_engine->rootContext()->setContextProperty("ImportController", m_importController.get()); - m_settingsController = QSharedPointer::create(m_settings, this); m_settingsUIController.reset( new SettingsUIController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settingsController)); m_engine->rootContext()->setContextProperty("SettingsController", m_settingsUIController.get()); - m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel)); - m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get()); + m_siteSplitUIController.reset(new SiteSplitUIController(m_splitTunnelingController, m_sitesModel)); + m_engine->rootContext()->setContextProperty("SitesController", m_siteSplitUIController.get()); - m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel)); - m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get()); + m_allowedDnsUIController.reset(new AllowedDnsUIController(m_dnsController, m_allowedDnsModel)); + m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsUIController.get()); - m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel)); - m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get()); + m_appSplitUIController.reset(new AppSplitUIController(m_splitTunnelingController, m_appSplitTunnelingModel)); + m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitUIController.get()); m_systemController.reset(new SystemController(m_settings)); m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get()); @@ -317,7 +321,6 @@ void CoreController::updateTranslator(const QLocale &locale) availableTranslations << it.next(); } - // This code allow to load translation for the language only, without country code const QString lang = locale.name().split("_").first(); const QString translationFilePrefix = QString(":/translations/amneziavpn_") + lang; QString strFileName = QString(":/translations/amneziavpn_%1.qm").arg(locale.name()); diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h index 02ec37904..06214c88c 100644 --- a/client/core/controllers/coreController.h +++ b/client/core/controllers/coreController.h @@ -9,8 +9,8 @@ #include "ui/controllers/api/apiSettingsController.h" #include "ui/controllers/api/apiPremV1MigrationController.h" #include "core/controllers/selfhosted/clientManagementController.h" -#include "ui/controllers/appSplitTunnelingController.h" -#include "ui/controllers/allowedDnsController.h" +#include "ui/controllers/appSplitUIController.h" +#include "ui/controllers/allowedDnsUIController.h" #include "ui/controllers/connectionController.h" #include "core/controllers/selfhosted/exportController.h" #include "core/controllers/selfhosted/installController.h" @@ -20,9 +20,11 @@ #include "ui/controllers/selfhosted/installUIController.h" #include "ui/controllers/pageController.h" #include "ui/controllers/settingsUIController.h" -#include "ui/controllers/sitesController.h" +#include "ui/controllers/siteSplitUIController.h" #include "ui/controllers/systemController.h" #include "core/controllers/settingsController.h" +#include "core/controllers/dnsController.h" +#include "core/controllers/splitTunnelingController.h" #include "ui/models/allowed_dns_model.h" #include "ui/models/containers_model.h" @@ -69,10 +71,11 @@ signals: private: void initModels(); - void initControllers(); + void initCoreControllers(); + void initUIControllers(); + void initSignalHandlers(); void initAndroidController(); void initAppleController(); - void initSignalHandlers(); void setupControllerSignalConnections(); void initNotificationHandler(); @@ -113,16 +116,18 @@ private: QScopedPointer m_installUIController; QScopedPointer m_importController; QScopedPointer m_settingsUIController; - QScopedPointer m_sitesController; + QScopedPointer m_siteSplitUIController; QScopedPointer m_systemController; - QScopedPointer m_appSplitTunnelingController; - QScopedPointer m_allowedDnsController; + QScopedPointer m_appSplitUIController; + QScopedPointer m_allowedDnsUIController; QScopedPointer m_apiSettingsController; QScopedPointer m_apiConfigsController; QScopedPointer m_apiPremV1MigrationController; QSharedPointer m_settingsController; + QSharedPointer m_dnsController; + QSharedPointer m_splitTunnelingController; QSharedPointer m_containersModel; QSharedPointer m_defaultServerContainersModel; diff --git a/client/core/controllers/dnsController.cpp b/client/core/controllers/dnsController.cpp index 5e3c21fc7..245d86182 100644 --- a/client/core/controllers/dnsController.cpp +++ b/client/core/controllers/dnsController.cpp @@ -1,4 +1,5 @@ #include "dnsController.h" +#include "core/networkUtilities.h" DnsController::DnsController(std::shared_ptr settings, QObject *parent) : QObject(parent), m_settings(settings) @@ -7,6 +8,10 @@ DnsController::DnsController(std::shared_ptr settings, QObject *parent bool DnsController::addDns(const QString &ip) { + if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) { + return false; + } + QStringList currentDnsServers = m_settings->allowedDnsServers(); if (currentDnsServers.contains(ip)) { diff --git a/client/core/controllers/settingsController.cpp b/client/core/controllers/settingsController.cpp index 7342c075f..02665ad99 100644 --- a/client/core/controllers/settingsController.cpp +++ b/client/core/controllers/settingsController.cpp @@ -39,13 +39,9 @@ void SettingsController::toggleAmneziaDns(bool enable) emit dnsConfigChanged(); } -void SettingsController::configureLogging(bool enable) +void SettingsController::configureLogging(bool enabled) { - m_settings->setSaveLogs(enable); - if (enable) { - m_settings->setLogEnableDate(QDateTime::currentDateTime()); - } - emit loggingConfigChanged(); + m_settings->setIsLoggingEnabled(enabled); } void SettingsController::checkLoggingExpiration() @@ -224,4 +220,14 @@ QDateTime SettingsController::getLogEnableDate() const return m_settings->getLogEnableDate(); } +QLocale SettingsController::getAppLanguage() const +{ + return m_settings->getAppLanguage(); +} + +void SettingsController::setAppLanguage(const QLocale &locale) +{ + m_settings->setAppLanguage(locale); +} + diff --git a/client/core/controllers/settingsController.h b/client/core/controllers/settingsController.h index 0bbbcedf4..8c62f8be8 100644 --- a/client/core/controllers/settingsController.h +++ b/client/core/controllers/settingsController.h @@ -67,6 +67,9 @@ public: // Log date QDateTime getLogEnableDate() const; + QLocale getAppLanguage() const; + void setAppLanguage(const QLocale &locale); + signals: void settingsReset(); void dnsConfigChanged(); diff --git a/client/core/controllers/splitTunnelingController.cpp b/client/core/controllers/splitTunnelingController.cpp index 4b3f77e61..5061e3b0c 100644 --- a/client/core/controllers/splitTunnelingController.cpp +++ b/client/core/controllers/splitTunnelingController.cpp @@ -1,24 +1,36 @@ #include "splitTunnelingController.h" #include "settings.h" +#include "core/networkUtilities.h" +#include -SplitTunnelingController::SplitTunnelingController(std::shared_ptr settings, QObject *parent) - : QObject(parent), m_settings(settings) +SplitTunnelingController::SplitTunnelingController(std::shared_ptr settings, + QSharedPointer vpnConnection, + QObject *parent) + : QObject(parent), m_settings(settings), m_vpnConnection(vpnConnection) { } // Apps split tunneling implementation bool SplitTunnelingController::addApp(const InstalledAppInfo &appInfo) { + InstalledAppInfo processedAppInfo = appInfo; + + // Migrated from AppSplitTunnelingController::addApp - app name extraction + if (processedAppInfo.appName.isEmpty() && !processedAppInfo.appPath.isEmpty()) { + QFileInfo fileInfo(processedAppInfo.appPath); + processedAppInfo.appName = fileInfo.fileName(); + } + auto currentApps = m_settings->getVpnApps(getAppsRouteMode()); - if (currentApps.contains(appInfo)) { + if (currentApps.contains(processedAppInfo)) { return false; } - currentApps.append(appInfo); + currentApps.append(processedAppInfo); m_settings->setVpnApps(getAppsRouteMode(), currentApps); - emit appAdded(appInfo); + emit appAdded(processedAppInfo); return true; } @@ -67,11 +79,37 @@ void SplitTunnelingController::setAppsSplitTunnelingEnabled(bool enabled) // Sites split tunneling implementation bool SplitTunnelingController::addSite(const QString &hostname, const QString &ip) { - if (!m_settings->addVpnSite(getSitesRouteMode(), hostname, ip)) { + QString processedHostname = hostname; + + // Migrated from SitesController::addSite - hostname processing + if (!NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) { + processedHostname.replace("https://", ""); + processedHostname.replace("http://", ""); + processedHostname.replace("ftp://", ""); + processedHostname = processedHostname.split("/", Qt::SkipEmptyParts).first(); + } + + if (!m_settings->addVpnSite(getSitesRouteMode(), processedHostname, ip)) { return false; } - emit siteAdded(hostname, ip); + // Migrated from SitesController::addSite - VPN route management + if (m_vpnConnection) { + if (!ip.isEmpty()) { + QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, + Q_ARG(QStringList, QStringList() << ip)); + } else if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(processedHostname)) { + QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, + Q_ARG(QStringList, QStringList() << processedHostname)); + } else { + // Migrated from SitesController::addSite - DNS resolution + int lookupId = QHostInfo::lookupHost(processedHostname, this, + SLOT(handleHostnameResolved(QHostInfo))); + m_pendingResolutions[lookupId] = processedHostname; + } + } + + emit siteAdded(processedHostname, ip); return true; } @@ -83,7 +121,26 @@ bool SplitTunnelingController::addSites(const QMap &sites, boo m_settings->addVpnSites(getSitesRouteMode(), sites); - // Emit signals for each added site + // Migrated from SitesController - VPN route management for batch adds + if (m_vpnConnection) { + QStringList ips; + auto i = sites.constBegin(); + while (i != sites.constEnd()) { + const QString &hostname = i.key(); + const QString &ip = i.value(); + + if (ip.isEmpty()) { + ips.append(hostname); + } else { + ips.append(ip); + } + ++i; + } + + QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, + Q_ARG(QStringList, ips)); + } + auto i = sites.constBegin(); while (i != sites.constEnd()) { emit siteAdded(i.key(), i.value()); @@ -99,10 +156,41 @@ bool SplitTunnelingController::removeSite(const QString &hostname) return false; } + // Migrated from SitesController::removeSite - VPN route management + if (m_vpnConnection) { + QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection, + Q_ARG(QStringList, QStringList() << hostname)); + } + emit siteRemoved(hostname); return true; } +// Migrated from SitesController - DNS resolution handler +void SplitTunnelingController::handleHostnameResolved(const QHostInfo &hostInfo) +{ + if (hostInfo.error() != QHostInfo::NoError) { + return; + } + + QString hostname = m_pendingResolutions.take(hostInfo.lookupId()); + if (hostname.isEmpty()) { + return; + } + + for (const QHostAddress &addr : hostInfo.addresses()) { + if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) { + if (m_vpnConnection) { + QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, + Q_ARG(QStringList, QStringList() << addr.toString())); + } + + m_settings->addVpnSite(getSitesRouteMode(), hostname, addr.toString()); + break; + } + } +} + QVector> SplitTunnelingController::getSites(Settings::RouteMode routeMode) const { QVector> sites; diff --git a/client/core/controllers/splitTunnelingController.h b/client/core/controllers/splitTunnelingController.h index bbd98e315..1fbc726f2 100644 --- a/client/core/controllers/splitTunnelingController.h +++ b/client/core/controllers/splitTunnelingController.h @@ -6,10 +6,12 @@ #include #include #include +#include #include "settings.h" #include "core/defs.h" #include "platforms/android/installedAppInfo.h" +#include "vpnconnection.h" using namespace amnezia; @@ -18,7 +20,9 @@ class SplitTunnelingController : public QObject Q_OBJECT public: - explicit SplitTunnelingController(std::shared_ptr settings, QObject *parent = nullptr); + explicit SplitTunnelingController(std::shared_ptr settings, + QSharedPointer vpnConnection = nullptr, + QObject *parent = nullptr); // Apps split tunneling bool addApp(const InstalledAppInfo &appInfo); @@ -50,14 +54,19 @@ signals: void appAdded(const InstalledAppInfo &appInfo); void appRemoved(const InstalledAppInfo &appInfo); - // Sites signals + // Sites signals void sitesRouteModelChanged(); void sitesSplitTunnelingToggled(); void siteAdded(const QString &hostname, const QString &ip); void siteRemoved(const QString &hostname); +private slots: + void handleHostnameResolved(const QHostInfo &hostInfo); + private: std::shared_ptr m_settings; + QSharedPointer m_vpnConnection; + QMap m_pendingResolutions; }; #endif // SPLITTUNNELINGCONTROLLER_H diff --git a/client/ui/controllers/allowedDnsController.cpp b/client/ui/controllers/allowedDnsUIController.cpp similarity index 60% rename from client/ui/controllers/allowedDnsController.cpp rename to client/ui/controllers/allowedDnsUIController.cpp index 12e8b599f..7b9102f1b 100644 --- a/client/ui/controllers/allowedDnsController.cpp +++ b/client/ui/controllers/allowedDnsUIController.cpp @@ -1,4 +1,4 @@ -#include "allowedDnsController.h" +#include "allowedDnsUIController.h" #include #include @@ -10,41 +10,39 @@ #include "core/networkUtilities.h" #include "core/defs.h" -AllowedDnsController::AllowedDnsController(const std::shared_ptr &settings, - const QSharedPointer &allowedDnsModel, - QObject *parent) - : QObject(parent), m_settings(settings), m_allowedDnsModel(allowedDnsModel) +AllowedDnsUIController::AllowedDnsUIController(QSharedPointer dnsController, + const QSharedPointer &allowedDnsModel, + QObject *parent) + : QObject(parent), m_dnsController(dnsController), m_allowedDnsModel(allowedDnsModel) { } -void AllowedDnsController::addDns(QString ip) +void AllowedDnsUIController::addDns(QString ip) { if (ip.isEmpty()) { return; } - if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) { - emit errorOccurred(tr("The address does not look like a valid IP address")); - return; - } - - if (m_allowedDnsModel->addDns(ip)) { + if (m_dnsController->addDns(ip)) { emit finished(tr("New DNS server added: %1").arg(ip)); } else { - emit errorOccurred(tr("DNS server already exists: %1").arg(ip)); + emit errorOccurred(tr("The address does not look like a valid IP address or already exists")); } } -void AllowedDnsController::removeDns(int index) +void AllowedDnsUIController::removeDns(int index) { auto modelIndex = m_allowedDnsModel->index(index); auto ip = m_allowedDnsModel->data(modelIndex, AllowedDnsModel::Roles::IpRole).toString(); - m_allowedDnsModel->removeDns(modelIndex); - - emit finished(tr("DNS server removed: %1").arg(ip)); + + if (m_dnsController->removeDns(ip)) { + emit finished(tr("DNS server removed: %1").arg(ip)); + } else { + emit errorOccurred(tr("Failed to remove DNS server: %1").arg(ip)); + } } -void AllowedDnsController::importDns(const QString &fileName, bool replaceExisting) +void AllowedDnsUIController::importDns(const QString &fileName, bool replaceExisting) { QByteArray jsonData; if (!SystemController::readFile(fileName, jsonData)) { @@ -77,14 +75,16 @@ void AllowedDnsController::importDns(const QString &fileName, bool replaceExisti dnsServers.append(ip); } - m_allowedDnsModel->addDnsList(dnsServers, replaceExisting); - - emit finished(tr("Import completed")); + if (m_dnsController->addDnsList(dnsServers, replaceExisting)) { + emit finished(tr("Import completed")); + } else { + emit errorOccurred(tr("Import failed")); + } } -void AllowedDnsController::exportDns(const QString &fileName) +void AllowedDnsUIController::exportDns(const QString &fileName) { - auto dnsServers = m_allowedDnsModel->getCurrentDnsServers(); + auto dnsServers = m_dnsController->getAllowedDnsServers(); QJsonArray jsonArray; diff --git a/client/ui/controllers/allowedDnsController.h b/client/ui/controllers/allowedDnsUIController.h similarity index 51% rename from client/ui/controllers/allowedDnsController.h rename to client/ui/controllers/allowedDnsUIController.h index 5509a036e..6c53a15d5 100644 --- a/client/ui/controllers/allowedDnsController.h +++ b/client/ui/controllers/allowedDnsUIController.h @@ -1,18 +1,18 @@ -#ifndef ALLOWEDDNSCONTROLLER_H -#define ALLOWEDDNSCONTROLLER_H +#ifndef ALLOWEDDNSUICONTROLLER_H +#define ALLOWEDDNSUICONTROLLER_H #include -#include "settings.h" #include "ui/models/allowed_dns_model.h" +#include "core/controllers/dnsController.h" -class AllowedDnsController : public QObject +class AllowedDnsUIController : public QObject { Q_OBJECT public: - explicit AllowedDnsController(const std::shared_ptr &settings, - const QSharedPointer &allowedDnsModel, - QObject *parent = nullptr); + explicit AllowedDnsUIController(QSharedPointer dnsController, + const QSharedPointer &allowedDnsModel, + QObject *parent = nullptr); public slots: void addDns(QString ip); @@ -28,8 +28,8 @@ signals: void saveFile(const QString &fileName, const QString &data); private: - std::shared_ptr m_settings; + QSharedPointer m_dnsController; QSharedPointer m_allowedDnsModel; }; -#endif // ALLOWEDDNSCONTROLLER_H +#endif // ALLOWEDDNSUICONTROLLER_H diff --git a/client/ui/controllers/appSplitTunnelingController.cpp b/client/ui/controllers/appSplitTunnelingController.cpp deleted file mode 100644 index 6452bba2c..000000000 --- a/client/ui/controllers/appSplitTunnelingController.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "appSplitTunnelingController.h" - -#include - -#include "core/defs.h" - -AppSplitTunnelingController::AppSplitTunnelingController(const std::shared_ptr &settings, - const QSharedPointer &appSplitTunnelingModel, QObject *parent) - : QObject(parent), m_settings(settings), m_appSplitTunnelingModel(appSplitTunnelingModel) -{ -} - -void AppSplitTunnelingController::addApp(const QString &appPath) -{ - - InstalledAppInfo appInfo { "", "", appPath }; - if (!appPath.isEmpty()) { - QFileInfo fileInfo(appPath); - appInfo.appName = fileInfo.fileName(); - } - - if (m_appSplitTunnelingModel->addApp(appInfo)) { - emit finished(tr("Application added: %1").arg(appInfo.appName)); - - } else { - emit errorOccurred(tr("The application has already been added")); - } -} - -void AppSplitTunnelingController::addApps(QVector> apps) -{ - for (const auto &app : apps) { - InstalledAppInfo appInfo { app.first, app.second, "" }; - - m_appSplitTunnelingModel->addApp(appInfo); - } - emit finished(tr("The selected applications have been added")); -} - -void AppSplitTunnelingController::removeApp(const int index) -{ - auto modelIndex = m_appSplitTunnelingModel->index(index); - auto appPath = m_appSplitTunnelingModel->data(modelIndex, AppSplitTunnelingModel::Roles::AppPathRole).toString(); - m_appSplitTunnelingModel->removeApp(modelIndex); - - QFileInfo fileInfo(appPath); - - emit finished(tr("Application removed: %1").arg(fileInfo.fileName())); -} diff --git a/client/ui/controllers/appSplitTunnelingController.h b/client/ui/controllers/appSplitTunnelingController.h deleted file mode 100644 index da1009ecf..000000000 --- a/client/ui/controllers/appSplitTunnelingController.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef APPSPLITTUNNELINGCONTROLLER_H -#define APPSPLITTUNNELINGCONTROLLER_H - -#include - -#include "settings.h" -#include "ui/models/appSplitTunnelingModel.h" - -class AppSplitTunnelingController : public QObject -{ - Q_OBJECT -public: - explicit AppSplitTunnelingController(const std::shared_ptr &settings, - const QSharedPointer &sitesModel, QObject *parent = nullptr); - -public slots: - void addApp(const QString &appPath); - void addApps(QVector> apps); - void removeApp(const int index); - -signals: - void errorOccurred(const QString &errorMessage); - void finished(const QString &message); - -private: - std::shared_ptr m_settings; - - QSharedPointer m_appSplitTunnelingModel; -}; - -#endif // APPSPLITTUNNELINGCONTROLLER_H diff --git a/client/ui/controllers/appSplitUIController.cpp b/client/ui/controllers/appSplitUIController.cpp new file mode 100644 index 000000000..63178c203 --- /dev/null +++ b/client/ui/controllers/appSplitUIController.cpp @@ -0,0 +1,59 @@ +#include "appSplitUIController.h" + +#include + +#include "core/defs.h" + +AppSplitUIController::AppSplitUIController(QSharedPointer splitTunnelingController, + const QSharedPointer &appSplitTunnelingModel, + QObject *parent) + : QObject(parent), m_splitTunnelingController(splitTunnelingController), m_appSplitTunnelingModel(appSplitTunnelingModel) +{ +} + +void AppSplitUIController::addApp(const QString &appPath) +{ + if (appPath.isEmpty()) { + return; + } + + InstalledAppInfo appInfo { "", "", appPath }; + if (m_splitTunnelingController->addApp(appInfo)) { + emit finished(tr("Application added")); + } else { + emit errorOccurred(tr("The application has already been added")); + } +} + +void AppSplitUIController::addApps(QVector> apps) +{ + bool success = false; + for (const auto &app : apps) { + InstalledAppInfo appInfo { app.first, app.second, "" }; + if (m_splitTunnelingController->addApp(appInfo)) { + success = true; + } + } + + if (success) { + emit finished(tr("The selected applications have been added")); + } else { + emit errorOccurred(tr("Failed to add applications")); + } +} + +void AppSplitUIController::removeApp(const int index) +{ + auto modelIndex = m_appSplitTunnelingModel->index(index); + auto appPath = m_appSplitTunnelingModel->data(modelIndex, AppSplitTunnelingModel::Roles::AppPathRole).toString(); + auto appName = m_appSplitTunnelingModel->data(modelIndex, AppSplitTunnelingModel::Roles::AppNameRole).toString(); + + InstalledAppInfo appInfo { appName, "", appPath }; + + if (m_splitTunnelingController->removeApp(appInfo)) { + QFileInfo fileInfo(appPath); + emit finished(tr("Application removed: %1").arg(fileInfo.fileName())); + } else { + emit errorOccurred(tr("Failed to remove application")); + } +} diff --git a/client/ui/controllers/appSplitUIController.h b/client/ui/controllers/appSplitUIController.h new file mode 100644 index 000000000..2d01bbb18 --- /dev/null +++ b/client/ui/controllers/appSplitUIController.h @@ -0,0 +1,31 @@ +#ifndef APPSPLITUICONTROLLER_H +#define APPSPLITUICONTROLLER_H + +#include + +#include "ui/models/appSplitTunnelingModel.h" +#include "core/controllers/splitTunnelingController.h" + +class AppSplitUIController : public QObject +{ + Q_OBJECT +public: + explicit AppSplitUIController(QSharedPointer splitTunnelingController, + const QSharedPointer &appSplitTunnelingModel, + QObject *parent = nullptr); + +public slots: + void addApp(const QString &appPath); + void addApps(QVector> apps); + void removeApp(const int index); + +signals: + void errorOccurred(const QString &errorMessage); + void finished(const QString &message); + +private: + QSharedPointer m_splitTunnelingController; + QSharedPointer m_appSplitTunnelingModel; +}; + +#endif // APPSPLITUICONTROLLER_H diff --git a/client/ui/controllers/siteSplitUIController.cpp b/client/ui/controllers/siteSplitUIController.cpp new file mode 100644 index 000000000..e7e61e3c3 --- /dev/null +++ b/client/ui/controllers/siteSplitUIController.cpp @@ -0,0 +1,109 @@ +#include "siteSplitUIController.h" + +#include +#include +#include +#include +#include +#include + +#include "systemController.h" +#include "core/networkUtilities.h" + +SiteSplitUIController::SiteSplitUIController(QSharedPointer splitTunnelingController, + const QSharedPointer &sitesModel, + QObject *parent) + : QObject(parent), m_splitTunnelingController(splitTunnelingController), m_sitesModel(sitesModel) +{ +} + +void SiteSplitUIController::addSite(QString hostname) +{ + if (hostname.isEmpty()) { + return; + } + + if (!hostname.contains(".")) { + emit errorOccurred(tr("Hostname not look like ip adress or domain name")); + return; + } + + if (m_splitTunnelingController->addSite(hostname)) { + emit finished(tr("New site added: %1").arg(hostname)); + } else { + emit errorOccurred(tr("Site already exists: %1").arg(hostname)); + } +} + +void SiteSplitUIController::removeSite(int index) +{ + auto modelIndex = m_sitesModel->index(index); + auto hostname = m_sitesModel->data(modelIndex, SitesModel::Roles::UrlRole).toString(); + + if (m_splitTunnelingController->removeSite(hostname)) { + emit finished(tr("Site removed: %1").arg(hostname)); + } else { + emit errorOccurred(tr("Failed to remove site: %1").arg(hostname)); + } +} + +void SiteSplitUIController::importSites(const QString &fileName, bool replaceExisting) +{ + QByteArray jsonData; + if (!SystemController::readFile(fileName, jsonData)) { + emit errorOccurred(tr("Can't open file: %1").arg(fileName)); + return; + } + + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData); + if (jsonDocument.isNull()) { + emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName)); + return; + } + + if (!jsonDocument.isArray()) { + emit errorOccurred(tr("The JSON data is not an array in file: %1").arg(fileName)); + return; + } + + auto jsonArray = jsonDocument.array(); + QMap sites; + + for (auto jsonValue : jsonArray) { + auto jsonObject = jsonValue.toObject(); + auto hostname = jsonObject.value("hostname").toString(""); + auto ip = jsonObject.value("ip").toString(""); + + if (!hostname.contains(".") && !NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) { + qDebug() << hostname << " not look like ip adress or domain name"; + continue; + } + + sites.insert(hostname, ip); + } + + if (m_splitTunnelingController->addSites(sites, replaceExisting)) { + emit finished(tr("Import completed")); + } else { + emit errorOccurred(tr("Import failed")); + } +} + +void SiteSplitUIController::exportSites(const QString &fileName) +{ + auto sites = m_splitTunnelingController->getSites(m_splitTunnelingController->getSitesRouteMode()); + + QJsonArray jsonArray; + + for (const auto &site : sites) { + QJsonObject jsonObject { { "hostname", site.first }, { "ip", site.second } }; + jsonArray.append(jsonObject); + } + + QJsonDocument jsonDocument(jsonArray); + QByteArray jsonData = jsonDocument.toJson(); + + SystemController::saveFile(fileName, jsonData); + + emit finished(tr("Export completed")); +} diff --git a/client/ui/controllers/siteSplitUIController.h b/client/ui/controllers/siteSplitUIController.h new file mode 100644 index 000000000..fe78ea356 --- /dev/null +++ b/client/ui/controllers/siteSplitUIController.h @@ -0,0 +1,35 @@ +#ifndef SITESPLITUICONTROLLER_H +#define SITESPLITUICONTROLLER_H + +#include + +#include "ui/models/sites_model.h" +#include "core/controllers/splitTunnelingController.h" + +class SiteSplitUIController : public QObject +{ + Q_OBJECT +public: + explicit SiteSplitUIController(QSharedPointer splitTunnelingController, + const QSharedPointer &sitesModel, + QObject *parent = nullptr); + +public slots: + void addSite(QString hostname); + void removeSite(int index); + + void importSites(const QString &fileName, bool replaceExisting); + void exportSites(const QString &fileName); + +signals: + void errorOccurred(const QString &errorMessage); + void finished(const QString &message); + + void saveFile(const QString &fileName, const QString &data); + +private: + QSharedPointer m_splitTunnelingController; + QSharedPointer m_sitesModel; +}; + +#endif // SITESPLITUICONTROLLER_H diff --git a/client/ui/controllers/sitesController.cpp b/client/ui/controllers/sitesController.cpp deleted file mode 100644 index d40be4585..000000000 --- a/client/ui/controllers/sitesController.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "sitesController.h" - -#include -#include -#include - -#include "systemController.h" -#include "core/networkUtilities.h" - -SitesController::SitesController(const std::shared_ptr &settings, - const QSharedPointer &vpnConnection, - const QSharedPointer &sitesModel, QObject *parent) - : QObject(parent), m_settings(settings), m_vpnConnection(vpnConnection), m_sitesModel(sitesModel) -{ -} - -void SitesController::addSite(QString hostname) -{ - if (hostname.isEmpty()) { - return; - } - - if (!hostname.contains(".")) { - emit errorOccurred(tr("Hostname not look like ip adress or domain name")); - return; - } - - if (!NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) { - // get domain name if it present - hostname.replace("https://", ""); - hostname.replace("http://", ""); - hostname.replace("ftp://", ""); - - hostname = hostname.split("/", Qt::SkipEmptyParts).first(); - } - - const auto &processSite = [this](const QString &hostname, const QString &ip) { - m_sitesModel->addSite(hostname, ip); - - if (!ip.isEmpty()) { - QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, - Q_ARG(QStringList, QStringList() << ip)); - } else if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) { - QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, - Q_ARG(QStringList, QStringList() << hostname)); - } - }; - - const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) { - const QList &addresses = hostInfo.addresses(); - for (const QHostAddress &addr : hostInfo.addresses()) { - if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) { - processSite(hostInfo.hostName(), addr.toString()); - break; - } - } - }; - - if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) { - processSite(hostname, ""); - } else { - processSite(hostname, ""); - QHostInfo::lookupHost(hostname, this, resolveCallback); - } - - emit finished(tr("New site added: %1").arg(hostname)); -} - -void SitesController::removeSite(int index) -{ - auto modelIndex = m_sitesModel->index(index); - auto hostname = m_sitesModel->data(modelIndex, SitesModel::Roles::UrlRole).toString(); - m_sitesModel->removeSite(modelIndex); - - QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection, - Q_ARG(QStringList, QStringList() << hostname)); - - emit finished(tr("Site removed: %1").arg(hostname)); -} - -void SitesController::importSites(const QString &fileName, bool replaceExisting) -{ - QByteArray jsonData; - if (!SystemController::readFile(fileName, jsonData)) { - emit errorOccurred(tr("Can't open file: %1").arg(fileName)); - return; - } - - QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData); - if (jsonDocument.isNull()) { - emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName)); - return; - } - - if (!jsonDocument.isArray()) { - emit errorOccurred(tr("The JSON data is not an array in file: %1").arg(fileName)); - return; - } - - auto jsonArray = jsonDocument.array(); - QMap sites; - QStringList ips; - - for (auto jsonValue : jsonArray) { - auto jsonObject = jsonValue.toObject(); - auto hostname = jsonObject.value("hostname").toString(""); - auto ip = jsonObject.value("ip").toString(""); - - if (!hostname.contains(".") && !NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) { - qDebug() << hostname << " not look like ip adress or domain name"; - continue; - } - - if (ip.isEmpty()) { - ips.append(hostname); - } else { - ips.append(ip); - } - sites.insert(hostname, ip); - } - - m_sitesModel->addSites(sites, replaceExisting); - - QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, Q_ARG(QStringList, ips)); - - emit finished(tr("Import completed")); -} - -void SitesController::exportSites(const QString &fileName) -{ - auto sites = m_sitesModel->getCurrentSites(); - - QJsonArray jsonArray; - - for (const auto &site : sites) { - QJsonObject jsonObject { { "hostname", site.first }, { "ip", site.second } }; - jsonArray.append(jsonObject); - } - - QJsonDocument jsonDocument(jsonArray); - QByteArray jsonData = jsonDocument.toJson(); - - SystemController::saveFile(fileName, jsonData); - - emit finished(tr("Export completed")); -} diff --git a/client/ui/controllers/sitesController.h b/client/ui/controllers/sitesController.h deleted file mode 100644 index e66478da9..000000000 --- a/client/ui/controllers/sitesController.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef SITESCONTROLLER_H -#define SITESCONTROLLER_H - -#include - -#include "settings.h" -#include "ui/models/sites_model.h" -#include "vpnconnection.h" - -class SitesController : public QObject -{ - Q_OBJECT -public: - explicit SitesController(const std::shared_ptr &settings, - const QSharedPointer &vpnConnection, - const QSharedPointer &sitesModel, QObject *parent = nullptr); - -public slots: - void addSite(QString hostname); - void removeSite(int index); - - void importSites(const QString &fileName, bool replaceExisting); - void exportSites(const QString &fileName); - -signals: - void errorOccurred(const QString &errorMessage); - void finished(const QString &message); - - void saveFile(const QString &fileName, const QString &data); - -private: - std::shared_ptr m_settings; - - QSharedPointer m_vpnConnection; - QSharedPointer m_sitesModel; -}; - -#endif // SITESCONTROLLER_H diff --git a/client/ui/models/allowed_dns_model.cpp b/client/ui/models/allowed_dns_model.cpp index c5d126612..634b0c6c0 100644 --- a/client/ui/models/allowed_dns_model.cpp +++ b/client/ui/models/allowed_dns_model.cpp @@ -1,11 +1,9 @@ #include "allowed_dns_model.h" #include "core/controllers/dnsController.h" -AllowedDnsModel::AllowedDnsModel(std::shared_ptr settings, - QSharedPointer dnsController, +AllowedDnsModel::AllowedDnsModel(QSharedPointer dnsController, QObject *parent) : QAbstractListModel(parent), - m_settings(settings), m_dnsController(dnsController) { // Connect to controller signals diff --git a/client/ui/models/allowed_dns_model.h b/client/ui/models/allowed_dns_model.h index c3139786b..78a5cf618 100644 --- a/client/ui/models/allowed_dns_model.h +++ b/client/ui/models/allowed_dns_model.h @@ -15,8 +15,7 @@ public: IpRole = Qt::UserRole + 1 }; - explicit AllowedDnsModel(std::shared_ptr settings, - QSharedPointer dnsController, + explicit AllowedDnsModel(QSharedPointer dnsController, QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; @@ -39,7 +38,6 @@ private slots: private: void refreshData(); - std::shared_ptr m_settings; QSharedPointer m_dnsController; QStringList m_dnsServers; }; diff --git a/client/ui/models/appSplitTunnelingModel.cpp b/client/ui/models/appSplitTunnelingModel.cpp index c34136c81..84636a1d3 100644 --- a/client/ui/models/appSplitTunnelingModel.cpp +++ b/client/ui/models/appSplitTunnelingModel.cpp @@ -3,11 +3,9 @@ #include -AppSplitTunnelingModel::AppSplitTunnelingModel(std::shared_ptr settings, - QSharedPointer splitTunnelingController, +AppSplitTunnelingModel::AppSplitTunnelingModel(QSharedPointer splitTunnelingController, QObject *parent) : QAbstractListModel(parent), - m_settings(settings), m_splitTunnelingController(splitTunnelingController) { // Connect to controller signals diff --git a/client/ui/models/appSplitTunnelingModel.h b/client/ui/models/appSplitTunnelingModel.h index bd6533607..9a0c4e2c5 100644 --- a/client/ui/models/appSplitTunnelingModel.h +++ b/client/ui/models/appSplitTunnelingModel.h @@ -3,7 +3,6 @@ #include -#include "settings.h" #include "core/defs.h" class SplitTunnelingController; @@ -19,8 +18,7 @@ public: PackageAppIconRole }; - explicit AppSplitTunnelingModel(std::shared_ptr settings, - QSharedPointer splitTunnelingController, + explicit AppSplitTunnelingModel(QSharedPointer splitTunnelingController, QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; @@ -55,9 +53,7 @@ private slots: private: void refreshData(); - std::shared_ptr m_settings; QSharedPointer m_splitTunnelingController; - QVector m_apps; }; diff --git a/client/ui/models/languageModel.cpp b/client/ui/models/languageModel.cpp index 09755e06d..d656a114c 100644 --- a/client/ui/models/languageModel.cpp +++ b/client/ui/models/languageModel.cpp @@ -1,6 +1,7 @@ #include "languageModel.h" -LanguageModel::LanguageModel(std::shared_ptr settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent) +LanguageModel::LanguageModel(QSharedPointer settingsController, QObject *parent) + : m_settingsController(settingsController), QAbstractListModel(parent) { QMetaEnum metaEnum = QMetaEnum::fromType(); for (int i = 0; i < metaEnum.keyCount(); i++) { @@ -72,7 +73,7 @@ void LanguageModel::changeLanguage(const LanguageSettings::AvailableLanguageEnum int LanguageModel::getCurrentLanguageIndex() { - auto locale = m_settings->getAppLanguage(); + auto locale = m_settingsController->getAppLanguage(); switch (locale.language()) { case QLocale::English: return static_cast(LanguageSettings::AvailableLanguageEnum::English); break; case QLocale::Russian: return static_cast(LanguageSettings::AvailableLanguageEnum::Russian); break; diff --git a/client/ui/models/languageModel.h b/client/ui/models/languageModel.h index d1a2e7d5e..bc29f70d5 100644 --- a/client/ui/models/languageModel.h +++ b/client/ui/models/languageModel.h @@ -3,8 +3,9 @@ #include #include +#include -#include "settings.h" +#include "core/controllers/settingsController.h" namespace LanguageSettings { @@ -45,7 +46,7 @@ public: IndexRole }; - LanguageModel(std::shared_ptr settings, QObject *parent = nullptr); + LanguageModel(QSharedPointer settingsController, QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; @@ -72,8 +73,7 @@ private: QString getLocalLanguageName(const LanguageSettings::AvailableLanguageEnum language); QVector m_availableLanguages; - - std::shared_ptr m_settings; + QSharedPointer m_settingsController; }; #endif // LANGUAGEMODEL_H diff --git a/client/ui/models/sites_model.cpp b/client/ui/models/sites_model.cpp index 49af80e02..91c25e133 100644 --- a/client/ui/models/sites_model.cpp +++ b/client/ui/models/sites_model.cpp @@ -1,11 +1,9 @@ #include "sites_model.h" #include "core/controllers/splitTunnelingController.h" -SitesModel::SitesModel(std::shared_ptr settings, - QSharedPointer splitTunnelingController, +SitesModel::SitesModel(QSharedPointer splitTunnelingController, QObject *parent) : QAbstractListModel(parent), - m_settings(settings), m_splitTunnelingController(splitTunnelingController) { // Connect to controller signals diff --git a/client/ui/models/sites_model.h b/client/ui/models/sites_model.h index 82b14630d..892ba10c4 100644 --- a/client/ui/models/sites_model.h +++ b/client/ui/models/sites_model.h @@ -3,8 +3,6 @@ #include -#include "settings.h" - class SplitTunnelingController; class SitesModel : public QAbstractListModel @@ -17,8 +15,7 @@ public: IpRole }; - explicit SitesModel(std::shared_ptr settings, - QSharedPointer splitTunnelingController, + explicit SitesModel(QSharedPointer splitTunnelingController, QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; @@ -56,7 +53,6 @@ private slots: private: void refreshData(); - std::shared_ptr m_settings; QSharedPointer m_splitTunnelingController; QVector> m_sites;