Compare commits

..

11 Commits

Author SHA1 Message Date
vladimir.kuznetsov ab4f454c19 Merge branch 'fix/ui-fixes-after-merge-with-d20ed4a' of github.com:amnezia-vpn/amnezia-client into fix/ui-fixes-after-merge-with-d20ed4a 2025-08-09 22:21:38 +08:00
vladimir.kuznetsov e96dfe5800 chore: page settings dns margins 2025-08-09 22:20:48 +08:00
Cyril Anisimov 768c51dbbe update OpenVPN settings page 2025-08-09 14:19:11 +02:00
vladimir.kuznetsov 5acbdd7af6 fix: ui fixes after merge with d20ed4a 2025-08-08 11:45:20 +08:00
serj95reg a6e6de33c8 feat: updated xray version in dockerfile to 25.8.3 (#1771) 2025-08-08 10:34:51 +08:00
Mitternacht822 53c7fd4d81 fix: android build (#1768)
* added signal-slot connection between corecontroller and systemtraynofificationhandler updating websiteurl

* cleared up the commented lines

* fixed andorid includes for systemtraynotificationhandler
2025-08-07 11:12:09 +08:00
Nethius 2608ea4367 chore: fix typo (#1769) 2025-08-06 11:00:43 +08:00
Cyril Anisimov d20ed4ad01 refactoring: improved stability of focus controller (#1464)
* change position view mode

* remove `parentFlickable` from `PageShare`

* replace `FlickableType` with `ListViewType` in `PageSettings`

* reorganize `PageSettingsAbout` for improved structure

* replace `Flickable` with `ListViewType` in drawer in `PageSettingsApiNativeConfigs`

* replace `FlickableType` with `ListViewType` in `PageSettingsApplication` and update layout structure

* replace `FlickableType` with `ListViewType` in `PageSettingsAppSplitTunneling` and adjust layout for better structure

* replace `FlickableType` with `ListViewType` in `PageSettingsBackup`

* replace `FlickableType` with `ListViewType` in `PageSettingsConnection`

* replace `FlickableType` with `ListViewType` in `PageSettingsDns`

* replace `FlickableType` with `ListViewType` in `PageSettingsLogging`

* replace `FlickableType` with `ListViewType` in `PageSettingsServerData`

* update structure of `PageSettingsServerProtocol`

* update `PageSettingsServersList`

* replace `ListView` with `ListViewType` in `PageSettingsSplitTunneling`

* replace `FlickableType` with `ListViewType` in `PageServiceDnsSettings`

* update `PageServiceSftpSettings`

* update `PageServiceSocksProxySettings`

* replace `FlickableType` with `ListViewType` in `PageServiceTorWebsiteSettings`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardApiServiceInfo`

* update `PageSetupWizardApiServicesList`

* replace `ListView` with `ListViewType` in `PageSetupWizardConfigSource`

* replace `ListView` with `ListViewType` in `PageSetupWizardCredentials`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardEasy`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardInstalling`

* replace `ListView` with `ListViewType` in `PageSetupWizardProtocols`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardProtocolSettings`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardTextKey`

* replace `FlickableType` with `ListViewType` in `PageSetupWizardViewConfig`

* update `PageProtocolAwgClientSettings`

* update `PageProtocolAwgSettings`

* replace `FlickableType` with `ListViewType` in `PageProtocolCloakSettings`

* replace `FlickableType` with `ListViewType` in `PageProtocolRaw`

* replace `FlickableType` with `ListViewType` in `PageProtocolShadowSocksSettings`

* replace `FlickableType` with `ListViewType` in `PageProtocolWireGuardClientSettings`

* replace `FlickableType` with `ListViewType` in `PageProtocolWireGuardSettings`

* replace `FlickableType` with `ListViewType` in `PageProtocolXraySettings`

* replace `FlickableType` with `ListViewType` in `PageShareFullAccess`

* replace `FlickableType` with `ListViewType` in `PageDeinstalling`

* update `PageDevMenu`

* remove `Flickable` references in `LabelWithButtonType`

* remove useless key navigation handlers from `ListViewType`

* replace `ListView` with `ListViewType` in `ListViewWithRadioButtonType.qml` and remove unnecessary properties

* remove references to `Flickable` in `TextAreaType.qml`

* remove references to `Flickable` in `TextAreaWithFooterType`

* remove references to `FlickableType` in `TextFieldWithHeaderType`

* remove references to `FlickableType` in `SwitcherType`

* remove references to `FlickableType` in `CheckBoxType`

* remove references to `FlickableType` in `CardWithIconsType.qml`

* remove references to `FlickableType` in `BasicButtonType.qml`

* update `ServersListView`

* update `SettingsContainersListView`

* update `InstalledAppsDrawer`

* update `SelectLanguageDrawer`

* update `HomeContainersListView`

* update `HomeSplitTunnelingDrawer`

* fix `PageSetupWizardApiServicesList`

---------

Co-authored-by: vladimir.kuznetsov <nethiuswork@gmail.com>
2025-08-06 10:35:51 +08:00
KsZnak eae2936449 Update README links.md [no ci]
Update README links.md
2025-08-04 19:35:45 +01:00
KsZnak da8ad1f6ba UTM added.md [no ci]
Update README_RU.md
2025-08-04 19:34:12 +01:00
Mitternacht822 5472347969 feature: added warning label when config files have changed in premium configuration files menu (#1718)
* added warning label when config files have changed in premium configuration files display

* moved warning display from PageSettingsApiNativeConfigs.qml to PageSettingsApiServerInfo.qml
2025-08-04 14:13:22 +08:00
17 changed files with 423 additions and 578 deletions
+5 -5
View File
@@ -9,17 +9,17 @@
### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md)
[Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
[Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
### [Website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
> [!TIP]
> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org ).
> If the [Amnezia website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror).
<a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
<a href="https://amnezia.org/en/downloads?utm_source=github&utm_campaign=amnezia_button-readme-en"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/en/downloads&utm_source=github&utm_campaign=amnezia_button-readme-en-mirrow"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
+4 -4
View File
@@ -6,16 +6,16 @@
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[AmneziaVPN](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
### [Сайт](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
> [!TIP]
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
> Если [сайт Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror).
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/ru/downloads&utm_source=github&utm_campaign=amnezia_button-readme-ru-mirror"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
+1 -1
View File
@@ -245,10 +245,10 @@ void CoreController::initNotificationHandler()
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection);
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
#endif
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_notificationHandler.get());
connect(this, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
#endif
}
void CoreController::updateTranslator(const QLocale &locale)
+3 -1
View File
@@ -5,7 +5,9 @@
#include <QQmlContext>
#include <QThread>
#include "ui/systemtray_notificationhandler.h"
#ifndef Q_OS_ANDROID
#include "ui/systemtray_notificationhandler.h"
#endif
#include "ui/controllers/api/apiConfigsController.h"
#include "ui/controllers/api/apiSettingsController.h"
+1 -1
View File
@@ -1,7 +1,7 @@
FROM alpine:3.15
LABEL maintainer="AmneziaVPN"
ARG XRAY_RELEASE="v1.8.6"
ARG XRAY_RELEASE="v25.8.3"
RUN apk add --no-cache curl unzip bash openssl netcat-openbsd dumb-init rng-tools xz
RUN apk --update upgrade --no-cache
+76 -107
View File
@@ -2,128 +2,72 @@
#include "protocols/protocols_defs.h"
OpenVpnConfigModel::OpenVpnConfigModel(QObject *parent)
: QObject(parent)
OpenVpnConfigModel::OpenVpnConfigModel(QObject *parent) : QAbstractListModel(parent)
{
}
QString OpenVpnConfigModel::subnetAddress() const
int OpenVpnConfigModel::rowCount(const QModelIndex &parent) const
{
return m_protocolConfig.value(amnezia::config_key::subnet_address).toString(amnezia::protocols::openvpn::defaultSubnetAddress);
Q_UNUSED(parent);
return 1;
}
void OpenVpnConfigModel::setSubnetAddress(const QString &subnetAddress)
bool OpenVpnConfigModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
m_protocolConfig.insert(amnezia::config_key::subnet_address, subnetAddress);
if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) {
return false;
}
switch (role) {
case Roles::SubnetAddressRole: m_protocolConfig.insert(amnezia::config_key::subnet_address, value.toString()); break;
case Roles::TransportProtoRole: m_protocolConfig.insert(config_key::transport_proto, value.toString()); break;
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break;
case Roles::AutoNegotiateEncryprionRole: m_protocolConfig.insert(config_key::ncp_disable, !value.toBool()); break;
case Roles::HashRole: m_protocolConfig.insert(config_key::hash, value.toString()); break;
case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break;
case Roles::TlsAuthRole: m_protocolConfig.insert(config_key::tls_auth, value.toBool()); break;
case Roles::BlockDnsRole: m_protocolConfig.insert(config_key::block_outside_dns, value.toBool()); break;
case Roles::AdditionalClientCommandsRole: m_protocolConfig.insert(config_key::additional_client_config, value.toString()); break;
case Roles::AdditionalServerCommandsRole: m_protocolConfig.insert(config_key::additional_server_config, value.toString()); break;
}
emit dataChanged(index, index, QList { role });
return true;
}
QString OpenVpnConfigModel::transportProto() const
QVariant OpenVpnConfigModel::data(const QModelIndex &index, int role) const
{
return m_protocolConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto);
}
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
return false;
}
void OpenVpnConfigModel::setTransportProto(const QString &transportProto)
{
m_protocolConfig.insert(config_key::transport_proto, transportProto);
}
QString OpenVpnConfigModel::port() const
{
return m_protocolConfig.value(config_key::port).toString(protocols::openvpn::defaultPort);
}
void OpenVpnConfigModel::setPort(const QString &port)
{
m_protocolConfig.insert(config_key::port, port);
}
bool OpenVpnConfigModel::autoNegotiateEncryption() const
{
return !m_protocolConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
}
void OpenVpnConfigModel::setAutoNegotiateEncryption(bool enabled)
{
m_protocolConfig.insert(config_key::ncp_disable, !enabled);
}
QString OpenVpnConfigModel::hash() const
{
return m_protocolConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash);
}
void OpenVpnConfigModel::setHash(const QString &hash)
{
m_protocolConfig.insert(config_key::hash, hash);
}
QString OpenVpnConfigModel::cipher() const
{
return m_protocolConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher);
}
void OpenVpnConfigModel::setCipher(const QString &cipher)
{
m_protocolConfig.insert(config_key::cipher, cipher);
}
bool OpenVpnConfigModel::tlsAuth() const
{
return m_protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
}
void OpenVpnConfigModel::setTlsAuth(bool enabled)
{
m_protocolConfig.insert(config_key::tls_auth, enabled);
}
bool OpenVpnConfigModel::blockDns() const
{
return m_protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns);
}
void OpenVpnConfigModel::setBlockDns(bool enabled)
{
m_protocolConfig.insert(config_key::block_outside_dns, enabled);
}
QString OpenVpnConfigModel::additionalClientCommands() const
{
return m_protocolConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig);
}
void OpenVpnConfigModel::setAdditionalClientCommands(const QString &commands)
{
m_protocolConfig.insert(config_key::additional_client_config, commands);
}
QString OpenVpnConfigModel::additionalServerCommands() const
{
return m_protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig);
}
void OpenVpnConfigModel::setAdditionalServerCommands(const QString &commands)
{
m_protocolConfig.insert(config_key::additional_server_config, commands);
}
bool OpenVpnConfigModel::isPortEditable() const
{
return m_container == DockerContainer::OpenVpn;
}
bool OpenVpnConfigModel::isTransportProtoEditable() const
{
return m_container == DockerContainer::OpenVpn;
}
bool OpenVpnConfigModel::hasRemoveButton() const
{
return m_container == DockerContainer::OpenVpn;
switch (role) {
case Roles::SubnetAddressRole:
return m_protocolConfig.value(amnezia::config_key::subnet_address).toString(amnezia::protocols::openvpn::defaultSubnetAddress);
case Roles::TransportProtoRole:
return m_protocolConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto);
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::openvpn::defaultPort);
case Roles::AutoNegotiateEncryprionRole:
return !m_protocolConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
case Roles::HashRole: return m_protocolConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash);
case Roles::CipherRole: return m_protocolConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher);
case Roles::TlsAuthRole: return m_protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
case Roles::BlockDnsRole:
return m_protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns);
case Roles::AdditionalClientCommandsRole:
return m_protocolConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig);
case Roles::AdditionalServerCommandsRole:
return m_protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig);
case Roles::IsPortEditable: return m_container == DockerContainer::OpenVpn ? true : false;
case Roles::IsTransportProtoEditable: return m_container == DockerContainer::OpenVpn ? true : false;
case Roles::HasRemoveButton: return m_container == DockerContainer::OpenVpn ? true : false;
}
return QVariant();
}
void OpenVpnConfigModel::updateModel(const QJsonObject &config)
{
beginResetModel();
m_container = ContainerProps::containerFromString(config.value(config_key::container).toString());
m_fullConfig = config;
@@ -156,6 +100,8 @@ void OpenVpnConfigModel::updateModel(const QJsonObject &config)
m_protocolConfig.insert(
config_key::additional_server_config,
protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig));
endResetModel();
}
QJsonObject OpenVpnConfigModel::getConfig()
@@ -163,3 +109,26 @@ QJsonObject OpenVpnConfigModel::getConfig()
m_fullConfig.insert(config_key::openvpn, m_protocolConfig);
return m_fullConfig;
}
QHash<int, QByteArray> OpenVpnConfigModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[SubnetAddressRole] = "subnetAddress";
roles[TransportProtoRole] = "transportProto";
roles[PortRole] = "port";
roles[AutoNegotiateEncryprionRole] = "autoNegotiateEncryprion";
roles[HashRole] = "hash";
roles[CipherRole] = "cipher";
roles[TlsAuthRole] = "tlsAuth";
roles[BlockDnsRole] = "blockDns";
roles[AdditionalClientCommandsRole] = "additionalClientCommands";
roles[AdditionalServerCommandsRole] = "additionalServerCommands";
roles[IsPortEditable] = "isPortEditable";
roles[IsTransportProtoEditable] = "isTransportProtoEditable";
roles[HasRemoveButton] = "hasRemoveButton";
return roles;
}
+27 -70
View File
@@ -1,90 +1,47 @@
#ifndef OPENVPNCONFIGMODEL_H
#define OPENVPNCONFIGMODEL_H
#include <QObject>
#include <QAbstractListModel>
#include <QJsonObject>
#include "containers/containers_defs.h"
class OpenVpnConfigModel : public QObject
class OpenVpnConfigModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QString subnetAddress READ subnetAddress WRITE setSubnetAddress NOTIFY subnetAddressChanged)
Q_PROPERTY(QString transportProto READ transportProto WRITE setTransportProto NOTIFY transportProtoChanged)
Q_PROPERTY(QString port READ port WRITE setPort NOTIFY portChanged)
Q_PROPERTY(bool autoNegotiateEncryption READ autoNegotiateEncryption WRITE setAutoNegotiateEncryption NOTIFY autoNegotiateEncryptionChanged)
Q_PROPERTY(QString hash READ hash WRITE setHash NOTIFY hashChanged)
Q_PROPERTY(QString cipher READ cipher WRITE setCipher NOTIFY cipherChanged)
Q_PROPERTY(bool tlsAuth READ tlsAuth WRITE setTlsAuth NOTIFY tlsAuthChanged)
Q_PROPERTY(bool blockDns READ blockDns WRITE setBlockDns NOTIFY blockDnsChanged)
Q_PROPERTY(QString additionalClientCommands READ additionalClientCommands WRITE setAdditionalClientCommands NOTIFY additionalClientCommandsChanged)
Q_PROPERTY(QString additionalServerCommands READ additionalServerCommands WRITE setAdditionalServerCommands NOTIFY additionalServerCommandsChanged)
Q_PROPERTY(bool isPortEditable READ isPortEditable NOTIFY isPortEditableChanged)
Q_PROPERTY(bool isTransportProtoEditable READ isTransportProtoEditable NOTIFY isTransportProtoEditableChanged)
Q_PROPERTY(bool hasRemoveButton READ hasRemoveButton NOTIFY hasRemoveButtonChanged)
public:
enum Roles {
SubnetAddressRole = Qt::UserRole + 1,
TransportProtoRole,
PortRole,
AutoNegotiateEncryprionRole,
HashRole,
CipherRole,
TlsAuthRole,
BlockDnsRole,
AdditionalClientCommandsRole,
AdditionalServerCommandsRole,
IsPortEditable,
IsTransportProtoEditable,
HasRemoveButton
};
explicit OpenVpnConfigModel(QObject *parent = nullptr);
~OpenVpnConfigModel() override = default;
OpenVpnConfigModel(const OpenVpnConfigModel &) = delete;
OpenVpnConfigModel &operator=(const OpenVpnConfigModel &) = delete;
OpenVpnConfigModel(OpenVpnConfigModel &&) = delete;
OpenVpnConfigModel &operator=(OpenVpnConfigModel &&) = delete;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QString subnetAddress() const;
void setSubnetAddress(const QString &subnetAddress);
QString transportProto() const;
void setTransportProto(const QString &transportProto);
QString port() const;
void setPort(const QString &port);
bool autoNegotiateEncryption() const;
void setAutoNegotiateEncryption(bool enabled);
QString hash() const;
void setHash(const QString &hash);
QString cipher() const;
void setCipher(const QString &cipher);
bool tlsAuth() const;
void setTlsAuth(bool enabled);
bool blockDns() const;
void setBlockDns(bool enabled);
QString additionalClientCommands() const;
void setAdditionalClientCommands(const QString &commands);
QString additionalServerCommands() const;
void setAdditionalServerCommands(const QString &commands);
bool isPortEditable() const;
bool isTransportProtoEditable() const;
bool hasRemoveButton() const;
Q_INVOKABLE QJsonObject getConfig();
signals:
void subnetAddressChanged(const QString &);
void transportProtoChanged(const QString &);
void portChanged(const QString &);
void autoNegotiateEncryptionChanged(bool);
void hashChanged(const QString &);
void cipherChanged(const QString &);
void tlsAuthChanged(bool);
void blockDnsChanged(bool);
void additionalClientCommandsChanged(const QString &);
void additionalServerCommandsChanged(const QString &);
void isPortEditableChanged(bool);
void isTransportProtoEditableChanged(bool);
void hasRemoveButtonChanged(bool);
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
public slots:
void updateModel(const QJsonObject &config);
QJsonObject getConfig();
protected:
QHash<int, QByteArray> roleNames() const override;
private:
DockerContainer m_container;
@@ -34,7 +34,7 @@ PageType {
ListViewType {
id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: saveButton.top
anchors.right: parent.right
anchors.left: parent.left
@@ -37,7 +37,7 @@ PageType {
ListViewType {
id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
@@ -24,9 +24,9 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
@@ -46,417 +46,345 @@ PageType {
width: listView.width
BaseHeaderType {
id: header
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("OpenVPN Settings")
}
}
model: OpenVpnConfigModel
delegate: ColumnLayout {
width: listView.width
spacing: 0
TextFieldWithHeaderType {
id: vpnAddressSubnetTextField
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("OpenVPN settings")
enabled: listView.enabled
headerText: qsTr("VPN address subnet")
textField.text: subnetAddress
textField.onEditingFinished: {
if (textField.text !== subnetAddress) {
subnetAddress = textField.text
}
}
checkEmptyText: true
}
}
model: ListModel {
ListElement { type: "subnetHeader" }
ListElement { type: "networkProtocolText" }
ListElement { type: "protoSelector" }
ListElement { type: "portTextField" }
ListElement { type: "encryptionSection" }
ListElement { type: "checkboxSection" }
ListElement { type: "clientCommands" }
ListElement { type: "serverCommands" }
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
delegate: DelegateChooser {
text: qsTr("Network protocol")
}
role: "type"
TransportProtoSelector {
id: transportProtoSelector
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
DelegateChoice {
// property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
roleValue: "subnetHeader"
ColumnLayout {
width: listView.width
rootWidth: root.width
TextFieldWithHeaderType {
id: vpnAddressSubnetTextField
enabled: isTransportProtoEditable
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
currentIndex: {
return transportProto === "tcp" ? 1 : 0
}
headerText: qsTr("VPN address subnet")
textField.text: OpenVpnConfigModel.subnetAddress
onCurrentIndexChanged: {
if (transportProto === "tcp" && currentIndex === 0) {
transportProto = "udp"
} else if (transportProto === "udp" && currentIndex === 1) {
transportProto = "tcp"
}
}
}
textField.onEditingFinished: {
if (textField.text !== OpenVpnConfigModel.subnetAddress) {
OpenVpnConfigModel.subnetAddress = textField.text
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true
}
SwitcherType {
id: autoNegotiateEncryprionSwitcher
Layout.fillWidth: true
Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Auto-negotiate encryption")
checked: autoNegotiateEncryprion
onCheckedChanged: {
if (checked !== autoNegotiateEncryprion) {
autoNegotiateEncryprion = checked
}
}
}
DropDownType {
id: hashDropDown
Layout.fillWidth: true
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: !autoNegotiateEncryprionSwitcher.checked
descriptionText: qsTr("Hash")
headerText: qsTr("Hash")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: hashListView
rootWidth: root.width
model: ListModel {
ListElement { name : qsTr("SHA512") }
ListElement { name : qsTr("SHA384") }
ListElement { name : qsTr("SHA256") }
ListElement { name : qsTr("SHA3-512") }
ListElement { name : qsTr("SHA3-384") }
ListElement { name : qsTr("SHA3-256") }
ListElement { name : qsTr("whirlpool") }
ListElement { name : qsTr("BLAKE2b512") }
ListElement { name : qsTr("BLAKE2s256") }
ListElement { name : qsTr("SHA1") }
}
clickedFunction: function() {
hashDropDown.text = selectedText
hash = hashDropDown.text
hashDropDown.closeTriggered()
}
Component.onCompleted: {
hashDropDown.text = hash
for (var i = 0; i < hashListView.model.count; i++) {
if (hashListView.model.get(i).name === hashDropDown.text) {
currentIndex = i
}
}
}
}
}
DelegateChoice {
roleValue: "networkProtocolText"
ColumnLayout {
width: listView.width
DropDownType {
id: cipherDropDown
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: !autoNegotiateEncryprionSwitcher.checked
text: qsTr("Network protocol")
descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: cipherListView
rootWidth: root.width
model: ListModel {
ListElement { name : qsTr("AES-256-GCM") }
ListElement { name : qsTr("AES-192-GCM") }
ListElement { name : qsTr("AES-128-GCM") }
ListElement { name : qsTr("AES-256-CBC") }
ListElement { name : qsTr("AES-192-CBC") }
ListElement { name : qsTr("AES-128-CBC") }
ListElement { name : qsTr("ChaCha20-Poly1305") }
ListElement { name : qsTr("ARIA-256-CBC") }
ListElement { name : qsTr("CAMELLIA-256-CBC") }
ListElement { name : qsTr("none") }
}
}
}
DelegateChoice {
roleValue: "protoSelector"
ColumnLayout {
width: listView.width
clickedFunction: function() {
cipherDropDown.text = selectedText
cipher = cipherDropDown.text
cipherDropDown.closeTriggered()
}
TransportProtoSelector {
id: transportProtoSelector
Component.onCompleted: {
cipherDropDown.text = cipher
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
rootWidth: root.width
enabled: OpenVpnConfigModel.isTransportProtoEditable
currentIndex: {
return OpenVpnConfigModel.transportProto === "tcp" ? 1 : 0
}
onCurrentIndexChanged: {
if (OpenVpnConfigModel.transportProto === "tcp" && currentIndex === 0) {
OpenVpnConfigModel.transportProto = "udp"
} else if (OpenVpnConfigModel.transportProto === "udp" && currentIndex === 1) {
OpenVpnConfigModel.transportProto = "tcp"
for (var i = 0; i < cipherListView.model.count; i++) {
if (cipherListView.model.get(i).name === cipherDropDown.text) {
currentIndex = i
}
}
}
}
}
DelegateChoice {
roleValue: "portTextField"
Rectangle {
id: contentRect
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.preferredHeight: checkboxLayout.implicitHeight
color: AmneziaStyle.color.onyxBlack
radius: 16
ColumnLayout {
width: listView.width
id: checkboxLayout
TextFieldWithHeaderType {
id: portTextField
anchors.fill: parent
CheckBoxType {
id: tlsAuthCheckBox
Layout.fillWidth: true
Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: OpenVpnConfigModel.isPortEditable
headerText: qsTr("Port")
textField.text: OpenVpnConfigModel.port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== OpenVpnConfigModel.port) {
OpenVpnConfigModel.port = textField.text
}
}
}
}
}
DelegateChoice {
roleValue: "encryptionSection"
ColumnLayout {
width: listView.width
SwitcherType {
id: autoNegotiateEncryprionSwitcher
Layout.fillWidth: true
Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Auto-negotiate encryption")
checked: OpenVpnConfigModel.autoNegotiateEncryption
text: qsTr("TLS auth")
checked: tlsAuth
onCheckedChanged: {
if (checked !== OpenVpnConfigModel.autoNegotiateEncryprion) {
OpenVpnConfigModel.autoNegotiateEncryprion = checked
if (checked !== tlsAuth) {
console.log("tlsAuth changed to: " + checked)
tlsAuth = checked
}
}
}
DropDownType {
id: hashDropDown
DividerType {}
CheckBoxType {
id: blockDnsCheckBox
Layout.fillWidth: true
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: !autoNegotiateEncryprionSwitcher.checked
descriptionText: qsTr("Hash")
headerText: qsTr("Hash")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: hashListView
rootWidth: root.width
model: ListModel {
ListElement { name : qsTr("SHA512") }
ListElement { name : qsTr("SHA384") }
ListElement { name : qsTr("SHA256") }
ListElement { name : qsTr("SHA3-512") }
ListElement { name : qsTr("SHA3-384") }
ListElement { name : qsTr("SHA3-256") }
ListElement { name : qsTr("whirlpool") }
ListElement { name : qsTr("BLAKE2b512") }
ListElement { name : qsTr("BLAKE2s256") }
ListElement { name : qsTr("SHA1") }
}
clickedFunction: function() {
hashDropDown.text = selectedText
OpenVpnConfigModel.hash = hashDropDown.text
hashDropDown.closeTriggered()
}
Component.onCompleted: {
hashDropDown.text = OpenVpnConfigModel.hash
for (var i = 0; i < hashListView.model.count; i++) {
if (hashListView.model.get(i).name === hashDropDown.text) {
currentIndex = i
}
}
}
}
}
DropDownType {
id: cipherDropDown
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: !autoNegotiateEncryprionSwitcher.checked
descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: cipherListView
rootWidth: root.width
model: ListModel {
ListElement { name : qsTr("AES-256-GCM") }
ListElement { name : qsTr("AES-192-GCM") }
ListElement { name : qsTr("AES-128-GCM") }
ListElement { name : qsTr("AES-256-CBC") }
ListElement { name : qsTr("AES-192-CBC") }
ListElement { name : qsTr("AES-128-CBC") }
ListElement { name : qsTr("ChaCha20-Poly1305") }
ListElement { name : qsTr("ARIA-256-CBC") }
ListElement { name : qsTr("CAMELLIA-256-CBC") }
ListElement { name : qsTr("none") }
}
clickedFunction: function() {
cipherDropDown.text = selectedText
OpenVpnConfigModel.cipher = cipherDropDown.text
cipherDropDown.closeTriggered()
}
Component.onCompleted: {
cipherDropDown.text = OpenVpnConfigModel.cipher
for (var i = 0; i < cipherListView.model.count; i++) {
if (cipherListView.model.get(i).name === cipherDropDown.text) {
currentIndex = i
}
}
}
}
}
}
}
DelegateChoice {
roleValue: "checkboxSection"
ColumnLayout {
width: listView.width
Rectangle {
id: contentRect
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.preferredHeight: checkboxLayout.implicitHeight
color: AmneziaStyle.color.onyxBlack
radius: 16
ColumnLayout {
id: checkboxLayout
anchors.fill: parent
CheckBoxType {
id: tlsAuthCheckBox
Layout.fillWidth: true
text: qsTr("TLS auth")
checked: OpenVpnConfigModel.tlsAuth
onCheckedChanged: {
if (checked !== OpenVpnConfigModel.tlsAuth) {
console.log("tlsAuth changed to: " + checked)
OpenVpnConfigModel.tlsAuth = checked
}
}
}
DividerType {}
CheckBoxType {
id: blockDnsCheckBox
Layout.fillWidth: true
text: qsTr("Block DNS requests outside of VPN")
checked: OpenVpnConfigModel.blockDns
onCheckedChanged: {
if (checked !== OpenVpnConfigModel.blockDns) {
OpenVpnConfigModel.blockDns = checked
}
}
}
}
}
}
}
DelegateChoice {
roleValue: "clientCommands"
ColumnLayout {
width: listView.width
SwitcherType {
id: additionalClientCommandsSwitcher
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
checked: OpenVpnConfigModel.additionalClientCommands !== ""
text: qsTr("Additional client configuration commands")
text: qsTr("Block DNS requests outside of VPN")
checked: blockDns
onCheckedChanged: {
if (!checked) {
OpenVpnConfigModel.additionalClientCommands = ""
}
// listView.positionViewAtIndex(index, ListView.Beginning)
}
}
TextAreaType {
id: additionalClientCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalClientCommandsSwitcher.checked
textAreaText: OpenVpnConfigModel.additionalClientCommands
placeholderText: qsTr("Commands:")
textArea.onEditingFinished: {
if (OpenVpnConfigModel.additionalClientCommands !== textAreaText) {
OpenVpnConfigModel.additionalClientCommands = textAreaText
if (checked !== blockDns) {
blockDns = checked
}
}
}
}
}
DelegateChoice {
roleValue: "serverCommands"
ColumnLayout {
width: listView.width
SwitcherType {
id: additionalClientCommandsSwitcher
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
SwitcherType {
id: additionalServerCommandsSwitcher
checked: additionalClientCommands !== ""
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Additional client configuration commands")
checked: OpenVpnConfigModel.additionalServerCommands !== ""
text: qsTr("Additional server configuration commands")
onCheckedChanged: {
if (!checked) {
OpenVpnConfigModel.additionalServerCommands = ""
}
// listView.positionViewAtIndex(index, ListView.Beginning)
}
}
TextAreaType {
id: additionalServerCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalServerCommandsSwitcher.checked
textAreaText: OpenVpnConfigModel.additionalServerCommands
placeholderText: qsTr("Commands:")
textArea.onEditingFinished: {
if (OpenVpnConfigModel.additionalServerCommands !== textAreaText) {
OpenVpnConfigModel.additionalServerCommands = textAreaText
}
}
onCheckedChanged: {
if (!checked) {
additionalClientCommands = ""
}
}
}
TextAreaType {
id: additionalClientCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalClientCommandsSwitcher.checked
textAreaText: additionalClientCommands
placeholderText: qsTr("Commands:")
textArea.onEditingFinished: {
if (additionalClientCommands !== textAreaText) {
additionalClientCommands = textAreaText
}
}
}
SwitcherType {
id: additionalServerCommandsSwitcher
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
checked: additionalServerCommands !== ""
text: qsTr("Additional server configuration commands")
onCheckedChanged: {
if (!checked) {
additionalServerCommands = ""
}
}
}
TextAreaType {
id: additionalServerCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalServerCommandsSwitcher.checked
textAreaText: additionalServerCommands
placeholderText: qsTr("Commands:")
textArea.onEditingFinished: {
if (additionalServerCommands !== textAreaText) {
additionalServerCommands = textAreaText
}
}
}
}
footer: ColumnLayout {
width: listView.width
BasicButtonType {
id: saveRestartButton
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
@@ -465,7 +393,7 @@ PageType {
Layout.rightMargin: 16
enabled: vpnAddressSubnetTextField.errorText === "" &&
portTextField.errorText === ""
portTextField.errorText === ""
text: qsTr("Save")
@@ -66,8 +66,6 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("VPN address subnet")
textField.text: subnetAddress
@@ -87,8 +85,6 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
-4
View File
@@ -43,8 +43,6 @@ PageType {
LabelWithButtonType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: isVisible
@@ -68,8 +66,6 @@ PageType {
visible: GC.isDesktop()
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Close application")
leftImageSource: "qrc:/images/controls/x-circle.svg"
@@ -71,6 +71,7 @@ PageType {
text: countryName
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
hideDescription: isWorkerExpired ? true : false
descriptionColor: AmneziaStyle.color.vibrantRed
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
@@ -187,7 +187,13 @@ PageType {
iconPath: "qrc:/images/controls/alert-circle.svg"
visible: ApiAccountInfoModel.data("hasExpiredWorker")
visible: {
for (let i = 0; i < ApiCountryModel.count; ++i) {
if (ApiCountryModel.get(i).isWorkerExpired)
return true;
}
return false;
}
}
LabelWithButtonType {
+7 -11
View File
@@ -66,6 +66,13 @@ PageType {
text: qsTr("If AmneziaDNS is not used or installed")
}
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
spacing: 16
TextFieldWithHeaderType {
id: primaryDns
@@ -96,13 +103,6 @@ PageType {
regularExpression: InstallController.ipAddressRegExp()
}
}
}
model: 1 // fake model to force the ListView to be created without a model
spacing: 16
delegate: ColumnLayout {
width: listView.width
BasicButtonType {
id: restoreDefaultButton
@@ -139,10 +139,6 @@ PageType {
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
}
footer: ColumnLayout {
width: listView.width
BasicButtonType {
id: saveButton
@@ -18,6 +18,8 @@ PageType {
signal lastItemTabClickedSignal()
property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess()
Connections {
target: InstallController
@@ -59,15 +61,13 @@ PageType {
target: ServersModel
function onProcessedServerIndexChanged() {
listView.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess()
root.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess()
}
}
ListViewType {
id: listView
property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess()
anchors.fill: parent
model: serverActions
@@ -107,7 +107,7 @@ PageType {
QtObject {
id: check
property bool isVisible: true
property bool isVisible: root.isServerWithWriteAccess
readonly property string title: qsTr("Check the server for previously installed Amnezia services")
readonly property string description: qsTr("Add them to the application if they were not displayed")
readonly property var tColor: AmneziaStyle.color.paleGray
@@ -121,7 +121,7 @@ PageType {
QtObject {
id: reboot
property bool isVisible: true
property bool isVisible: root.isServerWithWriteAccess
readonly property string title: qsTr("Reboot server")
readonly property string description: ""
readonly property var tColor: AmneziaStyle.color.vibrantRed
@@ -181,7 +181,7 @@ PageType {
QtObject {
id: clear
property bool isVisible: true
property bool isVisible: root.isServerWithWriteAccess
readonly property string title: qsTr("Clear server from Amnezia software")
readonly property string description: ""
readonly property var tColor: AmneziaStyle.color.vibrantRed
@@ -240,7 +240,7 @@ PageType {
QtObject {
id: switch_to_premium
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
readonly property string title: qsTr("Switch to the new Amnezia Premium subscription")
readonly property string description: ""
readonly property var tColor: AmneziaStyle.color.vibrantRed
@@ -161,10 +161,4 @@ PageType {
}
}
}
ShareConnectionDrawer {
id: shareConnectionDrawer
anchors.fill: parent
}
}