refactoring: separated split tunneling and dns controllers and models

This commit is contained in:
vladimir.kuznetsov
2025-07-31 23:17:46 +08:00
parent abb9ffe1b7
commit 9f0d4aaba1
25 changed files with 450 additions and 376 deletions

View File

@@ -17,8 +17,9 @@ CoreController::CoreController(const QSharedPointer<VpnConnection> &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<OpenVpnConfigModel>::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<ClientManagementController>::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<SettingsController>::create(m_settings, this);
m_dnsController = QSharedPointer<DnsController>::create(m_settings, this);
m_splitTunnelingController = QSharedPointer<SplitTunnelingController>::create(m_settings, m_vpnConnection, this);
m_exportController = QSharedPointer<ExportController>::create(m_settings, this);
m_installController = QSharedPointer<InstallController>::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<ExportController>::create(m_settings, this);
m_installController = QSharedPointer<InstallController>::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<SettingsController>::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());

View File

@@ -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<InstallUIController> m_installUIController;
QScopedPointer<ImportController> m_importController;
QScopedPointer<SettingsUIController> m_settingsUIController;
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SiteSplitUIController> m_siteSplitUIController;
QScopedPointer<SystemController> m_systemController;
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
QScopedPointer<AllowedDnsController> m_allowedDnsController;
QScopedPointer<AppSplitUIController> m_appSplitUIController;
QScopedPointer<AllowedDnsUIController> m_allowedDnsUIController;
QScopedPointer<ApiSettingsController> m_apiSettingsController;
QScopedPointer<ApiConfigsController> m_apiConfigsController;
QScopedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationController;
QSharedPointer<SettingsController> m_settingsController;
QSharedPointer<DnsController> m_dnsController;
QSharedPointer<SplitTunnelingController> m_splitTunnelingController;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ContainersModel> m_defaultServerContainersModel;

View File

@@ -1,4 +1,5 @@
#include "dnsController.h"
#include "core/networkUtilities.h"
DnsController::DnsController(std::shared_ptr<Settings> settings, QObject *parent)
: QObject(parent), m_settings(settings)
@@ -7,6 +8,10 @@ DnsController::DnsController(std::shared_ptr<Settings> 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)) {

View File

@@ -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);
}

View File

@@ -67,6 +67,9 @@ public:
// Log date
QDateTime getLogEnableDate() const;
QLocale getAppLanguage() const;
void setAppLanguage(const QLocale &locale);
signals:
void settingsReset();
void dnsConfigChanged();

View File

@@ -1,24 +1,36 @@
#include "splitTunnelingController.h"
#include "settings.h"
#include "core/networkUtilities.h"
#include <QFileInfo>
SplitTunnelingController::SplitTunnelingController(std::shared_ptr<Settings> settings, QObject *parent)
: QObject(parent), m_settings(settings)
SplitTunnelingController::SplitTunnelingController(std::shared_ptr<Settings> settings,
QSharedPointer<VpnConnection> 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<QString, QString> &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<QPair<QString, QString>> SplitTunnelingController::getSites(Settings::RouteMode routeMode) const
{
QVector<QPair<QString, QString>> sites;

View File

@@ -6,10 +6,12 @@
#include <QMap>
#include <QStringList>
#include <QSharedPointer>
#include <QHostInfo>
#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> settings, QObject *parent = nullptr);
explicit SplitTunnelingController(std::shared_ptr<Settings> settings,
QSharedPointer<VpnConnection> 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<Settings> m_settings;
QSharedPointer<VpnConnection> m_vpnConnection;
QMap<int, QString> m_pendingResolutions;
};
#endif // SPLITTUNNELINGCONTROLLER_H

View File

@@ -1,4 +1,4 @@
#include "allowedDnsController.h"
#include "allowedDnsUIController.h"
#include <QFile>
#include <QStandardPaths>
@@ -10,41 +10,39 @@
#include "core/networkUtilities.h"
#include "core/defs.h"
AllowedDnsController::AllowedDnsController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
QObject *parent)
: QObject(parent), m_settings(settings), m_allowedDnsModel(allowedDnsModel)
AllowedDnsUIController::AllowedDnsUIController(QSharedPointer<DnsController> dnsController,
const QSharedPointer<AllowedDnsModel> &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;

View File

@@ -1,18 +1,18 @@
#ifndef ALLOWEDDNSCONTROLLER_H
#define ALLOWEDDNSCONTROLLER_H
#ifndef ALLOWEDDNSUICONTROLLER_H
#define ALLOWEDDNSUICONTROLLER_H
#include <QObject>
#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> &settings,
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
QObject *parent = nullptr);
explicit AllowedDnsUIController(QSharedPointer<DnsController> dnsController,
const QSharedPointer<AllowedDnsModel> &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<Settings> m_settings;
QSharedPointer<DnsController> m_dnsController;
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
};
#endif // ALLOWEDDNSCONTROLLER_H
#endif // ALLOWEDDNSUICONTROLLER_H

View File

@@ -1,49 +0,0 @@
#include "appSplitTunnelingController.h"
#include <QFileInfo>
#include "core/defs.h"
AppSplitTunnelingController::AppSplitTunnelingController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AppSplitTunnelingModel> &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<QPair<QString, QString>> 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()));
}

