Implement model, controller and UI for killswitch dns exceptions
This commit is contained in:
parent
bb883b4880
commit
0466e71d49
14 changed files with 693 additions and 2 deletions
|
|
@ -47,6 +47,9 @@ void CoreController::initModels()
|
||||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
m_sitesModel.reset(new SitesModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
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_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
||||||
|
|
||||||
|
|
@ -129,6 +132,9 @@ void CoreController::initControllers()
|
||||||
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||||
|
|
||||||
|
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
|
||||||
|
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get());
|
||||||
|
|
||||||
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
||||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "ui/controllers/api/apiConfigsController.h"
|
#include "ui/controllers/api/apiConfigsController.h"
|
||||||
#include "ui/controllers/api/apiSettingsController.h"
|
#include "ui/controllers/api/apiSettingsController.h"
|
||||||
#include "ui/controllers/appSplitTunnelingController.h"
|
#include "ui/controllers/appSplitTunnelingController.h"
|
||||||
|
#include "ui/controllers/allowedDnsController.h"
|
||||||
#include "ui/controllers/connectionController.h"
|
#include "ui/controllers/connectionController.h"
|
||||||
#include "ui/controllers/exportController.h"
|
#include "ui/controllers/exportController.h"
|
||||||
#include "ui/controllers/focusController.h"
|
#include "ui/controllers/focusController.h"
|
||||||
|
|
@ -18,6 +19,7 @@
|
||||||
#include "ui/controllers/sitesController.h"
|
#include "ui/controllers/sitesController.h"
|
||||||
#include "ui/controllers/systemController.h"
|
#include "ui/controllers/systemController.h"
|
||||||
|
|
||||||
|
#include "ui/models/allowed_dns_model.h"
|
||||||
#include "ui/models/containers_model.h"
|
#include "ui/models/containers_model.h"
|
||||||
#include "ui/models/languageModel.h"
|
#include "ui/models/languageModel.h"
|
||||||
#include "ui/models/protocols/cloakConfigModel.h"
|
#include "ui/models/protocols/cloakConfigModel.h"
|
||||||
|
|
@ -103,6 +105,7 @@ private:
|
||||||
QScopedPointer<SitesController> m_sitesController;
|
QScopedPointer<SitesController> m_sitesController;
|
||||||
QScopedPointer<SystemController> m_systemController;
|
QScopedPointer<SystemController> m_systemController;
|
||||||
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
||||||
|
QScopedPointer<AllowedDnsController> m_allowedDnsController;
|
||||||
|
|
||||||
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
||||||
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
||||||
|
|
@ -113,6 +116,7 @@ private:
|
||||||
QSharedPointer<LanguageModel> m_languageModel;
|
QSharedPointer<LanguageModel> m_languageModel;
|
||||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||||
QSharedPointer<SitesModel> m_sitesModel;
|
QSharedPointer<SitesModel> m_sitesModel;
|
||||||
|
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
||||||
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
||||||
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@
|
||||||
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
||||||
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
|
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
|
||||||
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
||||||
|
<file>ui/qml/Components/AddSitePanel.qml</file>
|
||||||
<file>ui/qml/Config/GlobalConfig.qml</file>
|
<file>ui/qml/Config/GlobalConfig.qml</file>
|
||||||
<file>ui/qml/Config/qmldir</file>
|
<file>ui/qml/Config/qmldir</file>
|
||||||
<file>ui/qml/Controls2/BackButtonType.qml</file>
|
<file>ui/qml/Controls2/BackButtonType.qml</file>
|
||||||
|
|
@ -202,6 +203,7 @@
|
||||||
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
|
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
|
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsKillSwitch.qml</file>
|
<file>ui/qml/Pages2/PageSettingsKillSwitch.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
|
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsServerData.qml</file>
|
<file>ui/qml/Pages2/PageSettingsServerData.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>
|
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>
|
||||||
|
|
|
||||||
|
|
@ -558,3 +558,13 @@ void Settings::disableHomeAdLabel()
|
||||||
{
|
{
|
||||||
setValue("Conf/homeAdLabelVisible", false);
|
setValue("Conf/homeAdLabelVisible", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList Settings::allowedDnsServers() const
|
||||||
|
{
|
||||||
|
return value("Conf/allowedDnsServers").toStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::setAllowedDnsServers(const QStringList &servers)
|
||||||
|
{
|
||||||
|
setValue("Conf/allowedDnsServers", servers);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,9 @@ public:
|
||||||
bool isHomeAdLabelVisible();
|
bool isHomeAdLabelVisible();
|
||||||
void disableHomeAdLabel();
|
void disableHomeAdLabel();
|
||||||
|
|
||||||
|
QStringList allowedDnsServers() const;
|
||||||
|
void setAllowedDnsServers(const QStringList &servers);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void saveLogsChanged(bool enabled);
|
void saveLogsChanged(bool enabled);
|
||||||
void screenshotsEnabledChanged(bool enabled);
|
void screenshotsEnabledChanged(bool enabled);
|
||||||
|
|
|
||||||
101
client/ui/controllers/allowedDnsController.cpp
Normal file
101
client/ui/controllers/allowedDnsController.cpp
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
#include "allowedDnsController.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include "systemController.h"
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::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)) {
|
||||||
|
emit finished(tr("New DNS server added: %1").arg(ip));
|
||||||
|
} else {
|
||||||
|
emit errorOccurred(tr("DNS server already exists: %1").arg(ip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::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));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::importDns(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();
|
||||||
|
QStringList dnsServers;
|
||||||
|
|
||||||
|
for (auto jsonValue : jsonArray) {
|
||||||
|
auto ip = jsonValue.toString();
|
||||||
|
|
||||||
|
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
|
||||||
|
qDebug() << ip << " is not a valid IP address";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsServers.append(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_allowedDnsModel->addDnsList(dnsServers, replaceExisting);
|
||||||
|
|
||||||
|
emit finished(tr("Import completed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::exportDns(const QString &fileName)
|
||||||
|
{
|
||||||
|
auto dnsServers = m_allowedDnsModel->getCurrentDnsServers();
|
||||||
|
|
||||||
|
QJsonArray jsonArray;
|
||||||
|
|
||||||
|
for (const auto &ip : dnsServers) {
|
||||||
|
jsonArray.append(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument jsonDocument(jsonArray);
|
||||||
|
QByteArray jsonData = jsonDocument.toJson();
|
||||||
|
|
||||||
|
SystemController::saveFile(fileName, jsonData);
|
||||||
|
|
||||||
|
emit finished(tr("Export completed"));
|
||||||
|
}
|
||||||
35
client/ui/controllers/allowedDnsController.h
Normal file
35
client/ui/controllers/allowedDnsController.h
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef ALLOWEDDNSCONTROLLER_H
|
||||||
|
#define ALLOWEDDNSCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
#include "ui/models/allowed_dns_model.h"
|
||||||
|
|
||||||
|
class AllowedDnsController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit AllowedDnsController(const std::shared_ptr<Settings> &settings,
|
||||||
|
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void addDns(QString ip);
|
||||||
|
void removeDns(int index);
|
||||||
|
|
||||||
|
void importDns(const QString &fileName, bool replaceExisting);
|
||||||
|
void exportDns(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<AllowedDnsModel> m_allowedDnsModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ALLOWEDDNSCONTROLLER_H
|
||||||
|
|
@ -38,7 +38,8 @@ namespace PageLoader
|
||||||
PageSettingsApiInstructions,
|
PageSettingsApiInstructions,
|
||||||
PageSettingsApiNativeConfigs,
|
PageSettingsApiNativeConfigs,
|
||||||
PageSettingsApiDevices,
|
PageSettingsApiDevices,
|
||||||
|
PageSettingsKillSwitchExceptions,
|
||||||
|
|
||||||
PageServiceSftpSettings,
|
PageServiceSftpSettings,
|
||||||
PageServiceTorWebsiteSettings,
|
PageServiceTorWebsiteSettings,
|
||||||
PageServiceDnsSettings,
|
PageServiceDnsSettings,
|
||||||
|
|
|
||||||
86
client/ui/models/allowed_dns_model.cpp
Normal file
86
client/ui/models/allowed_dns_model.cpp
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
#include "allowed_dns_model.h"
|
||||||
|
|
||||||
|
AllowedDnsModel::AllowedDnsModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||||
|
: QAbstractListModel(parent), m_settings(settings)
|
||||||
|
{
|
||||||
|
fillDnsServers();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AllowedDnsModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent)
|
||||||
|
return m_dnsServers.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant AllowedDnsModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case IpRole:
|
||||||
|
return m_dnsServers.at(index.row());
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AllowedDnsModel::addDns(const QString &ip)
|
||||||
|
{
|
||||||
|
if (m_dnsServers.contains(ip)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||||
|
m_dnsServers.append(ip);
|
||||||
|
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||||
|
endInsertRows();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsModel::addDnsList(const QStringList &dnsServers, bool replaceExisting)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
if (replaceExisting) {
|
||||||
|
m_dnsServers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString &ip : dnsServers) {
|
||||||
|
if (!m_dnsServers.contains(ip)) {
|
||||||
|
m_dnsServers.append(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsModel::removeDns(QModelIndex index)
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() >= m_dnsServers.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginRemoveRows(QModelIndex(), index.row(), index.row());
|
||||||
|
m_dnsServers.removeAt(index.row());
|
||||||
|
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AllowedDnsModel::getCurrentDnsServers()
|
||||||
|
{
|
||||||
|
return m_dnsServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> AllowedDnsModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[IpRole] = "ip";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsModel::fillDnsServers()
|
||||||
|
{
|
||||||
|
m_dnsServers = m_settings->allowedDnsServers();
|
||||||
|
}
|
||||||
37
client/ui/models/allowed_dns_model.h
Normal file
37
client/ui/models/allowed_dns_model.h
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef ALLOWEDDNSMODEL_H
|
||||||
|
#define ALLOWEDDNSMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
class AllowedDnsModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Roles {
|
||||||
|
IpRole = Qt::UserRole + 1
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit AllowedDnsModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool addDns(const QString &ip);
|
||||||
|
void addDnsList(const QStringList &dnsServers, bool replaceExisting);
|
||||||
|
void removeDns(QModelIndex index);
|
||||||
|
QStringList getCurrentDnsServers();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void fillDnsServers();
|
||||||
|
|
||||||
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
QStringList m_dnsServers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ALLOWEDDNSMODEL_H
|
||||||
73
client/ui/qml/Components/AddSitePanel.qml
Normal file
73
client/ui/qml/Components/AddSitePanel.qml
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool enabled: true
|
||||||
|
property string placeholderText: ""
|
||||||
|
property alias textField: searchField.textField
|
||||||
|
|
||||||
|
signal addClicked(string text)
|
||||||
|
signal moreClicked()
|
||||||
|
|
||||||
|
implicitWidth: 360
|
||||||
|
implicitHeight: 96
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: background
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "#0E0F12"
|
||||||
|
opacity: 0.85
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: addSiteButton
|
||||||
|
|
||||||
|
enabled: root.enabled
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
topMargin: 16
|
||||||
|
leftMargin: 16
|
||||||
|
rightMargin: 16
|
||||||
|
bottomMargin: 24
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldWithHeaderType {
|
||||||
|
id: searchField
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
rightButtonClickedOnEnter: true
|
||||||
|
|
||||||
|
textField.placeholderText: root.placeholderText
|
||||||
|
buttonImageSource: "qrc:/images/controls/plus.svg"
|
||||||
|
|
||||||
|
clickedFunc: function() {
|
||||||
|
root.addClicked(textField.text)
|
||||||
|
textField.text = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageButtonType {
|
||||||
|
id: addSiteButtonImage
|
||||||
|
implicitWidth: 56
|
||||||
|
implicitHeight: 56
|
||||||
|
|
||||||
|
image: "qrc:/images/controls/more-vertical.svg"
|
||||||
|
imageColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
|
onClicked: root.moreClicked()
|
||||||
|
|
||||||
|
Keys.onReturnPressed: addSiteButtonImage.clicked()
|
||||||
|
Keys.onEnterPressed: addSiteButtonImage.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -93,6 +93,20 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
DividerType {}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
Layout.topMargin: 32
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
enabled: true
|
||||||
|
text: qsTr("Kill Switch Exceptions")
|
||||||
|
descriptionText: qsTr("IP addresses that will remain accessible even when Kill Switch is activated")
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
PageController.goToPage(PageEnum.PageSettingsKillSwitchExceptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
296
client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml
Normal file
296
client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml
Normal file
|
|
@ -0,0 +1,296 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Dialogs
|
||||||
|
|
||||||
|
import QtCore
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
import ProtocolEnum 1.0
|
||||||
|
import ContainerProps 1.0
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "./"
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
import "../Config"
|
||||||
|
import "../Components"
|
||||||
|
|
||||||
|
PageType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool pageEnabled: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: header
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
anchors.topMargin: 20
|
||||||
|
|
||||||
|
BackButtonType {
|
||||||
|
id: backButton
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseHeaderType {
|
||||||
|
enabled: root.pageEnabled
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Kill Switch Exceptions")
|
||||||
|
descriptionText: qsTr("Addresses from the list will remain accessible when Kill Switch is triggered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
anchors.top: header.bottom
|
||||||
|
anchors.topMargin: 16
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
enabled: root.pageEnabled
|
||||||
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
cacheBuffer: 200
|
||||||
|
displayMarginBeginning: 40
|
||||||
|
displayMarginEnd: 40
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBarType { }
|
||||||
|
|
||||||
|
footer: AddSitePanel {
|
||||||
|
id: addSitePanel
|
||||||
|
|
||||||
|
width: listView.width
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
enabled: root.pageEnabled
|
||||||
|
placeholderText: qsTr("IPv4 address")
|
||||||
|
|
||||||
|
onAddClicked: function(text) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
AllowedDnsController.addDns(text)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMoreClicked: {
|
||||||
|
moreActionsDrawer.openTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footerPositioning: ListView.OverlayFooter
|
||||||
|
|
||||||
|
model: SortFilterProxyModel {
|
||||||
|
id: dnsFilterModel
|
||||||
|
sourceModel: AllowedDnsModel
|
||||||
|
filters: [
|
||||||
|
RegExpFilter {
|
||||||
|
roleName: "ip"
|
||||||
|
pattern: ".*" + addSitePanel.textField.text + ".*"
|
||||||
|
caseSensitivity: Qt.CaseInsensitive
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
reuseItems: true
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
id: delegateContent
|
||||||
|
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: site
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: ip
|
||||||
|
rightImageSource: "qrc:/images/controls/trash.svg"
|
||||||
|
rightImageColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var headerText = qsTr("Delete ") + ip + "?"
|
||||||
|
var yesButtonText = qsTr("Continue")
|
||||||
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
var yesButtonFunction = function() {
|
||||||
|
AllowedDnsController.removeDns(dnsFilterModel.mapToSource(index))
|
||||||
|
if (!GC.isMobile()) {
|
||||||
|
site.rightButton.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var noButtonFunction = function() {
|
||||||
|
if (!GC.isMobile()) {
|
||||||
|
site.rightButton.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawerType2 {
|
||||||
|
id: moreActionsDrawer
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
expandedHeight: parent.height * 0.4375
|
||||||
|
|
||||||
|
expandedStateContent: ColumnLayout {
|
||||||
|
id: moreActionsDrawerContent
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
Header2Type {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Import / Export addresses")
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: importSitesButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Import")
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
importSitesDrawer.openTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: exportSitesButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Save address list")
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var fileName = ""
|
||||||
|
if (GC.isMobile()) {
|
||||||
|
fileName = "amnezia_killswitch_exceptions.json"
|
||||||
|
} else {
|
||||||
|
fileName = SystemController.getFileName(qsTr("Save addresses"),
|
||||||
|
qsTr("Address files (*.json)"),
|
||||||
|
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_killswitch_exceptions",
|
||||||
|
true,
|
||||||
|
".json")
|
||||||
|
}
|
||||||
|
if (fileName !== "") {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
AllowedDnsController.exportDns(fileName)
|
||||||
|
moreActionsDrawer.closeTriggered()
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawerType2 {
|
||||||
|
id: importSitesDrawer
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
expandedHeight: parent.height * 0.4375
|
||||||
|
|
||||||
|
expandedStateContent: Item {
|
||||||
|
implicitHeight: importSitesDrawer.expandedHeight
|
||||||
|
|
||||||
|
BackButtonType {
|
||||||
|
id: importSitesDrawerBackButton
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: 16
|
||||||
|
|
||||||
|
backButtonFunction: function() {
|
||||||
|
importSitesDrawer.closeTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlickableType {
|
||||||
|
anchors.top: importSitesDrawerBackButton.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
contentHeight: importSitesDrawerContent.height
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: importSitesDrawerContent
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
Header2Type {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Import address list")
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: importSitesButton2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Replace address list")
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var fileName = SystemController.getFileName(qsTr("Open address file"),
|
||||||
|
qsTr("Address files (*.json)"))
|
||||||
|
if (fileName !== "") {
|
||||||
|
importSitesDrawerContent.importSites(fileName, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: importSitesButton3
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Add imported addresses to existing ones")
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var fileName = SystemController.getFileName(qsTr("Open address file"),
|
||||||
|
qsTr("Address files (*.json)"))
|
||||||
|
if (fileName !== "") {
|
||||||
|
importSitesDrawerContent.importSites(fileName, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function importSites(fileName, replaceExistingSites) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
AllowedDnsController.importDns(fileName, replaceExistingSites)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
importSitesDrawer.closeTriggered()
|
||||||
|
moreActionsDrawer.closeTriggered()
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -214,7 +214,14 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) {
|
||||||
config.m_vpnDisabledApps.append(i.toString());
|
config.m_vpnDisabledApps.append(i.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// killSwitch toggle
|
for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) {
|
||||||
|
if (!dns.isString()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
config.m_allowedDnsServers.append(dns.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// killSwitch toggle
|
||||||
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
|
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
|
||||||
WindowsFirewall::create(this)->enablePeerTraffic(config);
|
WindowsFirewall::create(this)->enablePeerTraffic(config);
|
||||||
}
|
}
|
||||||
|
|
@ -280,6 +287,14 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn
|
||||||
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
|
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
|
||||||
dnsServers.append("127.0.0.1");
|
dnsServers.append("127.0.0.1");
|
||||||
dnsServers.append("127.0.0.53");
|
dnsServers.append("127.0.0.53");
|
||||||
|
|
||||||
|
for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) {
|
||||||
|
if (!dns.isString()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dnsServers.append(dns.toString());
|
||||||
|
}
|
||||||
|
|
||||||
LinuxFirewall::updateDNSServers(dnsServers);
|
LinuxFirewall::updateDNSServers(dnsServers);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
||||||
|
|
@ -307,6 +322,14 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn
|
||||||
QStringList dnsServers;
|
QStringList dnsServers;
|
||||||
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
|
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
|
||||||
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
|
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
|
||||||
|
|
||||||
|
for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) {
|
||||||
|
if (!dns.isString()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dnsServers.append(dns.toString());
|
||||||
|
}
|
||||||
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
|
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue