From c13b9754eb2fb90f0e00547004d03bb3f7d2fee7 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 13 Jul 2023 11:29:26 +0900 Subject: [PATCH] added protocol settings pages and models for openvpn, cloak and shadowsocks --- client/CMakeLists.txt | 4 +- client/amnezia_application.cpp | 86 ++-- client/amnezia_application.h | 20 + client/protocols/protocols_defs.h | 406 ++++++++------- client/resources.qrc | 4 +- client/ui/controllers/installController.cpp | 27 +- client/ui/controllers/installController.h | 4 + client/ui/controllers/pageController.h | 8 +- client/ui/models/containers_model.cpp | 38 +- client/ui/models/containers_model.h | 4 + client/ui/models/languageModel.cpp | 10 +- .../ui/models/protocols/cloakConfigModel.cpp | 81 +++ client/ui/models/protocols/cloakConfigModel.h | 40 ++ .../ui/models/protocols/ikev2ConfigModel.cpp | 76 +++ client/ui/models/protocols/ikev2ConfigModel.h | 39 ++ .../models/protocols/openvpnConfigModel.cpp | 152 ++++++ .../ui/models/protocols/openvpnConfigModel.h | 52 ++ .../protocols/shadowsocksConfigModel.cpp | 76 +++ .../models/protocols/shadowsocksConfigModel.h | 39 ++ .../models/protocols/wireguardConfigModel.cpp | 70 +++ .../models/protocols/wireguardConfigModel.h | 39 ++ client/ui/models/protocols_model.cpp | 75 +-- client/ui/models/protocols_model.h | 30 +- client/ui/models/servers_model.cpp | 1 + .../ui/pages_logic/GeneralSettingsLogic.cpp | 14 +- .../ui/pages_logic/ServerContainersLogic.cpp | 62 +-- .../Components/Protocols/OpenVpnSettings.qml | 147 ------ .../Components/SettingsContainersListView.qml | 29 +- .../qml/Components/ShareConnectionDrawer.qml | 8 +- .../qml/Components/TransportProtoSelector.qml | 4 + client/ui/qml/Controls2/CheckBoxType.qml | 46 +- client/ui/qml/Controls2/DropDownType.qml | 33 +- .../qml/Controls2/HorizontalRadioButton.qml | 25 +- .../qml/Controls2/TextFieldWithHeaderType.qml | 30 +- client/ui/qml/Pages2/PageHome.qml | 1 - .../qml/Pages2/PageProtocolCloakSettings.qml | 176 +++++++ .../Pages2/PageProtocolOpenVpnSettings.qml | 465 ++++++++++++++++++ .../PageProtocolShadowSocksSettings.qml | 162 ++++++ .../qml/Pages2/PageSettingsServerProtocol.qml | 93 +++- .../qml/Pages2/PageSetupWizardInstalling.qml | 4 - .../qml/Pages2/PageSetupWizardProtocols.qml | 2 +- client/ui/qml/Pages2/PageShare.qml | 24 +- 42 files changed, 2130 insertions(+), 576 deletions(-) create mode 100644 client/ui/models/protocols/cloakConfigModel.cpp create mode 100644 client/ui/models/protocols/cloakConfigModel.h create mode 100644 client/ui/models/protocols/ikev2ConfigModel.cpp create mode 100644 client/ui/models/protocols/ikev2ConfigModel.h create mode 100644 client/ui/models/protocols/openvpnConfigModel.cpp create mode 100644 client/ui/models/protocols/openvpnConfigModel.h create mode 100644 client/ui/models/protocols/shadowsocksConfigModel.cpp create mode 100644 client/ui/models/protocols/shadowsocksConfigModel.h create mode 100644 client/ui/models/protocols/wireguardConfigModel.cpp create mode 100644 client/ui/models/protocols/wireguardConfigModel.h delete mode 100644 client/ui/qml/Components/Protocols/OpenVpnSettings.qml create mode 100644 client/ui/qml/Pages2/PageProtocolCloakSettings.qml create mode 100644 client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml create mode 100644 client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index ad9a4cf4..be1c3b92 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -129,8 +129,8 @@ file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/ file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.h) file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.cpp) -file(GLOB UI_MODELS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.h) -file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.cpp) +file(GLOB UI_MODELS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.h ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.h) +file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.cpp ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.cpp) file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h) file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 7c3b466b..436dfc3f 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -79,38 +79,12 @@ void AmneziaApplication::init() m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance()); // - m_containersModel.reset(new ContainersModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get()); - - m_serversModel.reset(new ServersModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get()); - connect(m_serversModel.get(), &ServersModel::currentlyProcessedServerIndexChanged, m_containersModel.get(), - &ContainersModel::setCurrentlyProcessedServerIndex); - - m_languageModel.reset(new LanguageModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get()); - connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator); m_configurator = std::shared_ptr(new VpnConfigurator(m_settings, this)); m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator)); - m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection)); - m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get()); - - m_pageController.reset(new PageController(m_serversModel)); - m_engine->rootContext()->setContextProperty("PageController", m_pageController.get()); - - m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_settings)); - m_engine->rootContext()->setContextProperty("InstallController", m_installController.get()); - - m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings)); - m_engine->rootContext()->setContextProperty("ImportController", m_importController.get()); - - m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_settings, m_configurator)); - m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get()); - - m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_settings)); - m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get()); + initModels(); + initControllers(); // @@ -244,3 +218,59 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const { return m_engine; } + +void AmneziaApplication::initModels() +{ + m_containersModel.reset(new ContainersModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get()); + + m_serversModel.reset(new ServersModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get()); + connect(m_serversModel.get(), &ServersModel::currentlyProcessedServerIndexChanged, m_containersModel.get(), + &ContainersModel::setCurrentlyProcessedServerIndex); + + m_languageModel.reset(new LanguageModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get()); + connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator); + + m_protocolsModel.reset(new ProtocolsModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get()); + + m_openVpnConfigModel.reset(new OpenVpnConfigModel(this)); + m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get()); + + m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this)); + m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get()); + + m_cloakConfigModel.reset(new CloakConfigModel(this)); + m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get()); + + m_wireguardConfigModel.reset(new WireGuardConfigModel(this)); + m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireguardConfigModel.get()); + +#ifdef Q_OS_WINDOWS + m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this)); + m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get()); +#endif +} + +void AmneziaApplication::initControllers() +{ + m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection)); + m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get()); + + m_pageController.reset(new PageController(m_serversModel)); + m_engine->rootContext()->setContextProperty("PageController", m_pageController.get()); + + m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_settings)); + m_engine->rootContext()->setContextProperty("InstallController", m_installController.get()); + + m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings)); + m_engine->rootContext()->setContextProperty("ImportController", m_importController.get()); + + m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_settings, m_configurator)); + m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get()); + + m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_settings)); + m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get()); +} diff --git a/client/amnezia_application.h b/client/amnezia_application.h index 4e9cc348..fabc7818 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -21,6 +21,14 @@ #include "ui/controllers/settingsController.h" #include "ui/models/containers_model.h" #include "ui/models/languageModel.h" +#include "ui/models/protocols/cloakConfigModel.h" +#ifdef Q_OS_WINDOWS + #include "ui/models/protocols/ikev2ConfigModel.h" +#endif +#include "ui/models/protocols/openvpnConfigModel.h" +#include "ui/models/protocols/shadowsocksConfigModel.h" +#include "ui/models/protocols/wireguardConfigModel.h" +#include "ui/models/protocols_model.h" #include "ui/models/servers_model.h" #define amnApp (static_cast(QCoreApplication::instance())) @@ -56,6 +64,9 @@ public: QQmlApplicationEngine *qmlEngine() const; private: + void initModels(); + void initControllers(); + QQmlApplicationEngine *m_engine {}; std::shared_ptr m_settings; std::shared_ptr m_configurator; @@ -69,6 +80,15 @@ private: QSharedPointer m_containersModel; QSharedPointer m_serversModel; QScopedPointer m_languageModel; + QScopedPointer m_protocolsModel; + + QScopedPointer m_openVpnConfigModel; + QScopedPointer m_shadowSocksConfigModel; + QScopedPointer m_cloakConfigModel; + QScopedPointer m_wireguardConfigModel; +#ifdef Q_OS_WINDOWS + QScopedPointer m_ikev2ConfigModel; +#endif QSharedPointer m_vpnConnection; diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 1f890f4c..73c2abdf 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -1,229 +1,223 @@ #ifndef PROTOCOLS_DEFS_H #define PROTOCOLS_DEFS_H -#include #include +#include #include -namespace amnezia { -namespace config_key { - -// Json config strings -constexpr char hostName[] = "hostName"; -constexpr char userName[] = "userName"; -constexpr char password[] = "password"; -constexpr char port[] = "port"; -constexpr char local_port[] = "local_port"; - -constexpr char dns1[] = "dns1"; -constexpr char dns2[] = "dns2"; - - -constexpr char description[] = "description"; -constexpr char cert[] = "cert"; -constexpr char config[] = "config"; - - -constexpr char containers[] = "containers"; -constexpr char container[] = "container"; -constexpr char defaultContainer[] = "defaultContainer"; - -constexpr char vpnproto[] = "protocol"; -constexpr char protocols[] = "protocols"; - -constexpr char remote[] = "remote"; -constexpr char transport_proto[] = "transport_proto"; -constexpr char cipher[] = "cipher"; -constexpr char hash[] = "hash"; -constexpr char ncp_disable[] = "ncp_disable"; -constexpr char tls_auth[] = "tls_auth"; - -constexpr char client_priv_key[] = "client_priv_key"; -constexpr char client_pub_key[] = "client_pub_key"; -constexpr char server_priv_key[] = "server_priv_key"; -constexpr char server_pub_key[] = "server_pub_key"; -constexpr char psk_key[] = "psk_key"; - -constexpr char client_ip[] = "client_ip"; // internal ip address - -constexpr char site[] = "site"; -constexpr char block_outside_dns[] = "block_outside_dns"; - -constexpr char subnet_address[] = "subnet_address"; -constexpr char subnet_mask[] = "subnet_mask"; -constexpr char subnet_cidr[] = "subnet_cidr"; - -constexpr char additional_client_config[] = "additional_client_config"; -constexpr char additional_server_config[] = "additional_server_config"; - -// proto config keys -constexpr char last_config[] = "last_config"; - -constexpr char isThirdPartyConfig[] = "isThirdPartyConfig"; - -constexpr char openvpn[] = "openvpn"; -constexpr char wireguard[] = "wireguard"; - -} - -namespace protocols { - -namespace dns { -constexpr char amneziaDnsIp[] = "172.29.172.254"; -} - -namespace openvpn { -constexpr char defaultSubnetAddress[] = "10.8.0.0"; -constexpr char defaultSubnetMask[] = "255.255.255.0"; -constexpr char defaultSubnetCidr[] = "24"; - -constexpr char serverConfigPath[] = "/opt/amnezia/openvpn/server.conf"; -constexpr char caCertPath[] = "/opt/amnezia/openvpn/pki/ca.crt"; -constexpr char clientCertPath[] = "/opt/amnezia/openvpn/pki/issued"; -constexpr char taKeyPath[] = "/opt/amnezia/openvpn/ta.key"; -constexpr char clientsDirPath[] = "/opt/amnezia/openvpn/clients"; -constexpr char defaultPort[] = "1194"; -constexpr char defaultTransportProto[] = "udp"; -constexpr char defaultCipher[] = "AES-256-GCM"; -constexpr char defaultHash[] = "SHA512"; -constexpr bool defaultBlockOutsideDns = true; -constexpr bool defaultNcpDisable = false; -constexpr bool defaultTlsAuth = true; -constexpr char ncpDisableString[] = "ncp-disable"; -constexpr char tlsAuthString[] = "tls-auth /opt/amnezia/openvpn/ta.key 0"; - -constexpr char defaultAdditionalClientConfig[] = ""; -constexpr char defaultAdditionalServerConfig[] = ""; -} - -namespace shadowsocks { -constexpr char ssKeyPath[] = "/opt/amnezia/shadowsocks/shadowsocks.key"; -constexpr char defaultPort[] = "6789"; -constexpr char defaultLocalProxyPort[] = "8585"; -constexpr char defaultCipher[] = "chacha20-ietf-poly1305"; -} - -namespace cloak { -constexpr char ckPublicKeyPath[] = "/opt/amnezia/cloak/cloak_public.key"; -constexpr char ckBypassUidKeyPath[] = "/opt/amnezia/cloak/cloak_bypass_uid.key"; -constexpr char ckAdminKeyPath[] = "/opt/amnezia/cloak/cloak_admin_uid.key"; -constexpr char defaultPort[] = "443"; -constexpr char defaultRedirSite[] = "tile.openstreetmap.org"; -constexpr char defaultCipher[] = "chacha20-poly1305"; - -} - -namespace wireguard { -constexpr char defaultSubnetAddress[] = "10.8.1.0"; -constexpr char defaultSubnetMask[] = "255.255.255.0"; -constexpr char defaultSubnetCidr[] = "24"; - -constexpr char defaultPort[] = "51820"; -constexpr char serverConfigPath[] = "/opt/amnezia/wireguard/wg0.conf"; -constexpr char serverPublicKeyPath[] = "/opt/amnezia/wireguard/wireguard_server_public_key.key"; -constexpr char serverPskKeyPath[] = "/opt/amnezia/wireguard/wireguard_psk.key"; - -} - -namespace sftp { -constexpr char defaultUserName[] = "sftp_user"; - -} // namespace sftp - -} // namespace protocols - -namespace ProtocolEnumNS { -Q_NAMESPACE - -enum TransportProto { - Udp, - Tcp -}; -Q_ENUM_NS(TransportProto) - -enum Proto { - Any = 0, - OpenVpn, - ShadowSocks, - Cloak, - WireGuard, - Ikev2, - L2tp, - - // non-vpn - TorWebSite, - Dns, - FileShare, - Sftp -}; -Q_ENUM_NS(Proto) - -enum ServiceType { - None = 0, - Vpn, - Other -}; -Q_ENUM_NS(ServiceType) -} // namespace ProtocolEnumNS - -using namespace ProtocolEnumNS; - -class ProtocolProps : public QObject +namespace amnezia { - Q_OBJECT + namespace config_key + { -public: - Q_INVOKABLE static QList allProtocols(); + // Json config strings + constexpr char hostName[] = "hostName"; + constexpr char userName[] = "userName"; + constexpr char password[] = "password"; + constexpr char port[] = "port"; + constexpr char local_port[] = "local_port"; - // spelling may differ for various protocols - TCP for OpenVPN, tcp for others - Q_INVOKABLE static TransportProto transportProtoFromString(QString p); - Q_INVOKABLE static QString transportProtoToString(TransportProto proto, Proto p = Proto::Any); + constexpr char dns1[] = "dns1"; + constexpr char dns2[] = "dns2"; - Q_INVOKABLE static Proto protoFromString(QString p); - Q_INVOKABLE static QString protoToString(Proto p); + constexpr char description[] = "description"; + constexpr char cert[] = "cert"; + constexpr char config[] = "config"; - Q_INVOKABLE static QMap protocolHumanNames(); - Q_INVOKABLE static QMap protocolDescriptions(); + constexpr char containers[] = "containers"; + constexpr char container[] = "container"; + constexpr char defaultContainer[] = "defaultContainer"; - Q_INVOKABLE static ServiceType protocolService(Proto p); + constexpr char vpnproto[] = "protocol"; + constexpr char protocols[] = "protocols"; - Q_INVOKABLE static int defaultPort(Proto p); - Q_INVOKABLE static bool defaultPortChangeable(Proto p); + constexpr char remote[] = "remote"; + constexpr char transport_proto[] = "transport_proto"; + constexpr char cipher[] = "cipher"; + constexpr char hash[] = "hash"; + constexpr char ncp_disable[] = "ncp_disable"; + constexpr char tls_auth[] = "tls_auth"; - Q_INVOKABLE static TransportProto defaultTransportProto(Proto p); - Q_INVOKABLE static bool defaultTransportProtoChangeable(Proto p); + constexpr char client_priv_key[] = "client_priv_key"; + constexpr char client_pub_key[] = "client_pub_key"; + constexpr char server_priv_key[] = "server_priv_key"; + constexpr char server_pub_key[] = "server_pub_key"; + constexpr char psk_key[] = "psk_key"; + constexpr char client_ip[] = "client_ip"; // internal ip address - Q_INVOKABLE static QString key_proto_config_data(Proto p); - Q_INVOKABLE static QString key_proto_config_path(Proto p); + constexpr char site[] = "site"; + constexpr char block_outside_dns[] = "block_outside_dns"; -}; + constexpr char subnet_address[] = "subnet_address"; + constexpr char subnet_mask[] = "subnet_mask"; + constexpr char subnet_cidr[] = "subnet_cidr"; -static void declareQmlProtocolEnum() { - qmlRegisterUncreatableMetaObject( - ProtocolEnumNS::staticMetaObject, - "ProtocolEnum", - 1, 0, - "ProtocolEnum", - "Error: only enums" - ); + constexpr char additional_client_config[] = "additional_client_config"; + constexpr char additional_server_config[] = "additional_server_config"; - qmlRegisterUncreatableMetaObject( - ProtocolEnumNS::staticMetaObject, - "ProtocolEnum", - 1, 0, - "TransportProto", - "Error: only enums" - ); + // proto config keys + constexpr char last_config[] = "last_config"; - qmlRegisterUncreatableMetaObject( - ProtocolEnumNS::staticMetaObject, - "ProtocolEnum", - 1, 0, - "ServiceType", - "Error: only enums" - ); -} + constexpr char isThirdPartyConfig[] = "isThirdPartyConfig"; + + constexpr char openvpn[] = "openvpn"; + constexpr char wireguard[] = "wireguard"; + constexpr char shadowsocks[] = "shadowsocks"; + constexpr char cloak[] = "cloak"; + + } + + namespace protocols + { + + namespace dns + { + constexpr char amneziaDnsIp[] = "172.29.172.254"; + } + + namespace openvpn + { + constexpr char defaultSubnetAddress[] = "10.8.0.0"; + constexpr char defaultSubnetMask[] = "255.255.255.0"; + constexpr char defaultSubnetCidr[] = "24"; + + constexpr char serverConfigPath[] = "/opt/amnezia/openvpn/server.conf"; + constexpr char caCertPath[] = "/opt/amnezia/openvpn/pki/ca.crt"; + constexpr char clientCertPath[] = "/opt/amnezia/openvpn/pki/issued"; + constexpr char taKeyPath[] = "/opt/amnezia/openvpn/ta.key"; + constexpr char clientsDirPath[] = "/opt/amnezia/openvpn/clients"; + constexpr char defaultPort[] = "1194"; + constexpr char defaultTransportProto[] = "udp"; + constexpr char defaultCipher[] = "AES-256-GCM"; + constexpr char defaultHash[] = "SHA512"; + constexpr bool defaultBlockOutsideDns = true; + constexpr bool defaultNcpDisable = false; + constexpr bool defaultTlsAuth = true; + constexpr char ncpDisableString[] = "ncp-disable"; + constexpr char tlsAuthString[] = "tls-auth /opt/amnezia/openvpn/ta.key 0"; + + constexpr char defaultAdditionalClientConfig[] = ""; + constexpr char defaultAdditionalServerConfig[] = ""; + } + + namespace shadowsocks + { + constexpr char ssKeyPath[] = "/opt/amnezia/shadowsocks/shadowsocks.key"; + constexpr char defaultPort[] = "6789"; + constexpr char defaultLocalProxyPort[] = "8585"; + constexpr char defaultCipher[] = "chacha20-ietf-poly1305"; + } + + namespace cloak + { + constexpr char ckPublicKeyPath[] = "/opt/amnezia/cloak/cloak_public.key"; + constexpr char ckBypassUidKeyPath[] = "/opt/amnezia/cloak/cloak_bypass_uid.key"; + constexpr char ckAdminKeyPath[] = "/opt/amnezia/cloak/cloak_admin_uid.key"; + constexpr char defaultPort[] = "443"; + constexpr char defaultRedirSite[] = "tile.openstreetmap.org"; + constexpr char defaultCipher[] = "chacha20-poly1305"; + + } + + namespace wireguard + { + constexpr char defaultSubnetAddress[] = "10.8.1.0"; + constexpr char defaultSubnetMask[] = "255.255.255.0"; + constexpr char defaultSubnetCidr[] = "24"; + + constexpr char defaultPort[] = "51820"; + constexpr char serverConfigPath[] = "/opt/amnezia/wireguard/wg0.conf"; + constexpr char serverPublicKeyPath[] = "/opt/amnezia/wireguard/wireguard_server_public_key.key"; + constexpr char serverPskKeyPath[] = "/opt/amnezia/wireguard/wireguard_psk.key"; + + } + + namespace sftp + { + constexpr char defaultUserName[] = "sftp_user"; + + } // namespace sftp + + } // namespace protocols + + namespace ProtocolEnumNS + { + Q_NAMESPACE + + enum TransportProto { + Udp, + Tcp + }; + Q_ENUM_NS(TransportProto) + + enum Proto { + Any = 0, + OpenVpn, + ShadowSocks, + Cloak, + WireGuard, + Ikev2, + L2tp, + + // non-vpn + TorWebSite, + Dns, + FileShare, + Sftp + }; + Q_ENUM_NS(Proto) + + enum ServiceType { + None = 0, + Vpn, + Other + }; + Q_ENUM_NS(ServiceType) + } // namespace ProtocolEnumNS + + using namespace ProtocolEnumNS; + + class ProtocolProps : public QObject + { + Q_OBJECT + + public: + Q_INVOKABLE static QList allProtocols(); + + // spelling may differ for various protocols - TCP for OpenVPN, tcp for others + Q_INVOKABLE static TransportProto transportProtoFromString(QString p); + Q_INVOKABLE static QString transportProtoToString(TransportProto proto, Proto p = Proto::Any); + + Q_INVOKABLE static Proto protoFromString(QString p); + Q_INVOKABLE static QString protoToString(Proto p); + + Q_INVOKABLE static QMap protocolHumanNames(); + Q_INVOKABLE static QMap protocolDescriptions(); + + Q_INVOKABLE static ServiceType protocolService(Proto p); + + Q_INVOKABLE static int defaultPort(Proto p); + Q_INVOKABLE static bool defaultPortChangeable(Proto p); + + Q_INVOKABLE static TransportProto defaultTransportProto(Proto p); + Q_INVOKABLE static bool defaultTransportProtoChangeable(Proto p); + + Q_INVOKABLE static QString key_proto_config_data(Proto p); + Q_INVOKABLE static QString key_proto_config_path(Proto p); + }; + + static void declareQmlProtocolEnum() + { + qmlRegisterUncreatableMetaObject(ProtocolEnumNS::staticMetaObject, "ProtocolEnum", 1, 0, "ProtocolEnum", + "Error: only enums"); + + qmlRegisterUncreatableMetaObject(ProtocolEnumNS::staticMetaObject, "ProtocolEnum", 1, 0, "TransportProto", + "Error: only enums"); + + qmlRegisterUncreatableMetaObject(ProtocolEnumNS::staticMetaObject, "ProtocolEnum", 1, 0, "ServiceType", + "Error: only enums"); + } } // namespace amnezia diff --git a/client/resources.qrc b/client/resources.qrc index b7bdd463..dd75555f 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -250,7 +250,6 @@ ui/qml/Pages2/PageDeinstalling.qml ui/qml/Controls2/BackButtonType.qml ui/qml/Pages2/PageSettingsServerProtocol.qml - ui/qml/Components/Protocols/OpenVpnSettings.qml ui/qml/Components/TransportProtoSelector.qml ui/qml/Controls2/ListViewType.qml images/controls/radio-button.svg @@ -271,5 +270,8 @@ ui/qml/Filters/ContainersModelFilters.qml ui/qml/Components/SelectLanguageDrawer.qml ui/qml/Controls2/BusyIndicatorType.qml + ui/qml/Pages2/PageProtocolOpenVpnSettings.qml + ui/qml/Pages2/PageProtocolShadowSocksSettings.qml + ui/qml/Pages2/PageProtocolCloakSettings.qml diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 1809e082..6fc5f4e9 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -69,7 +69,7 @@ void InstallController::installServer(DockerContainer container, QJsonObject &co m_serversModel->addServer(server); m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1); - emit installServerFinished(isInstalledContainerFound); + emit installServerFinished(false); // todo incorrect notification about found containers return; } @@ -108,7 +108,7 @@ void InstallController::installContainer(DockerContainer container, QJsonObject } } - emit installContainerFinished(isInstalledContainerFound); + emit installContainerFinished(false); // todo incorrect notification about found containers return; } @@ -162,6 +162,29 @@ void InstallController::scanServerForInstalledContainers() emit installationErrorOccurred(errorString(errorCode)); } +void InstallController::updateContainer(QJsonObject config) +{ + int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); + ServerCredentials serverCredentials = + qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + + const DockerContainer container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + auto modelIndex = m_containersModel->index(container); + QJsonObject oldContainerConfig = + qvariant_cast(m_containersModel->data(modelIndex, ContainersModel::Roles::ConfigRole)); + + ServerController serverController(m_settings); + + auto errorCode = serverController.updateContainer(serverCredentials, container, oldContainerConfig, config); + if (errorCode == ErrorCode::NoError) { + m_containersModel->setData(modelIndex, config, ContainersModel::Roles::ConfigRole); + emit updateContainerFinished(); + return; + } + + emit installationErrorOccurred(errorString(errorCode)); +} + QRegularExpression InstallController::ipAddressPortRegExp() { return Utils::ipAddressPortRegExp(); diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index 6b01e102..75f4fdef 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -24,12 +24,16 @@ public slots: void scanServerForInstalledContainers(); + void updateContainer(QJsonObject config); + QRegularExpression ipAddressPortRegExp(); signals: void installContainerFinished(bool isInstalledContainerFound); void installServerFinished(bool isInstalledContainerFound); + void updateContainerFinished(); + void scanServerFinished(bool isInstalledContainerFound); void installationErrorOccurred(QString errorMessage); diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index f67da275..468068b8 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -36,7 +36,13 @@ namespace PageLoader PageSetupWizardInstalling, PageSetupWizardConfigSource, PageSetupWizardTextKey, - PageSetupWizardViewConfig + PageSetupWizardViewConfig, + + PageProtocolOpenVpnSettings, + PageProtocolShadowSocksSettings, + PageProtocolCloakSettings, + PageProtocolWireGuardSettings, + PageProtocolIKev2Settings }; Q_ENUM_NS(PageEnum) diff --git a/client/ui/models/containers_model.cpp b/client/ui/models/containers_model.cpp index ecacc184..7b6ffaee 100644 --- a/client/ui/models/containers_model.cpp +++ b/client/ui/models/containers_model.cpp @@ -29,6 +29,11 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i case ConfigRole: { m_settings->setContainerConfig(m_currentlyProcessedServerIndex, container, value.toJsonObject()); m_containers = m_settings->containers(m_currentlyProcessedServerIndex); + if (m_defaultContainerIndex != DockerContainer::None) { + break; + } else if (ContainerProps::containerService(container) == ServiceType::Other) { + break; + } } case ServiceTypeRole: // return ContainerProps::containerService(container); @@ -108,6 +113,11 @@ int ContainersModel::getCurrentlyProcessedContainerIndex() return m_currentlyProcessedContainerIndex; } +QString ContainersModel::getCurrentlyProcessedContainerName() +{ + return ContainerProps::containerHumanNames().value(static_cast(m_currentlyProcessedContainerIndex)); +} + void ContainersModel::removeAllContainers() { @@ -116,14 +126,39 @@ void ContainersModel::removeAllContainers() if (errorCode == ErrorCode::NoError) { beginResetModel(); + m_settings->setContainers(m_currentlyProcessedServerIndex, {}); - m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, DockerContainer::None); + m_containers = m_settings->containers(m_currentlyProcessedServerIndex); + + setData(index(DockerContainer::None, 0), true, IsDefaultRole); endResetModel(); } // todo process errors } +void ContainersModel::removeCurrentlyProcessedContainer() +{ + ServerController serverController(m_settings); + auto credentials = m_settings->serverCredentials(m_currentlyProcessedServerIndex); + auto dockerContainer = static_cast(m_currentlyProcessedContainerIndex); + + ErrorCode e = serverController.removeContainer(credentials, dockerContainer); + + beginResetModel(); // todo change to begin remove rows? + m_settings->removeContainerConfig(m_currentlyProcessedServerIndex, dockerContainer); + m_containers = m_settings->containers(m_currentlyProcessedServerIndex); + + if (m_defaultContainerIndex == m_currentlyProcessedContainerIndex) { + if (m_containers.isEmpty()) { + setData(index(DockerContainer::None, 0), true, IsDefaultRole); + } else { + setData(index(m_containers.begin().key(), 0), true, IsDefaultRole); + } + } + endResetModel(); +} + void ContainersModel::clearCachedProfiles() { const auto &containers = m_settings->containers(m_currentlyProcessedServerIndex); @@ -150,6 +185,7 @@ QHash ContainersModel::roleNames() const roles[DescRole] = "description"; roles[ServiceTypeRole] = "serviceType"; roles[DockerContainerRole] = "dockerContainer"; + roles[ConfigRole] = "config"; roles[IsEasySetupContainerRole] = "isEasySetupContainer"; roles[EasySetupHeaderRole] = "easySetupHeader"; diff --git a/client/ui/models/containers_model.h b/client/ui/models/containers_model.h index 7331ef22..b79b058a 100644 --- a/client/ui/models/containers_model.h +++ b/client/ui/models/containers_model.h @@ -45,10 +45,14 @@ public slots: QString getDefaultContainerName(); void setCurrentlyProcessedServerIndex(const int index); + void setCurrentlyProcessedContainerIndex(int index); int getCurrentlyProcessedContainerIndex(); + QString getCurrentlyProcessedContainerName(); + void removeAllContainers(); + void removeCurrentlyProcessedContainer(); void clearCachedProfiles(); bool isAmneziaDnsContainerInstalled(); diff --git a/client/ui/models/languageModel.cpp b/client/ui/models/languageModel.cpp index e95b2ccf..76eeb37d 100644 --- a/client/ui/models/languageModel.cpp +++ b/client/ui/models/languageModel.cpp @@ -22,14 +22,8 @@ QVariant LanguageModel::data(const QModelIndex &index, int role) const } switch (role) { - case NameRole: { - return m_availableLanguages[index.row()].name; - break; - } - case IndexRole: { - return static_cast(m_availableLanguages[index.row()].index); - break; - } + case NameRole: return m_availableLanguages[index.row()].name; + case IndexRole: return static_cast(m_availableLanguages[index.row()].index); } return QVariant(); } diff --git a/client/ui/models/protocols/cloakConfigModel.cpp b/client/ui/models/protocols/cloakConfigModel.cpp new file mode 100644 index 00000000..203f08b5 --- /dev/null +++ b/client/ui/models/protocols/cloakConfigModel.cpp @@ -0,0 +1,81 @@ +#include "cloakConfigModel.h" + +#include "protocols/protocols_defs.h" + +CloakConfigModel::CloakConfigModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +int CloakConfigModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +bool CloakConfigModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) { + return false; + } + + switch (role) { + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break; + case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break; + } + + emit dataChanged(index, index, QList { role }); + return true; +} + +QVariant CloakConfigModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) { + return false; + } + + switch (role) { + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::cloak::defaultPort); + case Roles::CipherRole: return m_protocolConfig.value(config_key::cipher).toString(protocols::cloak::defaultCipher); + case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite); + } + + return QVariant(); +} + +void CloakConfigModel::updateModel(const QJsonObject &config) +{ + beginResetModel(); + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + QJsonObject protocolConfig = config.value(config_key::cloak).toObject(); + + m_protocolConfig.insert(config_key::cipher, + protocolConfig.value(config_key::cipher).toString(protocols::cloak::defaultCipher)); + + m_protocolConfig.insert(config_key::port, + protocolConfig.value(config_key::port).toString(protocols::cloak::defaultPort)); + + m_protocolConfig.insert(config_key::site, + protocolConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite)); + + endResetModel(); +} + +QJsonObject CloakConfigModel::getConfig() +{ + m_fullConfig.insert(config_key::cloak, m_protocolConfig); + return m_fullConfig; +} + +QHash CloakConfigModel::roleNames() const +{ + QHash roles; + + roles[PortRole] = "port"; + roles[CipherRole] = "cipher"; + roles[SiteRole] = "site"; + + return roles; +} diff --git a/client/ui/models/protocols/cloakConfigModel.h b/client/ui/models/protocols/cloakConfigModel.h new file mode 100644 index 00000000..31ff8c53 --- /dev/null +++ b/client/ui/models/protocols/cloakConfigModel.h @@ -0,0 +1,40 @@ +#ifndef CLOAKCONFIGMODEL_H +#define CLOAKCONFIGMODEL_H + +#include +#include + +#include "containers/containers_defs.h" + +class CloakConfigModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + PortRole = Qt::UserRole + 1, + CipherRole, + SiteRole + }; + + explicit CloakConfigModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + 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 roleNames() const override; + +private: + DockerContainer m_container; + QJsonObject m_protocolConfig; + QJsonObject m_fullConfig; +}; + +#endif // CLOAKCONFIGMODEL_H diff --git a/client/ui/models/protocols/ikev2ConfigModel.cpp b/client/ui/models/protocols/ikev2ConfigModel.cpp new file mode 100644 index 00000000..f22b965c --- /dev/null +++ b/client/ui/models/protocols/ikev2ConfigModel.cpp @@ -0,0 +1,76 @@ +#include "ikev2ConfigModel.h".h " + +#include "protocols/protocols_defs.h" + +Ikev2ConfigModel::Ikev2ConfigModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +int Ikev2ConfigModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +bool Ikev2ConfigModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) { + return false; + } + + switch (role) { + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break; + } + + emit dataChanged(index, index, QList { role }); + return true; +} + +QVariant Ikev2ConfigModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) { + return false; + } + + switch (role) { + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort); + case Roles::CipherRole: + return m_protocolConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher); + } + + return QVariant(); +} + +void Ikev2ConfigModel::updateModel(const QJsonObject &config) +{ + beginResetModel(); + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + QJsonObject protocolConfig = config.value(config_key::shadowsocks).toObject(); + + m_protocolConfig.insert(config_key::cipher, + protocolConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher)); + + m_protocolConfig.insert(config_key::port, + protocolConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort)); + + endResetModel(); +} + +QJsonObject Ikev2ConfigModel::getConfig() +{ + m_fullConfig.insert(config_key::shadowsocks, m_protocolConfig); + return m_fullConfig; +} + +QHash Ikev2ConfigModel::roleNames() const +{ + QHash roles; + + roles[PortRole] = "port"; + roles[CipherRole] = "cipher"; + + return roles; +} diff --git a/client/ui/models/protocols/ikev2ConfigModel.h b/client/ui/models/protocols/ikev2ConfigModel.h new file mode 100644 index 00000000..e005f6a4 --- /dev/null +++ b/client/ui/models/protocols/ikev2ConfigModel.h @@ -0,0 +1,39 @@ +#ifndef IKEV2CONFIGMODEL_H +#define IKEV2CONFIGMODEL_H + +#include +#include + +#include "containers/containers_defs.h" + +class Ikev2ConfigModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + PortRole = Qt::UserRole + 1, + CipherRole + }; + + explicit Ikev2ConfigModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + 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 roleNames() const override; + +private: + DockerContainer m_container; + QJsonObject m_protocolConfig; + QJsonObject m_fullConfig; +}; + +#endif // IKEV2CONFIGMODEL_H diff --git a/client/ui/models/protocols/openvpnConfigModel.cpp b/client/ui/models/protocols/openvpnConfigModel.cpp new file mode 100644 index 00000000..1b73c987 --- /dev/null +++ b/client/ui/models/protocols/openvpnConfigModel.cpp @@ -0,0 +1,152 @@ +#include "openvpnConfigModel.h" + +#include "protocols/protocols_defs.h" + +OpenVpnConfigModel::OpenVpnConfigModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +int OpenVpnConfigModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +bool OpenVpnConfigModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + 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; +} + +QVariant OpenVpnConfigModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) { + return false; + } + + 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()); // todo maybe unused + + m_fullConfig = config; + QJsonObject protocolConfig = config.value(config_key::openvpn).toObject(); + + m_protocolConfig.insert(config_key::subnet_address, + protocolConfig.value(amnezia::config_key::subnet_address) + .toString(amnezia::protocols::openvpn::defaultSubnetAddress)); + + QString transportProto; + if (m_container == DockerContainer::OpenVpn) { + transportProto = + protocolConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto); + } else { + transportProto = "tcp"; + } + + m_protocolConfig.insert(config_key::transport_proto, transportProto); + + m_protocolConfig.insert(config_key::ncp_disable, + protocolConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable)); + m_protocolConfig.insert(config_key::cipher, + protocolConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher)); + m_protocolConfig.insert(config_key::hash, + protocolConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash)); + m_protocolConfig.insert(config_key::block_outside_dns, + protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth)); + m_protocolConfig.insert(config_key::port, + protocolConfig.value(config_key::port).toString(protocols::openvpn::defaultPort)); + m_protocolConfig.insert( + config_key::tls_auth, + protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns)); + m_protocolConfig.insert(config_key::additional_client_config, + protocolConfig.value(config_key::additional_client_config) + .toString(protocols::openvpn::defaultAdditionalClientConfig)); + m_protocolConfig.insert(config_key::additional_server_config, + protocolConfig.value(config_key::additional_server_config) + .toString(protocols::openvpn::defaultAdditionalServerConfig)); + + endResetModel(); +} + +QJsonObject OpenVpnConfigModel::getConfig() +{ + m_fullConfig.insert(config_key::openvpn, m_protocolConfig); + return m_fullConfig; +} + +QHash OpenVpnConfigModel::roleNames() const +{ + QHash 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; +} diff --git a/client/ui/models/protocols/openvpnConfigModel.h b/client/ui/models/protocols/openvpnConfigModel.h new file mode 100644 index 00000000..0357700c --- /dev/null +++ b/client/ui/models/protocols/openvpnConfigModel.h @@ -0,0 +1,52 @@ +#ifndef OPENVPNCONFIGMODEL_H +#define OPENVPNCONFIGMODEL_H + +#include +#include + +#include "containers/containers_defs.h" + +class OpenVpnConfigModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + SubnetAddressRole = Qt::UserRole + 1, + TransportProtoRole, + PortRole, + AutoNegotiateEncryprionRole, + HashRole, + CipherRole, + TlsAuthRole, + BlockDnsRole, + AdditionalClientCommandsRole, + AdditionalServerCommandsRole, + + IsPortEditable, + IsTransportProtoEditable, + + HasRemoveButton + }; + + explicit OpenVpnConfigModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + 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 roleNames() const override; + +private: + DockerContainer m_container; + QJsonObject m_protocolConfig; + QJsonObject m_fullConfig; +}; + +#endif // OPENVPNCONFIGMODEL_H diff --git a/client/ui/models/protocols/shadowsocksConfigModel.cpp b/client/ui/models/protocols/shadowsocksConfigModel.cpp new file mode 100644 index 00000000..60c8feee --- /dev/null +++ b/client/ui/models/protocols/shadowsocksConfigModel.cpp @@ -0,0 +1,76 @@ +#include "shadowsocksConfigModel.h" + +#include "protocols/protocols_defs.h" + +ShadowSocksConfigModel::ShadowSocksConfigModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +int ShadowSocksConfigModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +bool ShadowSocksConfigModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) { + return false; + } + + switch (role) { + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break; + } + + emit dataChanged(index, index, QList { role }); + return true; +} + +QVariant ShadowSocksConfigModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) { + return false; + } + + switch (role) { + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort); + case Roles::CipherRole: + return m_protocolConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher); + } + + return QVariant(); +} + +void ShadowSocksConfigModel::updateModel(const QJsonObject &config) +{ + beginResetModel(); + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + QJsonObject protocolConfig = config.value(config_key::shadowsocks).toObject(); + + m_protocolConfig.insert(config_key::cipher, + protocolConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher)); + + m_protocolConfig.insert(config_key::port, + protocolConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort)); + + endResetModel(); +} + +QJsonObject ShadowSocksConfigModel::getConfig() +{ + m_fullConfig.insert(config_key::shadowsocks, m_protocolConfig); + return m_fullConfig; +} + +QHash ShadowSocksConfigModel::roleNames() const +{ + QHash roles; + + roles[PortRole] = "port"; + roles[CipherRole] = "cipher"; + + return roles; +} diff --git a/client/ui/models/protocols/shadowsocksConfigModel.h b/client/ui/models/protocols/shadowsocksConfigModel.h new file mode 100644 index 00000000..d8fa036b --- /dev/null +++ b/client/ui/models/protocols/shadowsocksConfigModel.h @@ -0,0 +1,39 @@ +#ifndef SHADOWSOCKSCONFIGMODEL_H +#define SHADOWSOCKSCONFIGMODEL_H + +#include +#include + +#include "containers/containers_defs.h" + +class ShadowSocksConfigModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + PortRole = Qt::UserRole + 1, + CipherRole + }; + + explicit ShadowSocksConfigModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + 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 roleNames() const override; + +private: + DockerContainer m_container; + QJsonObject m_protocolConfig; + QJsonObject m_fullConfig; +}; + +#endif // SHADOWSOCKSCONFIGMODEL_H diff --git a/client/ui/models/protocols/wireguardConfigModel.cpp b/client/ui/models/protocols/wireguardConfigModel.cpp new file mode 100644 index 00000000..15e89865 --- /dev/null +++ b/client/ui/models/protocols/wireguardConfigModel.cpp @@ -0,0 +1,70 @@ +#include "wireguardConfigModel.h" + +#include "protocols/protocols_defs.h" + +WireGuardConfigModel::WireGuardConfigModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +int WireGuardConfigModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +bool WireGuardConfigModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) { + return false; + } + + switch (role) { + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break; + } + + emit dataChanged(index, index, QList { role }); + return true; +} + +QVariant WireGuardConfigModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) { + return false; + } + + switch (role) { + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort); + case Roles::CipherRole: + return m_protocolConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher); + } + + return QVariant(); +} + +void WireGuardConfigModel::updateModel(const QJsonObject &config) +{ + beginResetModel(); + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + QJsonObject protocolConfig = config.value(config_key::wireguard).toObject(); + + endResetModel(); +} + +QJsonObject WireGuardConfigModel::getConfig() +{ + m_fullConfig.insert(config_key::wireguard, m_protocolConfig); + return m_fullConfig; +} + +QHash WireGuardConfigModel::roleNames() const +{ + QHash roles; + + roles[PortRole] = "port"; + roles[CipherRole] = "cipher"; + + return roles; +} diff --git a/client/ui/models/protocols/wireguardConfigModel.h b/client/ui/models/protocols/wireguardConfigModel.h new file mode 100644 index 00000000..1deeacaf --- /dev/null +++ b/client/ui/models/protocols/wireguardConfigModel.h @@ -0,0 +1,39 @@ +#ifndef WIREGUARDCONFIGMODEL_H +#define WIREGUARDCONFIGMODEL_H + +#include +#include + +#include "containers/containers_defs.h" + +class WireGuardConfigModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + PortRole = Qt::UserRole + 1, + CipherRole + }; + + explicit WireGuardConfigModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + 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 roleNames() const override; + +private: + DockerContainer m_container; + QJsonObject m_protocolConfig; + QJsonObject m_fullConfig; +}; + +#endif // WIREGUARDCONFIGMODEL_H diff --git a/client/ui/models/protocols_model.cpp b/client/ui/models/protocols_model.cpp index 7359bb36..ac271eb9 100644 --- a/client/ui/models/protocols_model.cpp +++ b/client/ui/models/protocols_model.cpp @@ -1,62 +1,73 @@ #include "protocols_model.h" -ProtocolsModel::ProtocolsModel(std::shared_ptr settings, QObject *parent) : - m_settings(settings), - QAbstractListModel(parent) +ProtocolsModel::ProtocolsModel(std::shared_ptr settings, QObject *parent) + : m_settings(settings), QAbstractListModel(parent) { - } int ProtocolsModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return ProtocolProps::allProtocols().size(); + return m_content.size(); } -QHash ProtocolsModel::roleNames() const { +QHash ProtocolsModel::roleNames() const +{ QHash roles; - roles[NameRole] = "name_role"; - roles[DescRole] = "desc_role"; - roles[ServiceTypeRole] = "service_type_role"; - roles[IsInstalledRole] = "is_installed_role"; + + roles[ProtocolNameRole] = "protocolName"; + roles[ProtocolPageRole] = "protocolPage"; + return roles; } QVariant ProtocolsModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() < 0 - || index.row() >= ProtocolProps::allProtocols().size()) { + if (!index.isValid() || index.row() < 0 || index.row() >= m_content.size()) { return QVariant(); } - Proto p = ProtocolProps::allProtocols().at(index.row()); - if (role == NameRole) { - return ProtocolProps::protocolHumanNames().value(p); + switch (role) { + case ProtocolNameRole: { + amnezia::Proto proto = ProtocolProps::protoFromString(m_content.keys().at(index.row())); + return ProtocolProps::protocolHumanNames().value(proto); } - if (role == DescRole) { - return ProtocolProps::protocolDescriptions().value(p); - } - if (role == ServiceTypeRole) { - return ProtocolProps::protocolService(p); - } - if (role == IsInstalledRole) { - return ContainerProps::protocolsForContainer(m_selectedDockerContainer).contains(p); + case ProtocolPageRole: + return static_cast(protocolPage(ProtocolProps::protoFromString(m_content.keys().at(index.row())))); } + return QVariant(); } -void ProtocolsModel::setSelectedServerIndex(int index) +void ProtocolsModel::updateModel(const QJsonObject &content) { - beginResetModel(); - m_selectedServerIndex = index; - endResetModel(); + m_container = ContainerProps::containerFromString(content.value(config_key::container).toString()); + + m_content = content; + m_content.remove(config_key::container); } -void ProtocolsModel::setSelectedDockerContainer(DockerContainer c) +QJsonObject ProtocolsModel::getConfig() { - beginResetModel(); - m_selectedDockerContainer = c; - endResetModel(); + QJsonObject config = m_content; + config.insert(config_key::container, ContainerProps::containerToString(m_container)); + return config; } - +PageLoader::PageEnum ProtocolsModel::protocolPage(Proto protocol) const +{ + switch (protocol) { + case Proto::OpenVpn: return PageLoader::PageEnum::PageProtocolOpenVpnSettings; + case Proto::Cloak: return PageLoader::PageEnum::PageProtocolCloakSettings; + case Proto::ShadowSocks: return PageLoader::PageEnum::PageProtocolShadowSocksSettings; + case Proto::WireGuard: return PageLoader::PageEnum::PageProtocolWireGuardSettings; + case Proto::Ikev2: return PageLoader::PageEnum::PageProtocolIKev2Settings; + case Proto::L2tp: return PageLoader::PageEnum::PageProtocolOpenVpnSettings; + // non-vpn + case Proto::TorWebSite: return PageLoader::PageEnum::PageProtocolOpenVpnSettings; + case Proto::Dns: return PageLoader::PageEnum::PageProtocolOpenVpnSettings; + case Proto::FileShare: return PageLoader::PageEnum::PageProtocolOpenVpnSettings; + case Proto::Sftp: return PageLoader::PageEnum::PageProtocolOpenVpnSettings; + default: return PageLoader::PageEnum::PageProtocolOpenVpnSettings; + } +} diff --git a/client/ui/models/protocols_model.h b/client/ui/models/protocols_model.h index 48b6eeb6..1e279cfb 100644 --- a/client/ui/models/protocols_model.h +++ b/client/ui/models/protocols_model.h @@ -3,38 +3,40 @@ #include #include -#include -#include +#include "../controllers/pageController.h" #include "settings.h" -#include "containers/containers_defs.h" class ProtocolsModel : public QAbstractListModel { Q_OBJECT public: - ProtocolsModel(std::shared_ptr settings, QObject *parent = nullptr); -public: - enum SiteRoles { - NameRole = Qt::UserRole + 1, - DescRole, - ServiceTypeRole, - IsInstalledRole + enum Roles { + ProtocolNameRole = Qt::UserRole + 1, + ProtocolPageRole }; + ProtocolsModel(std::shared_ptr settings, QObject *parent = nullptr); + int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - Q_INVOKABLE void setSelectedServerIndex(int index); - Q_INVOKABLE void setSelectedDockerContainer(DockerContainer c); + +public slots: + void updateModel(const QJsonObject &content); + + QJsonObject getConfig(); protected: QHash roleNames() const override; private: - int m_selectedServerIndex; - DockerContainer m_selectedDockerContainer; + PageLoader::PageEnum protocolPage(Proto protocol) const; + std::shared_ptr m_settings; + + DockerContainer m_container; + QJsonObject m_content; }; #endif // PROTOCOLS_MODEL_H diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 1bacdd45..6fad9af6 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -123,6 +123,7 @@ bool ServersModel::isDefaultServerHasWriteAccess() void ServersModel::addServer(const QJsonObject &server) { + // todo beginInsertRows()? beginResetModel(); m_settings->addServer(server); m_servers = m_settings->serversArray(); diff --git a/client/ui/pages_logic/GeneralSettingsLogic.cpp b/client/ui/pages_logic/GeneralSettingsLogic.cpp index 0e92f8c9..141308f4 100644 --- a/client/ui/pages_logic/GeneralSettingsLogic.cpp +++ b/client/ui/pages_logic/GeneralSettingsLogic.cpp @@ -1,13 +1,11 @@ #include "GeneralSettingsLogic.h" #include "ShareConnectionLogic.h" -#include "../uilogic.h" #include "../models/protocols_model.h" +#include "../uilogic.h" -GeneralSettingsLogic::GeneralSettingsLogic(UiLogic *logic, QObject *parent): - PageLogicBase(logic, parent) +GeneralSettingsLogic::GeneralSettingsLogic(UiLogic *logic, QObject *parent) : PageLogicBase(logic, parent) { - } void GeneralSettingsLogic::onUpdatePage() @@ -32,9 +30,11 @@ void GeneralSettingsLogic::onPushButtonGeneralSettingsShareConnectionClicked() uiLogic()->m_selectedServerIndex = m_settings->defaultServerIndex(); uiLogic()->m_selectedDockerContainer = m_settings->defaultContainer(uiLogic()->m_selectedServerIndex); - qobject_cast(uiLogic()->protocolsModel())->setSelectedServerIndex(uiLogic()->m_selectedServerIndex); - qobject_cast(uiLogic()->protocolsModel())->setSelectedDockerContainer(uiLogic()->m_selectedDockerContainer); + // qobject_cast(uiLogic()->protocolsModel())->setSelectedServerIndex(uiLogic()->m_selectedServerIndex); qobject_cast(uiLogic()->protocolsModel())->setSelectedDockerContainer(uiLogic()->m_selectedDockerContainer); - uiLogic()->pageLogic()->updateSharingPage(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer); + uiLogic()->pageLogic()->updateSharingPage(uiLogic()->m_selectedServerIndex, + uiLogic()->m_selectedDockerContainer); emit uiLogic()->goToPage(Page::ShareConnection); } diff --git a/client/ui/pages_logic/ServerContainersLogic.cpp b/client/ui/pages_logic/ServerContainersLogic.cpp index 2b556dcc..a2971cf8 100644 --- a/client/ui/pages_logic/ServerContainersLogic.cpp +++ b/client/ui/pages_logic/ServerContainersLogic.cpp @@ -1,6 +1,6 @@ #include "ServerContainersLogic.h" -#include "ShareConnectionLogic.h" #include "ServerConfiguringProgressLogic.h" +#include "ShareConnectionLogic.h" #include @@ -9,24 +9,22 @@ #include "core/servercontroller.h" #include -#include "../uilogic.h" #include "../pages_logic/VpnLogic.h" -#include "vpnconnection.h" +#include "../uilogic.h" #include "core/errorstrings.h" +#include "vpnconnection.h" - -ServerContainersLogic::ServerContainersLogic(UiLogic *logic, QObject *parent): - PageLogicBase(logic, parent) +ServerContainersLogic::ServerContainersLogic(UiLogic *logic, QObject *parent) : PageLogicBase(logic, parent) { } void ServerContainersLogic::onUpdatePage() { -// ContainersModel *c_model = qobject_cast(uiLogic()->containersModel()); -// c_model->setSelectedServerIndex(uiLogic()->m_selectedServerIndex); + // ContainersModel *c_model = qobject_cast(uiLogic()->containersModel()); + // c_model->setSelectedServerIndex(uiLogic()->m_selectedServerIndex); ProtocolsModel *p_model = qobject_cast(uiLogic()->protocolsModel()); - p_model->setSelectedServerIndex(uiLogic()->m_selectedServerIndex); + // p_model->setSelectedServerIndex(uiLogic()->m_selectedServerIndex); set_isManagedServer(m_settings->haveAuthData(uiLogic()->m_selectedServerIndex)); uiLogic()->m_installCredentials = m_settings->serverCredentials(uiLogic()->m_selectedServerIndex); @@ -35,25 +33,29 @@ void ServerContainersLogic::onUpdatePage() void ServerContainersLogic::onPushButtonProtoSettingsClicked(DockerContainer c, Proto p) { - qDebug()<< "ServerContainersLogic::onPushButtonProtoSettingsClicked" << c << p; + qDebug() << "ServerContainersLogic::onPushButtonProtoSettingsClicked" << c << p; uiLogic()->m_selectedDockerContainer = c; - uiLogic()->protocolLogic(p)->updateProtocolPage(m_settings->protocolConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, p), - uiLogic()->m_selectedDockerContainer, - m_settings->haveAuthData(uiLogic()->m_selectedServerIndex)); + uiLogic()->protocolLogic(p)->updateProtocolPage( + m_settings->protocolConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, p), + uiLogic()->m_selectedDockerContainer, m_settings->haveAuthData(uiLogic()->m_selectedServerIndex)); emit uiLogic()->goToProtocolPage(p); } void ServerContainersLogic::onPushButtonDefaultClicked(DockerContainer c) { - if (m_settings->defaultContainer(uiLogic()->m_selectedServerIndex) == c) return; + if (m_settings->defaultContainer(uiLogic()->m_selectedServerIndex) == c) + return; m_settings->setDefaultContainer(uiLogic()->m_selectedServerIndex, c); uiLogic()->onUpdateAllPages(); - if (uiLogic()->m_selectedServerIndex != m_settings->defaultServerIndex()) return; - if (!uiLogic()->m_vpnConnection) return; - if (!uiLogic()->m_vpnConnection->isConnected()) return; + if (uiLogic()->m_selectedServerIndex != m_settings->defaultServerIndex()) + return; + if (!uiLogic()->m_vpnConnection) + return; + if (!uiLogic()->m_vpnConnection->isConnected()) + return; uiLogic()->pageLogic()->onDisconnect(); uiLogic()->pageLogic()->onConnect(); @@ -67,16 +69,19 @@ void ServerContainersLogic::onPushButtonShareClicked(DockerContainer c) void ServerContainersLogic::onPushButtonRemoveClicked(DockerContainer container) { - //buttonSetEnabledFunc(false); + // buttonSetEnabledFunc(false); ServerController serverController(m_settings); - ErrorCode e = serverController.removeContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), container); + ErrorCode e = + serverController.removeContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), container); m_settings->removeContainerConfig(uiLogic()->m_selectedServerIndex, container); - //buttonSetEnabledFunc(true); + // buttonSetEnabledFunc(true); if (m_settings->defaultContainer(uiLogic()->m_selectedServerIndex) == container) { const auto &c = m_settings->containers(uiLogic()->m_selectedServerIndex); - if (c.isEmpty()) m_settings->setDefaultContainer(uiLogic()->m_selectedServerIndex, DockerContainer::None); - else m_settings->setDefaultContainer(uiLogic()->m_selectedServerIndex, c.keys().first()); + if (c.isEmpty()) + m_settings->setDefaultContainer(uiLogic()->m_selectedServerIndex, DockerContainer::None); + else + m_settings->setDefaultContainer(uiLogic()->m_selectedServerIndex, c.keys().first()); } uiLogic()->onUpdateAllPages(); } @@ -96,7 +101,8 @@ void ServerContainersLogic::onPushButtonContinueClicked(DockerContainer c, int p if (!uiLogic()->isContainerAlreadyAddedToGui(c)) { auto installAction = [this, c, &config]() { ServerController serverController(m_settings); - return serverController.setupContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), c, config); + return serverController.setupContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), + c, config); }; errorCode = uiLogic()->pageLogic()->doInstallAction(installAction); @@ -107,16 +113,16 @@ void ServerContainersLogic::onPushButtonContinueClicked(DockerContainer c, int p } } } else { - emit uiLogic()->showWarningMessage("Attention! The container you are trying to install is already installed on the server. " - "All installed containers have been added to the application "); + emit uiLogic()->showWarningMessage( + "Attention! The container you are trying to install is already installed on the server. " + "All installed containers have been added to the application "); } uiLogic()->onUpdateAllPages(); } if (errorCode != ErrorCode::NoError) { - emit uiLogic()->showWarningMessage(tr("Error occurred while configuring server.") + "\n" + - tr("Error message: ") + errorString(errorCode) + "\n" + - tr("See logs for details.")); + emit uiLogic()->showWarningMessage(tr("Error occurred while configuring server.") + "\n" + tr("Error message: ") + + errorString(errorCode) + "\n" + tr("See logs for details.")); } emit uiLogic()->closePage(); } diff --git a/client/ui/qml/Components/Protocols/OpenVpnSettings.qml b/client/ui/qml/Components/Protocols/OpenVpnSettings.qml deleted file mode 100644 index f937dcfc..00000000 --- a/client/ui/qml/Components/Protocols/OpenVpnSettings.qml +++ /dev/null @@ -1,147 +0,0 @@ -import QtQuick 2.15 -import QtQuick.Controls -import QtQuick.Layouts - -import "../../Controls2" -import "../../Controls2/TextTypes" -import "../../Components" - -Item { - id: root - implicitHeight: col.implicitHeight - implicitWidth: col.implicitWidth - - ColumnLayout { - id: col - - anchors.fill: parent - - anchors.leftMargin: 16 - anchors.rightMargin: 16 - - spacing: 16 - - Header2TextType { - Layout.fillWidth: true - - text: "OpenVpn" - } - - TextFieldWithHeaderType { - Layout.fillWidth: true - - headerText: qsTr("VPN Addresses Subnet") - } - - ParagraphTextType { - Layout.fillWidth: true - - text: qsTr("Network protocol") - } - - TransportProtoSelector { - Layout.fillWidth: true - } - - TextFieldWithHeaderType { - Layout.fillWidth: true - - headerText: qsTr("Port") - } - - SwitcherType { - Layout.fillWidth: true - text: qsTr("Auto-negotiate encryption") - } - - DropDownType { - id: hash - Layout.fillWidth: true - implicitHeight: 74 - - descriptionText: qsTr("Hash") - headerText: qsTr("Hash") - - listView: ListViewType { - 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") } - } - currentIndex: 0 - - clickedFunction: { - hash.text = selectedText - hash.menuVisible = false - } - - Component.onCompleted: { - hash.text = selectedText - } - } - } - - DropDownType { - id: cipher - Layout.fillWidth: true - implicitHeight: 74 - - descriptionText: qsTr("Cipher") - headerText: qsTr("Cipher") - - listView: ListViewType { - 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") } - } - currentIndex: 0 - - clickedFunction: { - cipher.text = selectedText - cipher.menuVisible = false - } - - Component.onCompleted: { - cipher.text = selectedText - } - } - } - - CheckBoxType { - Layout.fillWidth: true - - text: qsTr("TLS auth") - } - - CheckBoxType { - Layout.fillWidth: true - - text: qsTr("Block DNS requests outside of VPN") - } - - SwitcherType { - Layout.fillWidth: true - - text: qsTr("Additional configuration commands") - } - } -} diff --git a/client/ui/qml/Components/SettingsContainersListView.qml b/client/ui/qml/Components/SettingsContainersListView.qml index 1b8ea40c..7204ac16 100644 --- a/client/ui/qml/Components/SettingsContainersListView.qml +++ b/client/ui/qml/Components/SettingsContainersListView.qml @@ -6,6 +6,7 @@ import SortFilterProxyModel 0.2 import PageEnum 1.0 import ProtocolEnum 1.0 +import ContainerEnum 1.0 import "../Controls2" import "../Controls2/TextTypes" @@ -88,8 +89,32 @@ ListView { onClicked: { if (isInstalled) { - ContainersModel.setCurrentlyProcessedContainerIndex(root.model.mapToSource(index)) - goToPage(PageEnum.PageSettingsServerProtocol) + var containerIndex = root.model.mapToSource(index) + ContainersModel.setCurrentlyProcessedContainerIndex(containerIndex) + switch (containerIndex) { + case ContainerEnum.OpenVpn: { + OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()) + goToPage(PageEnum.PageProtocolOpenVpnSettings) + break + } + case ContainerEnum.WireGuard: { + WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()) + goToPage(PageEnum.PageProtocolWireGuardSettings) + break + } + case ContainerEnum.Ipsec: { + Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()) + goToPage(PageEnum.PageProtocolIKev2Settings) + break + } + default: { + if (serviceType !== ProtocolEnum.Other) { //todo disable settings for dns container + ProtocolsModel.updateModel(config) + goToPage(PageEnum.PageSettingsServerProtocol) + } + } + } + } else { ContainersModel.setCurrentlyProcessedContainerIndex(root.model.mapToSource(index)) InstallController.setShouldCreateServer(false) diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index d5ed1029..275e41ea 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -75,9 +75,9 @@ DrawerType { text: qsTr("Copy") onClicked: { - configContent.selectAll() - configContent.copy() - configContent.select(0, 0) + configText.selectAll() + configText.copy() + configText.select(0, 0) } } @@ -138,6 +138,8 @@ DrawerType { } TextArea { + id: configText + Layout.fillWidth: true Layout.topMargin: 16 Layout.bottomMargin: 16 diff --git a/client/ui/qml/Components/TransportProtoSelector.qml b/client/ui/qml/Components/TransportProtoSelector.qml index dd315deb..bfd82cb1 100644 --- a/client/ui/qml/Components/TransportProtoSelector.qml +++ b/client/ui/qml/Components/TransportProtoSelector.qml @@ -25,6 +25,8 @@ Rectangle { HorizontalRadioButton { checked: root.currentIndex === 0 + hoverEnabled: root.enabled + implicitWidth: (rootWidth - 32) / 2 text: "UDP" @@ -36,6 +38,8 @@ Rectangle { HorizontalRadioButton { checked: root.currentIndex === 1 + hoverEnabled: root.enabled + implicitWidth: (rootWidth - 32) / 2 text: "TCP" diff --git a/client/ui/qml/Controls2/CheckBoxType.qml b/client/ui/qml/Controls2/CheckBoxType.qml index 1a22f326..6ce5ac4e 100644 --- a/client/ui/qml/Controls2/CheckBoxType.qml +++ b/client/ui/qml/Controls2/CheckBoxType.qml @@ -3,6 +3,8 @@ import QtQuick.Controls import QtQuick.Layouts import Qt5Compat.GraphicalEffects +import "TextTypes" + CheckBox { id: root @@ -26,6 +28,8 @@ CheckBox { indicator: Rectangle { id: checkBoxBackground + anchors.verticalCenter: parent.verticalCenter + implicitWidth: 56 implicitHeight: 56 radius: 16 @@ -57,43 +61,41 @@ CheckBox { anchors.centerIn: parent source: root.pressed ? imageSource : root.checked ? imageSource : "" - - ColorOverlay { - id: imageColor - anchors.fill: indicator - source: indicator - - color: root.pressed ? pressedImageColor : root.checked ? checkedImageColor : defaultImageColor + layer { + enabled: true + effect: ColorOverlay { + color: root.pressed ? pressedImageColor : root.checked ? checkedImageColor : defaultImageColor + } } } } } contentItem: ColumnLayout { - anchors.fill: parent + anchors.left: parent.left + anchors.right: parent.right anchors.leftMargin: 8 + checkBoxBackground.width - Text { - text: root.text - color: "#D7D8DB" - font.pixelSize: 18 - font.weight: 400 - font.family: "PT Root UI VF" + spacing: 4 - height: 22 + ListItemTitleType { Layout.fillWidth: true +// Layout.topMargin: 16 +// Layout.bottomMargin: description.visible ? 0 : 16 + + text: root.text } - Text { + CaptionTextType { + id: description + + Layout.fillWidth: true + Layout.bottomMargin: 16 + text: root.descriptionText color: "#878b91" - font.pixelSize: 13 - font.weight: 400 - font.family: "PT Root UI VF" - font.letterSpacing: 0.02 - height: 16 - Layout.fillWidth: true + visible: root.descriptionText !== "" } } diff --git a/client/ui/qml/Controls2/DropDownType.qml b/client/ui/qml/Controls2/DropDownType.qml index 85989ae6..7a4538ba 100644 --- a/client/ui/qml/Controls2/DropDownType.qml +++ b/client/ui/qml/Controls2/DropDownType.qml @@ -9,8 +9,11 @@ Item { property string text property string textColor: "#d7d8db" + property string textDisabledColor: "#878B91" property string descriptionText + property string descriptionTextColor: "#878B91" + property string descriptionTextDisabledColor: "#494B50" property string headerText property string headerBackButtonImage @@ -23,7 +26,6 @@ Item { property string rootButtonHoveredBorderColor: "#494B50" property string rootButtonDefaultBorderColor: "transparent" property string rootButtonPressedBorderColor: "#D7D8DB" - property int rootButtonBorderWidth: 1 property real drawerHeight: 0.9 property Component listView @@ -36,10 +38,18 @@ Item { onMenuVisibleChanged: { if (menuVisible) { rootButtonBackground.border.color = rootButtonPressedBorderColor - rootButtonBackground.border.width = rootButtonBorderWidth } else { rootButtonBackground.border.color = rootButtonDefaultBorderColor - rootButtonBackground.border.width = 0 + } + } + + onEnabledChanged: { + if (enabled) { + rootButtonBackground.color = rootButtonBackgroundColor + rootButtonBackground.border.color = rootButtonDefaultBorderColor + } else { + rootButtonBackground.color = "transparent" + rootButtonBackground.border.color = rootButtonHoveredBorderColor } } @@ -48,13 +58,10 @@ Item { anchors.fill: rootButtonContent radius: 16 - color: rootButtonBackgroundColor - border.color: rootButtonDefaultBorderColor - border.width: 0 + color: root.enabled ? rootButtonBackgroundColor : "transparent" + border.color: root.enabled ? rootButtonDefaultBorderColor : rootButtonHoveredBorderColor + border.width: 1 - Behavior on border.width { - PropertyAnimation { duration: 200 } - } Behavior on border.color { PropertyAnimation { duration: 200 } } @@ -77,7 +84,7 @@ Item { visible: root.descriptionText !== "" - color: "#878B91" + color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor text: root.descriptionText } @@ -87,7 +94,7 @@ Item { horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter - color: root.textColor + color: root.enabled ? root.textColor : root.textDisabledColor text: root.text wrapMode: Text.NoWrap @@ -108,18 +115,16 @@ Item { MouseArea { anchors.fill: rootButtonContent cursorShape: Qt.PointingHandCursor - hoverEnabled: true + hoverEnabled: root.enabled ? true : false onEntered: { if (menu.visible === false) { - rootButtonBackground.border.width = rootButtonBorderWidth rootButtonBackground.border.color = rootButtonHoveredBorderColor } } onExited: { if (menu.visible === false) { - rootButtonBackground.border.width = 0 rootButtonBackground.border.color = rootButtonDefaultBorderColor } } diff --git a/client/ui/qml/Controls2/HorizontalRadioButton.qml b/client/ui/qml/Controls2/HorizontalRadioButton.qml index 4a2a13dd..88fc2531 100644 --- a/client/ui/qml/Controls2/HorizontalRadioButton.qml +++ b/client/ui/qml/Controls2/HorizontalRadioButton.qml @@ -22,45 +22,44 @@ RadioButton { implicitWidth: content.implicitWidth implicitHeight: content.implicitHeight - hoverEnabled: true - indicator: Rectangle { anchors.fill: parent radius: 16 color: { - if (root.enabled) { +// if (root.enabled) { if (root.hovered) { return hoveredColor } else if (root.checked) { return selectedColor } return defaultColor - } else { - return disabledColor - } +// } else { +// return disabledColor +// } } border.color: { - if (root.enabled) { +// if (root.enabled) { if (root.pressed) { return pressedBorderColor } else if (root.checked) { return selectedBorderColor } - } - return defaultBodredColor + return defaultBodredColor +// } +// return defaultBodredColor } border.width: { - if (root.enabled) { +// if (root.enabled) { if(root.checked) { return 1 } return root.pressed ? 1 : 0 - } else { - return 0 - } +// } else { +// return 0 +// } } Behavior on color { diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index 85d651ef..13a7ea6e 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -8,6 +8,9 @@ Item { id: root property string headerText + property string headerTextDisabledColor: "#494B50" + property string headerTextColor: "#878b91" + property alias errorText: errorField.text property string buttonText @@ -15,9 +18,18 @@ Item { property alias textField: textField property alias textFieldText: textField.text + property string textFieldTextColor: "#d7d8db" + property string textFieldTextDisabledColor: "#878B91" + property string textFieldPlaceholderText property bool textFieldEditable: true + property string borderColor: "#2C2D30" + property string borderFocusedColor: "#d7d8db" + + property string backgroundColor: "#1c1d21" + property string backgroundDisabledColor: "transparent" + implicitWidth: content.implicitWidth implicitHeight: content.implicitHeight @@ -29,9 +41,9 @@ Item { id: backgroud Layout.fillWidth: true Layout.preferredHeight: 74 - color: "#1c1d21" + color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor radius: 16 - border.color: textField.focus ? "#d7d8db" : "#2C2D30" + border.color: textField.focus ? root.borderFocusedColor : root.borderColor border.width: 1 Behavior on border.color { @@ -43,7 +55,7 @@ Item { ColumnLayout { LabelTextType { text: root.headerText - color: "#878b91" + color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor Layout.fillWidth: true Layout.rightMargin: 16 @@ -55,9 +67,9 @@ Item { id: textField enabled: root.textFieldEditable - color: "#d7d8db" + color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor - placeholderText: textFieldPlaceholderText + placeholderText: root.textFieldPlaceholderText placeholderTextColor: "#494B50" selectionColor: "#412102" @@ -79,7 +91,7 @@ Item { background: Rectangle { anchors.fill: parent - color: "#1c1d21" + color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor } onTextChanged: { @@ -98,13 +110,13 @@ Item { textColor: "#D7D8DB" borderWidth: 0 - text: buttonText + text: root.buttonText Layout.rightMargin: 24 onClicked: { - if (clickedFunc && typeof clickedFunc === "function") { - clickedFunc() + if (root.clickedFunc && typeof root.clickedFunc === "function") { + root.clickedFunc() } } } diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index b97acad1..ab1eeba1 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -158,7 +158,6 @@ PageType { implicitHeight: 40 - rootButtonBorderWidth: 0 rootButtonImageColor: "#0E0E11" rootButtonBackgroundColor: "#D7D8DB" diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml new file mode 100644 index 00000000..9ee67303 --- /dev/null +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -0,0 +1,176 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + //todo move to main? + Connections { + target: InstallController + + function onInstallationErrorOccurred(errorMessage) { + PageController.showErrorMessage(errorMessage) + } + + function onUpdateContainerFinished() { + //todo change to notification + PageController.showErrorMessage(qsTr("Settings updated successfully")) + } + } + + ColumnLayout { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + } + } + + FlickableType { + id: fl + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + contentHeight: content.implicitHeight + + Column { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + ListView { + id: listview + + width: parent.width + height: listview.contentItem.height + + clip: true + interactive: false + + model: CloakConfigModel + + delegate: Item { + implicitWidth: listview.width + implicitHeight: col.implicitHeight + + ColumnLayout { + id: col + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + spacing: 0 + + HeaderType { + Layout.fillWidth: true + + headerText: qsTr("Cloak settings") + } + + TextFieldWithHeaderType { + Layout.fillWidth: true + Layout.topMargin: 32 + + headerText: qsTr("Masquerading as traffic from") + textFieldText: site + + textField.onEditingFinished: { + if (textFieldText !== site) { + site = textFieldText + } + } + } + + TextFieldWithHeaderType { + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("Port") + textFieldText: port + + textField.onEditingFinished: { + if (textFieldText !== port) { + port = textFieldText + } + } + } + + DropDownType { + id: cipherDropDown + Layout.fillWidth: true + Layout.topMargin: 16 + implicitHeight: 74 + + descriptionText: qsTr("Cipher") + headerText: qsTr("Cipher") + + listView: ListViewType { + id: cipherListView + + rootWidth: root.width + + model: ListModel { + ListElement { name : "chacha20-ietf-poly1305" } + ListElement { name : "xchacha20-ietf-poly1305" } + ListElement { name : "aes-256-gcm" } + ListElement { name : "aes-192-gcm" } + ListElement { name : "aes-128-gcm" } + } + + clickedFunction: function() { + cipherDropDown.text = selectedText + cipher = cipherDropDown.text + cipherDropDown.menuVisible = false + } + + Component.onCompleted: { + cipherDropDown.text = cipher + + for (var i = 0; i < cipherListView.model.count; i++) { + if (cipherListView.model.get(i).name === cipherDropDown.text) { + currentIndex = i + } + } + } + } + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + + text: qsTr("Save and Restart Amnesia") + + onClicked: { + forceActiveFocus() + PageController.showBusyIndicator(true) + InstallController.updateContainer(CloakConfigModel.getConfig()) + PageController.showBusyIndicator(false) + } + } + } + } + } + } + } +} diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml new file mode 100644 index 00000000..8e5556d0 --- /dev/null +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -0,0 +1,465 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + Connections { + target: InstallController + + function onInstallationErrorOccurred(errorMessage) { + PageController.showErrorMessage(errorMessage) + } + + function onUpdateContainerFinished() { + //todo change to notification + PageController.showErrorMessage(qsTr("Settings updated successfully")) + } + } + + ColumnLayout { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + } + } + + FlickableType { + id: fl + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + contentHeight: content.implicitHeight + + Column { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + ListView { + id: listview + + width: parent.width + height: listview.contentItem.height + + clip: true + interactive: false + + model: OpenVpnConfigModel + + delegate: Item { + implicitWidth: listview.width + implicitHeight: col.implicitHeight + + ColumnLayout { + id: col + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + spacing: 0 + + HeaderType { + Layout.fillWidth: true + + headerText: qsTr("OpenVPN settings") + } + + TextFieldWithHeaderType { + Layout.fillWidth: true + Layout.topMargin: 32 + + headerText: qsTr("VPN Addresses Subnet") + textFieldText: subnetAddress + + textField.onEditingFinished: { + if (textFieldText !== subnetAddress) { + subnetAddress = textFieldText + } + } + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 32 + + text: qsTr("Network protocol") + } + + TransportProtoSelector { + Layout.fillWidth: true + Layout.topMargin: 16 + rootWidth: root.width + + enabled: isTransportProtoEditable + + currentIndex: { + return transportProto === "tcp" ? 1 : 0 + } + + onCurrentIndexChanged: { + if (transportProto === "tcp" && currentIndex === 0) { + transportProto = "udp" + } else if (transportProto === "udp" && currentIndex === 1) { + transportProto = "tcp" + } + } + } + + TextFieldWithHeaderType { + Layout.fillWidth: true + Layout.topMargin: 40 + + enabled: isPortEditable + + headerText: qsTr("Port") + textFieldText: port + + textField.onEditingFinished: { + if (textFieldText !== port) { + port = textFieldText + } + } + } + + SwitcherType { + id: autoNegotiateEncryprionSwitcher + + Layout.fillWidth: true + Layout.topMargin: 24 + + text: qsTr("Auto-negotiate encryption") + checked: autoNegotiateEncryprion + + onCheckedChanged: { + if (checked !== autoNegotiateEncryprion) { + autoNegotiateEncryprion = checked + } + } + } + + DropDownType { + id: hashDropDown + Layout.fillWidth: true + Layout.topMargin: 20 + implicitHeight: 74 + + enabled: !autoNegotiateEncryprionSwitcher.checked + + descriptionText: qsTr("Hash") + headerText: qsTr("Hash") + + listView: ListViewType { + 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.menuVisible = false + } + + Component.onCompleted: { + hashDropDown.text = 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 + implicitHeight: 74 + + enabled: !autoNegotiateEncryprionSwitcher.checked + + descriptionText: qsTr("Cipher") + headerText: qsTr("Cipher") + + listView: ListViewType { + 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 + cipher = cipherDropDown.text + cipherDropDown.menuVisible = false + } + + Component.onCompleted: { + cipherDropDown.text = cipher + + for (var i = 0; i < cipherListView.model.count; i++) { + if (cipherListView.model.get(i).name === cipherDropDown.text) { + currentIndex = i + } + } + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 32 + Layout.preferredHeight: checkboxLayout.implicitHeight + color: "#1C1D21" + radius: 16 + + ColumnLayout { + id: checkboxLayout + CheckBoxType { + Layout.fillWidth: true + + text: qsTr("TLS auth") + checked: tlsAuth + + onCheckedChanged: { + if (checked !== tlsAuth) { + tlsAuth = checked + } + } + } + + DividerType {} + + CheckBoxType { + Layout.fillWidth: true + + text: qsTr("Block DNS requests outside of VPN") + checked: blockDns + + onCheckedChanged: { + if (checked !== blockDns) { + blockDns = checked + } + } + } + } + } + + SwitcherType { + id: additionalClientCommandsSwitcher + Layout.fillWidth: true + Layout.topMargin: 32 + + checked: additionalClientCommands !== "" + + text: qsTr("Additional client configuration commands") + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 16 + + height: 148 + color: "#1C1D21" + border.width: 1 + border.color: "#2C2D30" + radius: 16 + visible: additionalClientCommandsSwitcher.checked + + FlickableType { + anchors.top: parent.top + anchors.bottom: parent.bottom + contentHeight: additionalClientCommandsTextArea.implicitHeight + TextArea { + id: additionalClientCommandsTextArea + + width: parent.width + anchors.topMargin: 16 + anchors.bottomMargin: 16 + + topPadding: 16 + leftPadding: 16 + + color: "#D7D8DB" + selectionColor: "#412102" + selectedTextColor: "#D7D8DB" + placeholderTextColor: "#878B91" + + font.pixelSize: 16 + font.weight: Font.Medium + font.family: "PT Root UI VF" + + placeholderText: qsTr("Commands:") + text: additionalClientCommands + + wrapMode: Text.Wrap + + onEditingFinished: { + if (additionalClientCommands !== text) { + additionalClientCommands = text + } + } + } + } + } + + SwitcherType { + id: additionalServerCommandsSwitcher + Layout.fillWidth: true + Layout.topMargin: 16 + + checked: additionalServerCommands !== "" + + text: qsTr("Additional server configuration commands") + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 16 + + height: 148 + color: "#1C1D21" + border.width: 1 + border.color: "#2C2D30" + radius: 16 + visible: additionalServerCommandsSwitcher.checked + + FlickableType { + anchors.top: parent.top + anchors.bottom: parent.bottom + contentHeight: additionalServerCommandsTextArea.implicitHeight + TextArea { + id: additionalServerCommandsTextArea + + width: parent.width + anchors.topMargin: 16 + anchors.bottomMargin: 16 + + topPadding: 16 + leftPadding: 16 + + color: "#D7D8DB" + selectionColor: "#412102" + selectedTextColor: "#D7D8DB" + placeholderTextColor: "#878B91" + + font.pixelSize: 16 + font.weight: Font.Medium + font.family: "PT Root UI VF" + + placeholderText: qsTr("Commands:") + text: additionalServerCommands + + wrapMode: Text.Wrap + + onEditingFinished: { + if (additionalServerCommands !== text) { + additionalServerCommands = text + } + } + } + } + } + + BasicButtonType { + Layout.topMargin: 24 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + textColor: "#EB5757" + + text: qsTr("Remove OpenVPN") + + onClicked: { + questionDrawer.headerText = qsTr("Remove OpenVpn from server?") +// questionDrawer.descriptionText = qsTr("") + questionDrawer.yesButtonText = qsTr("Continue") + questionDrawer.noButtonText = qsTr("Cancel") + + questionDrawer.yesButtonFunction = function() { + questionDrawer.visible = false + goToPage(PageEnum.PageDeinstalling) + ContainersModel.removeCurrentlyProcessedContainer() + closePage() + closePage() //todo auto close to deinstall page? + } + questionDrawer.noButtonFunction = function() { + questionDrawer.visible = false + } + questionDrawer.visible = true + } + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + + text: qsTr("Save and Restart Amnesia") + + onClicked: { + forceActiveFocus() + PageController.showBusyIndicator(true) + InstallController.updateContainer(OpenVpnConfigModel.getConfig()) + PageController.showBusyIndicator(false) + } + } + } + } + } + } + + QuestionDrawer { + id: questionDrawer + } + } +} diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml new file mode 100644 index 00000000..57006cc4 --- /dev/null +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -0,0 +1,162 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + //todo move to main? + Connections { + target: InstallController + + function onInstallationErrorOccurred(errorMessage) { + PageController.showErrorMessage(errorMessage) + } + + function onUpdateContainerFinished() { + //todo change to notification + PageController.showErrorMessage(qsTr("Settings updated successfully")) + } + } + + ColumnLayout { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + } + } + + FlickableType { + id: fl + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + contentHeight: content.implicitHeight + + Column { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + ListView { + id: listview + + width: parent.width + height: listview.contentItem.height + + clip: true + interactive: false + + model: ShadowSocksConfigModel + + delegate: Item { + implicitWidth: listview.width + implicitHeight: col.implicitHeight + + ColumnLayout { + id: col + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + spacing: 0 + + HeaderType { + Layout.fillWidth: true + + headerText: qsTr("ShadowSocks settings") + } + + TextFieldWithHeaderType { + Layout.fillWidth: true + Layout.topMargin: 40 + + headerText: qsTr("Port") + textFieldText: port + + textField.onEditingFinished: { + if (textFieldText !== port) { + port = textFieldText + } + } + } + + DropDownType { + id: cipherDropDown + Layout.fillWidth: true + Layout.topMargin: 20 + implicitHeight: 74 + + descriptionText: qsTr("Cipher") + headerText: qsTr("Cipher") + + listView: ListViewType { + id: cipherListView + + rootWidth: root.width + + model: ListModel { + ListElement { name : "chacha20-ietf-poly1305" } + ListElement { name : "xchacha20-ietf-poly1305" } + ListElement { name : "aes-256-gcm" } + ListElement { name : "aes-192-gcm" } + ListElement { name : "aes-128-gcm" } + } + + clickedFunction: function() { + cipherDropDown.text = selectedText + cipher = cipherDropDown.text + cipherDropDown.menuVisible = false + } + + Component.onCompleted: { + cipherDropDown.text = cipher + + for (var i = 0; i < cipherListView.model.count; i++) { + if (cipherListView.model.get(i).name === cipherDropDown.text) { + currentIndex = i + } + } + } + } + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + + text: qsTr("Save and Restart Amnesia") + + onClicked: { + forceActiveFocus() + PageController.showBusyIndicator(true) + InstallController.updateContainer(ShadowSocksConfigModel.getConfig()) + PageController.showBusyIndicator(false) + } + } + } + } + } + } + } +} diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml index 498006df..ec2ca91f 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml @@ -6,6 +6,7 @@ import SortFilterProxyModel 0.2 import PageEnum 1.0 import ProtocolEnum 1.0 +import ContainerEnum 1.0 import ContainerProps 1.0 import "./" @@ -13,15 +14,37 @@ import "../Controls2" import "../Controls2/TextTypes" import "../Config" import "../Components" -import "../Components/Protocols" PageType { id: root + ColumnLayout { + id: header + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + } + + HeaderType { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: ContainersModel.getCurrentlyProcessedContainerName() + qsTr(" settings") + } + } + FlickableType { id: fl - anchors.fill: parent - contentHeight: content.height + openVpnSettings.implicitHeight + anchors.top: header.bottom + anchors.left: parent.left + anchors.right: parent.right + contentHeight: content.height Column { id: content @@ -29,8 +52,7 @@ PageType { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - - spacing: 16 + anchors.topMargin: 32 ListView { // todo change id naming @@ -39,16 +61,7 @@ PageType { height: container.contentItem.height clip: true interactive: false - model: SortFilterProxyModel { - id: proxyContainersModel - sourceModel: ContainersModel - filters: [ - ValueFilter { - roleName: "isCurrentlyProcessed" - value: true - } - ] - } + model: ProtocolsModel delegate: Item { implicitWidth: container.width @@ -58,24 +71,60 @@ PageType { id: delegateContent anchors.fill: parent - anchors.rightMargin: 16 - anchors.leftMargin: 16 - HeaderType { + LabelWithButtonType { + id: button + Layout.fillWidth: true - Layout.topMargin: 20 - headerText: name + text: protocolName + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + var containerIndex = ContainersModel.getCurrentlyProcessedContainerIndex() + switch (containerIndex) { + case ContainerEnum.OpenVpn: OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ContainerEnum.ShadowSocks: ShadowSocksConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ContainerEnum.Cloak: CloakConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ContainerEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ContainerEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break; + } + goToPage(protocolPage); + } + + MouseArea { + anchors.fill: button + cursorShape: Qt.PointingHandCursor + enabled: false + } } + + DividerType {} } } } - OpenVpnSettings { - id: openVpnSettings + LabelWithButtonType { + id: removeButton width: parent.width + + text: qsTr("Remove ") + ContainersModel.getCurrentlyProcessedContainerName() + textColor: "#EB5757" + + clickedFunction: function() { + ContainersModel.removeCurrentlyProcessedContainer() + closePage() + } + + MouseArea { + anchors.fill: removeButton + cursorShape: Qt.PointingHandCursor + enabled: false + } } + + DividerType {} } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml index f1f5324e..2f07db18 100644 --- a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml +++ b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml @@ -14,8 +14,6 @@ import "../Config" PageType { id: root - property real progressBarValue: 0 - Connections { target: InstallController @@ -128,8 +126,6 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 -// value: progressBarValue - Timer { id: timer diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml index f343fabf..6a8e301f 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml @@ -44,7 +44,7 @@ PageType { FlickableType { id: fl - anchors.top: backButton.top + anchors.top: backButton.bottom anchors.bottom: parent.bottom contentHeight: content.implicitHeight + content.anchors.topMargin + content.anchors.bottomMargin diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index c83b59d5..903e5658 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -31,8 +31,6 @@ PageType { shareConnectionDrawer.contentVisible = false PageController.showBusyIndicator(true) - console.log(type) - switch (type) { case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(); break; case PageShare.ConfigType.AmenziaFullAccess: ExportController.generateFullAccessConfig(); break; @@ -51,6 +49,7 @@ PageType { } property bool showContent: false + property bool shareButtonEnabled: false property list connectionTypesModel: [ amneziaConnectionFormat ] @@ -180,6 +179,7 @@ PageType { serverSelector.text = selectedText ServersModel.currentlyProcessedIndex = currentIndex protocolSelector.visible = true + root.shareButtonEnabled = false } Component.onCompleted: { @@ -253,18 +253,24 @@ PageType { currentIndex: 0 clickedFunction: function () { - serverSelector.text += ", " + selectedText - shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text - shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text - ContainersModel.setCurrentlyProcessedContainerIndex(proxyContainersModel.mapToSource(currentIndex)) + handler() protocolSelector.visible = false serverSelector.menuVisible = false - - fillConnectionTypeModel() } Component.onCompleted: { + handler() + } + + function handler() { + if (!proxyContainersModel.count) { + root.shareButtonEnabled = false + return + } else { + root.shareButtonEnabled = true + } + serverSelector.text += ", " + selectedText shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text @@ -336,6 +342,8 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 + enabled: shareButtonEnabled + text: qsTr("Share") onClicked: {