View File

@@ -1,31 +0,0 @@
#ifndef APPSPLITTUNNELINGCONTROLLER_H
#define APPSPLITTUNNELINGCONTROLLER_H
#include <QObject>
#include "settings.h"
#include "ui/models/appSplitTunnelingModel.h"
class AppSplitTunnelingController : public QObject
{
Q_OBJECT
public:
explicit AppSplitTunnelingController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AppSplitTunnelingModel> &sitesModel, QObject *parent = nullptr);
public slots:
void addApp(const QString &appPath);
void addApps(QVector<QPair<QString, QString>> apps);
void removeApp(const int index);
signals:
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
private:
std::shared_ptr<Settings> m_settings;
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
};
#endif // APPSPLITTUNNELINGCONTROLLER_H

View File

@@ -0,0 +1,59 @@
#include "appSplitUIController.h"
#include <QFileInfo>
#include "core/defs.h"
AppSplitUIController::AppSplitUIController(QSharedPointer<SplitTunnelingController> splitTunnelingController,
const QSharedPointer<AppSplitTunnelingModel> &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<QPair<QString, QString>> 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"));
}
}

View File

@@ -0,0 +1,31 @@
#ifndef APPSPLITUICONTROLLER_H
#define APPSPLITUICONTROLLER_H
#include <QObject>
#include "ui/models/appSplitTunnelingModel.h"
#include "core/controllers/splitTunnelingController.h"
class AppSplitUIController : public QObject
{
Q_OBJECT
public:
explicit AppSplitUIController(QSharedPointer<SplitTunnelingController> splitTunnelingController,
const QSharedPointer<AppSplitTunnelingModel> &appSplitTunnelingModel,
QObject *parent = nullptr);
public slots:
void addApp(const QString &appPath);
void addApps(QVector<QPair<QString, QString>> apps);
void removeApp(const int index);
signals:
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
private:
QSharedPointer<SplitTunnelingController> m_splitTunnelingController;
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
};
#endif // APPSPLITUICONTROLLER_H

View File

@@ -0,0 +1,109 @@
#include "siteSplitUIController.h"
#include <QFile>
#include <QHostInfo>
#include <QStandardPaths>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include "systemController.h"
#include "core/networkUtilities.h"
SiteSplitUIController::SiteSplitUIController(QSharedPointer<SplitTunnelingController> splitTunnelingController,
const QSharedPointer<SitesModel> &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<QString, QString> 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"));
}

View File

@@ -0,0 +1,35 @@
#ifndef SITESPLITUICONTROLLER_H
#define SITESPLITUICONTROLLER_H
#include <QObject>
#include "ui/models/sites_model.h"
#include "core/controllers/splitTunnelingController.h"
class SiteSplitUIController : public QObject
{
Q_OBJECT
public:
explicit SiteSplitUIController(QSharedPointer<SplitTunnelingController> splitTunnelingController,
const QSharedPointer<SitesModel> &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<SplitTunnelingController> m_splitTunnelingController;
QSharedPointer<SitesModel> m_sitesModel;
};
#endif // SITESPLITUICONTROLLER_H

View File

@@ -1,146 +0,0 @@
#include "sitesController.h"
#include <QFile>
#include <QHostInfo>
#include <QStandardPaths>
#include "systemController.h"
#include "core/networkUtilities.h"
SitesController::SitesController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<VpnConnection> &vpnConnection,
const QSharedPointer<SitesModel> &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<QHostAddress> &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<QString, QString> 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"));
}

View File

@@ -1,38 +0,0 @@
#ifndef SITESCONTROLLER_H
#define SITESCONTROLLER_H
#include <QObject>
#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> &settings,
const QSharedPointer<VpnConnection> &vpnConnection,
const QSharedPointer<SitesModel> &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<Settings> m_settings;
QSharedPointer<VpnConnection> m_vpnConnection;
QSharedPointer<SitesModel> m_sitesModel;
};
#endif // SITESCONTROLLER_H

View File

@@ -1,11 +1,9 @@
#include "allowed_dns_model.h"
#include "core/controllers/dnsController.h"
AllowedDnsModel::AllowedDnsModel(std::shared_ptr<Settings> settings,
QSharedPointer<DnsController> dnsController,
AllowedDnsModel::AllowedDnsModel(QSharedPointer<DnsController> dnsController,
QObject *parent)
: QAbstractListModel(parent),
m_settings(settings),
m_dnsController(dnsController)
{
// Connect to controller signals

View File

@@ -15,8 +15,7 @@ public:
IpRole = Qt::UserRole + 1
};
explicit AllowedDnsModel(std::shared_ptr<Settings> settings,
QSharedPointer<DnsController> dnsController,
explicit AllowedDnsModel(QSharedPointer<DnsController> dnsController,
QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -39,7 +38,6 @@ private slots:
private:
void refreshData();
std::shared_ptr<Settings> m_settings;
QSharedPointer<DnsController> m_dnsController;
QStringList m_dnsServers;
};

View File

@@ -3,11 +3,9 @@
#include <QFileInfo>
AppSplitTunnelingModel::AppSplitTunnelingModel(std::shared_ptr<Settings> settings,
QSharedPointer<SplitTunnelingController> splitTunnelingController,
AppSplitTunnelingModel::AppSplitTunnelingModel(QSharedPointer<SplitTunnelingController> splitTunnelingController,
QObject *parent)
: QAbstractListModel(parent),
m_settings(settings),
m_splitTunnelingController(splitTunnelingController)
{
// Connect to controller signals

View File

@@ -3,7 +3,6 @@
#include <QAbstractListModel>
#include "settings.h"
#include "core/defs.h"
class SplitTunnelingController;
@@ -19,8 +18,7 @@ public:
PackageAppIconRole
};
explicit AppSplitTunnelingModel(std::shared_ptr<Settings> settings,
QSharedPointer<SplitTunnelingController> splitTunnelingController,
explicit AppSplitTunnelingModel(QSharedPointer<SplitTunnelingController> splitTunnelingController,
QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -55,9 +53,7 @@ private slots:
private:
void refreshData();
std::shared_ptr<Settings> m_settings;
QSharedPointer<SplitTunnelingController> m_splitTunnelingController;
QVector<InstalledAppInfo> m_apps;
};

View File

@@ -1,6 +1,7 @@
#include "languageModel.h"
LanguageModel::LanguageModel(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent)
LanguageModel::LanguageModel(QSharedPointer<SettingsController> settingsController, QObject *parent)
: m_settingsController(settingsController), QAbstractListModel(parent)
{
QMetaEnum metaEnum = QMetaEnum::fromType<LanguageSettings::AvailableLanguageEnum>();
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<int>(LanguageSettings::AvailableLanguageEnum::English); break;
case QLocale::Russian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Russian); break;

View File

@@ -3,8 +3,9 @@
#include <QAbstractListModel>
#include <QQmlEngine>
#include <QLocale>
#include "settings.h"
#include "core/controllers/settingsController.h"
namespace LanguageSettings
{
@@ -45,7 +46,7 @@ public:
IndexRole
};
LanguageModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
LanguageModel(QSharedPointer<SettingsController> 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<LanguageModelData> m_availableLanguages;
std::shared_ptr<Settings> m_settings;
QSharedPointer<SettingsController> m_settingsController;
};
#endif // LANGUAGEMODEL_H

View File

@@ -1,11 +1,9 @@
#include "sites_model.h"
#include "core/controllers/splitTunnelingController.h"
SitesModel::SitesModel(std::shared_ptr<Settings> settings,
QSharedPointer<SplitTunnelingController> splitTunnelingController,
SitesModel::SitesModel(QSharedPointer<SplitTunnelingController> splitTunnelingController,
QObject *parent)
: QAbstractListModel(parent),
m_settings(settings),
m_splitTunnelingController(splitTunnelingController)
{
// Connect to controller signals

View File

@@ -3,8 +3,6 @@
#include <QAbstractListModel>
#include "settings.h"
class SplitTunnelingController;
class SitesModel : public QAbstractListModel
@@ -17,8 +15,7 @@ public:
IpRole
};
explicit SitesModel(std::shared_ptr<Settings> settings,
QSharedPointer<SplitTunnelingController> splitTunnelingController,
explicit SitesModel(QSharedPointer<SplitTunnelingController> splitTunnelingController,
QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -56,7 +53,6 @@ private slots:
private:
void refreshData();
std::shared_ptr<Settings> m_settings;
QSharedPointer<SplitTunnelingController> m_splitTunnelingController;
QVector<QPair<QString, QString>> m_sites;