From 25f8283edda8fcbbf0784d6860d901036cf06b46 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 12 Oct 2023 20:48:03 +0500 Subject: [PATCH 01/33] added getting the config from the cloud service --- client/CMakeLists.txt | 4 +- client/amnezia_application.cpp | 15 +- client/amnezia_application.h | 2 + client/configurators/cloak_configurator.cpp | 2 +- client/configurators/ikev2_configurator.cpp | 2 +- client/configurators/openvpn_configurator.cpp | 2 +- client/configurators/openvpn_configurator.h | 4 +- .../shadowsocks_configurator.cpp | 2 +- .../configurators/wireguard_configurator.cpp | 2 +- client/containers/containers_defs.cpp | 2 +- .../serverController.cpp} | 4 +- .../serverController.h} | 4 +- client/core/errorstrings.cpp | 2 +- client/protocols/openvpnprotocol.cpp | 2 +- client/protocols/protocols_defs.h | 8 +- client/translations/amneziavpn_ru.ts | 142 ++++++++++------- client/translations/amneziavpn_zh_CN.ts | 146 +++++++++++------- client/ui/controllers/cloudController.cpp | 109 +++++++++++++ client/ui/controllers/cloudController.h | 43 ++++++ .../ui/controllers/connectionController.cpp | 1 + client/ui/controllers/importController.cpp | 10 +- client/ui/controllers/installController.cpp | 2 +- client/ui/models/containers_model.cpp | 2 +- client/ui/models/servers_model.cpp | 13 ++ client/ui/models/servers_model.h | 3 + client/ui/qml/Components/ConnectButton.qml | 4 + client/vpnconnection.cpp | 6 +- 27 files changed, 399 insertions(+), 139 deletions(-) rename client/core/{servercontroller.cpp => controllers/serverController.cpp} (99%) rename client/core/{servercontroller.h => controllers/serverController.h} (98%) create mode 100644 client/ui/controllers/cloudController.cpp create mode 100644 client/ui/controllers/cloudController.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index cc192268..004b64f9 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -108,7 +108,7 @@ set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.h ${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h ${CMAKE_CURRENT_LIST_DIR}/core/server_defs.h - ${CMAKE_CURRENT_LIST_DIR}/core/servercontroller.h + ${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.h ${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h ${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h ${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h @@ -147,7 +147,7 @@ set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp ${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp ${CMAKE_CURRENT_LIST_DIR}/core/server_defs.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/servercontroller.cpp + ${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp ${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp ${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp ${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index d8039d9b..cb1512cc 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -139,7 +139,8 @@ void AmneziaApplication::init() &ConnectionController::openConnection); connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(), &ConnectionController::closeConnection); - connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated); + connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), + &NotificationHandler::onTranslationsUpdated); m_engine->load(url); m_systemController->setQmlRoot(m_engine->rootObjects().value(0)); @@ -226,14 +227,13 @@ void AmneziaApplication::loadTranslator() updateTranslator(locale); } - void AmneziaApplication::updateTranslator(const QLocale &locale) { if (!m_translator->isEmpty()) { QCoreApplication::removeTranslator(m_translator.get()); } - QString strFileName = QString(":/translations/amneziavpn")+QLatin1String("_")+locale.name()+".qm"; + QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm"; if (m_translator->load(strFileName)) { if (QCoreApplication::installTranslator(m_translator.get())) { m_settings->setAppLanguage(locale); @@ -330,7 +330,8 @@ void AmneziaApplication::initControllers() m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection)); m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get()); - connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated); + connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), + &ConnectionController::onTranslationsUpdated); m_pageController.reset(new PageController(m_serversModel, m_settings)); m_engine->rootContext()->setContextProperty("PageController", m_pageController.get()); @@ -361,4 +362,10 @@ void AmneziaApplication::initControllers() m_systemController.reset(new SystemController(m_settings)); m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get()); + + m_cloudController.reset(new CloudController(m_serversModel, m_containersModel)); + m_engine->rootContext()->setContextProperty("CloudController", m_cloudController.get()); + + connect(m_cloudController.get(), &CloudController::serverConfigUpdated, this, + [this]() { m_containersModel->setCurrentlyProcessedServerIndex(m_serversModel->getDefaultServerIndex()); }); } diff --git a/client/amnezia_application.h b/client/amnezia_application.h index 2dd74fcb..394ff943 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -24,6 +24,7 @@ #include "ui/controllers/settingsController.h" #include "ui/controllers/sitesController.h" #include "ui/controllers/systemController.h" +#include "ui/controllers/cloudController.h" #include "ui/models/containers_model.h" #include "ui/models/languageModel.h" #include "ui/models/protocols/cloakConfigModel.h" @@ -116,6 +117,7 @@ private: QScopedPointer m_settingsController; QScopedPointer m_sitesController; QScopedPointer m_systemController; + QScopedPointer m_cloudController; }; #endif // AMNEZIA_APPLICATION_H diff --git a/client/configurators/cloak_configurator.cpp b/client/configurators/cloak_configurator.cpp index fab378e2..9c540967 100644 --- a/client/configurators/cloak_configurator.cpp +++ b/client/configurators/cloak_configurator.cpp @@ -4,7 +4,7 @@ #include #include -#include "core/servercontroller.h" +#include "core/controllers/serverController.h" #include "containers/containers_defs.h" CloakConfigurator::CloakConfigurator(std::shared_ptr settings, QObject *parent): diff --git a/client/configurators/ikev2_configurator.cpp b/client/configurators/ikev2_configurator.cpp index 4ca0e5da..752d1750 100644 --- a/client/configurators/ikev2_configurator.cpp +++ b/client/configurators/ikev2_configurator.cpp @@ -11,7 +11,7 @@ #include "containers/containers_defs.h" #include "core/scripts_registry.h" #include "core/server_defs.h" -#include "core/servercontroller.h" +#include "core/controllers/serverController.h" #include "utilities.h" Ikev2Configurator::Ikev2Configurator(std::shared_ptr settings, QObject *parent) diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index a62bdd9c..e45c8506 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -16,7 +16,7 @@ #include "containers/containers_defs.h" #include "core/scripts_registry.h" #include "core/server_defs.h" -#include "core/servercontroller.h" +#include "core/controllers/serverController.h" #include "settings.h" #include "utilities.h" diff --git a/client/configurators/openvpn_configurator.h b/client/configurators/openvpn_configurator.h index 3b84e0a0..342bf753 100644 --- a/client/configurators/openvpn_configurator.h +++ b/client/configurators/openvpn_configurator.h @@ -32,9 +32,9 @@ public: ErrorCode signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId); -private: - ConnectionData createCertRequest(); + static ConnectionData createCertRequest(); +private: ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, ErrorCode *errorCode = nullptr); diff --git a/client/configurators/shadowsocks_configurator.cpp b/client/configurators/shadowsocks_configurator.cpp index a71064c8..99e4158c 100644 --- a/client/configurators/shadowsocks_configurator.cpp +++ b/client/configurators/shadowsocks_configurator.cpp @@ -5,7 +5,7 @@ #include #include "containers/containers_defs.h" -#include "core/servercontroller.h" +#include "core/controllers/serverController.h" ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr settings, QObject *parent): ConfiguratorBase(settings, parent) diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index 14059977..7b7d94d2 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -15,7 +15,7 @@ #include "containers/containers_defs.h" #include "core/scripts_registry.h" #include "core/server_defs.h" -#include "core/servercontroller.h" +#include "core/controllers/serverController.h" #include "settings.h" #include "utilities.h" diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index 20fc59f4..d2231415 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -54,7 +54,7 @@ QVector ContainerProps::protocolsForContainer(amnezia::DockerCon case DockerContainer::ShadowSocks: return { Proto::OpenVpn, Proto::ShadowSocks }; - case DockerContainer::Cloak: return { Proto::OpenVpn, Proto::ShadowSocks, Proto::Cloak }; + case DockerContainer::Cloak: return { Proto::OpenVpn, /*Proto::ShadowSocks,*/ Proto::Cloak }; case DockerContainer::Ipsec: return { Proto::Ikev2 /*, Protocol::L2tp */ }; diff --git a/client/core/servercontroller.cpp b/client/core/controllers/serverController.cpp similarity index 99% rename from client/core/servercontroller.cpp rename to client/core/controllers/serverController.cpp index b0f8146f..96306a58 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/controllers/serverController.cpp @@ -24,8 +24,8 @@ #include "containers/containers_defs.h" #include "logger.h" -#include "scripts_registry.h" -#include "server_defs.h" +#include "core/scripts_registry.h" +#include "core/server_defs.h" #include "settings.h" #include "utilities.h" diff --git a/client/core/servercontroller.h b/client/core/controllers/serverController.h similarity index 98% rename from client/core/servercontroller.h rename to client/core/controllers/serverController.h index 3191386c..ea6fd001 100644 --- a/client/core/servercontroller.h +++ b/client/core/controllers/serverController.h @@ -5,8 +5,8 @@ #include #include "containers/containers_defs.h" -#include "defs.h" -#include "sshclient.h" +#include "core/defs.h" +#include "core/sshclient.h" class Settings; class VpnConfigurator; diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index cd66186d..44135bae 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -58,7 +58,7 @@ QString errorString(ErrorCode code){ case (OpenVpnTapAdapterError): return QObject::tr("Can't setup OpenVPN TAP network adapter"); case (AddressPoolError): return QObject::tr("VPN pool error: no available addresses"); - case (ImportInvalidConfigError): return QObject::tr("The config does not contain any containers and credentiaks for connecting to the server"); + case (ImportInvalidConfigError): return QObject::tr("The config does not contain any containers and credentials for connecting to the server"); case(InternalError): default: diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index c38c6eea..f91cfe09 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -214,7 +214,7 @@ ErrorCode OpenVpnProtocol::start() m_openVpnProcess->setProgram(PermittedProcess::OpenVPN); QStringList arguments({ "--config", configPath(), "--management", m_managementHost, QString::number(mgmtPort), - "--management-client" /*, "--log", vpnLogFileNamePath */ + "--management-client" , "--log", "/Users/nethius/Documents/git/log" }); m_openVpnProcess->setArguments(arguments); diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 9472164b..fa326b2a 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -67,7 +67,13 @@ namespace amnezia constexpr char cloak[] = "cloak"; constexpr char sftp[] = "sftp"; - } + constexpr char configVersion[] = "config_version"; + constexpr char apiEdnpoint[] = "api_endpoint"; + constexpr char serviceTypeId[] = "service_type_id"; + constexpr char accessToken[] = "access_token"; + constexpr char certificate[] = "certificate"; + constexpr char publicKey[] = "public_key"; + } namespace protocols { diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts index e0bab018..20a7f022 100644 --- a/client/translations/amneziavpn_ru.ts +++ b/client/translations/amneziavpn_ru.ts @@ -23,44 +23,52 @@ + + CloudController + + + Error when retrieving configuration from cloud server + + + ConnectionController - + VPN Protocols is not installed. Please install VPN container at first - + Connection... - + Connected - + Settings updated successfully, Reconnnection... - + Reconnection... - - - + + + Connect - + Disconnection... @@ -130,7 +138,7 @@ ImportController - + Scanned %1 of %2. @@ -139,50 +147,55 @@ InstallController - + %1 installed successfully. - + %1 is already installed on the server. - + +Added containers that were already installed on the server + + + + Already installed containers were found on the server. All installed containers have been added to the application - + Settings updated successfully - + Server '%1' was removed - + All containers from server '%1' have been removed - + %1 has been removed from the server '%2' - + Please login as the user - + Server added successfully @@ -250,12 +263,12 @@ Already installed containers were found on the server. All installed containers PageHome - + VPN protocol - + Servers @@ -865,71 +878,76 @@ And if you don't like the app, all the more support it - the donation will + Allow application screenshots + + + + Auto start - + Launch the application every time - + starts - + Start minimized - + Launch application minimized - + Language - + Logging - + Enabled - + Disabled - + Reset settings and remove all data from the application - + Reset settings and remove all data from the application? - + All settings will be reset to default. All installed AmneziaVPN services will still remain on the server. - + Continue Продолжить - + Cancel @@ -1525,17 +1543,17 @@ It's okay as long as it's from someone you trust. PageSetupWizardEasy - + What is the level of internet control in your region? - + Set up a VPN yourself - + I want to choose a VPN protocol @@ -1545,7 +1563,7 @@ It's okay as long as it's from someone you trust. Продолжить - + Set up later @@ -1765,13 +1783,17 @@ It's okay as long as it's from someone you trust. - + + File with accessing settings to + + + + Connection to - - + File with connection settings to @@ -1801,23 +1823,19 @@ It's okay as long as it's from someone you trust. - - Protocols - - - - + + Protocol - - + + Connection format - + Share @@ -2264,7 +2282,7 @@ It's okay as long as it's from someone you trust. - The config does not contain any containers and credentiaks for connecting to the server + The config does not contain any containers and credentials for connecting to the server @@ -2433,6 +2451,16 @@ It's okay as long as it's from someone you trust. error 0x%1: %2 + + + WireGuard Configuration Highlighter + + + + + &Randomize colors + + SelectLanguageDrawer @@ -2459,22 +2487,22 @@ It's okay as long as it's from someone you trust. SettingsController - + Software version - + All settings have been reset to default values - + Cached profiles cleared - + Backup file is corrupted @@ -2504,7 +2532,7 @@ It's okay as long as it's from someone you trust. - Show content + Show connection settings @@ -2592,7 +2620,7 @@ It's okay as long as it's from someone you trust. VpnConnection - + Mbps diff --git a/client/translations/amneziavpn_zh_CN.ts b/client/translations/amneziavpn_zh_CN.ts index 37e27786..109ad9c8 100644 --- a/client/translations/amneziavpn_zh_CN.ts +++ b/client/translations/amneziavpn_zh_CN.ts @@ -23,44 +23,52 @@ VPN已连接 + + CloudController + + + Error when retrieving configuration from cloud server + + + ConnectionController - - - + + + Connect 连接 - + VPN Protocols is not installed. Please install VPN container at first 不存在VPN协议,请先安装 - + Connection... 连接中 - + Connected 已连接 - + Reconnection... 重连中 - + Disconnection... 断开中 - + Settings updated successfully, Reconnnection... 配置已更新,重连中 @@ -130,7 +138,7 @@ ImportController - + Scanned %1 of %2. 扫描 %1 of %2. @@ -147,41 +155,46 @@ - + %1 installed successfully. %1 安装成功。 - + %1 is already installed on the server. 服务器上已经安装 %1。 - + +Added containers that were already installed on the server + + + + Already installed containers were found on the server. All installed containers have been added to the application 在服务上发现已经安装协议并添加到应用程序 - + Settings updated successfully 配置更新成功 - + Server '%1' was removed 已移除服务器 '%1' - + All containers from server '%1' have been removed 服务器 '%1' 的所有容器已移除 - + %1 has been removed from the server '%2' %1 已从服务器 '%2' 上移除 @@ -202,12 +215,12 @@ Already installed containers were found on the server. All installed containers 协议已从 - + Please login as the user 请以用户身份登录 - + Server added successfully 服务器添加成功 @@ -275,12 +288,12 @@ Already installed containers were found on the server. All installed containers PageHome - + VPN protocol VPN协议 - + Servers 服务器 @@ -892,71 +905,76 @@ And if you don't like the app, all the more support it - the donation will + Allow application screenshots + + + + Auto start 自动运行 - + Launch the application every time 总是在系统 - + starts 启动时自动运行运用程序 - + Start minimized 最小化 - + Launch application minimized 开启应用程序时窗口最小化 - + Language 语言 - + Logging 日志 - + Enabled 开启 - + Disabled 禁用 - + Reset settings and remove all data from the application 重置并清理应用的所有数据 - + Reset settings and remove all data from the application? 重置并清理应用的所有数据? - + All settings will be reset to default. All installed AmneziaVPN services will still remain on the server. 所有配置恢复为默认值。在服务器上保留所有已安装的AmneziaVPN服务。 - + Continue 继续 - + Cancel 取消 @@ -1561,17 +1579,17 @@ It's okay as long as it's from someone you trust. PageSetupWizardEasy - + What is the level of internet control in your region? 您所在地区的互联网控制力度如何? - + Set up a VPN yourself 自己架设VPN - + I want to choose a VPN protocol 我想选择VPN协议 @@ -1581,7 +1599,7 @@ It's okay as long as it's from someone you trust. 继续 - + Set up later 稍后设置 @@ -1827,33 +1845,37 @@ It's okay as long as it's from someone you trust. - + File with accessing settings to + + + + File with connection settings to 连接配置文件的内容为: - Protocols - 协议 + 协议 - + + Protocol 协议 - + Connection to 连接到 - - + + Connection format 连接方式 - + Share 共享 @@ -2305,8 +2327,12 @@ It's okay as long as it's from someone you trust. + The config does not contain any containers and credentials for connecting to the server + + + The config does not contain any containers and credentiaks for connecting to the server - 该配置不包含任何用于连接到服务器的容器和凭据。 + 该配置不包含任何用于连接到服务器的容器和凭据。 @@ -2469,6 +2495,16 @@ It's okay as long as it's from someone you trust. error 0x%1: %2 错误 0x%1: %2 + + + WireGuard Configuration Highlighter + + + + + &Randomize colors + + SelectLanguageDrawer @@ -2495,22 +2531,22 @@ It's okay as long as it's from someone you trust. SettingsController - + Software version 软件版本 - + Backup file is corrupted 备份文件已损坏 - + All settings have been reset to default values 所配置恢复为默认值 - + Cached profiles cleared 缓存的配置文件已清除 @@ -2540,8 +2576,12 @@ It's okay as long as it's from someone you trust. + Show connection settings + + + Show content - 展示内容 + 展示内容 @@ -2628,7 +2668,7 @@ It's okay as long as it's from someone you trust. VpnConnection - + Mbps diff --git a/client/ui/controllers/cloudController.cpp b/client/ui/controllers/cloudController.cpp new file mode 100644 index 00000000..99309d90 --- /dev/null +++ b/client/ui/controllers/cloudController.cpp @@ -0,0 +1,109 @@ +#include "cloudController.h" + +#include +#include +#include + +#include "configurators/openvpn_configurator.h" + +CloudController::CloudController(const QSharedPointer &serversModel, + const QSharedPointer &containersModel, QObject *parent) + : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel) +{ +} + +QString CloudController::genPublicKey(ServiceTypeId serviceTypeId) +{ + switch (serviceTypeId) + { + case ServiceTypeId::AmneziaFreeRuWG: return "."; + case ServiceTypeId::AmneziaFreeRuCloak: return "."; + case ServiceTypeId::AmneziaFreeRuAWG: return "."; + case ServiceTypeId::AmneziaFreeRuReverseWG: return "."; + case ServiceTypeId::AmneziaFreeRuReverseCloak: return "."; + case ServiceTypeId::AmneziaFreeRuReverseAWG: return "."; + } +} + +QString CloudController::genCertificateRequest(ServiceTypeId serviceTypeId) +{ + switch (serviceTypeId) + { + case ServiceTypeId::AmneziaFreeRuWG: return ""; + case ServiceTypeId::AmneziaFreeRuCloak: { + auto data = OpenVpnConfigurator::createCertRequest(); + return data.request; + } + case ServiceTypeId::AmneziaFreeRuAWG: return ""; + case ServiceTypeId::AmneziaFreeRuReverseWG: return ""; + case ServiceTypeId::AmneziaFreeRuReverseCloak: return ""; + case ServiceTypeId::AmneziaFreeRuReverseAWG: return ""; + } +} + +bool CloudController::updateServerConfigFromCloud() +{ + auto serverConfig = m_serversModel->getDefaultServerConfig(); + + auto containerConfig = serverConfig.value(config_key::containers).toArray(); + + if (serverConfig.value(config_key::configVersion).toInt() && containerConfig.isEmpty()) { + QNetworkAccessManager manager; + + QNetworkRequest request; + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QString endpoint = serverConfig.value(config_key::apiEdnpoint).toString(); + request.setUrl(endpoint.replace("https", "http")); // + + ServiceTypeId serviceTypeId = static_cast(serverConfig.value(config_key::serviceTypeId).toInt()); + + QJsonObject obj; + obj[config_key::serviceTypeId] = serviceTypeId; + obj[config_key::accessToken] = serverConfig.value(config_key::accessToken); + + obj[config_key::publicKey] = genPublicKey(serviceTypeId); + obj[config_key::certificate] = genCertificateRequest(serviceTypeId); + + QByteArray requestBody = QJsonDocument(obj).toJson(); + + QScopedPointer reply; + reply.reset(manager.post(request, requestBody)); + + QEventLoop wait; + QObject::connect(reply.get(), &QNetworkReply::finished, &wait, &QEventLoop::quit); + wait.exec(); + + if(reply->error() == QNetworkReply::NoError){ + QString contents = QString::fromUtf8(reply->readAll()); + auto data = QJsonDocument::fromJson(contents.toUtf8()).object().value(config_key::config).toString(); + + data.replace("vpn://", ""); + QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); + + QByteArray ba_uncompressed = qUncompress(ba); + if (!ba_uncompressed.isEmpty()) { + ba = ba_uncompressed; + } + + QJsonObject cloudConfig = QJsonDocument::fromJson(ba).object(); + serverConfig.insert("cloudConfig", cloudConfig); + serverConfig.insert(config_key::dns1, cloudConfig.value(config_key::dns1)); + serverConfig.insert(config_key::dns2, cloudConfig.value(config_key::dns2)); + serverConfig.insert(config_key::containers, cloudConfig.value(config_key::containers)); + serverConfig.insert(config_key::hostName, cloudConfig.value(config_key::hostName)); + serverConfig.insert(config_key::defaultContainer, cloudConfig.value(config_key::defaultContainer)); + m_serversModel->editServer(serverConfig); + emit serverConfigUpdated(); + } else{ + QString err = reply->errorString(); + qDebug() << reply->error(); + qDebug() << err; + qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + emit errorOccurred(tr("Error when retrieving configuration from cloud server")); + return false; + } + + } + + return true; +} diff --git a/client/ui/controllers/cloudController.h b/client/ui/controllers/cloudController.h new file mode 100644 index 00000000..409e0981 --- /dev/null +++ b/client/ui/controllers/cloudController.h @@ -0,0 +1,43 @@ +#ifndef CLOUDCONTROLLER_H +#define CLOUDCONTROLLER_H + +#include + +#include "ui/models/containers_model.h" +#include "ui/models/servers_model.h" + +class CloudController : public QObject +{ + Q_OBJECT + + enum ServiceTypeId + { + AmneziaFreeRuWG = 0, + AmneziaFreeRuCloak, + AmneziaFreeRuAWG, + AmneziaFreeRuReverseWG, + AmneziaFreeRuReverseCloak, + AmneziaFreeRuReverseAWG + + }; + +public: + explicit CloudController(const QSharedPointer &serversModel, + const QSharedPointer &containersModel, QObject *parent = nullptr); + +public slots: + bool updateServerConfigFromCloud(); + +signals: + void errorOccurred(const QString &errorMessage); + void serverConfigUpdated(); + +private: + QString genPublicKey(ServiceTypeId serviceTypeId); + QString genCertificateRequest(ServiceTypeId serviceTypeId); + + QSharedPointer m_serversModel; + QSharedPointer m_containersModel; +}; + +#endif // CLOUDCONTROLLER_H diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index 74438dcc..de05d624 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -40,6 +40,7 @@ void ConnectionController::openConnection() } qApp->processEvents(); + emit connectToVpn(serverIndex, credentials, container, containerConfig); } diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index aaea8db3..8198a5a1 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -141,7 +141,9 @@ void ImportController::importConfig() credentials.userName = m_config.value(config_key::userName).toString(); credentials.secretData = m_config.value(config_key::password).toString(); - if (credentials.isValid() || m_config.contains(config_key::containers)) { + if (credentials.isValid() + || m_config.contains(config_key::containers) + || m_config.contains(config_key::configVersion)) { // todo m_serversModel->addServer(m_config); m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1); @@ -167,7 +169,11 @@ QJsonObject ImportController::extractAmneziaConfig(QString &data) ba = ba_uncompressed; } - return QJsonDocument::fromJson(ba).object(); + QJsonObject config = QJsonDocument::fromJson(ba).object(); + + qDebug() << config; + + return config; } QJsonObject ImportController::extractOpenVpnConfig(const QString &data) diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 72cc34b6..4885aa80 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -7,7 +7,7 @@ #include #include "core/errorstrings.h" -#include "core/servercontroller.h" +#include "core/controllers/serverController.h" #include "utilities.h" namespace diff --git a/client/ui/models/containers_model.cpp b/client/ui/models/containers_model.cpp index 6cf855a6..0ffdc00b 100644 --- a/client/ui/models/containers_model.cpp +++ b/client/ui/models/containers_model.cpp @@ -1,6 +1,6 @@ #include "containers_model.h" -#include "core/servercontroller.h" +#include "core/controllers/serverController.h" ContainersModel::ContainersModel(std::shared_ptr settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent) diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 7eea94e5..e0fe0787 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -168,6 +168,14 @@ void ServersModel::addServer(const QJsonObject &server) endResetModel(); } +void ServersModel::editServer(const QJsonObject &server) +{ + beginResetModel(); + m_settings->editServer(m_currentlyProcessedServerIndex, server); + m_servers = m_settings->serversArray(); + endResetModel(); +} + void ServersModel::removeServer() { beginResetModel(); @@ -219,3 +227,8 @@ ServerCredentials ServersModel::serverCredentials(int index) const return credentials; } + +QJsonObject ServersModel::getDefaultServerConfig() +{ + return m_servers.at(m_defaultServerIndex).toObject(); +} diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index d7b15844..feff3ec8 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -55,10 +55,13 @@ public slots: QString getCurrentlyProcessedServerHostName(); void addServer(const QJsonObject &server); + void editServer(const QJsonObject &server); void removeServer(); bool isDefaultServerConfigContainsAmneziaDns(); + QJsonObject getDefaultServerConfig(); + protected: QHash roleNames() const override; diff --git a/client/ui/qml/Components/ConnectButton.qml b/client/ui/qml/Components/ConnectButton.qml index 76e83da5..2fdcb9b2 100644 --- a/client/ui/qml/Components/ConnectButton.qml +++ b/client/ui/qml/Components/ConnectButton.qml @@ -138,6 +138,10 @@ Button { } onClicked: { + if (!CloudController.updateServerConfigFromCloud()) { + return + } + if (!ContainersModel.isAnyContainerInstalled()) { PageController.setTriggeredBtConnectButton(true) diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 1cff01e6..5cc6a6a0 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include "core/controllers/serverController.h" #ifdef AMNEZIA_DESKTOP #include "core/ipcclient.h" @@ -258,9 +258,7 @@ QJsonObject VpnConnection::createVpnConfiguration(int serverIndex, const ServerC for (ProtocolEnumNS::Proto proto : ContainerProps::protocolsForContainer(container)) { QJsonObject vpnConfigData = QJsonDocument::fromJson(createVpnConfigurationForProto(serverIndex, credentials, container, - containerConfig, proto, errorCode) - .toUtf8()) - .object(); + containerConfig, proto, errorCode).toUtf8()).object(); if (errorCode && *errorCode) { return {}; From 4ae9cddccefd7e3923f0b21d831ce94fd150a0fe Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 23 Oct 2023 21:43:25 +0500 Subject: [PATCH 02/33] fixed a typo in the serverController include --- client/configurators/awg_configurator.cpp | 2 +- client/core/controllers/serverController.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/configurators/awg_configurator.cpp b/client/configurators/awg_configurator.cpp index c3e42258..9470308a 100644 --- a/client/configurators/awg_configurator.cpp +++ b/client/configurators/awg_configurator.cpp @@ -3,7 +3,7 @@ #include #include -#include "core/servercontroller.h" +#include "core/controllers/serverController.h" AwgConfigurator::AwgConfigurator(std::shared_ptr settings, QObject *parent) : WireguardConfigurator(settings, true, parent) diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index cc81ed94..5786fa68 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -1,4 +1,4 @@ -#include "servercontroller.h" +#include "serverController.h" #include #include From 3e03002eaded6f90041e1e4d321c1ee783d363e8 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 23 Oct 2023 23:55:01 +0500 Subject: [PATCH 03/33] added getting cloud config for cloak --- client/ui/controllers/cloudController.cpp | 29 +++++++++++++++++++---- client/ui/controllers/cloudController.h | 5 ++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/client/ui/controllers/cloudController.cpp b/client/ui/controllers/cloudController.cpp index 99309d90..188e12e3 100644 --- a/client/ui/controllers/cloudController.cpp +++ b/client/ui/controllers/cloudController.cpp @@ -31,8 +31,8 @@ QString CloudController::genCertificateRequest(ServiceTypeId serviceTypeId) { case ServiceTypeId::AmneziaFreeRuWG: return ""; case ServiceTypeId::AmneziaFreeRuCloak: { - auto data = OpenVpnConfigurator::createCertRequest(); - return data.request; + m_certRequest = OpenVpnConfigurator::createCertRequest(); + return m_certRequest.request; } case ServiceTypeId::AmneziaFreeRuAWG: return ""; case ServiceTypeId::AmneziaFreeRuReverseWG: return ""; @@ -41,6 +41,23 @@ QString CloudController::genCertificateRequest(ServiceTypeId serviceTypeId) } } +void CloudController::processCloudConfig(ServiceTypeId serviceTypeId, QString &config) +{ + switch (serviceTypeId) + { + case ServiceTypeId::AmneziaFreeRuWG: return; + case ServiceTypeId::AmneziaFreeRuCloak: { + config.replace("", "\n"); + config.replace("$OPENVPN_PRIV_KEY", m_certRequest.privKey); + return; + } + case ServiceTypeId::AmneziaFreeRuAWG: return; + case ServiceTypeId::AmneziaFreeRuReverseWG: return; + case ServiceTypeId::AmneziaFreeRuReverseCloak: return; + case ServiceTypeId::AmneziaFreeRuReverseAWG: return; + } +} + bool CloudController::updateServerConfigFromCloud() { auto serverConfig = m_serversModel->getDefaultServerConfig(); @@ -53,7 +70,7 @@ bool CloudController::updateServerConfigFromCloud() QNetworkRequest request; request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QString endpoint = serverConfig.value(config_key::apiEdnpoint).toString(); - request.setUrl(endpoint.replace("https", "http")); // + request.setUrl(endpoint.replace("https", "http")); // todo remove ServiceTypeId serviceTypeId = static_cast(serverConfig.value(config_key::serviceTypeId).toInt()); @@ -85,7 +102,11 @@ bool CloudController::updateServerConfigFromCloud() ba = ba_uncompressed; } - QJsonObject cloudConfig = QJsonDocument::fromJson(ba).object(); + QString configStr = ba; + processCloudConfig(serviceTypeId, configStr); + + QJsonObject cloudConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); + serverConfig.insert("cloudConfig", cloudConfig); serverConfig.insert(config_key::dns1, cloudConfig.value(config_key::dns1)); serverConfig.insert(config_key::dns2, cloudConfig.value(config_key::dns2)); diff --git a/client/ui/controllers/cloudController.h b/client/ui/controllers/cloudController.h index 409e0981..36c8427b 100644 --- a/client/ui/controllers/cloudController.h +++ b/client/ui/controllers/cloudController.h @@ -5,6 +5,7 @@ #include "ui/models/containers_model.h" #include "ui/models/servers_model.h" +#include "configurators/openvpn_configurator.h" class CloudController : public QObject { @@ -36,8 +37,12 @@ private: QString genPublicKey(ServiceTypeId serviceTypeId); QString genCertificateRequest(ServiceTypeId serviceTypeId); + void processCloudConfig(ServiceTypeId serviceTypeId, QString &config); + QSharedPointer m_serversModel; QSharedPointer m_containersModel; + + OpenVpnConfigurator::ConnectionData m_certRequest; }; #endif // CLOUDCONTROLLER_H From 9cfcb714aebfd7c31a105e647621479dd1068946 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 30 Oct 2023 14:20:21 +0500 Subject: [PATCH 04/33] added native config generation for ss and cloak --- client/ui/controllers/exportController.cpp | 80 +++++++++++++++++++ client/ui/controllers/exportController.h | 2 + .../qml/Pages2/PageSetupWizardCredentials.qml | 10 ++- client/ui/qml/Pages2/PageShare.qml | 43 ++++++++-- 4 files changed, 127 insertions(+), 8 deletions(-) diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index ef5cc4e3..06bddb8a 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -10,6 +10,8 @@ #include "configurators/openvpn_configurator.h" #include "configurators/wireguard_configurator.h" +#include "configurators/shadowsocks_configurator.h" +#include "configurators/cloak_configurator.h" #include "core/errorstrings.h" #include "systemController.h" #ifdef Q_OS_ANDROID @@ -155,6 +157,8 @@ void ExportController::generateOpenVpnConfig() m_config.append(line + "\n"); } + m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); + emit exportConfigChanged(); } @@ -187,6 +191,82 @@ void ExportController::generateWireGuardConfig() m_config.append(line + "\n"); } + m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); + + emit exportConfigChanged(); +} + +void ExportController::generateShadowSocksConfig() +{ + clearPreviousConfig(); + + int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); + ServerCredentials credentials = + qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + + DockerContainer container = static_cast(m_containersModel->getCurrentlyProcessedContainerIndex()); + QModelIndex containerModelIndex = m_containersModel->index(container); + QJsonObject containerConfig = + qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); + containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); + + ErrorCode errorCode = ErrorCode::NoError; + QString config = m_configurator->shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, &errorCode); + + if (errorCode) { + emit exportErrorOccurred(errorString(errorCode)); + return; + } + config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::ShadowSocks, config); + QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object(); + + m_config = QString("%1:%2@%3:%4") + .arg(configJson.value("method").toString(), + configJson.value("password").toString(), + configJson.value("server").toString(), + configJson.value("server_port").toString()); + + m_config = "ss://" + m_config.toUtf8().toBase64(); + + m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); + + emit exportConfigChanged(); +} + +void ExportController::generateCloakConfig() +{ + clearPreviousConfig(); + + int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); + ServerCredentials credentials = + qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + + DockerContainer container = static_cast(m_containersModel->getCurrentlyProcessedContainerIndex()); + QModelIndex containerModelIndex = m_containersModel->index(container); + QJsonObject containerConfig = + qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); + containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); + + ErrorCode errorCode = ErrorCode::NoError; + QString config = m_configurator->cloakConfigurator->genCloakConfig(credentials, container, containerConfig, &errorCode); + + if (errorCode) { + emit exportErrorOccurred(errorString(errorCode)); + return; + } + config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::Cloak, config); + QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object(); + + configJson.remove(config_key::transport_proto); + configJson.insert("ProxyMethod", "shadowsocks"); + + QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n"); + for (const QString &line : lines) { + m_config.append(line + "\n"); + } + + m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); + emit exportConfigChanged(); } diff --git a/client/ui/controllers/exportController.h b/client/ui/controllers/exportController.h index 24eaa5c8..ee94e741 100644 --- a/client/ui/controllers/exportController.h +++ b/client/ui/controllers/exportController.h @@ -31,6 +31,8 @@ public slots: void generateConnectionConfig(); void generateOpenVpnConfig(); void generateWireGuardConfig(); + void generateShadowSocksConfig(); + void generateCloakConfig(); QString getConfig(); QList getQrCodes(); diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index 5c32b0c5..3eadb647 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -54,7 +54,7 @@ PageType { regularExpression: InstallController.ipAddressPortRegExp() } - onTextFieldTextChanged: { + onFocusChanged: { textField.text = textField.text.replace(/^\s+|\s+$/g, ''); } } @@ -81,6 +81,10 @@ PageType { clickedFunc: function() { hidePassword = !hidePassword } + + onFocusChanged: { + textField.text = textField.text.replace(/^\s+|\s+$/g, ''); + } } BasicButtonType { @@ -90,6 +94,7 @@ PageType { text: qsTr("Continue") onClicked: function() { + forceActiveFocus() if (!isCredentialsFilled()) { return } @@ -112,8 +117,7 @@ PageType { Layout.fillWidth: true Layout.topMargin: 12 - text: qsTr("All data you enter will remain strictly confidential -and will not be shared or disclosed to the Amnezia or any third parties") + text: qsTr("All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties") } } } diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index ced7a5ff..577a9b3a 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -20,7 +20,9 @@ PageType { AmneziaConnection, AmneziaFullAccess, OpenVpn, - WireGuard + WireGuard, + ShadowSocks, + Cloak } Connections { @@ -44,18 +46,32 @@ PageType { break; } case PageShare.ConfigType.OpenVpn: { - ExportController.generateOpenVpnConfig(); + ExportController.generateOpenVpnConfig() shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config") shareConnectionDrawer.configExtension = ".ovpn" shareConnectionDrawer.configFileName = "amnezia_for_openvpn" - break; + break } case PageShare.ConfigType.WireGuard: { - ExportController.generateWireGuardConfig(); + ExportController.generateWireGuardConfig() shareConnectionDrawer.configCaption = qsTr("Save WireGuard config") shareConnectionDrawer.configExtension = ".conf" shareConnectionDrawer.configFileName = "amnezia_for_wireguard" - break; + break + } + case PageShare.ConfigType.ShadowSocks: { + ExportController.generateShadowSocksConfig() + shareConnectionDrawer.configCaption = qsTr("Save ShadowSocks config") + shareConnectionDrawer.configExtension = ".json" + shareConnectionDrawer.configFileName = "amnezia_for_shadowsocks" + break + } + case PageShare.ConfigType.Cloak: { + ExportController.generateCloakConfig() + shareConnectionDrawer.configCaption = qsTr("Save Cloak config") + shareConnectionDrawer.configExtension = ".json" + shareConnectionDrawer.configFileName = "amnezia_for_cloak" + break } } @@ -96,6 +112,16 @@ PageType { property string name: qsTr("WireGuard native format") property var type: PageShare.ConfigType.WireGuard } + QtObject { + id: shadowSocksConnectionFormat + property string name: qsTr("ShadowSocks native format") + property var type: PageShare.ConfigType.ShadowSocks + } + QtObject { + id: cloakConnectionFormat + property string name: qsTr("Cloak native format") + property var type: PageShare.ConfigType.Cloak + } FlickableType { anchors.top: parent.top @@ -322,6 +348,13 @@ PageType { root.connectionTypesModel.push(openVpnConnectionFormat) } else if (index === ContainerProps.containerFromString("amnezia-wireguard")) { root.connectionTypesModel.push(wireGuardConnectionFormat) + } else if (index === ContainerProps.containerFromString("amnezia-shadowsocks")) { + root.connectionTypesModel.push(openVpnConnectionFormat) + root.connectionTypesModel.push(shadowSocksConnectionFormat) + } else if (index === ContainerProps.containerFromString("amnezia-openvpn-cloak")) { + root.connectionTypesModel.push(openVpnConnectionFormat) + root.connectionTypesModel.push(shadowSocksConnectionFormat) + root.connectionTypesModel.push(cloakConnectionFormat) } } } From 8497aeeb912bf932f2ac9ddf384aa47569063f87 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 6 Nov 2023 14:10:31 +0300 Subject: [PATCH 05/33] removed the debugging code --- client/protocols/openvpnprotocol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index f91cfe09..c38c6eea 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -214,7 +214,7 @@ ErrorCode OpenVpnProtocol::start() m_openVpnProcess->setProgram(PermittedProcess::OpenVPN); QStringList arguments({ "--config", configPath(), "--management", m_managementHost, QString::number(mgmtPort), - "--management-client" , "--log", "/Users/nethius/Documents/git/log" + "--management-client" /*, "--log", vpnLogFileNamePath */ }); m_openVpnProcess->setArguments(arguments); From 362a82f94496075b2187cf1bfbe0d2acf79894c0 Mon Sep 17 00:00:00 2001 From: tiaga Date: Tue, 14 Nov 2023 16:57:04 +0700 Subject: [PATCH 06/33] Improve logic of install_docker.sh - check packages update only when it's required - avoid `dnf/yum update` for RHEL-based systems --- client/server_scripts/install_docker.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/client/server_scripts/install_docker.sh b/client/server_scripts/install_docker.sh index 58f92540..e6708ddf 100644 --- a/client/server_scripts/install_docker.sh +++ b/client/server_scripts/install_docker.sh @@ -1,19 +1,19 @@ -if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); docker_pkg="docker.io"; dist="debian";\ -elif which dnf > /dev/null 2>&1; then pm=$(which dnf); docker_pkg="docker"; dist="fedora";\ -elif which yum > /dev/null 2>&1; then pm=$(which yum); docker_pkg="docker"; dist="centos";\ +if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\ +elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\ +elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\ else echo "Packet manager not found"; exit 1; fi;\ -echo "Dist: $dist, Packet manager: $pm, Docker pkg: $docker_pkg";\ +echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\ if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\ -if ! command -v sudo > /dev/null 2>&1; then $pm update -yq; $pm install -yq sudo; fi;\ -if ! command -v fuser > /dev/null 2>&1; then sudo $pm install -yq psmisc; fi;\ -if ! command -v lsof > /dev/null 2>&1; then sudo $pm install -yq lsof; fi;\ -if ! command -v docker > /dev/null 2>&1; then sudo $pm update -yq; sudo $pm install -yq $docker_pkg;\ +if ! command -v sudo > /dev/null 2>&1; then $pm $check_pkgs; $pm $silent_inst sudo; fi;\ +if ! command -v fuser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst psmisc; fi;\ +if ! command -v lsof > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst lsof; fi;\ +if ! command -v docker > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\ if [ "$dist" = "fedora" ] || [ "$dist" = "centos" ] || [ "$dist" = "debian" ]; then sudo systemctl enable docker && sudo systemctl start docker; fi;\ fi;\ if [ "$dist" = "debian" ]; then \ docker_service=$(systemctl list-units --full --all | grep docker.service | grep -v inactive | grep -v dead | grep -v failed);\ - if [ -z "$docker_service" ]; then sudo $pm update -yq; sudo $pm install -yq curl $docker_pkg; fi;\ + if [ -z "$docker_service" ]; then sudo $pm $check_pkgs; sudo $pm $silent_inst curl $docker_pkg; fi;\ sleep 3 && sudo systemctl start docker && sleep 3;\ fi;\ -if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install Docker";exit 1;fi;\ +if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install Docker"; exit 1; fi;\ docker --version From e8a2e54d059d02b6487eae4e9cb9d355464dc1bd Mon Sep 17 00:00:00 2001 From: pokamest Date: Wed, 15 Nov 2023 12:51:39 +0000 Subject: [PATCH 07/33] Typo fix --- client/core/defs.h | 2 +- client/core/errorstrings.cpp | 2 +- client/core/sshclient.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/core/defs.h b/client/core/defs.h index 35515103..9547dd92 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -36,7 +36,7 @@ enum ErrorCode ServerPacketManagerError, // Ssh connection errors - SshRequsetDeniedError, SshInterruptedError, SshInternalError, + SshRequestDeniedError, SshInterruptedError, SshInternalError, SshPrivateKeyError, SshPrivateKeyFormatError, SshTimeoutError, // Ssh sftp errors diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index cd66186d..b17a5a9e 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -19,7 +19,7 @@ QString errorString(ErrorCode code){ case(ServerUserNotInSudo): return QObject::tr("The user does not have permission to use sudo"); // Libssh errors - case(SshRequsetDeniedError): return QObject::tr("Ssh request was denied"); + case(SshRequestDeniedError): return QObject::tr("Ssh request was denied"); case(SshInterruptedError): return QObject::tr("Ssh request was interrupted"); case(SshInternalError): return QObject::tr("Ssh internal error"); case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered"); diff --git a/client/core/sshclient.cpp b/client/core/sshclient.cpp index 797bdc6f..0ac95662 100644 --- a/client/core/sshclient.cpp +++ b/client/core/sshclient.cpp @@ -333,7 +333,7 @@ namespace libssh { switch (errorCode) { case(SSH_NO_ERROR): return ErrorCode::NoError; - case(SSH_REQUEST_DENIED): return ErrorCode::SshRequsetDeniedError; + case(SSH_REQUEST_DENIED): return ErrorCode::SshRequestDeniedError; case(SSH_EINTR): return ErrorCode::SshInterruptedError; case(SSH_FATAL): return ErrorCode::SshInternalError; default: return ErrorCode::SshInternalError; From c6a312845aa8545b1373dbf3472208b0e02dd857 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 21 Nov 2023 20:13:51 +0700 Subject: [PATCH 08/33] added client management --- client/amnezia_application.cpp | 9 +- client/amnezia_application.h | 2 + client/configurators/awg_configurator.cpp | 7 +- client/configurators/awg_configurator.h | 2 +- client/configurators/openvpn_configurator.cpp | 8 +- client/configurators/openvpn_configurator.h | 2 +- client/configurators/vpn_configurator.cpp | 8 +- client/configurators/vpn_configurator.h | 14 +- .../configurators/wireguard_configurator.cpp | 4 +- client/configurators/wireguard_configurator.h | 2 +- client/ui/controllers/exportController.cpp | 73 +++- client/ui/controllers/exportController.h | 12 +- client/ui/models/clientManagementModel.cpp | 356 ++++++++++++++---- client/ui/models/clientManagementModel.h | 52 ++- client/ui/models/servers_model.cpp | 5 + client/ui/models/servers_model.h | 1 + client/ui/qml/Pages2/PageShare.qml | 266 +++++++++++-- client/vpnconnection.cpp | 7 +- client/vpnconnection.h | 2 - 19 files changed, 675 insertions(+), 157 deletions(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index b1ed39ee..5c82599d 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -279,7 +279,7 @@ void AmneziaApplication::initModels() { m_containersModel.reset(new ContainersModel(m_settings, this)); m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get()); - connect(m_vpnConnection.get(), &VpnConnection::newVpnConfigurationCreated, m_containersModel.get(), + connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, m_containersModel.get(), &ContainersModel::updateContainersConfig); m_serversModel.reset(new ServersModel(m_settings, this)); @@ -322,6 +322,11 @@ void AmneziaApplication::initModels() m_sftpConfigModel.reset(new SftpConfigModel(this)); m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get()); + + m_clientManagementModel.reset(new ClientManagementModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get()); + connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, m_clientManagementModel.get(), + &ClientManagementModel::appendClient); } void AmneziaApplication::initControllers() @@ -347,7 +352,7 @@ void AmneziaApplication::initControllers() 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_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, m_settings, m_configurator)); m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get()); m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings)); diff --git a/client/amnezia_application.h b/client/amnezia_application.h index 32300421..2638c66f 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -39,6 +39,7 @@ #include "ui/models/servers_model.h" #include "ui/models/services/sftpConfigModel.h" #include "ui/models/sites_model.h" +#include "ui/models/clientManagementModel.h" #define amnApp (static_cast(QCoreApplication::instance())) @@ -94,6 +95,7 @@ private: QSharedPointer m_languageModel; QSharedPointer m_protocolsModel; QSharedPointer m_sitesModel; + QSharedPointer m_clientManagementModel; QScopedPointer m_openVpnConfigModel; QScopedPointer m_shadowSocksConfigModel; diff --git a/client/configurators/awg_configurator.cpp b/client/configurators/awg_configurator.cpp index c3e42258..ca6c282b 100644 --- a/client/configurators/awg_configurator.cpp +++ b/client/configurators/awg_configurator.cpp @@ -10,11 +10,10 @@ AwgConfigurator::AwgConfigurator(std::shared_ptr settings, QObject *pa { } -QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials, - DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode) +QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode) { - QString config = WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, errorCode); + QString config = WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, clientId, errorCode); QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object(); QString awgConfig = jsonConfig.value(config_key::config).toString(); diff --git a/client/configurators/awg_configurator.h b/client/configurators/awg_configurator.h index cf0f2cae..ef40804c 100644 --- a/client/configurators/awg_configurator.h +++ b/client/configurators/awg_configurator.h @@ -12,7 +12,7 @@ public: AwgConfigurator(std::shared_ptr settings, QObject *parent = nullptr); QString genAwgConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr); }; #endif // AWGCONFIGURATOR_H diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index 8c58a376..d863fdab 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -83,7 +83,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co } QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode) + const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode) { ServerController serverController(m_settings); QString config = @@ -131,13 +131,13 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig) config.append("block-ipv6\n"); } if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { - + // no redirect-gateway } if (m_settings->routeMode() == Settings::VpnAllExceptSites) { -#ifndef Q_OS_ANDROID +#ifndef Q_OS_ANDROID config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n"); -#endif +#endif // Prevent ipv6 leak config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n"); config.append("block-ipv6\n"); diff --git a/client/configurators/openvpn_configurator.h b/client/configurators/openvpn_configurator.h index 3b84e0a0..6d927697 100644 --- a/client/configurators/openvpn_configurator.h +++ b/client/configurators/openvpn_configurator.h @@ -24,7 +24,7 @@ public: }; QString genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr); QString processConfigWithLocalSettings(QString jsonConfig); QString processConfigWithExportSettings(QString jsonConfig); diff --git a/client/configurators/vpn_configurator.cpp b/client/configurators/vpn_configurator.cpp index 6c5286c2..3018b52f 100644 --- a/client/configurators/vpn_configurator.cpp +++ b/client/configurators/vpn_configurator.cpp @@ -28,11 +28,11 @@ VpnConfigurator::VpnConfigurator(std::shared_ptr settings, QObject *pa } QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode) + const QJsonObject &containerConfig, Proto proto, QString &clientId, ErrorCode *errorCode) { switch (proto) { case Proto::OpenVpn: - return openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, errorCode); + return openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, clientId, errorCode); case Proto::ShadowSocks: return shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, errorCode); @@ -40,10 +40,10 @@ QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentia case Proto::Cloak: return cloakConfigurator->genCloakConfig(credentials, container, containerConfig, errorCode); case Proto::WireGuard: - return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, errorCode); + return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, clientId, errorCode); case Proto::Awg: - return awgConfigurator->genAwgConfig(credentials, container, containerConfig, errorCode); + return awgConfigurator->genAwgConfig(credentials, container, containerConfig, clientId, errorCode); case Proto::Ikev2: return ikev2Configurator->genIkev2Config(credentials, container, containerConfig, errorCode); diff --git a/client/configurators/vpn_configurator.h b/client/configurators/vpn_configurator.h index ac89b0e4..61dc2ac6 100644 --- a/client/configurators/vpn_configurator.h +++ b/client/configurators/vpn_configurator.h @@ -6,7 +6,6 @@ #include "configurator_base.h" #include "core/defs.h" - class OpenVpnConfigurator; class ShadowSocksConfigurator; class CloakConfigurator; @@ -16,14 +15,15 @@ class SshConfigurator; class AwgConfigurator; // Retrieve connection settings from server -class VpnConfigurator : ConfiguratorBase +class VpnConfigurator : public ConfiguratorBase { Q_OBJECT public: explicit VpnConfigurator(std::shared_ptr settings, QObject *parent = nullptr); QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, Proto proto, QString &clientId, + ErrorCode *errorCode = nullptr); QPair getDnsForConfig(int serverIndex); QString &processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config); @@ -32,8 +32,8 @@ public: QString &processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config); // workaround for containers which is not support normal configuration - void updateContainerConfigAfterInstallation(DockerContainer container, - QJsonObject &containerConfig, const QString &stdOut); + void updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig, + const QString &stdOut); std::shared_ptr openVpnConfigurator; std::shared_ptr shadowSocksConfigurator; @@ -42,6 +42,10 @@ public: std::shared_ptr ikev2Configurator; std::shared_ptr sshConfigurator; std::shared_ptr awgConfigurator; + +signals: + void newVpnConfigCreated(const QString &clientId, const QString &clientName, const DockerContainer container, + ServerCredentials credentials); }; #endif // VPN_CONFIGURATOR_H diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index e22c8282..9a6651ef 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -177,7 +177,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon } QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode) + const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode) { ServerController serverController(m_settings); QString scriptData = amnezia::scriptData(m_configTemplate, container); @@ -205,6 +205,8 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede jConfig[config_key::psk_key] = connData.pskKey; jConfig[config_key::server_pub_key] = connData.serverPubKey; + clientId = connData.clientPubKey; + return QJsonDocument(jConfig).toJson(); } diff --git a/client/configurators/wireguard_configurator.h b/client/configurators/wireguard_configurator.h index 7f8e1587..c1b4aa3c 100644 --- a/client/configurators/wireguard_configurator.h +++ b/client/configurators/wireguard_configurator.h @@ -26,7 +26,7 @@ public: }; QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr); QString processConfigWithLocalSettings(QString config); QString processConfigWithExportSettings(QString config); diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index ef5cc4e3..a8be624d 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -8,7 +8,9 @@ #include #include +#include "configurators/cloak_configurator.h" #include "configurators/openvpn_configurator.h" +#include "configurators/shadowsocks_configurator.h" #include "configurators/wireguard_configurator.h" #include "core/errorstrings.h" #include "systemController.h" @@ -19,11 +21,13 @@ ExportController::ExportController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, + const QSharedPointer &clientManagementModel, const std::shared_ptr &settings, const std::shared_ptr &configurator, QObject *parent) : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), + m_clientManagementModel(clientManagementModel), m_settings(settings), m_configurator(configurator) { @@ -75,13 +79,12 @@ void ExportController::generateFullAccessConfigAndroid() } #endif -void ExportController::generateConnectionConfig() +void ExportController::generateConnectionConfig(const QString &clientName) { clearPreviousConfig(); int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); - ServerCredentials credentials = - qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + ServerCredentials credentials = m_serversModel->getCurrentlyProcessedServerCredentials(); DockerContainer container = static_cast(m_containersModel->getCurrentlyProcessedContainerIndex()); QModelIndex containerModelIndex = m_containersModel->index(container); @@ -93,17 +96,25 @@ void ExportController::generateConnectionConfig() for (Proto protocol : ContainerProps::protocolsForContainer(container)) { QJsonObject protocolConfig = m_settings->protocolConfig(serverIndex, container, protocol); - QString vpnConfig = - m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, protocol, &errorCode); + QString clientId; + QString vpnConfig = m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, protocol, + clientId, &errorCode); if (errorCode) { emit exportErrorOccurred(errorString(errorCode)); return; } protocolConfig.insert(config_key::last_config, vpnConfig); containerConfig.insert(ProtocolProps::protoToString(protocol), protocolConfig); + if (protocol == Proto::OpenVpn || protocol == Proto::Awg || protocol == Proto::WireGuard) { + errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials); + if (errorCode) { + emit exportErrorOccurred(errorString(errorCode)); + return; + } + } } - QJsonObject config = m_settings->server(serverIndex); + QJsonObject config = m_settings->server(serverIndex); // todo change to servers_model if (!errorCode) { config.remove(config_key::userName); config.remove(config_key::password); @@ -126,13 +137,12 @@ void ExportController::generateConnectionConfig() emit exportConfigChanged(); } -void ExportController::generateOpenVpnConfig() +void ExportController::generateOpenVpnConfig(const QString &clientName) { clearPreviousConfig(); int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); - ServerCredentials credentials = - qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + ServerCredentials credentials = m_serversModel->getCurrentlyProcessedServerCredentials(); DockerContainer container = static_cast(m_containersModel->getCurrentlyProcessedContainerIndex()); QModelIndex containerModelIndex = m_containersModel->index(container); @@ -141,8 +151,9 @@ void ExportController::generateOpenVpnConfig() containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); ErrorCode errorCode = ErrorCode::NoError; - QString config = - m_configurator->openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, &errorCode); + QString clientId; + QString config = m_configurator->openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, + clientId, &errorCode); if (errorCode) { emit exportErrorOccurred(errorString(errorCode)); return; @@ -155,16 +166,23 @@ void ExportController::generateOpenVpnConfig() m_config.append(line + "\n"); } + m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); + + errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials); + if (errorCode) { + emit exportErrorOccurred(errorString(errorCode)); + return; + } + emit exportConfigChanged(); } -void ExportController::generateWireGuardConfig() +void ExportController::generateWireGuardConfig(const QString &clientName) { clearPreviousConfig(); int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); - ServerCredentials credentials = - qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + ServerCredentials credentials = m_serversModel->getCurrentlyProcessedServerCredentials(); DockerContainer container = static_cast(m_containersModel->getCurrentlyProcessedContainerIndex()); QModelIndex containerModelIndex = m_containersModel->index(container); @@ -172,9 +190,10 @@ void ExportController::generateWireGuardConfig() qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); + QString clientId; ErrorCode errorCode = ErrorCode::NoError; QString config = m_configurator->wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, - &errorCode); + clientId, &errorCode); if (errorCode) { emit exportErrorOccurred(errorString(errorCode)); return; @@ -187,6 +206,14 @@ void ExportController::generateWireGuardConfig() m_config.append(line + "\n"); } + m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); + + errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials); + if (errorCode) { + emit exportErrorOccurred(errorString(errorCode)); + return; + } + emit exportConfigChanged(); } @@ -205,6 +232,22 @@ void ExportController::exportConfig(const QString &fileName) SystemController::saveFile(fileName, m_config); } +void ExportController::revokeConfig(const int row, const DockerContainer container, ServerCredentials credentials) +{ + ErrorCode errorCode = m_clientManagementModel->revokeClient(row, container, credentials); + if (errorCode != ErrorCode::NoError) { + emit exportErrorOccurred(errorString(errorCode)); + } +} + +void ExportController::renameClient(const int row, const QString &clientName, const DockerContainer container, ServerCredentials credentials) +{ + ErrorCode errorCode = m_clientManagementModel->renameClient(row, clientName, container, credentials); + if (errorCode != ErrorCode::NoError) { + emit exportErrorOccurred(errorString(errorCode)); + } +} + QList ExportController::generateQrCodeImageSeries(const QByteArray &data) { double k = 850; diff --git a/client/ui/controllers/exportController.h b/client/ui/controllers/exportController.h index 24eaa5c8..b34a05bc 100644 --- a/client/ui/controllers/exportController.h +++ b/client/ui/controllers/exportController.h @@ -6,6 +6,7 @@ #include "configurators/vpn_configurator.h" #include "ui/models/containers_model.h" #include "ui/models/servers_model.h" +#include "ui/models/clientManagementModel.h" #ifdef Q_OS_ANDROID #include "platforms/android/authResultReceiver.h" #endif @@ -16,6 +17,7 @@ class ExportController : public QObject public: explicit ExportController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, + const QSharedPointer &clientManagementModel, const std::shared_ptr &settings, const std::shared_ptr &configurator, QObject *parent = nullptr); @@ -28,15 +30,18 @@ public slots: #if defined(Q_OS_ANDROID) void generateFullAccessConfigAndroid(); #endif - void generateConnectionConfig(); - void generateOpenVpnConfig(); - void generateWireGuardConfig(); + void generateConnectionConfig(const QString &clientName); + void generateOpenVpnConfig(const QString &clientName); + void generateWireGuardConfig(const QString &clientName); QString getConfig(); QList getQrCodes(); void exportConfig(const QString &fileName); + void revokeConfig(const int row, const DockerContainer container, ServerCredentials credentials); + void renameClient(const int row, const QString &clientName, const DockerContainer container, ServerCredentials credentials); + signals: void generateConfig(int type); void exportErrorOccurred(const QString &errorMessage); @@ -55,6 +60,7 @@ private: QSharedPointer m_serversModel; QSharedPointer m_containersModel; + QSharedPointer m_clientManagementModel; std::shared_ptr m_settings; std::shared_ptr m_configurator; diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index 87652ff2..c4c4fb7a 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -1,104 +1,330 @@ #include "clientManagementModel.h" +#include #include -ClientManagementModel::ClientManagementModel(QObject *parent) : QAbstractListModel(parent) -{ +#include "core/servercontroller.h" -} - -void ClientManagementModel::clearData() +ClientManagementModel::ClientManagementModel(std::shared_ptr settings, QObject *parent) + : m_settings(settings), QAbstractListModel(parent) { - beginResetModel(); - m_content.clear(); - endResetModel(); -} - -void ClientManagementModel::setContent(const QVector &data) -{ - beginResetModel(); - m_content = data; - endResetModel(); -} - -QJsonObject ClientManagementModel::getContent(amnezia::Proto protocol) -{ - QJsonObject clientsTable; - for (const auto &item : m_content) { - if (protocol == amnezia::Proto::OpenVpn) { - clientsTable[item.toJsonObject()["openvpnCertId"].toString()] = item.toJsonObject(); - } else if (protocol == amnezia::Proto::WireGuard) { - clientsTable[item.toJsonObject()["wireguardPublicKey"].toString()] = item.toJsonObject(); - } - } - return clientsTable; } int ClientManagementModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return static_cast(m_content.size()); + return static_cast(m_clientsTable.size()); } QVariant ClientManagementModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() < 0 - || index.row() >= static_cast(m_content.size())) { + if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_clientsTable.size())) { return QVariant(); } - if (role == NameRole) { - return m_content[index.row()].toJsonObject()["clientName"].toString(); - } else if (role == OpenVpnCertIdRole) { - return m_content[index.row()].toJsonObject()["openvpnCertId"].toString(); - } else if (role == OpenVpnCertDataRole) { - return m_content[index.row()].toJsonObject()["openvpnCertData"].toString(); - } else if (role == WireGuardPublicKey) { - return m_content[index.row()].toJsonObject()["wireguardPublicKey"].toString(); + auto client = m_clientsTable.at(index.row()).toObject(); + auto userData = client.value("userData").toObject(); + + switch (role) { + case UserNameRole: return userData.value("clientName").toString(); + case ContainerNameRole: + return ContainerProps::containerHumanNames().value( + static_cast(userData.value("container").toInt())); } return QVariant(); } -void ClientManagementModel::setData(const QModelIndex &index, QVariant data, int role) +ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCredentials credentials) { - if (!index.isValid() || index.row() < 0 - || index.row() >= static_cast(m_content.size())) { - return; + ServerController serverController(m_settings); + + ErrorCode error = ErrorCode::NoError; + QString stdOut; + auto cbReadStdOut = [&](const QString &data, libssh::Client &) { + stdOut += data + "\n"; + return ErrorCode::NoError; + }; + + const QString clientsTableFile = + QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + const QByteArray clientsTableString = + serverController.getTextFileFromContainer(container, credentials, clientsTableFile, &error); + if (error != ErrorCode::NoError) { + return error; } - auto client = m_content[index.row()].toJsonObject(); - if (role == NameRole) { - client["clientName"] = data.toString(); - } else if (role == OpenVpnCertIdRole) { - client["openvpnCertId"] = data.toString(); - } else if (role == OpenVpnCertDataRole) { - client["openvpnCertData"] = data.toString(); - } else if (role == WireGuardPublicKey) { - client["wireguardPublicKey"] = data.toString(); - } else { - return; - } - if (m_content[index.row()] != client) { - m_content[index.row()] = client; - emit dataChanged(index, index); + beginResetModel(); + m_clientsTable = QJsonDocument::fromJson(clientsTableString).array(); + + if (m_clientsTable.isEmpty()) { + int count = 0; + + if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks + || container == DockerContainer::Cloak) { + const QString getOpenVpnClientsList = + "sudo docker exec -i $CONTAINER_NAME bash -c 'ls /opt/amnezia/openvpn/pki/issued'"; + QString script = serverController.replaceVars(getOpenVpnClientsList, + serverController.genVarsForScript(credentials, container)); + error = serverController.runScript(credentials, script, cbReadStdOut); + if (error != ErrorCode::NoError) { + return error; + } + + if (!stdOut.isEmpty()) { + QStringList certsIds = stdOut.split("\n", Qt::SkipEmptyParts); + certsIds.removeAll("AmneziaReq.crt"); + + for (auto &openvpnCertId : certsIds) { + openvpnCertId.replace(".crt", ""); + if (!isClientExists(openvpnCertId)) { + QJsonObject client; + client["userId"] = openvpnCertId; + + QJsonObject userData; + userData["clientName"] = QString("Client %1").arg(count); + userData["container"] = container; + client["userData"] = userData; + + m_clientsTable.push_back(client); + + count++; + } + } + } + } else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) { + const QString wireGuardConfigFile = + QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg"); + const QString wireguardConfigString = + serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); + if (error != ErrorCode::NoError) { + return error; + } + + auto configLines = wireguardConfigString.split("\n", Qt::SkipEmptyParts); + QStringList wireguardKeys; + for (const auto &line : configLines) { + auto configPair = line.split(" = ", Qt::SkipEmptyParts); + if (configPair.front() == "PublicKey") { + wireguardKeys.push_back(configPair.back()); + } + } + + for (auto &wireguardKey : wireguardKeys) { + if (!isClientExists(wireguardKey)) { + QJsonObject client; + client["userId"] = wireguardKey; + + QJsonObject userData; + userData["clientName"] = QString("Client %1").arg(count); + userData["container"] = container; + client["userData"] = userData; + + m_clientsTable.push_back(client); + + count++; + } + } + } + + const QByteArray newClientsTableString = QJsonDocument(m_clientsTable).toJson(); + if (clientsTableString != newClientsTableString) { + error = serverController.uploadTextFileToContainer(container, credentials, newClientsTableString, + clientsTableFile); + } } + + endResetModel(); + return error; } -bool ClientManagementModel::removeRows(int row) +bool ClientManagementModel::isClientExists(const QString &clientId) { + for (const QJsonValue &value : qAsConst(m_clientsTable)) { + if (value.isObject()) { + QJsonObject obj = value.toObject(); + if (obj.contains("userId") && obj["userId"].toString() == clientId) { + return true; + } + } + } + return false; +} + +ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QString &clientName, + const DockerContainer container, ServerCredentials credentials) +{ + ErrorCode error; + if (m_clientsTable.empty()) { + error = updateModel(container, credentials); + if (error != ErrorCode::NoError) { + return error; + } + + for (int i = 0; i < m_clientsTable.size(); i++) { + if (m_clientsTable.at(i).toObject().value("userId") == (clientId)) { + error = renameClient(i, clientName, container, credentials); + if (error != ErrorCode::NoError) { + return error; + } + } + } + } else { + beginResetModel(); + QJsonObject client; + client["userId"] = clientId; + + QJsonObject userData; + userData["clientName"] = clientName; + userData["container"] = container; + client["userData"] = userData; + m_clientsTable.push_back(client); + endResetModel(); + + const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); + + ServerController serverController(m_settings); + const QString clientsTableFile = + QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + + error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); + } + + return error; +} + +ErrorCode ClientManagementModel::renameClient(const int row, const QString &clientName, const DockerContainer container, + ServerCredentials credentials) +{ + auto client = m_clientsTable.at(row).toObject(); + auto userData = client["userData"].toObject(); + userData["clientName"] = clientName; + client["userData"] = userData; + + m_clientsTable.replace(row, client); + emit dataChanged(index(row, 0), index(row, 0)); + + const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); + + ServerController serverController(m_settings); + const QString clientsTableFile = + QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + + ErrorCode error = + serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); + + return error; +} + +ErrorCode ClientManagementModel::revokeClient(const int row, const DockerContainer container, + ServerCredentials credentials) +{ + if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks + || container == DockerContainer::Cloak) { + return revokeOpenVpn(row, container, credentials); + } else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) { + return revokeWireGuard(row, container, credentials); + } + return ErrorCode::NoError; +} + +ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContainer container, + ServerCredentials credentials) +{ + auto client = m_clientsTable.at(row).toObject(); + QString clientId = client.value("userId").toString(); + + const QString getOpenVpnCertData = QString("sudo docker exec -i $CONTAINER_NAME bash -c '" + "cd /opt/amnezia/openvpn ;\\" + "easyrsa revoke %1 ;\\" + "easyrsa gen-crl ;\\" + "cp pki/crl.pem .'") + .arg(clientId); + + ServerController serverController(m_settings); + const QString script = + serverController.replaceVars(getOpenVpnCertData, serverController.genVarsForScript(credentials, container)); + ErrorCode error = serverController.runScript(credentials, script); + if (error != ErrorCode::NoError) { + return error; + } + beginRemoveRows(QModelIndex(), row, row); - m_content.removeAt(row); + m_clientsTable.removeAt(row); endRemoveRows(); - return true; + + const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); + + const QString clientsTableFile = + QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); + if (error != ErrorCode::NoError) { + return error; + } + + return ErrorCode::NoError; +} + +ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerContainer container, + ServerCredentials credentials) +{ + ErrorCode error; + ServerController serverController(m_settings); + + const QString wireGuardConfigFile = + QString("/opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg"); + const QString wireguardConfigString = + serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); + if (error != ErrorCode::NoError) { + return error; + } + + auto client = m_clientsTable.at(row).toObject(); + QString clientId = client.value("userId").toString(); + + auto configSections = wireguardConfigString.split("[", Qt::SkipEmptyParts); + for (auto §ion : configSections) { + if (section.contains(clientId)) { + configSections.removeOne(section); + break; + } + } + QString newWireGuardConfig = configSections.join("["); + newWireGuardConfig.insert(0, "["); + error = serverController.uploadTextFileToContainer(container, credentials, newWireGuardConfig, wireGuardConfigFile); + if (error != ErrorCode::NoError) { + return error; + } + + beginRemoveRows(QModelIndex(), row, row); + m_clientsTable.removeAt(row); + endRemoveRows(); + + const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); + + const QString clientsTableFile = + QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); + if (error != ErrorCode::NoError) { + return error; + } + + const QString script = "sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'"; + error = serverController.runScript( + credentials, + serverController.replaceVars(script.arg(wireGuardConfigFile), + serverController.genVarsForScript(credentials, container))); + if (error != ErrorCode::NoError) { + return error; + } + + return ErrorCode::NoError; } QHash ClientManagementModel::roleNames() const { QHash roles; - roles[NameRole] = "clientName"; - roles[OpenVpnCertIdRole] = "openvpnCertId"; - roles[OpenVpnCertDataRole] = "openvpnCertData"; - roles[WireGuardPublicKey] = "wireguardPublicKey"; + roles[UserNameRole] = "userName"; + roles[ContainerNameRole] = "containerName"; return roles; } diff --git a/client/ui/models/clientManagementModel.h b/client/ui/models/clientManagementModel.h index 5230c337..c51ce816 100644 --- a/client/ui/models/clientManagementModel.h +++ b/client/ui/models/clientManagementModel.h @@ -2,36 +2,62 @@ #define CLIENTMANAGEMENTMODEL_H #include +#include #include "protocols/protocols_defs.h" +#include "settings.h" class ClientManagementModel : public QAbstractListModel { Q_OBJECT -public: - enum ClientRoles { - NameRole = Qt::UserRole + 1, - OpenVpnCertIdRole, - OpenVpnCertDataRole, - WireGuardPublicKey, + struct ClientManagementData + { + QString userId; + QJsonObject userData; + + bool operator==(const ClientManagementData &r) const + { + return userId == r.userId; + } + + bool operator==(const QString &otherUserId) const + { + return userId == otherUserId; + } }; - ClientManagementModel(QObject *parent = nullptr); +public: + enum Roles { + UserNameRole = Qt::UserRole + 1, + ContainerNameRole, + }; + + ClientManagementModel(std::shared_ptr settings, QObject *parent = nullptr); - void clearData(); - void setContent(const QVector &data); - QJsonObject getContent(amnezia::Proto protocol); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - void setData(const QModelIndex &index, QVariant data, int role = Qt::DisplayRole); - bool removeRows(int row); + +public slots: + ErrorCode updateModel(DockerContainer container, ServerCredentials credentials); + ErrorCode appendClient(const QString &clientId, const QString &clientName, const DockerContainer container, + ServerCredentials credentials); + ErrorCode renameClient(const int row, const QString &userName, const DockerContainer container, + ServerCredentials credentials); + ErrorCode revokeClient(const int index, const DockerContainer container, ServerCredentials credentials); protected: QHash roleNames() const override; private: - QVector m_content; + bool isClientExists(const QString &clientId); + + ErrorCode revokeOpenVpn(const int row, const DockerContainer container, ServerCredentials credentials); + ErrorCode revokeWireGuard(const int row, const DockerContainer container, ServerCredentials credentials); + + QJsonArray m_clientsTable; + + std::shared_ptr m_settings; }; #endif // CLIENTMANAGEMENTMODEL_H diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index a2a28630..69a4f9d9 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -145,6 +145,11 @@ QString ServersModel::getCurrentlyProcessedServerHostName() return qvariant_cast(data(m_currentlyProcessedServerIndex, HostNameRole)); } +const ServerCredentials ServersModel::getCurrentlyProcessedServerCredentials() +{ + return serverCredentials(m_currentlyProcessedServerIndex); +} + bool ServersModel::isDefaultServerCurrentlyProcessed() { return m_defaultServerIndex == m_currentlyProcessedServerIndex; diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index ad1d5a83..3010632a 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -53,6 +53,7 @@ public slots: int getCurrentlyProcessedServerIndex(); QString getCurrentlyProcessedServerHostName(); + const ServerCredentials getCurrentlyProcessedServerCredentials(); void addServer(const QJsonObject &server); void removeServer(); diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index ced7a5ff..01813703 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -23,10 +23,22 @@ PageType { WireGuard } + signal revokeConfig(int index) + onRevokeConfig: function(index) { + PageController.showBusyIndicator(true) + ExportController.revokeConfig(index, + ContainersModel.getCurrentlyProcessedContainerIndex(), + ServersModel.getCurrentlyProcessedServerCredentials()) + PageController.showBusyIndicator(false) + } + Connections { target: ExportController function onGenerateConfig(type) { + shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text + shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text + shareConnectionDrawer.needCloseButton = false shareConnectionDrawer.open() @@ -34,7 +46,7 @@ PageType { PageController.showBusyIndicator(true) switch (type) { - case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(); break; + case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(userNameTextField.textFieldText); break; case PageShare.ConfigType.AmneziaFullAccess: { if (Qt.platform.os === "android") { ExportController.generateFullAccessConfigAndroid(); @@ -44,14 +56,14 @@ PageType { break; } case PageShare.ConfigType.OpenVpn: { - ExportController.generateOpenVpnConfig(); + ExportController.generateOpenVpnConfig(userNameTextField.textFieldText) shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config") shareConnectionDrawer.configExtension = ".ovpn" shareConnectionDrawer.configFileName = "amnezia_for_openvpn" break; } case PageShare.ConfigType.WireGuard: { - ExportController.generateWireGuardConfig(); + ExportController.generateWireGuardConfig(userNameTextField.textFieldText) shareConnectionDrawer.configCaption = qsTr("Save WireGuard config") shareConnectionDrawer.configExtension = ".conf" shareConnectionDrawer.configFileName = "amnezia_for_wireguard" @@ -73,8 +85,6 @@ PageType { } } - property string fullConfigServerSelectorText - property string connectionServerSelectorText property bool showContent: false property bool shareButtonEnabled: true property list connectionTypesModel: [ @@ -147,20 +157,21 @@ PageType { onClicked: { accessTypeSelector.currentIndex = 0 - serverSelector.text = root.connectionServerSelectorText } } HorizontalRadioButton { - checked: root.currentIndex === 1 + checked: accessTypeSelector.currentIndex === 1 implicitWidth: (root.width - 32) / 2 - text: qsTr("Full access") + text: qsTr("Users") onClicked: { accessTypeSelector.currentIndex = 1 - serverSelector.text = root.fullConfigServerSelectorText - root.shareButtonEnabled = true + PageController.showBusyIndicator(true) + ClientManagementModel.updateModel(ContainersModel.getCurrentlyProcessedContainerIndex(), + ServersModel.getCurrentlyProcessedServerCredentials()) + PageController.showBusyIndicator(false) } } } @@ -171,11 +182,25 @@ PageType { Layout.topMargin: 24 Layout.bottomMargin: 24 - text: accessTypeSelector.currentIndex === 0 ? qsTr("Share VPN access without the ability to manage the server") : - qsTr("Share access to server management. The user with whom you share full access to the server will be able to add and remove any protocols and services to the server, as well as change settings.") + visible: accessTypeSelector.currentIndex === 0 + + text: qsTr("Share VPN access without the ability to manage the server") color: "#878B91" } + TextFieldWithHeaderType { + id: userNameTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + visible: accessTypeSelector.currentIndex === 0 + + headerText: qsTr("User name") + textFieldText: "New client" + + checkEmptyText: true + } + DropDownType { id: serverSelector @@ -217,10 +242,11 @@ PageType { serverSelector.severSelectorIndexChanged() } - if (accessTypeSelector.currentIndex !== 0) { - shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text - shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text - } + //full access label +// if (accessTypeSelector.currentIndex !== 0) { +// shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text +// shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text +// } serverSelector.menuVisible = false } @@ -231,8 +257,6 @@ PageType { function handler() { serverSelector.text = selectedText - root.fullConfigServerSelectorText = selectedText - root.connectionServerSelectorText = selectedText ServersModel.currentlyProcessedIndex = proxyServersModel.mapToSource(currentIndex) } } @@ -241,8 +265,6 @@ PageType { DropDownType { id: protocolSelector - visible: accessTypeSelector.currentIndex === 0 - Layout.fillWidth: true Layout.topMargin: 16 @@ -280,12 +302,6 @@ PageType { protocolSelector.menuVisible = false } - Component.onCompleted: { - if (accessTypeSelector.currentIndex === 0) { - handler() - } - } - Connections { target: serverSelector @@ -304,13 +320,17 @@ PageType { } protocolSelector.text = selectedText - root.connectionServerSelectorText = serverSelector.text - shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text - shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text ContainersModel.setCurrentlyProcessedContainerIndex(proxyContainersModel.mapToSource(currentIndex)) - fillConnectionTypeModel() + if (accessTypeSelector.currentIndex === 0) { + fillConnectionTypeModel() + } else { + PageController.showBusyIndicator(true) + ClientManagementModel.updateModel(ContainersModel.getCurrentlyProcessedContainerIndex(), + ServersModel.getCurrentlyProcessedServerCredentials()) + PageController.showBusyIndicator(false) + } } function fillConnectionTypeModel() { @@ -378,18 +398,196 @@ PageType { Layout.topMargin: 40 enabled: shareButtonEnabled + visible: accessTypeSelector.currentIndex === 0 text: qsTr("Share") imageSource: "qrc:/images/controls/share-2.svg" onClicked: { - if (accessTypeSelector.currentIndex === 0) { - ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type) - } else { - ExportController.generateConfig(PageShare.ConfigType.AmneziaFullAccess) + ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type) + } + } + + Header2Type { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 16 + + visible: accessTypeSelector.currentIndex === 1 + + headerText: qsTr("Users") + } + + ListView { + id: usersListView + Layout.fillWidth: true + Layout.preferredHeight: childrenRect.height + + visible: accessTypeSelector.currentIndex === 1 + + model: ClientManagementModel + + clip: true + interactive: false + + delegate: Item { + implicitWidth: usersListView.width + implicitHeight: delegateContent.implicitHeight + + ColumnLayout { + id: delegateContent + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.rightMargin: -16 + anchors.leftMargin: -16 + + LabelWithButtonType { + Layout.fillWidth: true + + text: userName + descriptionText: containerName + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + userInfoDrower.open() + } + } + + DividerType {} + + DrawerType { + id: userInfoDrower + + width: root.width + height: root.height * 0.45 + + ColumnLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 16 + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + spacing: 8 + + Header2Type { + Layout.fillWidth: true + Layout.bottomMargin: 24 + + headerText: userName + descriptionText: serverSelector.text + ", " + containerName + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 24 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + disabledColor: "#878B91" + textColor: "#D7D8DB" + borderWidth: 1 + + text: qsTr("Rename") + + onClicked: function() { + clientNameEditDrawer.open() + } + + DrawerType { + id: clientNameEditDrawer + + width: root.width + height: root.height * 0.35 + + onVisibleChanged: { + if (clientNameEditDrawer.visible) { + clientName.textField.forceActiveFocus() + } + } + + ColumnLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 16 + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + TextFieldWithHeaderType { + id: clientName + + Layout.fillWidth: true + headerText: qsTr("Client name") + textFieldText: userName + } + + BasicButtonType { + Layout.fillWidth: true + + text: qsTr("Save") + + onClicked: { + if (clientName.textFieldText !== userName) { + PageController.showBusyIndicator(true) + ExportController.renameClient(index, + clientName.textFieldText, + ContainersModel.getCurrentlyProcessedContainerIndex(), + ServersModel.getCurrentlyProcessedServerCredentials()) + PageController.showBusyIndicator(false) + clientNameEditDrawer.close() + } + } + } + } + } + } + + BasicButtonType { + Layout.fillWidth: true + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + disabledColor: "#878B91" + textColor: "#D7D8DB" + borderWidth: 1 + + text: qsTr("Revoke") + + onClicked: function() { + questionDrawer.headerText = qsTr("Revoke the config for a user - ") + userName + "?" + questionDrawer.descriptionText = qsTr("The user will no longer be able to connect to your server.") + questionDrawer.yesButtonText = qsTr("Continue") + questionDrawer.noButtonText = qsTr("Cancel") + + questionDrawer.yesButtonFunction = function() { + questionDrawer.close() + userInfoDrower.close() + root.revokeConfig(index) + } + questionDrawer.noButtonFunction = function() { + questionDrawer.close() + } + questionDrawer.open() + + + + } + } + } + } } } } + QuestionDrawer { + id: questionDrawer + } } } } diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index dea40f24..af278276 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -227,12 +227,15 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, const Ser configData = lastVpnConfig.value(proto); configData = m_configurator->processConfigWithLocalSettings(serverIndex, container, proto, configData); } else { - configData = m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, proto, errorCode); + QString clientId; + configData = m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, proto, clientId, errorCode); if (errorCode && *errorCode) { return ""; } + emit m_configurator->newVpnConfigCreated(clientId, "unnamed client", container, credentials); + QString configDataBeforeLocalProcessing = configData; configData = m_configurator->processConfigWithLocalSettings(serverIndex, container, proto, configData); @@ -323,7 +326,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede ErrorCode e = ErrorCode::NoError; m_vpnConfiguration = createVpnConfiguration(serverIndex, credentials, container, containerConfig, &e); - emit newVpnConfigurationCreated(); + if (e) { emit connectionStateChanged(Vpn::ConnectionState::Error); return; diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 45582de5..ebc0b80e 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -79,8 +79,6 @@ signals: void serviceIsNotReady(); - void newVpnConfigurationCreated(); - protected slots: void onBytesChanged(quint64 receivedBytes, quint64 sentBytes); void onConnectionStateChanged(Vpn::ConnectionState state); From d38c7ce6a5b9c727a67e7129ea64d90e422a09de Mon Sep 17 00:00:00 2001 From: pokamest Date: Wed, 22 Nov 2023 13:57:05 +0000 Subject: [PATCH 09/33] Error codes cleanup --- client/core/defs.h | 2 -- client/core/errorstrings.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/client/core/defs.h b/client/core/defs.h index 9547dd92..7de55286 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -47,7 +47,6 @@ enum ErrorCode SshSftpNoMediaError, // Local errors - FailedToSaveConfigData, OpenVpnConfigMissing, OpenVpnManagementServerError, ConfigMissing, @@ -67,7 +66,6 @@ enum ErrorCode // 3rd party utils errors OpenSslFailed, - OpenVpnExecutableCrashed, ShadowSocksExecutableCrashed, CloakExecutableCrashed, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index b17a5a9e..2a34576d 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -42,7 +42,6 @@ QString errorString(ErrorCode code){ case(SshSftpNoMediaError): return QObject::tr("Sftp error: No media was in remote drive"); // Local errors - case (FailedToSaveConfigData): return QObject::tr("Failed to save config to disk"); case (OpenVpnConfigMissing): return QObject::tr("OpenVPN config missing"); case (OpenVpnManagementServerError): return QObject::tr("OpenVPN management server error"); From e8ceeb6e20a022133f5e7e561b2a01a79327697b Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 23 Nov 2023 00:03:43 +0700 Subject: [PATCH 10/33] added full access sharing --- client/resources.qrc | 1 + client/ui/controllers/pageController.h | 4 +- client/ui/models/clientManagementModel.cpp | 254 +++++++++++-------- client/ui/models/clientManagementModel.h | 7 +- client/ui/qml/Pages2/PageShare.qml | 94 ++++--- client/ui/qml/Pages2/PageShareFullAccess.qml | 155 +++++++++++ 6 files changed, 376 insertions(+), 139 deletions(-) create mode 100644 client/ui/qml/Pages2/PageShareFullAccess.qml diff --git a/client/resources.qrc b/client/resources.qrc index 4c63383c..1baf993b 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -222,5 +222,6 @@ server_scripts/awg/configure_container.sh server_scripts/awg/run_container.sh server_scripts/awg/Dockerfile + ui/qml/Pages2/PageShareFullAccess.qml diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index 20c3bbed..f7e697fb 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -51,7 +51,9 @@ namespace PageLoader PageProtocolWireGuardSettings, PageProtocolAwgSettings, PageProtocolIKev2Settings, - PageProtocolRaw + PageProtocolRaw, + + PageShareFullAccess }; Q_ENUM_NS(PageEnum) diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index c4c4fb7a..f53794d5 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -4,6 +4,19 @@ #include #include "core/servercontroller.h" +#include "logger.h" + +namespace +{ + Logger logger("ClientManagementModel"); + + namespace configKey { + constexpr char clientId[] = "clientId"; + constexpr char clientName[] = "clientName"; + constexpr char container[] = "container"; + constexpr char userData[] = "userData"; + } +} ClientManagementModel::ClientManagementModel(std::shared_ptr settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent) @@ -23,13 +36,13 @@ QVariant ClientManagementModel::data(const QModelIndex &index, int role) const } auto client = m_clientsTable.at(index.row()).toObject(); - auto userData = client.value("userData").toObject(); + auto userData = client.value(configKey::userData).toObject(); switch (role) { - case UserNameRole: return userData.value("clientName").toString(); + case ClientNameRole: return userData.value(configKey::clientName).toString(); case ContainerNameRole: return ContainerProps::containerHumanNames().value( - static_cast(userData.value("container").toInt())); + static_cast(userData.value(configKey::container).toInt())); } return QVariant(); @@ -40,17 +53,13 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr ServerController serverController(m_settings); ErrorCode error = ErrorCode::NoError; - QString stdOut; - auto cbReadStdOut = [&](const QString &data, libssh::Client &) { - stdOut += data + "\n"; - return ErrorCode::NoError; - }; const QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); const QByteArray clientsTableString = serverController.getTextFileFromContainer(container, credentials, clientsTableFile, &error); if (error != ErrorCode::NoError) { + logger.error() << "Failed to get the clientsTable file from the server"; return error; } @@ -62,75 +71,21 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) { - const QString getOpenVpnClientsList = - "sudo docker exec -i $CONTAINER_NAME bash -c 'ls /opt/amnezia/openvpn/pki/issued'"; - QString script = serverController.replaceVars(getOpenVpnClientsList, - serverController.genVarsForScript(credentials, container)); - error = serverController.runScript(credentials, script, cbReadStdOut); - if (error != ErrorCode::NoError) { - return error; - } - - if (!stdOut.isEmpty()) { - QStringList certsIds = stdOut.split("\n", Qt::SkipEmptyParts); - certsIds.removeAll("AmneziaReq.crt"); - - for (auto &openvpnCertId : certsIds) { - openvpnCertId.replace(".crt", ""); - if (!isClientExists(openvpnCertId)) { - QJsonObject client; - client["userId"] = openvpnCertId; - - QJsonObject userData; - userData["clientName"] = QString("Client %1").arg(count); - userData["container"] = container; - client["userData"] = userData; - - m_clientsTable.push_back(client); - - count++; - } - } - } + error = getOpenVpnClients(serverController, container, credentials, count); } else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) { - const QString wireGuardConfigFile = - QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg"); - const QString wireguardConfigString = - serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); - if (error != ErrorCode::NoError) { - return error; - } - - auto configLines = wireguardConfigString.split("\n", Qt::SkipEmptyParts); - QStringList wireguardKeys; - for (const auto &line : configLines) { - auto configPair = line.split(" = ", Qt::SkipEmptyParts); - if (configPair.front() == "PublicKey") { - wireguardKeys.push_back(configPair.back()); - } - } - - for (auto &wireguardKey : wireguardKeys) { - if (!isClientExists(wireguardKey)) { - QJsonObject client; - client["userId"] = wireguardKey; - - QJsonObject userData; - userData["clientName"] = QString("Client %1").arg(count); - userData["container"] = container; - client["userData"] = userData; - - m_clientsTable.push_back(client); - - count++; - } - } + error = getWireGuardClients(serverController, container, credentials, count); + } + if (error != ErrorCode::NoError) { + return error; } const QByteArray newClientsTableString = QJsonDocument(m_clientsTable).toJson(); if (clientsTableString != newClientsTableString) { error = serverController.uploadTextFileToContainer(container, credentials, newClientsTableString, clientsTableFile); + if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the clientsTable file to the server"; + } } } @@ -138,12 +93,95 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr return error; } +ErrorCode ClientManagementModel::getOpenVpnClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count) +{ + ErrorCode error = ErrorCode::NoError; + QString stdOut; + auto cbReadStdOut = [&](const QString &data, libssh::Client &) { + stdOut += data + "\n"; + return ErrorCode::NoError; + }; + + const QString getOpenVpnClientsList = + "sudo docker exec -i $CONTAINER_NAME bash -c 'ls /opt/amnezia/openvpn/pki/issued'"; + QString script = serverController.replaceVars(getOpenVpnClientsList, + serverController.genVarsForScript(credentials, container)); + error = serverController.runScript(credentials, script, cbReadStdOut); + if (error != ErrorCode::NoError) { + logger.error() << "Failed to retrieve the list of issued certificates on the server"; + return error; + } + + if (!stdOut.isEmpty()) { + QStringList certsIds = stdOut.split("\n", Qt::SkipEmptyParts); + certsIds.removeAll("AmneziaReq.crt"); + + for (auto &openvpnCertId : certsIds) { + openvpnCertId.replace(".crt", ""); + if (!isClientExists(openvpnCertId)) { + QJsonObject client; + client[configKey::clientId] = openvpnCertId; + + QJsonObject userData; + userData[configKey::clientName] = QString("Client %1").arg(count); + userData[configKey::container] = container; + client[configKey::userData] = userData; + + m_clientsTable.push_back(client); + + count++; + } + } + } + return error; +} + +ErrorCode ClientManagementModel::getWireGuardClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count) +{ + ErrorCode error = ErrorCode::NoError; + + const QString wireGuardConfigFile = + QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg"); + const QString wireguardConfigString = + serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); + if (error != ErrorCode::NoError) { + logger.error() << "Failed to get the wg conf file from the server"; + return error; + } + + auto configLines = wireguardConfigString.split("\n", Qt::SkipEmptyParts); + QStringList wireguardKeys; + for (const auto &line : configLines) { + auto configPair = line.split(" = ", Qt::SkipEmptyParts); + if (configPair.front() == "PublicKey") { + wireguardKeys.push_back(configPair.back()); + } + } + + for (auto &wireguardKey : wireguardKeys) { + if (!isClientExists(wireguardKey)) { + QJsonObject client; + client[configKey::clientId] = wireguardKey; + + QJsonObject userData; + userData[configKey::clientName] = QString("Client %1").arg(count); + userData[configKey::container] = container; + client[configKey::userData] = userData; + + m_clientsTable.push_back(client); + + count++; + } + } + return error; +} + bool ClientManagementModel::isClientExists(const QString &clientId) { for (const QJsonValue &value : qAsConst(m_clientsTable)) { if (value.isObject()) { QJsonObject obj = value.toObject(); - if (obj.contains("userId") && obj["userId"].toString() == clientId) { + if (obj.contains(configKey::clientId) && obj[configKey::clientId].toString() == clientId) { return true; } } @@ -155,39 +193,38 @@ ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QSt const DockerContainer container, ServerCredentials credentials) { ErrorCode error; - if (m_clientsTable.empty()) { - error = updateModel(container, credentials); - if (error != ErrorCode::NoError) { - return error; + + error = updateModel(container, credentials); + if (error != ErrorCode::NoError) { + return error; + } + + for (int i = 0; i < m_clientsTable.size(); i++) { + if (m_clientsTable.at(i).toObject().value(configKey::clientId) == clientId) { + return renameClient(i, clientName, container, credentials); } + } - for (int i = 0; i < m_clientsTable.size(); i++) { - if (m_clientsTable.at(i).toObject().value("userId") == (clientId)) { - error = renameClient(i, clientName, container, credentials); - if (error != ErrorCode::NoError) { - return error; - } - } - } - } else { - beginResetModel(); - QJsonObject client; - client["userId"] = clientId; + beginResetModel(); + QJsonObject client; + client[configKey::clientId] = clientId; - QJsonObject userData; - userData["clientName"] = clientName; - userData["container"] = container; - client["userData"] = userData; - m_clientsTable.push_back(client); - endResetModel(); + QJsonObject userData; + userData[configKey::clientName] = clientName; + userData[configKey::container] = container; + client[configKey::userData] = userData; + m_clientsTable.push_back(client); + endResetModel(); - const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); + const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); - ServerController serverController(m_settings); - const QString clientsTableFile = - QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + ServerController serverController(m_settings); + const QString clientsTableFile = + QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); - error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); + error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); + if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the clientsTable file to the server"; } return error; @@ -197,9 +234,9 @@ ErrorCode ClientManagementModel::renameClient(const int row, const QString &clie ServerCredentials credentials) { auto client = m_clientsTable.at(row).toObject(); - auto userData = client["userData"].toObject(); - userData["clientName"] = clientName; - client["userData"] = userData; + auto userData = client[configKey::userData].toObject(); + userData[configKey::clientName] = clientName; + client[configKey::userData] = userData; m_clientsTable.replace(row, client); emit dataChanged(index(row, 0), index(row, 0)); @@ -212,6 +249,9 @@ ErrorCode ClientManagementModel::renameClient(const int row, const QString &clie ErrorCode error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); + if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the clientsTable file to the server"; + } return error; } @@ -232,7 +272,7 @@ ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContai ServerCredentials credentials) { auto client = m_clientsTable.at(row).toObject(); - QString clientId = client.value("userId").toString(); + QString clientId = client.value(configKey::clientId).toString(); const QString getOpenVpnCertData = QString("sudo docker exec -i $CONTAINER_NAME bash -c '" "cd /opt/amnezia/openvpn ;\\" @@ -246,6 +286,7 @@ ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContai serverController.replaceVars(getOpenVpnCertData, serverController.genVarsForScript(credentials, container)); ErrorCode error = serverController.runScript(credentials, script); if (error != ErrorCode::NoError) { + logger.error() << "Failed to revoke the certificate"; return error; } @@ -259,6 +300,7 @@ ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContai QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the clientsTable file to the server"; return error; } @@ -276,11 +318,12 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont const QString wireguardConfigString = serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); if (error != ErrorCode::NoError) { + logger.error() << "Failed to get the wg conf file from the server"; return error; } auto client = m_clientsTable.at(row).toObject(); - QString clientId = client.value("userId").toString(); + QString clientId = client.value(configKey::clientId).toString(); auto configSections = wireguardConfigString.split("[", Qt::SkipEmptyParts); for (auto §ion : configSections) { @@ -293,6 +336,7 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont newWireGuardConfig.insert(0, "["); error = serverController.uploadTextFileToContainer(container, credentials, newWireGuardConfig, wireGuardConfigFile); if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the wg conf file to the server"; return error; } @@ -306,6 +350,7 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); if (error != ErrorCode::NoError) { + logger.error() << "Failed to upload the clientsTable file to the server"; return error; } @@ -315,6 +360,7 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont serverController.replaceVars(script.arg(wireGuardConfigFile), serverController.genVarsForScript(credentials, container))); if (error != ErrorCode::NoError) { + logger.error() << "Failed to execute the command 'wg syncconf' on the server"; return error; } @@ -324,7 +370,7 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont QHash ClientManagementModel::roleNames() const { QHash roles; - roles[UserNameRole] = "userName"; + roles[ClientNameRole] = "clientName"; roles[ContainerNameRole] = "containerName"; return roles; } diff --git a/client/ui/models/clientManagementModel.h b/client/ui/models/clientManagementModel.h index c51ce816..25089b66 100644 --- a/client/ui/models/clientManagementModel.h +++ b/client/ui/models/clientManagementModel.h @@ -4,7 +4,7 @@ #include #include -#include "protocols/protocols_defs.h" +#include "core/servercontroller.h" #include "settings.h" class ClientManagementModel : public QAbstractListModel @@ -29,7 +29,7 @@ class ClientManagementModel : public QAbstractListModel public: enum Roles { - UserNameRole = Qt::UserRole + 1, + ClientNameRole = Qt::UserRole + 1, ContainerNameRole, }; @@ -55,6 +55,9 @@ private: ErrorCode revokeOpenVpn(const int row, const DockerContainer container, ServerCredentials credentials); ErrorCode revokeWireGuard(const int row, const DockerContainer container, ServerCredentials credentials); + ErrorCode getOpenVpnClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count); + ErrorCode getWireGuardClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count); + QJsonArray m_clientsTable; std::shared_ptr m_settings; diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 01813703..cb847583 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -18,7 +18,6 @@ PageType { enum ConfigType { AmneziaConnection, - AmneziaFullAccess, OpenVpn, WireGuard } @@ -46,24 +45,16 @@ PageType { PageController.showBusyIndicator(true) switch (type) { - case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(userNameTextField.textFieldText); break; - case PageShare.ConfigType.AmneziaFullAccess: { - if (Qt.platform.os === "android") { - ExportController.generateFullAccessConfigAndroid(); - } else { - ExportController.generateFullAccessConfig(); - } - break; - } + case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(clientNameTextField.textFieldText); break; case PageShare.ConfigType.OpenVpn: { - ExportController.generateOpenVpnConfig(userNameTextField.textFieldText) + ExportController.generateOpenVpnConfig(clientNameTextField.textFieldText) shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config") shareConnectionDrawer.configExtension = ".ovpn" shareConnectionDrawer.configFileName = "amnezia_for_openvpn" break; } case PageShare.ConfigType.WireGuard: { - ExportController.generateWireGuardConfig(userNameTextField.textFieldText) + ExportController.generateWireGuardConfig(clientNameTextField.textFieldText) shareConnectionDrawer.configCaption = qsTr("Save WireGuard config") shareConnectionDrawer.configExtension = ".conf" shareConnectionDrawer.configFileName = "amnezia_for_wireguard" @@ -129,6 +120,51 @@ PageType { Layout.topMargin: 24 headerText: qsTr("Share VPN Access") + + actionButtonImage: "qrc:/images/controls/more-vertical.svg" + actionButtonFunction: function() { + shareFullAccessDrawer.open() + } + + DrawerType { + id: shareFullAccessDrawer + + width: root.width + height: root.height * 0.45 + + + ColumnLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 16 + + spacing: 0 + + Header2Type { + Layout.fillWidth: true + Layout.bottomMargin: 16 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: qsTr("Share full access to the server and VPN") + descriptionText: qsTr("Use for your own devices, or share with those you trust to manage the server.") + } + + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Share") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + PageController.goToPage(PageEnum.PageShareFullAccess) + shareFullAccessDrawer.close() + } + } + } + } } Rectangle { @@ -189,7 +225,7 @@ PageType { } TextFieldWithHeaderType { - id: userNameTextField + id: clientNameTextField Layout.fillWidth: true Layout.topMargin: 16 @@ -242,11 +278,6 @@ PageType { serverSelector.severSelectorIndexChanged() } - //full access label -// if (accessTypeSelector.currentIndex !== 0) { -// shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text -// shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text -// } serverSelector.menuVisible = false } @@ -419,7 +450,7 @@ PageType { } ListView { - id: usersListView + id: clientsListView Layout.fillWidth: true Layout.preferredHeight: childrenRect.height @@ -431,7 +462,7 @@ PageType { interactive: false delegate: Item { - implicitWidth: usersListView.width + implicitWidth: clientsListView.width implicitHeight: delegateContent.implicitHeight ColumnLayout { @@ -447,19 +478,19 @@ PageType { LabelWithButtonType { Layout.fillWidth: true - text: userName + text: clientName descriptionText: containerName rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { - userInfoDrower.open() + clientInfoDrawer.open() } } DividerType {} DrawerType { - id: userInfoDrower + id: clientInfoDrawer width: root.width height: root.height * 0.45 @@ -478,7 +509,7 @@ PageType { Layout.fillWidth: true Layout.bottomMargin: 24 - headerText: userName + headerText: clientName descriptionText: serverSelector.text + ", " + containerName } @@ -507,7 +538,7 @@ PageType { onVisibleChanged: { if (clientNameEditDrawer.visible) { - clientName.textField.forceActiveFocus() + clientNameEditor.textField.forceActiveFocus() } } @@ -520,11 +551,10 @@ PageType { anchors.rightMargin: 16 TextFieldWithHeaderType { - id: clientName - + id: clientNameEditor Layout.fillWidth: true headerText: qsTr("Client name") - textFieldText: userName + textFieldText: clientName } BasicButtonType { @@ -533,10 +563,10 @@ PageType { text: qsTr("Save") onClicked: { - if (clientName.textFieldText !== userName) { + if (clientNameEditor.textFieldText !== clientName) { PageController.showBusyIndicator(true) ExportController.renameClient(index, - clientName.textFieldText, + clientNameEditor.textFieldText, ContainersModel.getCurrentlyProcessedContainerIndex(), ServersModel.getCurrentlyProcessedServerCredentials()) PageController.showBusyIndicator(false) @@ -561,14 +591,14 @@ PageType { text: qsTr("Revoke") onClicked: function() { - questionDrawer.headerText = qsTr("Revoke the config for a user - ") + userName + "?" + questionDrawer.headerText = qsTr("Revoke the config for a user - ") + clientName + "?" questionDrawer.descriptionText = qsTr("The user will no longer be able to connect to your server.") questionDrawer.yesButtonText = qsTr("Continue") questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.yesButtonFunction = function() { questionDrawer.close() - userInfoDrower.close() + clientInfoDrawer.close() root.revokeConfig(index) } questionDrawer.noButtonFunction = function() { diff --git a/client/ui/qml/Pages2/PageShareFullAccess.qml b/client/ui/qml/Pages2/PageShareFullAccess.qml new file mode 100644 index 00000000..e59c57ef --- /dev/null +++ b/client/ui/qml/Pages2/PageShareFullAccess.qml @@ -0,0 +1,155 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 +import ContainerProps 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Components" + +PageType { + id: root + + BackButtonType { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 20 + } + + FlickableType { + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + contentHeight: content.height + + ColumnLayout { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.rightMargin: 16 + anchors.leftMargin: 16 + + spacing: 0 + + HeaderType { + Layout.fillWidth: true + Layout.topMargin: 24 + + headerText: qsTr("Full access to the server and VPN") + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + + text: qsTr("We recommend that you use full access to the server only for your own additional devices.\n") + + qsTr("If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. ") + color: "#878B91" + } + + DropDownType { + id: serverSelector + + signal severSelectorIndexChanged + property int currentIndex: 0 + + Layout.fillWidth: true + Layout.topMargin: 16 + + drawerHeight: 0.4375 + + descriptionText: qsTr("Server") + headerText: qsTr("Server") + + listView: ListViewWithRadioButtonType { + id: serverSelectorListView + + rootWidth: root.width + imageSource: "qrc:/images/controls/check.svg" + + model: SortFilterProxyModel { + id: proxyServersModel + sourceModel: ServersModel + filters: [ + ValueFilter { + roleName: "hasWriteAccess" + value: true + } + ] + } + + currentIndex: 0 + + clickedFunction: function() { + handler() + + if (serverSelector.currentIndex !== serverSelectorListView.currentIndex) { + serverSelector.currentIndex = serverSelectorListView.currentIndex + } + + shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text + shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text + serverSelector.menuVisible = false + } + + Component.onCompleted: { + handler() + } + + function handler() { + serverSelector.text = selectedText + ServersModel.currentlyProcessedIndex = proxyServersModel.mapToSource(currentIndex) + } + } + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 40 + + text: qsTr("Share") + imageSource: "qrc:/images/controls/share-2.svg" + + onClicked: function() { + shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text + shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text + + shareConnectionDrawer.needCloseButton = false + + shareConnectionDrawer.open() + shareConnectionDrawer.contentVisible = false + PageController.showBusyIndicator(true) + + if (Qt.platform.os === "android") { + ExportController.generateFullAccessConfigAndroid(); + } else { + ExportController.generateFullAccessConfig(); + } + + PageController.showBusyIndicator(false) + + shareConnectionDrawer.needCloseButton = true + PageController.showTopCloseButton(true) + + shareConnectionDrawer.contentVisible = true + } + } + + ShareConnectionDrawer { + id: shareConnectionDrawer + } + } + } +} From 5dc3b64e0b618a79ccdce8181960e449fed54fb4 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 23 Nov 2023 14:32:16 +0700 Subject: [PATCH 11/33] added search bar for client management page --- client/images/controls/close.svg | 6 ++ client/images/controls/search.svg | 6 ++ client/resources.qrc | 2 + client/ui/controllers/exportController.cpp | 8 +++ client/ui/controllers/exportController.h | 1 + client/ui/models/clientManagementModel.cpp | 13 ++--- client/ui/models/clientManagementModel.h | 17 ------ client/ui/qml/Controls2/SwitcherType.qml | 1 + client/ui/qml/Pages2/PageShare.qml | 64 ++++++++++++++++++---- 9 files changed, 82 insertions(+), 36 deletions(-) create mode 100644 client/images/controls/close.svg create mode 100644 client/images/controls/search.svg diff --git a/client/images/controls/close.svg b/client/images/controls/close.svg new file mode 100644 index 00000000..0643cdc8 --- /dev/null +++ b/client/images/controls/close.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/client/images/controls/search.svg b/client/images/controls/search.svg new file mode 100644 index 00000000..56fa50e1 --- /dev/null +++ b/client/images/controls/search.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/client/resources.qrc b/client/resources.qrc index 1baf993b..dfadcb20 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -223,5 +223,7 @@ server_scripts/awg/run_container.sh server_scripts/awg/Dockerfile ui/qml/Pages2/PageShareFullAccess.qml + images/controls/close.svg + images/controls/search.svg diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index a8be624d..e3719e50 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -232,6 +232,14 @@ void ExportController::exportConfig(const QString &fileName) SystemController::saveFile(fileName, m_config); } +void ExportController::updateClientManagementModel(const DockerContainer container, ServerCredentials credentials) +{ + ErrorCode errorCode = m_clientManagementModel->updateModel(container, credentials); + if (errorCode != ErrorCode::NoError) { + emit exportErrorOccurred(errorString(errorCode)); + } +} + void ExportController::revokeConfig(const int row, const DockerContainer container, ServerCredentials credentials) { ErrorCode errorCode = m_clientManagementModel->revokeClient(row, container, credentials); diff --git a/client/ui/controllers/exportController.h b/client/ui/controllers/exportController.h index b34a05bc..56db9da0 100644 --- a/client/ui/controllers/exportController.h +++ b/client/ui/controllers/exportController.h @@ -39,6 +39,7 @@ public slots: void exportConfig(const QString &fileName); + void updateClientManagementModel(const DockerContainer container, ServerCredentials credentials); void revokeConfig(const int row, const DockerContainer container, ServerCredentials credentials); void renameClient(const int row, const QString &clientName, const DockerContainer container, ServerCredentials credentials); diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index f53794d5..bfc33eb9 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -40,9 +40,6 @@ QVariant ClientManagementModel::data(const QModelIndex &index, int role) const switch (role) { case ClientNameRole: return userData.value(configKey::clientName).toString(); - case ContainerNameRole: - return ContainerProps::containerHumanNames().value( - static_cast(userData.value(configKey::container).toInt())); } return QVariant(); @@ -50,6 +47,9 @@ QVariant ClientManagementModel::data(const QModelIndex &index, int role) const ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCredentials credentials) { + beginResetModel(); + m_clientsTable = QJsonArray(); + ServerController serverController(m_settings); ErrorCode error = ErrorCode::NoError; @@ -60,10 +60,10 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr serverController.getTextFileFromContainer(container, credentials, clientsTableFile, &error); if (error != ErrorCode::NoError) { logger.error() << "Failed to get the clientsTable file from the server"; + endResetModel(); return error; } - beginResetModel(); m_clientsTable = QJsonDocument::fromJson(clientsTableString).array(); if (m_clientsTable.isEmpty()) { @@ -76,6 +76,7 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr error = getWireGuardClients(serverController, container, credentials, count); } if (error != ErrorCode::NoError) { + endResetModel(); return error; } @@ -124,7 +125,6 @@ ErrorCode ClientManagementModel::getOpenVpnClients(ServerController &serverContr QJsonObject userData; userData[configKey::clientName] = QString("Client %1").arg(count); - userData[configKey::container] = container; client[configKey::userData] = userData; m_clientsTable.push_back(client); @@ -165,7 +165,6 @@ ErrorCode ClientManagementModel::getWireGuardClients(ServerController &serverCon QJsonObject userData; userData[configKey::clientName] = QString("Client %1").arg(count); - userData[configKey::container] = container; client[configKey::userData] = userData; m_clientsTable.push_back(client); @@ -211,7 +210,6 @@ ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QSt QJsonObject userData; userData[configKey::clientName] = clientName; - userData[configKey::container] = container; client[configKey::userData] = userData; m_clientsTable.push_back(client); endResetModel(); @@ -371,6 +369,5 @@ QHash ClientManagementModel::roleNames() const { QHash roles; roles[ClientNameRole] = "clientName"; - roles[ContainerNameRole] = "containerName"; return roles; } diff --git a/client/ui/models/clientManagementModel.h b/client/ui/models/clientManagementModel.h index 25089b66..f5312db7 100644 --- a/client/ui/models/clientManagementModel.h +++ b/client/ui/models/clientManagementModel.h @@ -11,26 +11,9 @@ class ClientManagementModel : public QAbstractListModel { Q_OBJECT - struct ClientManagementData - { - QString userId; - QJsonObject userData; - - bool operator==(const ClientManagementData &r) const - { - return userId == r.userId; - } - - bool operator==(const QString &otherUserId) const - { - return userId == otherUserId; - } - }; - public: enum Roles { ClientNameRole = Qt::UserRole + 1, - ContainerNameRole, }; ClientManagementModel(std::shared_ptr settings, QObject *parent = nullptr); diff --git a/client/ui/qml/Controls2/SwitcherType.qml b/client/ui/qml/Controls2/SwitcherType.qml index 1dbd0e84..37024872 100644 --- a/client/ui/qml/Controls2/SwitcherType.qml +++ b/client/ui/qml/Controls2/SwitcherType.qml @@ -87,6 +87,7 @@ Switch { id: content anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left ListItemTitleType { Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index cb847583..e3b28529 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -76,6 +76,7 @@ PageType { } } + property bool isSearchBarVisible: false property bool showContent: false property bool shareButtonEnabled: true property list connectionTypesModel: [ @@ -205,8 +206,8 @@ PageType { onClicked: { accessTypeSelector.currentIndex = 1 PageController.showBusyIndicator(true) - ClientManagementModel.updateModel(ContainersModel.getCurrentlyProcessedContainerIndex(), - ServersModel.getCurrentlyProcessedServerCredentials()) + ExportController.updateClientManagementModel(ContainersModel.getCurrentlyProcessedContainerIndex(), + ServersModel.getCurrentlyProcessedServerCredentials()) PageController.showBusyIndicator(false) } } @@ -354,12 +355,12 @@ PageType { ContainersModel.setCurrentlyProcessedContainerIndex(proxyContainersModel.mapToSource(currentIndex)) - if (accessTypeSelector.currentIndex === 0) { - fillConnectionTypeModel() - } else { + fillConnectionTypeModel() + + if (accessTypeSelector.currentIndex === 1) { PageController.showBusyIndicator(true) - ClientManagementModel.updateModel(ContainersModel.getCurrentlyProcessedContainerIndex(), - ServersModel.getCurrentlyProcessedServerCredentials()) + ExportController.updateClientManagementModel(ContainersModel.getCurrentlyProcessedContainerIndex(), + ServersModel.getCurrentlyProcessedServerCredentials()) PageController.showBusyIndicator(false) } } @@ -444,9 +445,36 @@ PageType { Layout.topMargin: 24 Layout.bottomMargin: 16 - visible: accessTypeSelector.currentIndex === 1 + visible: accessTypeSelector.currentIndex === 1 && !root.isSearchBarVisible headerText: qsTr("Users") + actionButtonImage: "qrc:/images/controls/search.svg" + actionButtonFunction: function() { + root.isSearchBarVisible = true + } + } + + RowLayout { + Layout.topMargin: 24 + Layout.bottomMargin: 16 + visible: accessTypeSelector.currentIndex === 1 && root.isSearchBarVisible + + TextFieldWithHeaderType { + id: searchTextField + Layout.fillWidth: true + + textFieldPlaceholderText: qsTr("Search") + } + + ImageButtonType { + image: "qrc:/images/controls/close.svg" + imageColor: "#D7D8DB" + + onClicked: function() { + root.isSearchBarVisible = false + searchTextField.textFieldText = "" + } + } } ListView { @@ -456,7 +484,15 @@ PageType { visible: accessTypeSelector.currentIndex === 1 - model: ClientManagementModel + model: SortFilterProxyModel { + id: proxyClientManagementModel + sourceModel: ClientManagementModel + filters: RegExpFilter { + roleName: "clientName" + pattern: ".*" + searchTextField.textFieldText + ".*" + caseSensitivity: Qt.CaseInsensitive + } + } clip: true interactive: false @@ -479,7 +515,6 @@ PageType { Layout.fillWidth: true text: clientName - descriptionText: containerName rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { @@ -510,7 +545,7 @@ PageType { Layout.bottomMargin: 24 headerText: clientName - descriptionText: serverSelector.text + ", " + containerName + descriptionText: serverSelector.text } BasicButtonType { @@ -620,4 +655,11 @@ PageType { } } } + MouseArea { + anchors.fill: parent + onPressed: function(mouse) { + forceActiveFocus() + mouse.accepted = false + } + } } From 1bf808c9ee546135ffdb13d0d47f7db033c50de0 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sat, 25 Nov 2023 13:02:02 +0700 Subject: [PATCH 12/33] fixed qr code generation for native configs --- client/ui/controllers/exportController.cpp | 46 +++++++++++-------- client/ui/controllers/exportController.h | 3 ++ .../qml/Components/ShareConnectionDrawer.qml | 31 ++++++++++++- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index 06bddb8a..048eadb0 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -8,10 +8,10 @@ #include #include -#include "configurators/openvpn_configurator.h" -#include "configurators/wireguard_configurator.h" -#include "configurators/shadowsocks_configurator.h" #include "configurators/cloak_configurator.h" +#include "configurators/openvpn_configurator.h" +#include "configurators/shadowsocks_configurator.h" +#include "configurators/wireguard_configurator.h" #include "core/errorstrings.h" #include "systemController.h" #ifdef Q_OS_ANDROID @@ -157,8 +157,6 @@ void ExportController::generateOpenVpnConfig() m_config.append(line + "\n"); } - m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); - emit exportConfigChanged(); } @@ -191,7 +189,8 @@ void ExportController::generateWireGuardConfig() m_config.append(line + "\n"); } - m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); + qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_config.toUtf8(), qrcodegen::QrCode::Ecc::LOW); + m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1))); emit exportConfigChanged(); } @@ -211,7 +210,8 @@ void ExportController::generateShadowSocksConfig() containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); ErrorCode errorCode = ErrorCode::NoError; - QString config = m_configurator->shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, &errorCode); + QString config = m_configurator->shadowSocksConfigurator->genShadowSocksConfig(credentials, container, + containerConfig, &errorCode); if (errorCode) { emit exportErrorOccurred(errorString(errorCode)); @@ -220,15 +220,20 @@ void ExportController::generateShadowSocksConfig() config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::ShadowSocks, config); QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object(); - m_config = QString("%1:%2@%3:%4") - .arg(configJson.value("method").toString(), - configJson.value("password").toString(), - configJson.value("server").toString(), - configJson.value("server_port").toString()); + QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n"); + for (const QString &line : lines) { + m_config.append(line + "\n"); + } - m_config = "ss://" + m_config.toUtf8().toBase64(); + m_nativeConfigString = + QString("%1:%2@%3:%4") + .arg(configJson.value("method").toString(), configJson.value("password").toString(), + configJson.value("server").toString(), configJson.value("server_port").toString()); - m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); + m_nativeConfigString = "ss://" + m_config.toUtf8().toBase64(); + + qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_nativeConfigString.toUtf8(), qrcodegen::QrCode::Ecc::LOW); + m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1))); emit exportConfigChanged(); } @@ -248,7 +253,8 @@ void ExportController::generateCloakConfig() containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); ErrorCode errorCode = ErrorCode::NoError; - QString config = m_configurator->cloakConfigurator->genCloakConfig(credentials, container, containerConfig, &errorCode); + QString config = + m_configurator->cloakConfigurator->genCloakConfig(credentials, container, containerConfig, &errorCode); if (errorCode) { emit exportErrorOccurred(errorString(errorCode)); @@ -265,8 +271,6 @@ void ExportController::generateCloakConfig() m_config.append(line + "\n"); } - m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); - emit exportConfigChanged(); } @@ -275,6 +279,11 @@ QString ExportController::getConfig() return m_config; } +QString ExportController::getNativeConfigString() +{ + return m_nativeConfigString; +} + QList ExportController::getQrCodes() { return m_qrCodes; @@ -299,7 +308,7 @@ QList ExportController::generateQrCodeImageSeries(const QByteArray &dat QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW); - QString svg = QString::fromStdString(toSvgString(qr, 0)); + QString svg = QString::fromStdString(toSvgString(qr, 1)); chunks.append(svgToBase64(svg)); } @@ -319,5 +328,6 @@ int ExportController::getQrCodesCount() void ExportController::clearPreviousConfig() { m_config.clear(); + m_nativeConfigString.clear(); m_qrCodes.clear(); } diff --git a/client/ui/controllers/exportController.h b/client/ui/controllers/exportController.h index ee94e741..25658529 100644 --- a/client/ui/controllers/exportController.h +++ b/client/ui/controllers/exportController.h @@ -22,6 +22,7 @@ public: Q_PROPERTY(QList qrCodes READ getQrCodes NOTIFY exportConfigChanged) Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY exportConfigChanged) Q_PROPERTY(QString config READ getConfig NOTIFY exportConfigChanged) + Q_PROPERTY(QString nativeConfigString READ getNativeConfigString NOTIFY exportConfigChanged) public slots: void generateFullAccessConfig(); @@ -35,6 +36,7 @@ public slots: void generateCloakConfig(); QString getConfig(); + QString getNativeConfigString(); QList getQrCodes(); void exportConfig(const QString &fileName); @@ -61,6 +63,7 @@ private: std::shared_ptr m_configurator; QString m_config; + QString m_nativeConfigString; QList m_qrCodes; #ifdef Q_OS_ANDROID diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index 1158dadc..e354e951 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -112,6 +112,30 @@ DrawerType { } } + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 8 + + visible: nativeConfigString.text !== "" + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + disabledColor: "#878B91" + textColor: "#D7D8DB" + borderWidth: 1 + + text: qsTr("Copy config string") + imageSource: "qrc:/images/controls/copy.svg" + + onClicked: { + nativeConfigString.selectAll() + nativeConfigString.copy() + nativeConfigString.select(0, 0) + PageController.showNotificationMessage(qsTr("Copied")) + } + } + BasicButtonType { Layout.fillWidth: true Layout.topMargin: 24 @@ -170,6 +194,12 @@ DrawerType { } TextField { + id: nativeConfigString + visible: false + text: ExportController.nativeConfigString + } + + TextArea { id: configText Layout.fillWidth: true @@ -213,7 +243,6 @@ DrawerType { Image { anchors.fill: parent - anchors.margins: 2 smooth: false source: ExportController.qrCodesCount ? ExportController.qrCodes[0] : "" From 3084892ed8d3cceff093b08214778151ac8d2c36 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sun, 26 Nov 2023 21:15:58 +0700 Subject: [PATCH 13/33] added selection of default server and container on the sharing page --- client/configurators/openvpn_configurator.cpp | 2 ++ client/ui/qml/Pages2/PageShare.qml | 13 +++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index d863fdab..972e460c 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -113,6 +113,8 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia QJsonObject jConfig; jConfig[config_key::config] = config; + clientId = connData.clientId; + return QJsonDocument(jConfig).toJson(); } diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index e3b28529..0d7ece75 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -242,7 +242,7 @@ PageType { id: serverSelector signal severSelectorIndexChanged - property int currentIndex: 0 + property int currentIndex: -1 Layout.fillWidth: true Layout.topMargin: 16 @@ -269,8 +269,6 @@ PageType { ] } - currentIndex: 0 - clickedFunction: function() { handler() @@ -283,8 +281,9 @@ PageType { } Component.onCompleted: { - handler() - serverSelector.severSelectorIndexChanged() + serverSelectorListView.currentIndex = ServersModel.isDefaultServerHasWriteAccess() ? + proxyServersModel.mapFromSource(ServersModel.defaultIndex) : 0 + serverSelectorListView.triggerCurrentItem() } function handler() { @@ -329,6 +328,8 @@ PageType { currentIndex: 0 clickedFunction: function() { + protocolSelectorListView.currentItem.y + handler() protocolSelector.menuVisible = false @@ -338,7 +339,7 @@ PageType { target: serverSelector function onSeverSelectorIndexChanged() { - protocolSelectorListView.currentIndex = 0 + protocolSelectorListView.currentIndex = proxyContainersModel.mapFromSource(ContainersModel.getDefaultContainer()) protocolSelectorListView.triggerCurrentItem() } } From c164814abd92a35364837073e504f31909be8e3d Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 27 Nov 2023 10:59:48 +0700 Subject: [PATCH 14/33] fixed default server setting after importing --- client/ui/qml/Pages2/PageSetupWizardViewConfig.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml index ac35651f..65a6f319 100644 --- a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -24,7 +24,7 @@ PageType { } function onImportFinished() { - if (ConnectionController.isConnected) { + if (!ConnectionController.isConnected) { ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1); } From 426ac49f6ff98b2562fa0b3ef154b106d2a27e60 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 27 Nov 2023 12:46:59 +0700 Subject: [PATCH 15/33] fixed "auto-connect" option --- client/amnezia_application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index e3adf67a..240c6d35 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -354,7 +354,7 @@ void AmneziaApplication::initControllers() m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings)); m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get()); - if (m_settingsController->isAutoStartEnabled() && m_serversModel->getDefaultServerIndex() >= 0) { + if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) { QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); }); } From 2d22b52b5dec849e098fb0cb38ef4b069d44ae9b Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 27 Nov 2023 13:42:08 +0700 Subject: [PATCH 16/33] limited client name length --- client/ui/qml/Pages2/PageShare.qml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 0d7ece75..3e08bf87 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -529,7 +529,7 @@ PageType { id: clientInfoDrawer width: root.width - height: root.height * 0.45 + height: root.height * 0.5 ColumnLayout { anchors.top: parent.top @@ -591,6 +591,7 @@ PageType { Layout.fillWidth: true headerText: qsTr("Client name") textFieldText: clientName + textField.maximumLength: 30 } BasicButtonType { @@ -641,9 +642,6 @@ PageType { questionDrawer.close() } questionDrawer.open() - - - } } } @@ -651,6 +649,7 @@ PageType { } } } + QuestionDrawer { id: questionDrawer } From a53e904f7b22c28914cafa3f1d20ead88c94c71b Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 27 Nov 2023 13:56:52 +0700 Subject: [PATCH 17/33] fixed appProcessIsRunning() for win11 --- deploy/installer/config/controlscript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/installer/config/controlscript.js b/deploy/installer/config/controlscript.js index 39404530..63831e01 100644 --- a/deploy/installer/config/controlscript.js +++ b/deploy/installer/config/controlscript.js @@ -76,7 +76,7 @@ function raiseInstallerWindow() function appProcessIsRunning() { if (runningOnWindows()) { - var cmdArgs = ["/FI", "WINDOWTITLE eq " + appName()]; + var cmdArgs = ["| findstr " + appExecutableFileName()]; var result = installer.execute("tasklist", cmdArgs); if ( Number(result[1]) === 0 ) { From 4fd0852bb33a6f93f66f06fffbb15c778598dc29 Mon Sep 17 00:00:00 2001 From: tiaga Date: Mon, 27 Nov 2023 21:47:54 +0700 Subject: [PATCH 18/33] Improve install_docker.sh - add delays before and after Docker activation - cancel installation if `sudo` wasn't found --- client/server_scripts/install_docker.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/server_scripts/install_docker.sh b/client/server_scripts/install_docker.sh index e6708ddf..6f19e090 100644 --- a/client/server_scripts/install_docker.sh +++ b/client/server_scripts/install_docker.sh @@ -7,13 +7,13 @@ if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\ if ! command -v sudo > /dev/null 2>&1; then $pm $check_pkgs; $pm $silent_inst sudo; fi;\ if ! command -v fuser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst psmisc; fi;\ if ! command -v lsof > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst lsof; fi;\ -if ! command -v docker > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\ - if [ "$dist" = "fedora" ] || [ "$dist" = "centos" ] || [ "$dist" = "debian" ]; then sudo systemctl enable docker && sudo systemctl start docker; fi;\ +if ! command -v docker > /dev/null 2>&1; then \ + sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\ + sleep 5; sudo systemctl enable --now docker; sleep 5;\ fi;\ -if [ "$dist" = "debian" ]; then \ - docker_service=$(systemctl list-units --full --all | grep docker.service | grep -v inactive | grep -v dead | grep -v failed);\ - if [ -z "$docker_service" ]; then sudo $pm $check_pkgs; sudo $pm $silent_inst curl $docker_pkg; fi;\ - sleep 3 && sudo systemctl start docker && sleep 3;\ +if [ "$(systemctl is-active docker)" != "active" ]; then \ + sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\ + sleep 5; sudo systemctl start docker; sleep 5;\ fi;\ -if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install Docker"; exit 1; fi;\ +if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install sudo, command not found"; exit 1; fi;\ docker --version From db602ac65bb6cb3b1bf028dbdfe54bb188165dd3 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 29 Nov 2023 10:57:47 +0700 Subject: [PATCH 19/33] fixed ss string generation --- client/ui/controllers/exportController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index 048eadb0..4c084332 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -230,7 +230,7 @@ void ExportController::generateShadowSocksConfig() .arg(configJson.value("method").toString(), configJson.value("password").toString(), configJson.value("server").toString(), configJson.value("server_port").toString()); - m_nativeConfigString = "ss://" + m_config.toUtf8().toBase64(); + m_nativeConfigString = "ss://" + m_nativeConfigString.toUtf8().toBase64(); qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_nativeConfigString.toUtf8(), qrcodegen::QrCode::Ecc::LOW); m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1))); From a672434909773f94efab736f92f182546f20beb1 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 30 Nov 2023 07:13:43 +0300 Subject: [PATCH 20/33] =?UTF-8?q?changed=20tasklist=20|=20findstr=20=D0=BD?= =?UTF-8?q?=D0=B0=20tasklist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/installer/config/controlscript.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/deploy/installer/config/controlscript.js b/deploy/installer/config/controlscript.js index 63831e01..d0c82636 100644 --- a/deploy/installer/config/controlscript.js +++ b/deploy/installer/config/controlscript.js @@ -76,9 +76,7 @@ function raiseInstallerWindow() function appProcessIsRunning() { if (runningOnWindows()) { - var cmdArgs = ["| findstr " + appExecutableFileName()]; - var result = installer.execute("tasklist", cmdArgs); - + var result = installer.execute("tasklist"); if ( Number(result[1]) === 0 ) { if (result[0].indexOf(appExecutableFileName()) !== -1) { return true; From 3defb09da96dc15d3632c6a8269c85e47cab854a Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 30 Nov 2023 19:21:57 +0700 Subject: [PATCH 21/33] added a button to cancel installation if the package manager on the server is busy --- client/core/servercontroller.cpp | 6 +++--- client/core/servercontroller.h | 2 +- client/ui/controllers/installController.cpp | 3 +++ client/ui/controllers/installController.h | 1 + .../qml/Pages2/PageSetupWizardInstalling.qml | 19 +++++++++++++++++++ client/ui/qml/Pages2/PageSetupWizardStart.qml | 1 + client/ui/qml/Pages2/PageStart.qml | 1 + 7 files changed, 29 insertions(+), 4 deletions(-) diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 398b46b3..5bc5e519 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -634,9 +634,9 @@ QString ServerController::checkSshConnection(const ServerCredentials &credential return stdOut; } -void ServerController::setCancelInstallation(const bool cancel) +void ServerController::cancelInstallation() { - m_cancelInstallation = cancel; + m_cancelInstallation = true; } ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials) @@ -737,6 +737,7 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container) { + m_cancelInstallation = false; QString stdOut; auto cbReadStdOut = [&](const QString &data, libssh::Client &) { stdOut += data + "\n"; @@ -784,7 +785,6 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential watcher.setFuture(future); wait.exec(); - m_cancelInstallation = false; emit serverIsBusy(false); return future.result(); diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index 3191386c..6883274e 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -56,7 +56,7 @@ public: QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr); - void setCancelInstallation(const bool cancel); + void cancelInstallation(); ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function &callback); diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 80e74764..6e898a35 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -130,6 +130,7 @@ void InstallController::installServer(DockerContainer container, QJsonObject &co { ServerController serverController(m_settings); connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy); + connect(this, &InstallController::cancelInstallation, &serverController, &ServerController::cancelInstallation); QMap installedContainers; ErrorCode errorCode = @@ -181,6 +182,7 @@ void InstallController::installContainer(DockerContainer container, QJsonObject ServerController serverController(m_settings); connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy); + connect(this, &InstallController::cancelInstallation, &serverController, &ServerController::cancelInstallation); QMap installedContainers; ErrorCode errorCode = serverController.getAlreadyInstalledContainers(serverCredentials, installedContainers); @@ -282,6 +284,7 @@ void InstallController::updateContainer(QJsonObject config) ServerController serverController(m_settings); connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy); + connect(this, &InstallController::cancelInstallation, &serverController, &ServerController::cancelInstallation); auto errorCode = serverController.updateContainer(serverCredentials, container, oldContainerConfig, config); if (errorCode == ErrorCode::NoError) { diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index a5fd2875..cd0e6b22 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -65,6 +65,7 @@ signals: void passphraseRequestFinished(); void serverIsBusy(const bool isBusy); + void cancelInstallation(); void currentContainerUpdated(); diff --git a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml index c82ce855..391d408f 100644 --- a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml +++ b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml @@ -19,6 +19,7 @@ PageType { property bool isTimerRunning: true property string progressBarText: qsTr("Usually it takes no more than 5 minutes") + property bool isCancelButtonVisible: false Connections { target: InstallController @@ -61,11 +62,13 @@ PageType { function onServerIsBusy(isBusy) { if (isBusy) { + root.isCancelButtonVisible = true root.progressBarText = qsTr("Amnezia has detected that your server is currently ") + qsTr("busy installing other software. Amnezia installation ") + qsTr("will pause until the server finishes installing other software") root.isTimerRunning = false } else { + root.isCancelButtonVisible = false root.progressBarText = qsTr("Usually it takes no more than 5 minutes") root.isTimerRunning = true } @@ -150,6 +153,22 @@ PageType { text: root.progressBarText } + + BasicButtonType { + id: cancelIntallationButton + + Layout.fillWidth: true + Layout.topMargin: 24 + + visible: root.isCancelButtonVisible + + text: qsTr("Cancel installation") + + onClicked: { + InstallController.cancelInstallation() + PageController.showBusyIndicator(true) + } + } } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardStart.qml b/client/ui/qml/Pages2/PageSetupWizardStart.qml index 994ec200..2f89bc57 100644 --- a/client/ui/qml/Pages2/PageSetupWizardStart.qml +++ b/client/ui/qml/Pages2/PageSetupWizardStart.qml @@ -60,6 +60,7 @@ PageType { target: InstallController function onInstallationErrorOccurred(errorMessage) { + PageController.showBusyIndicator(false) PageController.showErrorMessage(errorMessage) var currentPageName = stackView.currentItem.objectName diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index ab02ace4..c92bdb8f 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -82,6 +82,7 @@ PageType { target: InstallController function onInstallationErrorOccurred(errorMessage) { + PageController.showBusyIndicator(false) PageController.showErrorMessage(errorMessage) var needCloseCurrentPage = false From e619fd4af9584e3e36deb52e7dd78590f47b213a Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Fri, 1 Dec 2023 14:16:27 +0700 Subject: [PATCH 22/33] replaced loader with PageSetupWizardInstalling when updating container settings --- client/ui/controllers/connectionController.cpp | 2 ++ client/ui/qml/Pages2/PageProtocolAwgSettings.qml | 3 +-- client/ui/qml/Pages2/PageProtocolCloakSettings.qml | 5 +++-- client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml | 3 +-- client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml | 5 +++-- client/ui/qml/Pages2/PageStart.qml | 2 ++ 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index 74438dcc..b8004d09 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -110,6 +110,8 @@ void ConnectionController::onCurrentContainerUpdated() if (m_isConnected || m_isConnectionInProgress) { emit reconnectWithUpdatedContainer(tr("Settings updated successfully, Reconnnection...")); openConnection(); + } else { + emit reconnectWithUpdatedContainer(tr("Settings updated successfully")); } } diff --git a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml index 237a8b46..a4f5abe3 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -312,9 +312,8 @@ PageType { onClicked: { forceActiveFocus() - PageController.showBusyIndicator(true) + PageController.goToPage(PageEnum.PageSetupWizardInstalling); InstallController.updateContainer(AwgConfigModel.getConfig()) - PageController.showBusyIndicator(false) } } } diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml index 78e666a7..98e9c28f 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -4,6 +4,8 @@ import QtQuick.Layouts import SortFilterProxyModel 0.2 +import PageEnum 1.0 + import "./" import "../Controls2" import "../Controls2/TextTypes" @@ -160,9 +162,8 @@ PageType { onClicked: { forceActiveFocus() - PageController.showBusyIndicator(true) + PageController.goToPage(PageEnum.PageSetupWizardInstalling); InstallController.updateContainer(CloakConfigModel.getConfig()) - PageController.showBusyIndicator(false) } } } diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 55cdcf04..971f9f39 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -390,9 +390,8 @@ PageType { onClicked: { forceActiveFocus() - PageController.showBusyIndicator(true) + PageController.goToPage(PageEnum.PageSetupWizardInstalling); InstallController.updateContainer(OpenVpnConfigModel.getConfig()) - PageController.showBusyIndicator(false) } } } diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml index 2453281f..573aca06 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -4,6 +4,8 @@ import QtQuick.Layouts import SortFilterProxyModel 0.2 +import PageEnum 1.0 + import "./" import "../Controls2" import "../Controls2/TextTypes" @@ -138,9 +140,8 @@ PageType { onClicked: { forceActiveFocus() - PageController.showBusyIndicator(true) + PageController.goToPage(PageEnum.PageSetupWizardInstalling); InstallController.updateContainer(ShadowSocksConfigModel.getConfig()) - PageController.showBusyIndicator(false) } } } diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index c92bdb8f..3afdf73a 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -100,6 +100,7 @@ PageType { function onUpdateContainerFinished(message) { PageController.showNotificationMessage(message) + PageController.closePage() } } @@ -108,6 +109,7 @@ PageType { function onReconnectWithUpdatedContainer(message) { PageController.showNotificationMessage(message) + PageController.closePage() } } From 3605f62feb22334d836329f8bb7d52c682717e92 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 5 Dec 2023 17:28:14 +0700 Subject: [PATCH 23/33] added support for the updated api --- client/protocols/protocols_defs.h | 5 -- client/ui/controllers/cloudController.cpp | 70 +++++++++++------------ client/ui/controllers/cloudController.h | 6 +- 3 files changed, 35 insertions(+), 46 deletions(-) diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index c86d503b..e602e028 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -80,11 +80,6 @@ namespace amnezia constexpr char awg[] = "awg"; constexpr char configVersion[] = "config_version"; - constexpr char apiEdnpoint[] = "api_endpoint"; - constexpr char serviceTypeId[] = "service_type_id"; - constexpr char accessToken[] = "access_token"; - constexpr char certificate[] = "certificate"; - constexpr char publicKey[] = "public_key"; constexpr char splitTunnelSites[] = "splitTunnelSites"; constexpr char splitTunnelType[] = "splitTunnelType"; diff --git a/client/ui/controllers/cloudController.cpp b/client/ui/controllers/cloudController.cpp index 188e12e3..321296ed 100644 --- a/client/ui/controllers/cloudController.cpp +++ b/client/ui/controllers/cloudController.cpp @@ -6,56 +6,49 @@ #include "configurators/openvpn_configurator.h" +namespace { + namespace configKey { + constexpr char cloak[] = "cloak"; + + constexpr char apiEdnpoint[] = "api_endpoint"; + constexpr char accessToken[] = "api_key"; + constexpr char certificate[] = "certificate"; + constexpr char publicKey[] = "public_key"; + constexpr char protocol[] = "protocol"; + } +} + CloudController::CloudController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, QObject *parent) : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel) { } -QString CloudController::genPublicKey(ServiceTypeId serviceTypeId) +QString CloudController::genPublicKey(const QString &protocol) { - switch (serviceTypeId) - { - case ServiceTypeId::AmneziaFreeRuWG: return "."; - case ServiceTypeId::AmneziaFreeRuCloak: return "."; - case ServiceTypeId::AmneziaFreeRuAWG: return "."; - case ServiceTypeId::AmneziaFreeRuReverseWG: return "."; - case ServiceTypeId::AmneziaFreeRuReverseCloak: return "."; - case ServiceTypeId::AmneziaFreeRuReverseAWG: return "."; + if (protocol == configKey::cloak) { + return "."; } + return QString(); } -QString CloudController::genCertificateRequest(ServiceTypeId serviceTypeId) +QString CloudController::genCertificateRequest(const QString &protocol) { - switch (serviceTypeId) - { - case ServiceTypeId::AmneziaFreeRuWG: return ""; - case ServiceTypeId::AmneziaFreeRuCloak: { - m_certRequest = OpenVpnConfigurator::createCertRequest(); - return m_certRequest.request; - } - case ServiceTypeId::AmneziaFreeRuAWG: return ""; - case ServiceTypeId::AmneziaFreeRuReverseWG: return ""; - case ServiceTypeId::AmneziaFreeRuReverseCloak: return ""; - case ServiceTypeId::AmneziaFreeRuReverseAWG: return ""; + if (protocol == configKey::cloak) { + m_certRequest = OpenVpnConfigurator::createCertRequest(); + return m_certRequest.request; } + return QString(); } -void CloudController::processCloudConfig(ServiceTypeId serviceTypeId, QString &config) +void CloudController::processCloudConfig(const QString &protocol, QString &config) { - switch (serviceTypeId) - { - case ServiceTypeId::AmneziaFreeRuWG: return; - case ServiceTypeId::AmneziaFreeRuCloak: { + if (protocol == configKey::cloak) { config.replace("", "\n"); config.replace("$OPENVPN_PRIV_KEY", m_certRequest.privKey); return; } - case ServiceTypeId::AmneziaFreeRuAWG: return; - case ServiceTypeId::AmneziaFreeRuReverseWG: return; - case ServiceTypeId::AmneziaFreeRuReverseCloak: return; - case ServiceTypeId::AmneziaFreeRuReverseAWG: return; - } + return; } bool CloudController::updateServerConfigFromCloud() @@ -69,19 +62,19 @@ bool CloudController::updateServerConfigFromCloud() QNetworkRequest request; request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QString endpoint = serverConfig.value(config_key::apiEdnpoint).toString(); + request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8()); + QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString(); request.setUrl(endpoint.replace("https", "http")); // todo remove - ServiceTypeId serviceTypeId = static_cast(serverConfig.value(config_key::serviceTypeId).toInt()); + QString protocol = serverConfig.value(configKey::protocol).toString(); QJsonObject obj; - obj[config_key::serviceTypeId] = serviceTypeId; - obj[config_key::accessToken] = serverConfig.value(config_key::accessToken); - obj[config_key::publicKey] = genPublicKey(serviceTypeId); - obj[config_key::certificate] = genCertificateRequest(serviceTypeId); + obj[configKey::publicKey] = genPublicKey(protocol); + obj[configKey::certificate] = genCertificateRequest(protocol); QByteArray requestBody = QJsonDocument(obj).toJson(); + qDebug() << requestBody; QScopedPointer reply; reply.reset(manager.post(request, requestBody)); @@ -103,7 +96,7 @@ bool CloudController::updateServerConfigFromCloud() } QString configStr = ba; - processCloudConfig(serviceTypeId, configStr); + processCloudConfig(protocol, configStr); QJsonObject cloudConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); @@ -117,6 +110,7 @@ bool CloudController::updateServerConfigFromCloud() emit serverConfigUpdated(); } else{ QString err = reply->errorString(); + qDebug() << QString::fromUtf8(reply->readAll());; qDebug() << reply->error(); qDebug() << err; qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); diff --git a/client/ui/controllers/cloudController.h b/client/ui/controllers/cloudController.h index 36c8427b..aaaed18e 100644 --- a/client/ui/controllers/cloudController.h +++ b/client/ui/controllers/cloudController.h @@ -34,10 +34,10 @@ signals: void serverConfigUpdated(); private: - QString genPublicKey(ServiceTypeId serviceTypeId); - QString genCertificateRequest(ServiceTypeId serviceTypeId); + QString genPublicKey(const QString &protocol); + QString genCertificateRequest(const QString &protocol); - void processCloudConfig(ServiceTypeId serviceTypeId, QString &config); + void processCloudConfig(const QString &protocol, QString &config); QSharedPointer m_serversModel; QSharedPointer m_containersModel; From c4014518cb630380bffd52711937aa2b22e7f771 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 5 Dec 2023 17:38:38 +0700 Subject: [PATCH 24/33] renamed CloudController to ApiController --- client/amnezia_application.cpp | 6 ++-- client/amnezia_application.h | 4 +-- ...{cloudController.cpp => apiController.cpp} | 35 ++++++++++--------- .../{cloudController.h => apiController.h} | 27 +++++--------- client/ui/qml/Components/ConnectButton.qml | 2 +- 5 files changed, 33 insertions(+), 41 deletions(-) rename client/ui/controllers/{cloudController.cpp => apiController.cpp} (79%) rename client/ui/controllers/{cloudController.h => apiController.h} (52%) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index fa947d29..1632835c 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -364,9 +364,9 @@ void AmneziaApplication::initControllers() m_systemController.reset(new SystemController(m_settings)); m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get()); - m_cloudController.reset(new CloudController(m_serversModel, m_containersModel)); - m_engine->rootContext()->setContextProperty("CloudController", m_cloudController.get()); + m_cloudController.reset(new ApiController(m_serversModel, m_containersModel)); + m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get()); - connect(m_cloudController.get(), &CloudController::serverConfigUpdated, this, + connect(m_cloudController.get(), &ApiController::serverConfigUpdated, this, [this]() { m_containersModel->setCurrentlyProcessedServerIndex(m_serversModel->getDefaultServerIndex()); }); } diff --git a/client/amnezia_application.h b/client/amnezia_application.h index b82bb9bb..a66b42c8 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -24,7 +24,7 @@ #include "ui/controllers/settingsController.h" #include "ui/controllers/sitesController.h" #include "ui/controllers/systemController.h" -#include "ui/controllers/cloudController.h" +#include "ui/controllers/apiController.h" #include "ui/models/containers_model.h" #include "ui/models/languageModel.h" #include "ui/models/protocols/cloakConfigModel.h" @@ -119,7 +119,7 @@ private: QScopedPointer m_settingsController; QScopedPointer m_sitesController; QScopedPointer m_systemController; - QScopedPointer m_cloudController; + QScopedPointer m_cloudController; }; #endif // AMNEZIA_APPLICATION_H diff --git a/client/ui/controllers/cloudController.cpp b/client/ui/controllers/apiController.cpp similarity index 79% rename from client/ui/controllers/cloudController.cpp rename to client/ui/controllers/apiController.cpp index 321296ed..d0c440df 100644 --- a/client/ui/controllers/cloudController.cpp +++ b/client/ui/controllers/apiController.cpp @@ -1,13 +1,15 @@ -#include "cloudController.h" +#include "apiController.h" +#include #include #include -#include #include "configurators/openvpn_configurator.h" -namespace { - namespace configKey { +namespace +{ + namespace configKey + { constexpr char cloak[] = "cloak"; constexpr char apiEdnpoint[] = "api_endpoint"; @@ -18,13 +20,13 @@ namespace { } } -CloudController::CloudController(const QSharedPointer &serversModel, - const QSharedPointer &containersModel, QObject *parent) +ApiController::ApiController(const QSharedPointer &serversModel, + const QSharedPointer &containersModel, QObject *parent) : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel) { } -QString CloudController::genPublicKey(const QString &protocol) +QString ApiController::genPublicKey(const QString &protocol) { if (protocol == configKey::cloak) { return "."; @@ -32,7 +34,7 @@ QString CloudController::genPublicKey(const QString &protocol) return QString(); } -QString CloudController::genCertificateRequest(const QString &protocol) +QString ApiController::genCertificateRequest(const QString &protocol) { if (protocol == configKey::cloak) { m_certRequest = OpenVpnConfigurator::createCertRequest(); @@ -41,7 +43,7 @@ QString CloudController::genCertificateRequest(const QString &protocol) return QString(); } -void CloudController::processCloudConfig(const QString &protocol, QString &config) +void ApiController::processCloudConfig(const QString &protocol, QString &config) { if (protocol == configKey::cloak) { config.replace("", "\n"); @@ -51,7 +53,7 @@ void CloudController::processCloudConfig(const QString &protocol, QString &confi return; } -bool CloudController::updateServerConfigFromCloud() +bool ApiController::updateServerConfigFromApi() { auto serverConfig = m_serversModel->getDefaultServerConfig(); @@ -62,7 +64,8 @@ bool CloudController::updateServerConfigFromCloud() QNetworkRequest request; request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8()); + request.setRawHeader("Authorization", + "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8()); QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString(); request.setUrl(endpoint.replace("https", "http")); // todo remove @@ -83,12 +86,13 @@ bool CloudController::updateServerConfigFromCloud() QObject::connect(reply.get(), &QNetworkReply::finished, &wait, &QEventLoop::quit); wait.exec(); - if(reply->error() == QNetworkReply::NoError){ + if (reply->error() == QNetworkReply::NoError) { QString contents = QString::fromUtf8(reply->readAll()); auto data = QJsonDocument::fromJson(contents.toUtf8()).object().value(config_key::config).toString(); data.replace("vpn://", ""); - QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); + QByteArray ba = QByteArray::fromBase64(data.toUtf8(), + QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); QByteArray ba_uncompressed = qUncompress(ba); if (!ba_uncompressed.isEmpty()) { @@ -108,16 +112,15 @@ bool CloudController::updateServerConfigFromCloud() serverConfig.insert(config_key::defaultContainer, cloudConfig.value(config_key::defaultContainer)); m_serversModel->editServer(serverConfig); emit serverConfigUpdated(); - } else{ + } else { QString err = reply->errorString(); - qDebug() << QString::fromUtf8(reply->readAll());; + qDebug() << QString::fromUtf8(reply->readAll()); //todo remove debug output qDebug() << reply->error(); qDebug() << err; qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); emit errorOccurred(tr("Error when retrieving configuration from cloud server")); return false; } - } return true; diff --git a/client/ui/controllers/cloudController.h b/client/ui/controllers/apiController.h similarity index 52% rename from client/ui/controllers/cloudController.h rename to client/ui/controllers/apiController.h index aaaed18e..5917f70f 100644 --- a/client/ui/controllers/cloudController.h +++ b/client/ui/controllers/apiController.h @@ -1,33 +1,22 @@ -#ifndef CLOUDCONTROLLER_H -#define CLOUDCONTROLLER_H +#ifndef APICONTROLLER_H +#define APICONTROLLER_H #include +#include "configurators/openvpn_configurator.h" #include "ui/models/containers_model.h" #include "ui/models/servers_model.h" -#include "configurators/openvpn_configurator.h" -class CloudController : public QObject +class ApiController : public QObject { Q_OBJECT - enum ServiceTypeId - { - AmneziaFreeRuWG = 0, - AmneziaFreeRuCloak, - AmneziaFreeRuAWG, - AmneziaFreeRuReverseWG, - AmneziaFreeRuReverseCloak, - AmneziaFreeRuReverseAWG - - }; - public: - explicit CloudController(const QSharedPointer &serversModel, - const QSharedPointer &containersModel, QObject *parent = nullptr); + explicit ApiController(const QSharedPointer &serversModel, + const QSharedPointer &containersModel, QObject *parent = nullptr); public slots: - bool updateServerConfigFromCloud(); + bool updateServerConfigFromApi(); signals: void errorOccurred(const QString &errorMessage); @@ -45,4 +34,4 @@ private: OpenVpnConfigurator::ConnectionData m_certRequest; }; -#endif // CLOUDCONTROLLER_H +#endif // APICONTROLLER_H diff --git a/client/ui/qml/Components/ConnectButton.qml b/client/ui/qml/Components/ConnectButton.qml index 757662ae..a0a57e6a 100644 --- a/client/ui/qml/Components/ConnectButton.qml +++ b/client/ui/qml/Components/ConnectButton.qml @@ -138,7 +138,7 @@ Button { } onClicked: { - if (!CloudController.updateServerConfigFromCloud()) { + if (!ApiController.updateServerConfigFromApi()) { return } From f3f98a50ed220f362da45baea5ecb8a95dd791e5 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 5 Dec 2023 17:44:13 +0700 Subject: [PATCH 25/33] fixed include path of servercontroller --- client/ui/models/clientManagementModel.cpp | 2 +- client/ui/models/clientManagementModel.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index bfc33eb9..8ec31d02 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -3,7 +3,7 @@ #include #include -#include "core/servercontroller.h" +#include "core/controllers/serverController.h" #include "logger.h" namespace diff --git a/client/ui/models/clientManagementModel.h b/client/ui/models/clientManagementModel.h index f5312db7..6b6adf68 100644 --- a/client/ui/models/clientManagementModel.h +++ b/client/ui/models/clientManagementModel.h @@ -4,7 +4,7 @@ #include #include -#include "core/servercontroller.h" +#include "core/controllers/serverController.h" #include "settings.h" class ClientManagementModel : public QAbstractListModel From b4c89ad58f8d90697e12c6fc876c29750a94c948 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Fri, 8 Dec 2023 13:50:03 +0700 Subject: [PATCH 26/33] Reworked the interaction between models. Now only serversModel directly interacts with server config --- client/amnezia_application.cpp | 35 ++- client/containers/containers_defs.cpp | 2 +- client/protocols/protocols_defs.h | 1 + .../ui/controllers/connectionController.cpp | 7 +- client/ui/controllers/exportController.cpp | 32 +- client/ui/controllers/installController.cpp | 28 +- client/ui/controllers/settingsController.cpp | 3 +- client/ui/controllers/settingsController.h | 2 + client/ui/models/containers_model.cpp | 157 +--------- client/ui/models/containers_model.h | 29 +- client/ui/models/servers_model.cpp | 295 +++++++++++++++++- client/ui/models/servers_model.h | 45 ++- .../qml/Components/HomeContainersListView.qml | 3 +- client/ui/qml/Pages2/PageHome.qml | 49 +-- .../Pages2/PageServiceTorWebsiteSettings.qml | 2 +- .../ui/qml/Pages2/PageSettingsServersList.qml | 2 +- .../qml/Pages2/PageSetupWizardInstalling.qml | 2 +- client/ui/qml/Pages2/PageShare.qml | 4 +- client/vpnconnection.cpp | 4 +- 19 files changed, 412 insertions(+), 290 deletions(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 1ef3316b..5079ed87 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -277,19 +277,16 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const void AmneziaApplication::initModels() { - m_containersModel.reset(new ContainersModel(m_settings, this)); + m_containersModel.reset(new ContainersModel(this)); m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get()); - connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, m_containersModel.get(), - &ContainersModel::updateContainersConfig); 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); - connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(), - &ContainersModel::setCurrentlyProcessedServerIndex); - connect(m_containersModel.get(), &ContainersModel::containersModelUpdated, m_serversModel.get(), - &ServersModel::updateContainersConfig); + connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), + &ContainersModel::updateModel); + connect(m_serversModel.get(), &ServersModel::defaultContainerChanged, m_containersModel.get(), + &ContainersModel::setDefaultContainer); + m_containersModel->setDefaultContainer(m_serversModel->getDefaultContainer()); // make better? m_languageModel.reset(new LanguageModel(m_settings, this)); m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get()); @@ -298,7 +295,7 @@ void AmneziaApplication::initModels() m_sitesModel.reset(new SitesModel(m_settings, this)); m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get()); - + m_protocolsModel.reset(new ProtocolsModel(m_settings, this)); m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get()); @@ -327,8 +324,13 @@ void AmneziaApplication::initModels() m_clientManagementModel.reset(new ClientManagementModel(m_settings, this)); m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get()); - connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, m_clientManagementModel.get(), - &ClientManagementModel::appendClient); + + connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this, + [this](const QString &clientId, const QString &clientName, const DockerContainer container, + ServerCredentials credentials) { + m_serversModel->reloadContainerConfig(); + m_clientManagementModel->appendClient(clientId, clientName, container, credentials); + }); } void AmneziaApplication::initControllers() @@ -354,7 +356,8 @@ void AmneziaApplication::initControllers() 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_clientManagementModel, m_settings, m_configurator)); + m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, + m_settings, m_configurator)); m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get()); m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings)); @@ -362,6 +365,8 @@ void AmneziaApplication::initControllers() if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) { QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); }); } + connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled , m_serversModel.get(), + &ServersModel::toggleAmneziaDns); m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel)); m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get()); @@ -372,6 +377,6 @@ void AmneziaApplication::initControllers() m_cloudController.reset(new ApiController(m_serversModel, m_containersModel)); m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get()); - connect(m_cloudController.get(), &ApiController::serverConfigUpdated, this, - [this]() { m_containersModel->setCurrentlyProcessedServerIndex(m_serversModel->getDefaultServerIndex()); }); + // connect(m_cloudController.get(), &ApiController::serverConfigUpdated, this, + // [this]() { m_containersModel->setCurrentlyProcessedServerIndex(m_serversModel->getDefaultServerIndex()); }); } diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index e133e79e..8bff8b72 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -58,7 +58,7 @@ QVector ContainerProps::protocolsForContainer(amnezia::DockerCon case DockerContainer::Ipsec: return { Proto::Ikev2 /*, Protocol::L2tp */ }; - case DockerContainer::Dns: return {}; + case DockerContainer::Dns: return { Proto::Dns }; case DockerContainer::Sftp: return { Proto::Sftp }; diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index e602e028..8d7d9bfb 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -21,6 +21,7 @@ namespace amnezia constexpr char dns2[] = "dns2"; constexpr char description[] = "description"; + constexpr char name[] = "name"; constexpr char cert[] = "cert"; constexpr char config[] = "config"; diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index de05d624..81bba3bf 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -26,13 +26,10 @@ ConnectionController::ConnectionController(const QSharedPointer &s void ConnectionController::openConnection() { int serverIndex = m_serversModel->getDefaultServerIndex(); - ServerCredentials credentials = - qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); DockerContainer container = m_containersModel->getDefaultContainer(); - QModelIndex containerModelIndex = m_containersModel->index(container); - const QJsonObject &containerConfig = - qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); + const QJsonObject &containerConfig = m_containersModel->getContainerConfig(container); if (container == DockerContainer::None) { emit connectionErrorOccurred(tr("VPN Protocols is not installed.\n Please install VPN container at first")); diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index 52dadba5..9930926f 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -84,12 +84,10 @@ void ExportController::generateConnectionConfig(const QString &clientName) clearPreviousConfig(); int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); - ServerCredentials credentials = m_serversModel->getCurrentlyProcessedServerCredentials(); + ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); DockerContainer container = static_cast(m_containersModel->getCurrentlyProcessedContainerIndex()); - QModelIndex containerModelIndex = m_containersModel->index(container); - QJsonObject containerConfig = - qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); + QJsonObject containerConfig = m_containersModel->getContainerConfig(container); containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); ErrorCode errorCode = ErrorCode::NoError; @@ -142,12 +140,10 @@ void ExportController::generateOpenVpnConfig(const QString &clientName) clearPreviousConfig(); int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); - ServerCredentials credentials = m_serversModel->getCurrentlyProcessedServerCredentials(); + ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); DockerContainer container = static_cast(m_containersModel->getCurrentlyProcessedContainerIndex()); - QModelIndex containerModelIndex = m_containersModel->index(container); - QJsonObject containerConfig = - qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); + QJsonObject containerConfig = m_containersModel->getContainerConfig(container); containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); ErrorCode errorCode = ErrorCode::NoError; @@ -182,12 +178,10 @@ void ExportController::generateWireGuardConfig(const QString &clientName) clearPreviousConfig(); int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); - ServerCredentials credentials = m_serversModel->getCurrentlyProcessedServerCredentials(); + ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); DockerContainer container = static_cast(m_containersModel->getCurrentlyProcessedContainerIndex()); - QModelIndex containerModelIndex = m_containersModel->index(container); - QJsonObject containerConfig = - qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); + QJsonObject containerConfig = m_containersModel->getContainerConfig(container); containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); QString clientId; @@ -223,13 +217,10 @@ void ExportController::generateShadowSocksConfig() clearPreviousConfig(); int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); - ServerCredentials credentials = - qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); DockerContainer container = static_cast(m_containersModel->getCurrentlyProcessedContainerIndex()); - QModelIndex containerModelIndex = m_containersModel->index(container); - QJsonObject containerConfig = - qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); + QJsonObject containerConfig = m_containersModel->getContainerConfig(container); containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); ErrorCode errorCode = ErrorCode::NoError; @@ -262,13 +253,10 @@ void ExportController::generateCloakConfig() clearPreviousConfig(); int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); - ServerCredentials credentials = - qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); DockerContainer container = static_cast(m_containersModel->getCurrentlyProcessedContainerIndex()); - QModelIndex containerModelIndex = m_containersModel->index(container); - QJsonObject containerConfig = - qvariant_cast(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); + QJsonObject containerConfig = m_containersModel->getContainerConfig(container); containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); ErrorCode errorCode = ErrorCode::NoError; diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 422d9849..77e25f43 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -199,12 +199,9 @@ void InstallController::installContainer(DockerContainer container, QJsonObject if (errorCode == ErrorCode::NoError) { for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) { - auto modelIndex = m_containersModel->index(iterator.key()); - QJsonObject containerConfig = - qvariant_cast(m_containersModel->data(modelIndex, ContainersModel::Roles::ConfigRole)); + QJsonObject containerConfig = m_containersModel->getContainerConfig(iterator.key()); if (containerConfig.isEmpty()) { - m_containersModel->setData(m_containersModel->index(iterator.key()), iterator.value(), - ContainersModel::Roles::ConfigRole); + m_serversModel->addContainerConfig(iterator.key(), iterator.value()); if (container != iterator.key()) { // skip the newly installed container isInstalledContainerAddedToGui = true; } @@ -252,12 +249,9 @@ void InstallController::scanServerForInstalledContainers() bool isInstalledContainerAddedToGui = false; for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) { - auto modelIndex = m_containersModel->index(iterator.key()); - QJsonObject containerConfig = - qvariant_cast(m_containersModel->data(modelIndex, ContainersModel::Roles::ConfigRole)); + QJsonObject containerConfig = m_containersModel->getContainerConfig(iterator.key()); if (containerConfig.isEmpty()) { - m_containersModel->setData(m_containersModel->index(iterator.key()), iterator.value(), - ContainersModel::Roles::ConfigRole); + m_serversModel->addContainerConfig(iterator.key(), iterator.value()); isInstalledContainerAddedToGui = true; } } @@ -276,16 +270,14 @@ void InstallController::updateContainer(QJsonObject config) 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)); + QJsonObject oldContainerConfig = m_containersModel->getContainerConfig(container); ServerController serverController(m_settings); connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy); auto errorCode = serverController.updateContainer(serverCredentials, container, oldContainerConfig, config); if (errorCode == ErrorCode::NoError) { - m_containersModel->setData(modelIndex, config, ContainersModel::Roles::ConfigRole); + m_serversModel->updateContainerConfig(container, config); m_protocolModel->updateModel(config); if ((serverIndex == m_serversModel->getDefaultServerIndex()) @@ -315,7 +307,7 @@ void InstallController::removeAllContainers() int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString(); - ErrorCode errorCode = m_containersModel->removeAllContainers(); + ErrorCode errorCode = m_serversModel->removeAllContainers(); if (errorCode == ErrorCode::NoError) { emit removeAllContainersFinished(tr("All containers from server '%1' have been removed").arg(serverName)); return; @@ -329,12 +321,12 @@ void InstallController::removeCurrentlyProcessedContainer() QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString(); int container = m_containersModel->getCurrentlyProcessedContainerIndex(); - QString containerName = m_containersModel->data(container, ContainersModel::Roles::NameRole).toString(); + QString containerName = m_containersModel->getCurrentlyProcessedContainerName(); - ErrorCode errorCode = m_containersModel->removeCurrentlyProcessedContainer(); + ErrorCode errorCode = m_serversModel->removeContainer(container); if (errorCode == ErrorCode::NoError) { - emit removeCurrentlyProcessedContainerFinished(tr("%1 has been removed from the server '%2'").arg(containerName).arg(serverName)); + emit removeCurrentlyProcessedContainerFinished(tr("%1 has been removed from the server '%2'").arg(containerName, serverName)); return; } emit installationErrorOccurred(errorString(errorCode)); diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 78d0dd67..9fa4d76b 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -42,6 +42,7 @@ SettingsController::SettingsController(const QSharedPointer &serve void SettingsController::toggleAmneziaDns(bool enable) { m_settings->setUseAmneziaDns(enable); + emit amneziaDnsToggled(enable); } bool SettingsController::isAmneziaDnsEnabled() @@ -138,7 +139,7 @@ void SettingsController::clearSettings() void SettingsController::clearCachedProfiles() { - m_containersModel->clearCachedProfiles(); + m_serversModel->clearCachedProfiles(); emit changeSettingsFinished(tr("Cached profiles cleared")); } diff --git a/client/ui/controllers/settingsController.h b/client/ui/controllers/settingsController.h index be041f3e..710d255f 100644 --- a/client/ui/controllers/settingsController.h +++ b/client/ui/controllers/settingsController.h @@ -70,6 +70,8 @@ signals: void importBackupFromOutside(QString filePath); + void amneziaDnsToggled(bool enable); + private: QSharedPointer m_serversModel; QSharedPointer m_containersModel; diff --git a/client/ui/models/containers_model.cpp b/client/ui/models/containers_model.cpp index 00b04ff4..7e73b2e9 100644 --- a/client/ui/models/containers_model.cpp +++ b/client/ui/models/containers_model.cpp @@ -1,9 +1,9 @@ #include "containers_model.h" -#include "core/controllers/serverController.h" +#include -ContainersModel::ContainersModel(std::shared_ptr settings, QObject *parent) - : m_settings(settings), QAbstractListModel(parent) +ContainersModel::ContainersModel(QObject *parent) + : QAbstractListModel(parent) { } @@ -13,37 +13,6 @@ int ContainersModel::rowCount(const QModelIndex &parent) const return ContainerProps::allContainers().size(); } -bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) { - return false; - } - - DockerContainer container = ContainerProps::allContainers().at(index.row()); - - switch (role) { - 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 IsDefaultRole: { //todo remove - m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container); - m_defaultContainerIndex = container; - emit defaultContainerChanged(); - } - default: break; - } - - emit containersModelUpdated(); - emit dataChanged(index, index); - return true; -} - QVariant ContainersModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) { @@ -84,37 +53,32 @@ QVariant ContainersModel::data(const int index, int role) const return data(modelIndex, role); } -void ContainersModel::setCurrentlyProcessedServerIndex(const int index) +void ContainersModel::updateModel(QJsonArray &containers) { beginResetModel(); - m_currentlyProcessedServerIndex = index; - m_containers = m_settings->containers(m_currentlyProcessedServerIndex); - m_defaultContainerIndex = m_settings->defaultContainer(m_currentlyProcessedServerIndex); + m_containers.clear(); + for (const QJsonValue &val : containers) { + m_containers.insert(ContainerProps::containerFromString(val.toObject().value(config_key::container).toString()), + val.toObject()); + } endResetModel(); - emit defaultContainerChanged(); } -void ContainersModel::setCurrentlyProcessedContainerIndex(int index) +void ContainersModel::setDefaultContainer(const int containerIndex) { - m_currentlyProcessedContainerIndex = index; + m_defaultContainerIndex = static_cast(containerIndex); + emit dataChanged(index(containerIndex, 0), index(containerIndex, 0)); } + DockerContainer ContainersModel::getDefaultContainer() { return m_defaultContainerIndex; } -QString ContainersModel::getDefaultContainerName() +void ContainersModel::setCurrentlyProcessedContainerIndex(int index) { - return ContainerProps::containerHumanNames().value(m_defaultContainerIndex); -} - -void ContainersModel::setDefaultContainer(int index) -{ - auto container = static_cast(index); - m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container); - m_defaultContainerIndex = container; - emit defaultContainerChanged(); + m_currentlyProcessedContainerIndex = index; } int ContainersModel::getCurrentlyProcessedContainerIndex() @@ -127,91 +91,9 @@ QString ContainersModel::getCurrentlyProcessedContainerName() return ContainerProps::containerHumanNames().value(static_cast(m_currentlyProcessedContainerIndex)); } -QJsonObject ContainersModel::getCurrentlyProcessedContainerConfig() +QJsonObject ContainersModel::getContainerConfig(const int containerIndex) { - return qvariant_cast(data(index(m_currentlyProcessedContainerIndex), ConfigRole)); -} - -QStringList ContainersModel::getAllInstalledServicesName(const int serverIndex) -{ - QStringList servicesName; - const auto &containers = m_settings->containers(serverIndex); - for (const DockerContainer &container : containers.keys()) { - if (ContainerProps::containerService(container) == ServiceType::Other && m_containers.contains(container)) { - if (container == DockerContainer::Dns) { - servicesName.append("DNS"); - } else if (container == DockerContainer::Sftp) { - servicesName.append("SFTP"); - } else if (container == DockerContainer::TorWebSite) { - servicesName.append("TOR"); - } - } - } - servicesName.sort(); - return servicesName; -} - -ErrorCode ContainersModel::removeAllContainers() -{ - - ServerController serverController(m_settings); - ErrorCode errorCode = - serverController.removeAllContainers(m_settings->serverCredentials(m_currentlyProcessedServerIndex)); - - if (errorCode == ErrorCode::NoError) { - beginResetModel(); - - m_settings->setContainers(m_currentlyProcessedServerIndex, {}); - m_containers = m_settings->containers(m_currentlyProcessedServerIndex); - - setData(index(DockerContainer::None, 0), true, IsDefaultRole); - endResetModel(); - } - return errorCode; -} - -ErrorCode ContainersModel::removeCurrentlyProcessedContainer() -{ - ServerController serverController(m_settings); - auto credentials = m_settings->serverCredentials(m_currentlyProcessedServerIndex); - auto dockerContainer = static_cast(m_currentlyProcessedContainerIndex); - - ErrorCode errorCode = serverController.removeContainer(credentials, dockerContainer); - - if (errorCode == ErrorCode::NoError) { - beginResetModel(); - 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(); - } - return errorCode; -} - -void ContainersModel::clearCachedProfiles() -{ - const auto &containers = m_settings->containers(m_currentlyProcessedServerIndex); - for (DockerContainer container : containers.keys()) { - m_settings->clearLastConnectionConfig(m_currentlyProcessedServerIndex, container); - } -} - -bool ContainersModel::isAmneziaDnsContainerInstalled() -{ - return m_containers.contains(DockerContainer::Dns); -} - -bool ContainersModel::isAmneziaDnsContainerInstalled(const int serverIndex) -{ - QMap containers = m_settings->containers(serverIndex); - return containers.contains(DockerContainer::Dns); + return qvariant_cast(data(index(containerIndex), ConfigRole)); } bool ContainersModel::isAnyContainerInstalled() @@ -228,11 +110,6 @@ bool ContainersModel::isAnyContainerInstalled() return false; } -void ContainersModel::updateContainersConfig() -{ - m_containers = m_settings->containers(m_currentlyProcessedServerIndex); -} - QHash ContainersModel::roleNames() const { QHash roles; diff --git a/client/ui/models/containers_model.h b/client/ui/models/containers_model.h index 8f087d87..23a91ea5 100644 --- a/client/ui/models/containers_model.h +++ b/client/ui/models/containers_model.h @@ -7,13 +7,12 @@ #include #include "containers/containers_defs.h" -#include "settings.h" class ContainersModel : public QAbstractListModel { Q_OBJECT public: - ContainersModel(std::shared_ptr settings, QObject *parent = nullptr); + ContainersModel(QObject *parent = nullptr); enum Roles { NameRole = Qt::UserRole + 1, @@ -37,37 +36,24 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const int index, int role) const; - Q_PROPERTY(QString defaultContainerName READ getDefaultContainerName NOTIFY defaultContainerChanged) - public slots: + void updateModel(QJsonArray &containers); + DockerContainer getDefaultContainer(); - QString getDefaultContainerName(); - void setDefaultContainer(int index); + void setDefaultContainer(const int containerIndex); - void setCurrentlyProcessedServerIndex(const int index); - - void setCurrentlyProcessedContainerIndex(int index); + void setCurrentlyProcessedContainerIndex(int containerIndex); int getCurrentlyProcessedContainerIndex(); QString getCurrentlyProcessedContainerName(); - QJsonObject getCurrentlyProcessedContainerConfig(); - QStringList getAllInstalledServicesName(const int serverIndex); - ErrorCode removeAllContainers(); - ErrorCode removeCurrentlyProcessedContainer(); - void clearCachedProfiles(); - - bool isAmneziaDnsContainerInstalled(); - bool isAmneziaDnsContainerInstalled(const int serverIndex); + QJsonObject getContainerConfig(const int containerIndex); bool isAnyContainerInstalled(); - void updateContainersConfig(); - protected: QHash roleNames() const override; @@ -78,11 +64,8 @@ signals: private: QMap m_containers; - int m_currentlyProcessedServerIndex; int m_currentlyProcessedContainerIndex; DockerContainer m_defaultContainerIndex; - - std::shared_ptr m_settings; }; #endif // CONTAINERS_MODEL_H diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 04ba1fe1..ed1f4b0b 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -1,5 +1,7 @@ #include "servers_model.h" +#include "core/controllers/serverController.h" + ServersModel::ServersModel(std::shared_ptr settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent) { @@ -8,6 +10,11 @@ ServersModel::ServersModel(std::shared_ptr settings, QObject *parent) m_currentlyProcessedServerIndex = m_defaultServerIndex; connect(this, &ServersModel::defaultServerIndexChanged, this, &ServersModel::defaultServerNameChanged); + connect(this, &ServersModel::defaultServerIndexChanged, this, &ServersModel::defaultServerDescriptionChanged); + connect(this, &ServersModel::defaultServerIndexChanged, this, [this](const int serverIndex) { + auto defaultContainer = ContainerProps::containerFromString(m_servers.at(serverIndex).toObject().value(config_key::defaultContainer).toString()); + emit ServersModel::defaultContainerChanged(defaultContainer); + }); } int ServersModel::rowCount(const QModelIndex &parent) const @@ -50,14 +57,23 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const } const QJsonObject server = m_servers.at(index.row()).toObject(); - + const auto configVersion = server.value(config_key::configVersion).toInt(); switch (role) { case NameRole: { - auto description = server.value(config_key::description).toString(); - if (description.isEmpty()) { + if (configVersion) { + return server.value(config_key::name).toString(); + } + auto name = server.value(config_key::description).toString(); + if (name.isEmpty()) { return server.value(config_key::hostName).toString(); } - return description; + return name; + } + case ServerDescriptionRole: { + if (configVersion) { + return server.value(config_key::description).toString(); + } + return server.value(config_key::hostName).toString(); } case HostNameRole: return server.value(config_key::hostName).toString(); case CredentialsRole: return QVariant::fromValue(serverCredentials(index.row())); @@ -72,6 +88,9 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const QString primaryDns = server.value(config_key::dns1).toString(); return primaryDns == protocols::dns::amneziaDnsIp; } + case DefaultContainerRole: { + return ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString()); + } } return QVariant(); @@ -114,6 +133,53 @@ const QString ServersModel::getDefaultServerHostName() return qvariant_cast(data(m_defaultServerIndex, HostNameRole)); } +QString ServersModel::getDefaultServerDescription(const QJsonObject &server) +{ + const auto configVersion = server.value(config_key::configVersion).toInt(); + + QString description; + + if (configVersion) { + return server.value(config_key::description).toString(); + } else if (isDefaultServerHasWriteAccess()) { + if (m_isAmneziaDnsEnabled + && isAmneziaDnsContainerInstalled(m_defaultServerIndex)) { + description += "Amnezia DNS | "; + } + } else { + if (isDefaultServerConfigContainsAmneziaDns()) { + description += "Amnezia DNS | "; + } + } + return description; +} + +const QString ServersModel::getDefaultServerDescriptionCollapsed() +{ + const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject(); + const auto configVersion = server.value(config_key::configVersion).toInt(); + auto description = getDefaultServerDescription(server); + if (configVersion) { + return description; + } + + auto container = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString()); + + return description += ContainerProps::containerHumanNames().value(container) + " | " + server.value(config_key::hostName).toString(); +} + +const QString ServersModel::getDefaultServerDescriptionExpanded() +{ + const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject(); + const auto configVersion = server.value(config_key::configVersion).toInt(); + auto description = getDefaultServerDescription(server); + if (configVersion) { + return description; + } + + return description += server.value(config_key::hostName).toString(); +} + const int ServersModel::getServersCount() { return m_servers.count(); @@ -132,6 +198,7 @@ bool ServersModel::hasServerWithWriteAccess() void ServersModel::setCurrentlyProcessedServerIndex(const int index) { m_currentlyProcessedServerIndex = index; + updateContainersModel(); emit currentlyProcessedServerIndexChanged(m_currentlyProcessedServerIndex); } @@ -150,6 +217,11 @@ const ServerCredentials ServersModel::getCurrentlyProcessedServerCredentials() return serverCredentials(m_currentlyProcessedServerIndex); } +const ServerCredentials ServersModel::getServerCredentials(const int index) +{ + return serverCredentials(index); +} + bool ServersModel::isDefaultServerCurrentlyProcessed() { return m_defaultServerIndex == m_currentlyProcessedServerIndex; @@ -179,6 +251,7 @@ void ServersModel::editServer(const QJsonObject &server) m_settings->editServer(m_currentlyProcessedServerIndex, server); m_servers = m_settings->serversArray(); endResetModel(); + updateContainersModel(); } void ServersModel::removeServer() @@ -206,23 +279,27 @@ bool ServersModel::isDefaultServerConfigContainsAmneziaDns() return primaryDns == protocols::dns::amneziaDnsIp; } -void ServersModel::updateContainersConfig() -{ - auto server = m_settings->server(m_currentlyProcessedServerIndex); - m_servers.replace(m_currentlyProcessedServerIndex, server); -} - QHash ServersModel::roleNames() const { QHash roles; + + roles[NameRole] = "serverName"; roles[NameRole] = "name"; + roles[ServerDescriptionRole] = "serverDescription"; + roles[HostNameRole] = "hostName"; + roles[CredentialsRole] = "credentials"; roles[CredentialsLoginRole] = "credentialsLogin"; + roles[IsDefaultRole] = "isDefault"; roles[IsCurrentlyProcessedRole] = "isCurrentlyProcessed"; + roles[HasWriteAccessRole] = "hasWriteAccess"; + roles[ContainsAmneziaDnsRole] = "containsAmneziaDns"; + + roles[DefaultContainerRole] = "defaultContainer"; return roles; } @@ -239,7 +316,205 @@ ServerCredentials ServersModel::serverCredentials(int index) const return credentials; } +void ServersModel::updateContainersModel() +{ + auto containers = m_servers.at(m_currentlyProcessedServerIndex).toObject().value(config_key::containers).toArray(); + emit containersUpdated(containers); +} + QJsonObject ServersModel::getDefaultServerConfig() { return m_servers.at(m_defaultServerIndex).toObject(); } + +void ServersModel::reloadContainerConfig() +{ + QJsonObject server = m_servers.at(m_currentlyProcessedServerIndex).toObject(); + auto container = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString()); + + auto containers = server.value(config_key::containers).toArray(); + + auto config = m_settings->containerConfig(m_currentlyProcessedServerIndex, container); + for (auto i = 0; i < containers.size(); i++) { + auto c = ContainerProps::containerFromString(containers.at(i).toObject().value(config_key::container).toString()); + if (c == container) { + containers.replace(i, config); + break; + } + } + + server.insert(config_key::containers, containers); + editServer(server); +} + +void ServersModel::updateContainerConfig(const int containerIndex, const QJsonObject config) +{ + auto container = static_cast(containerIndex); + QJsonObject server = m_servers.at(m_currentlyProcessedServerIndex).toObject(); + + auto containers = server.value(config_key::containers).toArray(); + for (auto i = 0; i < containers.size(); i++) { + auto c = ContainerProps::containerFromString(containers.at(i).toObject().value(config_key::container).toString()); + if (c == container) { + containers.replace(i, config); + break; + } + } + + server.insert(config_key::containers, containers); + + auto defaultContainer = server.value(config_key::defaultContainer).toString(); + if ((ContainerProps::containerFromString(defaultContainer) == DockerContainer::None || ContainerProps::containerService(container) != ServiceType::Other)) { + server.insert(config_key::defaultContainer, ContainerProps::containerToString(container)); + } + + editServer(server); +} + +void ServersModel::addContainerConfig(const int containerIndex, const QJsonObject config) +{ + auto container = static_cast(containerIndex); + QJsonObject server = m_servers.at(m_currentlyProcessedServerIndex).toObject(); + + auto containers = server.value(config_key::containers).toArray(); + containers.push_back(config); + + server.insert(config_key::containers, containers); + + bool isDefaultContainerChanged = false; + auto defaultContainer = server.value(config_key::defaultContainer).toString(); + if ((ContainerProps::containerFromString(defaultContainer) == DockerContainer::None || ContainerProps::containerService(container) != ServiceType::Other)) { + server.insert(config_key::defaultContainer, ContainerProps::containerToString(container)); + isDefaultContainerChanged = true; + } + + editServer(server); + if (isDefaultContainerChanged) { + emit defaultContainerChanged(container); + } +} + +void ServersModel::setDefaultContainer(const int containerIndex) +{ + auto container = static_cast(containerIndex); + QJsonObject s = m_servers.at(m_currentlyProcessedServerIndex).toObject(); + s.insert(config_key::defaultContainer, ContainerProps::containerToString(container)); + editServer(s); //check + emit defaultContainerChanged(container); +} + +DockerContainer ServersModel::getDefaultContainer() +{ + return qvariant_cast(data(m_currentlyProcessedServerIndex, DefaultContainerRole)); +} + +const QString ServersModel::getDefaultContainerName() +{ + auto defaultContainer = getDefaultContainer(); + return ContainerProps::containerHumanNames().value(defaultContainer); +} + +ErrorCode ServersModel::removeAllContainers() +{ + ServerController serverController(m_settings); + ErrorCode errorCode = + serverController.removeAllContainers(m_settings->serverCredentials(m_currentlyProcessedServerIndex)); + + if (errorCode == ErrorCode::NoError) { + QJsonObject s = m_servers.at(m_currentlyProcessedServerIndex).toObject(); + s.insert(config_key::containers, {}); + s.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None)); + + editServer(s); + emit defaultContainerChanged(DockerContainer::None); + } + return errorCode; +} + +ErrorCode ServersModel::removeContainer(const int containerIndex) +{ + ServerController serverController(m_settings); + auto credentials = m_settings->serverCredentials(m_currentlyProcessedServerIndex); + auto dockerContainer = static_cast(containerIndex); + + ErrorCode errorCode = serverController.removeContainer(credentials, dockerContainer); + + if (errorCode == ErrorCode::NoError) { + QJsonObject server = m_servers.at(m_currentlyProcessedServerIndex).toObject(); + + auto containers = server.value(config_key::containers).toArray(); + for (auto it = containers.begin(); it != containers.end(); it++) { + if (it->toObject().value(config_key::container).toString() == ContainerProps::containerToString(dockerContainer)) { + containers.erase(it); + break; + } + } + + server.insert(config_key::containers, containers); + + auto defaultContainer = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString()); + if (defaultContainer == containerIndex) { + if (containers.empty()) { + defaultContainer = DockerContainer::None; + } else { + defaultContainer = ContainerProps::containerFromString(containers.begin()->toObject().value(config_key::container).toString()); + } + server.insert(config_key::defaultContainer, ContainerProps::containerToString(defaultContainer)); + } + + editServer(server); + emit defaultContainerChanged(defaultContainer); + } + return errorCode; +} + +void ServersModel::clearCachedProfiles() +{ + const auto &containers = m_settings->containers(m_currentlyProcessedServerIndex); + for (DockerContainer container : containers.keys()) { + m_settings->clearLastConnectionConfig(m_currentlyProcessedServerIndex, container); + } + + m_servers.replace(m_currentlyProcessedServerIndex, m_settings->server(m_currentlyProcessedServerIndex)); + updateContainersModel(); +} + +bool ServersModel::isAmneziaDnsContainerInstalled(const int serverIndex) +{ + QJsonObject server = m_servers.at(serverIndex).toObject(); + auto containers = server.value(config_key::containers).toArray(); + for (auto it = containers.begin(); it != containers.end(); it++) { + if (it->toObject().value(config_key::container).toString() == ContainerProps::containerToString(DockerContainer::Dns)) { + return true; + } + } + return false; +} + +QStringList ServersModel::getAllInstalledServicesName(const int serverIndex) +{ + QStringList servicesName; + QJsonObject server = m_servers.at(serverIndex).toObject(); + const auto containers = server.value(config_key::containers).toArray(); + for (auto it = containers.begin(); it != containers.end(); it++) { + auto container = ContainerProps::containerFromString(it->toObject().value(config_key::container).toString()); + if (ContainerProps::containerService(container) == ServiceType::Other) { + if (container == DockerContainer::Dns) { + servicesName.append("DNS"); + } else if (container == DockerContainer::Sftp) { + servicesName.append("SFTP"); + } else if (container == DockerContainer::TorWebSite) { + servicesName.append("TOR"); + } + } + } + servicesName.sort(); + return servicesName; +} + +void ServersModel::toggleAmneziaDns(bool enabled) +{ + m_isAmneziaDnsEnabled = enabled; + emit defaultServerDescriptionChanged(); +} + diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index f2998846..901605e2 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -11,13 +11,21 @@ class ServersModel : public QAbstractListModel public: enum Roles { NameRole = Qt::UserRole + 1, + ServerDescriptionRole, + HostNameRole, + CredentialsRole, CredentialsLoginRole, + IsDefaultRole, IsCurrentlyProcessedRole, + HasWriteAccessRole, - ContainsAmneziaDnsRole + + ContainsAmneziaDnsRole, + + DefaultContainerRole }; ServersModel(std::shared_ptr settings, QObject *parent = nullptr); @@ -33,6 +41,10 @@ public: Q_PROPERTY(int defaultIndex READ getDefaultServerIndex WRITE setDefaultServerIndex NOTIFY defaultServerIndexChanged) Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerNameChanged) Q_PROPERTY(QString defaultServerHostName READ getDefaultServerHostName NOTIFY defaultServerIndexChanged) + Q_PROPERTY(QString defaultContainerName READ getDefaultContainerName NOTIFY defaultContainerChanged) + Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerDescriptionChanged) + Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded NOTIFY defaultServerDescriptionChanged) + Q_PROPERTY(int currentlyProcessedIndex READ getCurrentlyProcessedServerIndex WRITE setCurrentlyProcessedServerIndex NOTIFY currentlyProcessedServerIndexChanged) @@ -41,6 +53,8 @@ public slots: const int getDefaultServerIndex(); const QString getDefaultServerName(); const QString getDefaultServerHostName(); + const QString getDefaultServerDescriptionCollapsed(); + const QString getDefaultServerDescriptionExpanded(); bool isDefaultServerCurrentlyProcessed(); bool isCurrentlyProcessedServerHasWriteAccess(); @@ -54,15 +68,33 @@ public slots: QString getCurrentlyProcessedServerHostName(); const ServerCredentials getCurrentlyProcessedServerCredentials(); + const ServerCredentials getServerCredentials(const int index); void addServer(const QJsonObject &server); void editServer(const QJsonObject &server); void removeServer(); bool isDefaultServerConfigContainsAmneziaDns(); + bool isAmneziaDnsContainerInstalled(const int serverIndex); QJsonObject getDefaultServerConfig(); - void updateContainersConfig(); + + void reloadContainerConfig(); + void updateContainerConfig(const int containerIndex, const QJsonObject config); + void addContainerConfig(const int containerIndex, const QJsonObject config); + + void clearCachedProfiles(); + + ErrorCode removeContainer(const int containerIndex); + ErrorCode removeAllContainers(); + + void setDefaultContainer(const int containerIndex); + DockerContainer getDefaultContainer(); + const QString getDefaultContainerName(); + + QStringList getAllInstalledServicesName(const int serverIndex); + + void toggleAmneziaDns(bool enabled); protected: QHash roleNames() const override; @@ -71,9 +103,16 @@ signals: void currentlyProcessedServerIndexChanged(const int index); void defaultServerIndexChanged(const int index); void defaultServerNameChanged(); + void defaultServerDescriptionChanged(); + + void containersUpdated(QJsonArray &containers); + void defaultContainerChanged(const int containerIndex); private: ServerCredentials serverCredentials(int index) const; + void updateContainersModel(); + + QString getDefaultServerDescription(const QJsonObject &server); QJsonArray m_servers; @@ -81,6 +120,8 @@ private: int m_defaultServerIndex; int m_currentlyProcessedServerIndex; + + bool m_isAmneziaDnsEnabled = m_settings->useAmneziaDns(); }; #endif // SERVERSMODEL_H diff --git a/client/ui/qml/Components/HomeContainersListView.qml b/client/ui/qml/Components/HomeContainersListView.qml index f05b90d6..78ea9330 100644 --- a/client/ui/qml/Components/HomeContainersListView.qml +++ b/client/ui/qml/Components/HomeContainersListView.qml @@ -60,9 +60,8 @@ ListView { } if (checked) { - isDefault = true + ServersModel.setDefaultContainer(proxyContainersModel.mapToSource(index)) - menuContent.currentIndex = index containersDropDown.menuVisible = false } else { if (!isSupported && isInstalled) { diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 222f7764..8374dbc3 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -22,10 +22,6 @@ PageType { property string borderColor: "#2C2D30" - property string defaultServerName: ServersModel.defaultServerName - property string defaultServerHostName: ServersModel.defaultServerHostName - property string defaultContainerName: ContainersModel.defaultContainerName - Connections { target: PageController @@ -40,41 +36,6 @@ PageType { } } - Connections { - target: ServersModel - - function onDefaultServerIndexChanged() { - updateDescriptions() - } - } - - Connections { - target: ContainersModel - - function onDefaultContainerChanged() { - updateDescriptions() - } - } - - function updateDescriptions() { - var description = "" - if (ServersModel.isDefaultServerHasWriteAccess()) { - if (SettingsController.isAmneziaDnsEnabled() - && ContainersModel.isAmneziaDnsContainerInstalled(ServersModel.getDefaultServerIndex())) { - description += "Amnezia DNS | " - } - } else { - if (ServersModel.isDefaultServerConfigContainsAmneziaDns()) { - description += "Amnezia DNS | " - } - } - - collapsedServerMenuDescription.text = description + root.defaultContainerName + " | " + root.defaultServerHostName - expandedServersMenuDescription.text = description + root.defaultServerHostName - } - - Component.onCompleted: updateDescriptions() - MouseArea { anchors.fill: parent enabled: buttonContent.state === "expanded" @@ -267,7 +228,7 @@ PageType { maximumLineCount: 2 elide: Qt.ElideRight - text: root.defaultServerName + text: ServersModel.defaultServerName horizontalAlignment: Qt.AlignHCenter Behavior on opacity { @@ -304,6 +265,7 @@ PageType { Layout.bottomMargin: 44 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter visible: buttonContent.collapsedVisibility + text: ServersModel.defaultServerDescriptionCollapsed } ColumnLayout { @@ -319,7 +281,7 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - text: root.defaultServerName + text: ServersModel.defaultServerName horizontalAlignment: Qt.AlignHCenter maximumLineCount: 2 elide: Qt.ElideRight @@ -331,6 +293,7 @@ PageType { Layout.fillWidth: true horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter + text: ServersModel.defaultServerDescriptionExpanded } RowLayout { @@ -349,7 +312,7 @@ PageType { rootButtonTextTopMargin: 8 rootButtonTextBottomMargin: 8 - text: root.defaultContainerName + text: ServersModel.defaultContainerName textColor: "#0E0E11" headerText: qsTr("VPN protocol") headerBackButtonImage: "qrc:/images/controls/arrow-left.svg" @@ -468,7 +431,7 @@ PageType { var description = "" if (hasWriteAccess) { if (SettingsController.isAmneziaDnsEnabled() - && ContainersModel.isAmneziaDnsContainerInstalled(index)) { + && ServersModel.isAmneziaDnsContainerInstalled(index)) { description += "Amnezia DNS | " } } else { diff --git a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml index 3bfa5bb0..aca4575c 100644 --- a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml +++ b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml @@ -66,8 +66,8 @@ PageType { text: qsTr("Website address") descriptionText: { - var config = ContainersModel.getCurrentlyProcessedContainerConfig() var containerIndex = ContainersModel.getCurrentlyProcessedContainerIndex() + var config = ContainersModel.getContainerConfig(containerIndex) return config[ContainerProps.containerTypeToString(containerIndex)]["site"] } diff --git a/client/ui/qml/Pages2/PageSettingsServersList.qml b/client/ui/qml/Pages2/PageSettingsServersList.qml index 040aafc3..dca904ae 100644 --- a/client/ui/qml/Pages2/PageSettingsServersList.qml +++ b/client/ui/qml/Pages2/PageSettingsServersList.qml @@ -77,7 +77,7 @@ PageType { text: name descriptionText: { var servicesNameString = "" - var servicesName = ContainersModel.getAllInstalledServicesName(index) + var servicesName = ServersModel.getAllInstalledServicesName(index) for (var i = 0; i < servicesName.length; i++) { servicesNameString += servicesName[i] + " · " } diff --git a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml index c82ce855..f7e82692 100644 --- a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml +++ b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml @@ -25,7 +25,7 @@ PageType { function onInstallContainerFinished(finishedMessage, isServiceInstall) { if (!ConnectionController.isConnected && !isServiceInstall) { - ContainersModel.setDefaultContainer(ContainersModel.getCurrentlyProcessedContainerIndex()) + ServersModel.setDefaultContainer(ContainersModel.getCurrentlyProcessedContainerIndex()) } PageController.closePage() // close installing page diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 67a66931..ac9c7efb 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -354,8 +354,6 @@ PageType { currentIndex: 0 clickedFunction: function() { - protocolSelectorListView.currentItem.y - handler() protocolSelector.menuVisible = false @@ -365,7 +363,7 @@ PageType { target: serverSelector function onSeverSelectorIndexChanged() { - protocolSelectorListView.currentIndex = proxyContainersModel.mapFromSource(ContainersModel.getDefaultContainer()) + protocolSelectorListView.currentIndex = proxyContainersModel.mapFromSource(ServersModel.getDefaultContainer()) protocolSelectorListView.triggerCurrentItem() } } diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index dd4cf185..80163ef1 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -234,8 +234,6 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, const Ser return ""; } - emit m_configurator->newVpnConfigCreated(clientId, "unnamed client", container, credentials); - QString configDataBeforeLocalProcessing = configData; configData = m_configurator->processConfigWithLocalSettings(serverIndex, container, proto, configData); @@ -247,6 +245,8 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, const Ser protoObject.insert(config_key::last_config, configDataBeforeLocalProcessing); m_settings->setProtocolConfig(serverIndex, container, proto, protoObject); } + + emit m_configurator->newVpnConfigCreated(clientId, "unnamed client", container, credentials); } return configData; From e66fbc328987cef77e1434cd82400978e79cb368 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 11 Dec 2023 13:42:42 +0700 Subject: [PATCH 27/33] added default container installation, after downloading the config from the api --- client/amnezia_application.cpp | 3 --- client/ui/controllers/apiController.cpp | 6 ++++-- client/ui/controllers/apiController.h | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 5079ed87..40aab515 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -376,7 +376,4 @@ void AmneziaApplication::initControllers() m_cloudController.reset(new ApiController(m_serversModel, m_containersModel)); m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get()); - - // connect(m_cloudController.get(), &ApiController::serverConfigUpdated, this, - // [this]() { m_containersModel->setCurrentlyProcessedServerIndex(m_serversModel->getDefaultServerIndex()); }); } diff --git a/client/ui/controllers/apiController.cpp b/client/ui/controllers/apiController.cpp index d0c440df..14a05410 100644 --- a/client/ui/controllers/apiController.cpp +++ b/client/ui/controllers/apiController.cpp @@ -109,9 +109,11 @@ bool ApiController::updateServerConfigFromApi() serverConfig.insert(config_key::dns2, cloudConfig.value(config_key::dns2)); serverConfig.insert(config_key::containers, cloudConfig.value(config_key::containers)); serverConfig.insert(config_key::hostName, cloudConfig.value(config_key::hostName)); - serverConfig.insert(config_key::defaultContainer, cloudConfig.value(config_key::defaultContainer)); + + auto defaultContainer = cloudConfig.value(config_key::defaultContainer).toString(); + serverConfig.insert(config_key::defaultContainer, defaultContainer); m_serversModel->editServer(serverConfig); - emit serverConfigUpdated(); + emit m_serversModel->defaultContainerChanged(ContainerProps::containerFromString(defaultContainer)); } else { QString err = reply->errorString(); qDebug() << QString::fromUtf8(reply->readAll()); //todo remove debug output diff --git a/client/ui/controllers/apiController.h b/client/ui/controllers/apiController.h index 5917f70f..1ce933c6 100644 --- a/client/ui/controllers/apiController.h +++ b/client/ui/controllers/apiController.h @@ -20,7 +20,6 @@ public slots: signals: void errorOccurred(const QString &errorMessage); - void serverConfigUpdated(); private: QString genPublicKey(const QString &protocol); From 39d1f1677f780d44ac8d39c5330161351fecdd99 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 13 Dec 2023 14:14:37 +0700 Subject: [PATCH 28/33] fixed description update, after changing the default protocol --- client/ui/controllers/importController.cpp | 2 -- client/ui/models/servers_model.cpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index a8aab2b2..062b10a4 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -169,8 +169,6 @@ QJsonObject ImportController::extractAmneziaConfig(QString &data) QJsonObject config = QJsonDocument::fromJson(ba).object(); - qDebug() << config; - return config; } diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index ed1f4b0b..ad927fce 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -10,7 +10,7 @@ ServersModel::ServersModel(std::shared_ptr settings, QObject *parent) m_currentlyProcessedServerIndex = m_defaultServerIndex; connect(this, &ServersModel::defaultServerIndexChanged, this, &ServersModel::defaultServerNameChanged); - connect(this, &ServersModel::defaultServerIndexChanged, this, &ServersModel::defaultServerDescriptionChanged); + connect(this, &ServersModel::defaultContainerChanged, this, &ServersModel::defaultServerDescriptionChanged); connect(this, &ServersModel::defaultServerIndexChanged, this, [this](const int serverIndex) { auto defaultContainer = ContainerProps::containerFromString(m_servers.at(serverIndex).toObject().value(config_key::defaultContainer).toString()); emit ServersModel::defaultContainerChanged(defaultContainer); From f52c3c430ff52336e8ac5add7f3a9d556d66bdc1 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 14 Dec 2023 11:03:27 +0700 Subject: [PATCH 29/33] added notification after config revokation --- client/ui/qml/Pages2/PageShare.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index ac9c7efb..38010b8f 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -31,6 +31,7 @@ PageType { ContainersModel.getCurrentlyProcessedContainerIndex(), ServersModel.getCurrentlyProcessedServerCredentials()) PageController.showBusyIndicator(false) + PageController.showNotificationMessage(qsTr("Config revoked")) } Connections { From 7b1b8dc749137c074948d622b4957619f5af41e4 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 14 Dec 2023 18:50:09 +0700 Subject: [PATCH 30/33] fixed server search for sftp, dns and tor containers --- client/containers/containers_defs.cpp | 2 +- client/core/servercontroller.cpp | 66 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index b6f1b111..a523ef84 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -58,7 +58,7 @@ QVector ContainerProps::protocolsForContainer(amnezia::DockerCon case DockerContainer::Ipsec: return { Proto::Ikev2 /*, Protocol::L2tp */ }; - case DockerContainer::Dns: return {}; + case DockerContainer::Dns: return { Proto::Dns }; case DockerContainer::Sftp: return { Proto::Sftp }; diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 5bc5e519..e7c047db 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -31,6 +31,11 @@ #include +namespace +{ + Logger logger("ServerController"); +} + ServerController::ServerController(std::shared_ptr settings, QObject *parent) : m_settings(settings) { } @@ -857,6 +862,67 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential containerConfig[config_key::responsePacketMagicHeader] = serverConfigMap.value(config_key::responsePacketMagicHeader); containerConfig[config_key::underloadPacketMagicHeader] = serverConfigMap.value(config_key::underloadPacketMagicHeader); containerConfig[config_key::transportPacketMagicHeader] = serverConfigMap.value(config_key::transportPacketMagicHeader); + } else if (protocol == Proto::Sftp) { + stdOut.clear(); + script = QString("sudo docker inspect --format '{{.Config.Cmd}}' %1").arg(name); + + ErrorCode errorCode = runScript(credentials, script, cbReadStdOut, cbReadStdErr); + if (errorCode != ErrorCode::NoError) { + return errorCode; + } + + auto sftpInfo = stdOut.split(":"); + if (sftpInfo.size() < 2) { + logger.error() << "Key parameters for the sftp container are missing"; + continue; + } + auto userName = sftpInfo.at(0); + userName = userName.remove(0, 1); + auto password = sftpInfo.at(1); + + containerConfig.insert(config_key::userName, userName); + containerConfig.insert(config_key::password, password); + } + + config.insert(config_key::container, ContainerProps::containerToString(container)); + } + config.insert(ProtocolProps::protoToString(protocol), containerConfig); + } + installedContainers.insert(container, config); + } + const static QRegularExpression torOrDnsRegExp("(amnezia-(?:torwebsite|dns)).*?([0-9]*)/(udp|tcp).*"); + QRegularExpressionMatch torOrDnsRegMatch = torOrDnsRegExp.match(containerInfo); + if (torOrDnsRegMatch.hasMatch()) { + QString name = torOrDnsRegMatch.captured(1); + QString port = torOrDnsRegMatch.captured(2); + QString transportProto = torOrDnsRegMatch.captured(3); + DockerContainer container = ContainerProps::containerFromString(name); + + QJsonObject config; + Proto mainProto = ContainerProps::defaultProtocol(container); + for (auto protocol : ContainerProps::protocolsForContainer(container)) { + QJsonObject containerConfig; + if (protocol == mainProto) { + containerConfig.insert(config_key::port, port); + containerConfig.insert(config_key::transport_proto, transportProto); + + if (protocol == Proto::TorWebSite) { + stdOut.clear(); + script = QString("sudo docker exec -i %1 sh -c 'cat /var/lib/tor/hidden_service/hostname'").arg(name); + + ErrorCode errorCode = runScript(credentials, script, cbReadStdOut, cbReadStdErr); + if (errorCode != ErrorCode::NoError) { + return errorCode; + } + + if (stdOut.isEmpty()) { + logger.error() << "Key parameters for the tor container are missing"; + continue; + } + + QString onion = stdOut; + onion.replace("\n", ""); + containerConfig.insert(config_key::site, onion); } config.insert(config_key::container, ContainerProps::containerToString(container)); From 491f09a51b3be01d31c8486557051e19edab3ac1 Mon Sep 17 00:00:00 2001 From: Morteza Sherafati Date: Sun, 17 Dec 2023 18:37:07 +0000 Subject: [PATCH 31/33] added persian translation file and menu item --- client/CMakeLists.txt | 1 + client/translations/amneziavpn_fa_IR.ts | 3027 +++++++++++++++++++++++ client/ui/models/languageModel.cpp | 3 + client/ui/models/languageModel.h | 3 +- 4 files changed, 3033 insertions(+), 1 deletion(-) create mode 100644 client/translations/amneziavpn_fa_IR.ts diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 6c4f1ae4..6e8933b0 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -56,6 +56,7 @@ set(CMAKE_AUTORCC ON) set(AMNEZIAVPN_TS_FILES ${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts ${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts + ${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_fa_IR.ts ) file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui) diff --git a/client/translations/amneziavpn_fa_IR.ts b/client/translations/amneziavpn_fa_IR.ts new file mode 100644 index 00000000..5c6ad424 --- /dev/null +++ b/client/translations/amneziavpn_fa_IR.ts @@ -0,0 +1,3027 @@ + + + + + AmneziaApplication + + Split tunneling for WireGuard is not implemented, the option was disabled + Раздельное туннелирование для "Wireguard" не реализовано,опция отключена + + + + Split tunneling for %1 is not implemented, the option was disabled + جداسازی ترافیک برای %1 پیاده سازی نشده، این گزینه غیرفعال است + + + + AndroidController + + + AmneziaVPN + AmneziaVPN + + + + VPN Connected + Refers to the app - which is currently running the background and waiting + وی‎پی‎ان متصل است + + + + ConnectionController + + + VPN Protocols is not installed. + Please install VPN container at first + پروتکل وی‎پی‎ان نصب نشده است +لطفا کانتینر وی‎پی‎ان را نصب کنید + + + + Connection... + ارتباط + + + + Connected + متصل + + + + Settings updated successfully, Reconnnection... + تنظیمات به روز رسانی شد +در حال اتصال دوباره... + + + + Reconnection... + در حال اتصال دوباره... + + + + + + + Connect + اتصال + + + + Disconnection... + قطع ارتباط... + + + + ConnectionTypeSelectionDrawer + + + Add new connection + ایجاد ارتباط جدید + + + + Configure your server + تنظیم سرور + + + + Open config file, key or QR code + بارگذاری فایل تنظیمات، کلید یا QR Code + + + + ContextMenuType + + + C&ut + &بریدن + + + + &Copy + &کپی + + + + &Paste + &پیوست + + + + &SelectAll + &انتخاب همه + + + + ExportController + + + Access error! + خطای دسترسی! + + + + HomeContainersListView + + + Unable change protocol while there is an active connection + امکان تغییر پروتکل در هنگام متصل بودن وجود ندارد + + + + The selected protocol is not supported on the current platform + پروتکل انتخاب شده بر روی این پلتفرم پشتیبانی نمی‎‎شود + + + Reconnect via VPN Procotol: + Переподключение через VPN протокол: + + + + ImportController + + + Scanned %1 of %2. + ارزیابی %1 از %2. + + + + InstallController + + + + %1 installed successfully. + %1 با موفقیت نصب شد + + + + + %1 is already installed on the server. + %1 در حال حاضر بر روی سرور نصب شده است + + + + +Added containers that were already installed on the server + +کانتینرهایی که بر روی سرور موجود بودند اضافه شدند + + + + +Already installed containers were found on the server. All installed containers have been added to the application + +کانتینرهای نصب شده بر روی سرور شناسایی شدند. تمام کانتینترهای نصب شده به نرم افزار اضافه شدند + + + + Settings updated successfully + تنظیمات با موفقیت به‎روز‎رسانی شدند + + + + Server '%1' was removed + سرور %1 حذف شد + + + + All containers from server '%1' have been removed + تمام کانتینترها از سرور %1 حذف شدند + + + + %1 has been removed from the server '%2' + %1 از سرور %2 حذف شد + + + + Please login as the user + لطفا به عنوان کاربر وارد شوید + + + + Server added successfully + سرور با موفقیت اضافه شد + + + + KeyChainClass + + + Read key failed: %1 + خواندن کلید انجام نشد: %1 + + + + Write key failed: %1 + نوشتن کلید انجام نشد: %1 + + + + Delete key failed: %1 + حذف کلید انجام نشد: %1 + + + + NotificationHandler + + + + AmneziaVPN + AmneziaVPN + + + + VPN Connected + وی‎پی‎ان متصل است + + + + VPN Disconnected + وی‎پی‎ان قطع است + + + + AmneziaVPN notification + اخطار AmneziaVPN + + + + Unsecured network detected: + شبکه ناامن شناسایی شد: + + + + PageDeinstalling + + + Removing services from %1 + حذف سرویس‎ها از %1 + + + + Usually it takes no more than 5 minutes + معمولا بیش از 5 دقیقه طول نمی‎کشد + + + + PageHome + + + VPN protocol + پروتکل وی‎پی‎ان + + + + Servers + سرورها + + + + Unable change server while there is an active connection + امکان تغییر سرور در هنگام متصل بودن وجود ندارد + + + + PageProtocolAwgSettings + + + AmneziaWG settings + تنظیمات AmneziaWG + + + + Port + پورت + + + + Junk packet count + تعداد بسته‎های ناخواسته + + + + Junk packet minimum size + Junk packet minimum size + + + + Junk packet maximum size + Junk packet maximum size + + + + Init packet junk size + Init packet junk size + + + + Response packet junk size + Response packet junk size + + + + Init packet magic header + Init packet magic header + + + + Response packet magic header + Response packet magic header + + + + Transport packet magic header + Transport packet magic header + + + + Underload packet magic header + Underload packet magic header + + + + Remove AmneziaWG + حذف AmneziaWG + + + + Remove AmneziaWG from server? + آیا میخواهید AmneziaWG از سرور حذف شود؟ + + + + All users with whom you shared a connection will no longer be able to connect to it. + تمام کاربرانی که این اتصال را با آن‎ها با اشتراک گذاشته‎اید دیگر نمی‎توانند به آن متصل شوند. + + + All users who you shared a connection with will no longer be able to connect to it. + Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться. + + + + Continue + ادامه + + + + Cancel + کنسل + + + + Save and Restart Amnezia + ذخیره و راه اندازی مجدد Amnezia + + + + PageProtocolCloakSettings + + + Cloak settings + تنظیمات Cloak + + + + Disguised as traffic from + پنهان کردن به عنوان ترافیک از + + + + Port + پورت + + + + + Cipher + رمزگذاری + + + + Save and Restart Amnezia + ذخیره و راه اندازی دوباره Amnezia + + + + PageProtocolOpenVpnSettings + + + OpenVPN settings + تنظیمات OpenVPN + + + + VPN Addresses Subnet + آدرس زیرشبکه وی‎پی‎ان + + + + Network protocol + پروتکل شبکه + + + + Port + پورت + + + + Auto-negotiate encryption + رمزگذاری خودکار + + + + + Hash + هش + + + + SHA512 + SHA512 + + + + SHA384 + SHA384 + + + + SHA256 + SHA256 + + + + SHA3-512 + SHA3-512 + + + + SHA3-384 + SHA3-384 + + + + SHA3-256 + SHA3-256 + + + + whirlpool + whirlpool + + + + BLAKE2b512 + BLAKE2b512 + + + + BLAKE2s256 + BLAKE2s256 + + + + SHA1 + SHA1 + + + + + Cipher + رمزگذاری + + + + AES-256-GCM + AES-256-GCM + + + + AES-192-GCM + AES-192-GCM + + + + AES-128-GCM + AES-128-GCM + + + + AES-256-CBC + AES-256-CBC + + + + AES-192-CBC + AES-192-CBC + + + + AES-128-CBC + AES-128-CBC + + + + ChaCha20-Poly1305 + ChaCha20-Poly1305 + + + + ARIA-256-CBC + ARIA-256-CBC + + + + CAMELLIA-256-CBC + CAMELLIA-256-CBC + + + + none + none + + + + TLS auth + اعتبار TLS + + + + Block DNS requests outside of VPN + مسدود کردن درخواست‎های DNS خارج از وی‎پی‎ان + + + + Additional client configuration commands + تنظیمات و دستورات اضافه برنامه متصل شونده + + + + + Commands: + دستورات: + + + + Additional server configuration commands + تنظیمات و دستورات اضافه سرور + + + + Remove OpenVPN + حذف OpenVPN + + + + Remove OpenVpn from server? + آیا میخواهید OpenVPN از سرور حذف شود؟ + + + + All users with whom you shared a connection will no longer be able to connect to it. + تمام کاربرانی که این اتصال را با آن‎ها با اشتراک گذاشته‎اید دیگر نمی‎توانند به آن متصل شوند. + + + All users who you shared a connection with will no longer be able to connect to it. + Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться. + + + + Continue + ادامه + + + + Cancel + کنسل + + + + Save and Restart Amnezia + ذخیره و راه اندازی دوباره Amnezia + + + + PageProtocolRaw + + + settings + تنظیمات + + + + Show connection options + نمایش تنظیمات اتصال + + + + Connection options %1 + تنظیمات اتصال %1 + + + + Remove + حذف + + + + Remove %1 from server? + %1 از سرور حذف شود؟ + + + + All users with whom you shared a connection will no longer be able to connect to it. + تمام کاربرانی که این اتصال را با آن‎ها با اشتراک گذاشته‎اید دیگر نمی‎توانند به آن متصل شوند. + + + All users who you shared a connection with will no longer be able to connect to it. + Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться. + + + + Continue + ادامه + + + + Cancel + کنسل + + + + PageProtocolShadowSocksSettings + + + ShadowSocks settings + تنظیمات ShadowSocks + + + + Port + پورت + + + + + Cipher + رمزگذاری + + + + Save and Restart Amnezia + ذخیره و راه اندازی دوباره Amnezia + + + + PageServerContainers + + Continue + Продолжить + + + + PageServiceDnsSettings + + + A DNS service is installed on your server, and it is only accessible via VPN. + + یک سرویس DSN بر روی سرور شما نصب شده و فقط از طریق وی‎پی‎ان قابل دسترسی می‎باشد + + + + + The DNS address is the same as the address of your server. You can configure DNS in the settings, under the connections tab. + آدرس DSN همان آدرس سرور شماست. میتوانید از قسمت تنظیمات و تب اتصالات DSN خود را تنظیم کنید + + + + Remove + جذف + + + + Remove %1 from server? + %1 از سرور حذف شود؟ + + + + Continue + ادامه + + + + Cancel + کنسل + + + + PageServiceSftpSettings + + + Settings updated successfully + تنظیمات با موفقیت به‎روز‎رسانی شد + + + + SFTP settings + تنظیمات SFTP + + + + Host + هاست + + + + + + + Copied + کپی شد + + + + Port + پورت + + + + Login + ورود + + + + Password + رمز عبور + + + + Mount folder on device + بارگذاری پوشه بر روی دستگاه + + + + In order to mount remote SFTP folder as local drive, perform following steps: <br> + برای بارگذاری پوشه SFTP بر روی درایو محلی قدم‎های زیر را انجام دهید: <br> + + + + + <br>1. Install the latest version of + <br> 1. آخرین نسخه را نصب کنید: + + + + + <br>2. Install the latest version of + <br> 2. آخرین نسخه را نصب کنید: + + + + Detailed instructions + جزییات دستورالعمل‎ها + + + + Remove SFTP and all data stored there + حذف SFTP و تمام داده‎های ذخیره شده در آن + + + + Remove SFTP and all data stored there? + پوشه SFTP و تمام داده‎های آن حذف شوند؟ + + + + Continue + ادامه + + + + Cancel + کنسل + + + + PageServiceTorWebsiteSettings + + + Settings updated successfully + تنظیمات با موفقیت به‎روز‎‌رسانی شد + + + + Tor website settings + تنظیمات وب‎سایت Tor + + + + Website address + آدرس وب‎سایت + + + + Copied + کپی شد + + + + Use <a href="https://www.torproject.org/download/" style="color: #FBB26A;">Tor Browser</a> to open this url. + از <a href="https://www.torproject.org/download/" style="color: #FBB26A;">Tor Browser</a> برای باز کردن این url استفاده کنید. + + + + After installation it takes several minutes while your onion site will become available in the Tor Network. + بعد از نصب چند دقیقه طول میکشد که سایت پیازی شما در شبکه Tor در دسترس قرار گیرد. + + + + When configuring WordPress set the this onion address as domain. + زمانی که سایت وردپرس را تنظیم میکنید این آدرس پیازی را به عنوان دامنه قرار دهید. + + + When configuring WordPress set the this address as domain. + При настройке WordPress укажите этот onion адрес в качестве домена. + + + + Remove website + حذف وب سایت + + + + The site with all data will be removed from the tor network. + سایت با تمام داده‎ها از شبکه Tor حذف خواهد شد. + + + + Continue + ادامه + + + + Cancel + کنسل + + + + PageSettings + + + Settings + تنظیمات + + + + Servers + سرورها + + + + Connection + ارتباط + + + + Application + نرم‎افزار + + + + Backup + بک‎آپ + + + + About AmneziaVPN + درباره Amnezia + + + + Close application + بستن نرم‎افزار + + + + PageSettingsAbout + + + Support the project with a donation + حمایت از پروژه با کمک‎های مالی + + + + This is a free and open source application. If you like it, support the developers with a donation. + این نرم‎افزار یک پروژه رایگان است. اگر آن را دوست دارید با کمک‎های مالی از توسعه‎دهندگان آن حمایت کنید. + + + + And if you don’t like the application, all the more reason to support it - the donation will be used for the improving the application. + و اگر آن‎را دوست ندارید دلایل بیشتری برای کمک به نرم‎افزار است، کمک‎های مالی شما برای بهبود نرم‎افزار استفاده میشود. + + + + Card on Patreon + کارت روی Patreon + + + + https://www.patreon.com/amneziavpn + https://www.patreon.com/amneziavpn + + + + Show other methods on Github + نمایش متد‎های دیگر در گیت هاب + + + + Contacts + مخاطب + + + + Telegram group + گروه تلگرام + + + + To discuss features + برای گفتگو در مورد ویژگی‎ها + + + + https://t.me/amnezia_vpn_en + https://t.me/amnezia_vpn + + + + Mail + ایمیل + + + + For reviews and bug reports + برای ارائه نظرات و گزارشات باگ + + + + Github + Github + + + + https://github.com/amnezia-vpn/amnezia-client + https://github.com/amnezia-vpn/amnezia-client + + + + Website + وب سایت + + + + https://amnezia.org + https://amnezia.org + + + + Check for updates + بررسی بروز‎رسانی + + + + PageSettingsApplication + + + Application + نرم افزار + + + + Allow application screenshots + مجوز اسکرین‎شات در برنامه + + + + Auto start + شروع خودکار + + + + Launch the application every time the device is starts + راه‎اندازی نرم‎افزار با هر بار روشن شدن دستگاه + + + + Start minimized + شروع به صورت کوچک + + + + Launch application minimized + راه‎اندازی برنامه به صورت کوچک + + + + Language + زبان + + + + Logging + گزارشات + + + + Enabled + فعال + + + + Disabled + غیر فعال + + + + Reset settings and remove all data from the application + ریست کردن تنظیمات و حذف تمام داده‎ها از نرم‎افزار + + + + Reset settings and remove all data from the application? + ریست کردن تنظیمات و حذف تمام داده‎ها از نرم‎افزار؟ + + + + All settings will be reset to default. All installed AmneziaVPN services will still remain on the server. + تمام تنظیمات به حالت پیش‎فرض ریست می‎شوند. تمام سرویس‎های Amnezia بر روی سرور باقی می‎مانند. + + + + Continue + ادامه + + + + Cancel + کنسل + + + + PageSettingsBackup + + + Backup + پشتیبان‎گیری + + + + Settings restored from backup file + تنظیمات از فایل پشتیبان بازیابی شد + + + + Configuration backup + پشتیبان‎گیری از پیکربندی + + + + You can save your settings to a backup file to restore them the next time you install the application. + می‎توانید تنظیمات را در یک فایل پشتیبان ذخیره کرده و دفعه بعد که نرم‎افزار را نصب کردید آن‎ها را بازیابی کنید. + + + + Make a backup + ایجاد یک پشتیبان + + + + Save backup file + ذخیره فایل پشتیبان + + + + + Backup files (*.backup) + Backup files (*.backup) + + + + Backup file saved + فایل پشتیبان ذخیره شد + + + + Restore from backup + بازیابی از پشتیبان + + + + Open backup file + باز کردن فایل پشتیبان + + + + Import settings from a backup file? + ورود تنظیمات از فایل پشتیبان؟ + + + + All current settings will be reset + تمام تنظیمات جاری ریست خواهد شد + + + + Continue + ادامه + + + + Cancel + کنسل + + + + PageSettingsConnection + + + Connection + ارتباط + + + + Auto connect + اتصال خودکار + + + + Connect to VPN on app start + اتصال به وی‎‎پی‎ان با شروع نرم‎افزار + + + + Use AmneziaDNS + استفاده از AmneziaDNS + + + + If AmneziaDNS is installed on the server + اگر AmneziaDNS بر روی سرور نصب شده باشد + + + + DNS servers + سرورهای DNS + + + + If AmneziaDNS is not used or installed + اگر AmneziaDNS نصب نشده یا استفاده نشود + + + + Site-based split tunneling + جداسازی ترافیک بر اساس سایت + + + + Allows you to select which sites you want to access through the VPN + میتوانید مشخص کنید که چه سایت‎هایی از وی‎پی‎ان استفاده کنند + + + + App-based split tunneling + جداسازی ترافیک بر اساس نرم‎افزار + + + + Allows you to use the VPN only for certain applications + میتوانید مشخص کنید که چه نرم‎افزارهایی از وی‎پی‎ان استفاده کنند + + + + PageSettingsDns + + + DNS servers + سرورهای DNS + + + + If AmneziaDNS is not used or installed + اگر AmneziaDNS نصب نباشد یا استفاده نشود + + + + Primary DNS + DNS اصلی + + + + Secondary DNS + DNS ثانویه + + + + Restore default + بازگشت به پیش‎فرض + + + + Restore default DNS settings? + بازگشت به تنظیمات پیش‎فرض DNS؟ + + + + Continue + ادامه + + + + Cancel + کنسل + + + + Settings have been reset + تنظیمات ریست شد + + + + Save + ذخیره + + + + Settings saved + ذخیره تنظیمات + + + + PageSettingsLogging + + + Logging + گزارشات + + + + Save logs + ذخیره گزارشات + + + + Open folder with logs + باز کردن پوشه گزارشات + + + + Save + ذخیره + + + + Logs files (*.log) + Logs files (*.log) + + + + Logs file saved + فایل گزارشات ذخیره شد + + + + Save logs to file + ذخیره گزارشات در فایل + + + + Clear logs? + پاک کردن گزارشات؟ + + + + Continue + ادامه + + + + Cancel + کنسل + + + + Logs have been cleaned up + گزارشات پاک شدند + + + + Clear logs + پاک کردن گزارشات + + + + PageSettingsServerData + + + All installed containers have been added to the application + تمام کانتینرهای نصب شده به نرم‎افزار اضافه شدند + + + + Clear Amnezia cache + پاک کردن حافظه داخلی Amnezia + + + + May be needed when changing other settings + وقتی تنظیمات دیگر را تغییر دهید ممکن است نیاز باشد + + + + Clear cached profiles? + پاک کردن پروفایل ذخیره شده؟ + + + + No new installed containers found + کانتینر نصب شده جدیدی پیدا نشد + + + + + + + + + + + Continue + ادامه + + + + + + Cancel + کنسل + + + + Check the server for previously installed Amnezia services + چک کردن سرویس‎های نصب شده Amnezia بر روی سرور + + + + Add them to the application if they were not displayed + اضافه کردن آنها به نرم‎افزار اگر نمایش داده نشده‎اند + + + + Remove server from application + حذف کردن سرور از نرم‎افزار + + + + Remove server? + حذف سرور؟ + + + + All installed AmneziaVPN services will still remain on the server. + تمام سرویس‎های نصب‎شده Amnezia همچنان بر روی سرور باقی خواهند ماند. + + + + Clear server from Amnezia software + پاک کردن سرور از نرم‎افزار Amnezia + + + + Clear server from Amnezia software? + سرور از نرم‎افزار Amnezia پاک شود؟ + + + + All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted. + تمام کانتینرها از سرور پاک شوند، به این معنی که تمام فایل‎های پیکربندی، کلیدها و مجوزها حذف خواهند شد. + + + + PageSettingsServerInfo + + + Server name + نام سرور + + + + Save + ذخیره + + + + Protocols + پروتکل‎ها + + + + Services + سرویس‎ها + + + + Data + داده + + + + PageSettingsServerProtocol + + + settings + تنظیمات + + + + Remove + حذف + + + + Remove %1 from server? + حذف %1 از سرور؟ + + + + All users with whom you shared a connection will no longer be able to connect to it. + تمام کاربرانی که این ارتباط را با آنها به اشتراک گذاشته‎اید دیگر نمی‎توانند به آن متصل شوند. + + + All users who you shared a connection with will no longer be able to connect to it. + Все пользователи, которым вы поделились VPN, больше не смогут к нему подключаться. + + + + Continue + ادامه + + + + Cancel + کنسل + + + + PageSettingsServersList + + + Servers + سرورها + + + + PageSettingsSplitTunneling + + + Addresses from the list should be accessed via VPN + دسترسی به آدرس‎های لیست از طریق وی‎پی‎ان + + + + Addresses from the list should not be accessed via VPN + دسترسی به آدرس‎های لیست بدون وی‎پی‎ان + + + + Split tunneling + جداسازی ترافیک + + + + Mode + حالت + + + + Remove + حذف + + + + Continue + ادامه + + + + Cancel + کنسل + + + + Site or IP + سایت یا آی‎پی + + + + Import/Export Sites + بارگذاری / خروجی‎گرفتن از سایت‎ها + + + + Import + بارگذاری + + + + Save site list + ذخیره لیست سایت‎ها + + + + Save sites + ذخیره سایت‎ها + + + + + + Sites files (*.json) + Sites files (*.json) + + + + Import a list of sites + بارگذاری لیست سایت‎ها + + + + Replace site list + جایگزین کردن لیست سایت + + + + + Open sites file + باز کردن فایل سایت‎ها + + + + Add imported sites to existing ones + اضافه کردن سایت‎های بارگذاری شده به سایت‎های موجود + + + + PageSetupWizardConfigSource + + + Server connection + ارتباط سرور + + + + Do not use connection code from public sources. It may have been created to intercept your data. + +It's okay as long as it's from someone you trust. + از کد اتصالاتی که در منابع عمومی هستند استفاده نکنید. ممکن است برای شنود اطلاعات شما ایجاد شده باشند. + +ایرادی ندارد که از طرف کسی باشد که به او اعتماد دارید. + + + + What do you have? + چی داری؟ + + + + File with connection settings + فایل شامل تنظیمات اتصال + + + + File with connection settings or backup + فایل شامل تنظیمات اتصال یا بک‎آپ + + + + Open config file + باز کردن فایل تنظیمات + + + + QR-code + QR-Code + + + + Key as text + متن شامل کلید + + + + PageSetupWizardCredentials + + Server connection + Подключение к серверу + + + + Server IP address [:port] + آدرس آی‎پی سرور (:پورت) + + + + 255.255.255.255:88 + 255.255.255.255:88 + + + + Password / SSH private key + Password / SSH private key + + + + Continue + ادامه + + + + All data you enter will remain strictly confidential +and will not be shared or disclosed to the Amnezia or any third parties + تمام داده‎هایی که شما وارد می‎کنید به شدت محرمانه‎ است و با Amnezia یا هر شخص ثالث دیگری به اشتراک گذاشته نمی‎شود + + + + Enter the address in the format 255.255.255.255:88 + آدرس را با فرمت 255.255.255.255:88 وارد کنید + + + + Login to connect via SSH + ورود و اتصال با استفاده از SSH + + + + Configure your server + سرور خود را پیکربندی کنید + + + + Ip address cannot be empty + آدرس آی‎پی نمی‎تواند خالی باشد + + + + Login cannot be empty + نام‎کاربری نمی‎تواند خالی باشد + + + + Password/private key cannot be empty + پسورد یا کلید خصوصی نمی‎تواند خالی باشد + + + + PageSetupWizardEasy + + + What is the level of internet control in your region? + سطح کنترل اینترنت در منطقه شما چگونه است؟ + + + + Set up a VPN yourself + یک وی‎پی‎ان برای خودتان بسازید + + + + I want to choose a VPN protocol + می‎خواهم پروتکل وی‎پی‎ان را انتخاب کنم + + + + Continue + ادامه + + + + Set up later + بعدا تنظیم شود + + + + PageSetupWizardInstalling + + + The server has already been added to the application + سرور در حال حاضر به نرم‎افزار اضافه شده است + + + Amnesia has detected that your server is currently + Amnesia обнаружила, что ваш сервер в настоящее время + + + busy installing other software. Amnesia installation + занят установкой других протоколов или сервисов. Установка Amnesia + + + + Amnezia has detected that your server is currently + برنامه Amnezia تشخیص داده است که سرور در حال حاضر + + + + busy installing other software. Amnezia installation + مشغول نصب نرم‎افزار دیگری است. نصب Amnezia + + + + will pause until the server finishes installing other software + متوقف شده تا زمانی که سرور نصب نرم‎افزار دیگر را تمام کند + + + + Installing + در حال نصب + + + + + Usually it takes no more than 5 minutes + معمولا بیش از 5 دقیقه طول نمی‎کشد + + + + PageSetupWizardProtocolSettings + + + Installing %1 + در حال نصب %1 + + + + More detailed + جزییات بیشتر + + + + Close + بستن + + + + Network protocol + پروتکل شبکه + + + + Port + پورت + + + + Install + نصب + + + + PageSetupWizardProtocols + + + VPN protocol + پروتکل وی‎پی‎ان + + + + Choose the one with the highest priority for you. Later, you can install other protocols and additional services, such as DNS proxy and SFTP. + پروتکلی که بیشترین اولویت را برای شما دارد انتخاب کنید. بعدا، میتوانید پروتکل‎ها و سرویس‎های اضافه مانند پروکسی DNS و SFTP را هم نصب کنید + + + + PageSetupWizardQrReader + + + Point the camera at the QR code and hold for a couple of seconds. + دوربین را روی QR Code بگیرید و برای چند ثانیه آن را نگه دارید. + + + + PageSetupWizardStart + + + Settings restored from backup file + تنظیمات از فایل بک‎آپ بازیابی شدند + + + + Free service for creating a personal VPN on your server. + سرویس رایگان برای ایجاد وی‎پی‎ان شخصی بر روی سرور خودتان. + + + + Helps you access blocked content without revealing your privacy, even to VPN providers. + به شما کمک می‎کند که بدون فاش کردن حریم شخصی خودتان حتی برای ارائه دهنده وی‎پی‎ان به محتوای مسدود شده دسترسی پیدا کنید. + + + + I have the data to connect + من داده برای اتصال دارم + + + + I have nothing + من هیچی ندارم + + + + PageSetupWizardTextKey + + + Connection key + کلید ارتباط + + + + A line that starts with vpn://... + یک کلید متنی که با vpn:// شروع می‎شود + + + + Key + کلید + + + + Insert + وارد کردن + + + + Continue + ادامه + + + + PageSetupWizardViewConfig + + + New connection + ارتباط جدید + + + + Do not use connection code from public sources. It could be created to intercept your data. + از کد اتصالی که در منابع عمومی هست استفاده نکنید. ممکن است برای شنود اطلاعات شما ایجاد شده باشد. + + + + Collapse content + جمع کردن محتوا + + + + Show content + نمایش محتوا + + + + Connect + اتصال + + + + PageShare + + + OpenVpn native format + فرمت محلی OpenVPN + + + + WireGuard native format + فرمت محلی WireGuard + + + VPN Access + VPN-Доступ + + + + Connection + ارتباط + + + VPN access without the ability to manage the server + Доступ к VPN, без возможности управления сервером + + + Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings. + Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки. + + + + + Server + سرور + + + + Accessing + در حال دسترسی به + + + + File with accessing settings to + فایل شامل تنظیمات دسترسی به + + + + Connection to + ارتباط با + + + + File with connection settings to + فایل شامل تنظیمات ارتباط با + + + + Save OpenVPN config + ذخیره تنظیمات OpenVPN + + + + Save WireGuard config + ذخیره تنظیمات WireGuard + + + + For the AmneziaVPN app + برای نرم‎افزار AmneziaVPN + + + + Share VPN Access + به اشتراک گذاشتن دسترسی وی‎پی‎ان + + + + Full access + دسترسی کامل + + + + Share VPN access without the ability to manage the server + به اشتراک گذاشتن دسترسی وی‎پی‎ان بدون امکان مدیریت سرور + + + + Share access to server management. The user with whom you share full access to the server will be able to add and remove any protocols and services to the server, as well as change settings. + به اشتراک گذاری دسترسی به مدیریت سرور. کاربری که دسترسی کامل سرور با او به اشتراک گذاشته می‎شود می‎تواند پروتکل‌‎ها و سرویس‎ها را در سرور حذف یا اضافه کند و یا تنظیمات سرور را تغییر دهد. + + + + + Protocol + پروتکل + + + + + Connection format + فرمت ارتباط + + + + Share + اشتراک‎گذاری + + + + PopupType + + + Close + بستن + + + + QKeychain::DeletePasswordJobPrivate + + + Password entry not found + Password entry not found + + + + Could not decrypt data + Could not decrypt data + + + + + Unknown error + Unknown error + + + + Could not open wallet: %1; %2 + Could not open wallet: %1; %2 + + + + Password not found + Password not found + + + + Could not open keystore + Could not open keystore + + + + Could not remove private key from keystore + Could not remove private key from keystore + + + + QKeychain::JobPrivate + + + Unknown error + Unknown error + + + + Access to keychain denied + Access to keychain denied + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + Could not store data in settings: access error + + + + Could not store data in settings: format error + Could not store data in settings: format error + + + + Could not delete data from settings: access error + Could not delete data from settings: access error + + + + Could not delete data from settings: format error + Could not delete data from settings: format error + + + + Entry not found + Entry not found + + + + QKeychain::ReadPasswordJobPrivate + + + Password entry not found + Password entry not found + + + + + Could not decrypt data + Could not decrypt data + + + + D-Bus is not running + D-Bus is not running + + + + + Unknown error + Unknown error + + + + No keychain service available + No keychain service available + + + + Could not open wallet: %1; %2 + Could not open wallet: %1; %2 + + + + Access to keychain denied + Access to keychain denied + + + + Could not determine data type: %1; %2 + Could not determine data type: %1; %2 + + + + + Entry not found + Entry not found + + + + Unsupported entry type 'Map' + Unsupported entry type 'Map' + + + + Unknown kwallet entry type '%1' + Unknown kwallet entry type '%1' + + + + Password not found + Password not found + + + + Could not open keystore + Could not open keystore + + + + Could not retrieve private key from keystore + Could not retrieve private key from keystore + + + + Could not create decryption cipher + Could not create decryption cipher + + + + QKeychain::WritePasswordJobPrivate + + + Credential size exceeds maximum size of %1 + Credential size exceeds maximum size of %1 + + + + Credential key exceeds maximum size of %1 + Credential key exceeds maximum size of %1 + + + + Writing credentials failed: Win32 error code %1 + Writing credentials failed: Win32 error code %1 + + + + Encryption failed + Encryption failed + + + + D-Bus is not running + D-Bus is not running + + + + + Unknown error + Unknown error + + + + Could not open wallet: %1; %2 + Could not open wallet: %1; %2 + + + + Password not found + Password not found + + + + Could not open keystore + Could not open keystore + + + + Could not create private key generator + Could not create private key generator + + + + Could not generate new private key + Could not generate new private key + + + + Could not retrieve private key from keystore + Could not retrieve private key from keystore + + + + Could not create encryption cipher + Could not create encryption cipher + + + + Could not encrypt data + Could not encrypt data + + + + QObject + + + No error + No error + + + + Unknown Error + Unknown Error + + + + Function not implemented + Function not implemented + + + + Server check failed + Server check failed + + + + Server port already used. Check for another software + Server port already used. Check for another software + + + + Server error: Docker container missing + Server error: Docker container missing + + + + Server error: Docker failed + Server error: Docker failed + + + + Installation canceled by user + Installation canceled by user + + + + The user does not have permission to use sudo + The user does not have permission to use sudo + + + + Ssh request was denied + Ssh request was denied + + + + Ssh request was interrupted + Ssh request was interrupted + + + + Ssh internal error + Ssh internal error + + + + Invalid private key or invalid passphrase entered + Invalid private key or invalid passphrase entered + + + + The selected private key format is not supported, use openssh ED25519 key types or PEM key types + The selected private key format is not supported, use openssh ED25519 key types or PEM key types + + + + Timeout connecting to server + Timeout connecting to server + + + + Sftp error: End-of-file encountered + Sftp error: End-of-file encountered + + + + Sftp error: File does not exist + Sftp error: File does not exist + + + + Sftp error: Permission denied + Sftp error: Permission denied + + + + Sftp error: Generic failure + Sftp error: Generic failure + + + + Sftp error: Garbage received from server + Sftp error: Garbage received from server + + + + Sftp error: No connection has been set up + Sftp error: No connection has been set up + + + + Sftp error: There was a connection, but we lost it + Sftp error: There was a connection, but we lost it + + + + Sftp error: Operation not supported by libssh yet + Sftp error: Operation not supported by libssh yet + + + + Sftp error: Invalid file handle + Sftp error: Invalid file handle + + + + Sftp error: No such file or directory path exists + Sftp error: No such file or directory path exists + + + + Sftp error: An attempt to create an already existing file or directory has been made + Sftp error: An attempt to create an already existing file or directory has been made + + + + Sftp error: Write-protected filesystem + Sftp error: Write-protected filesystem + + + + Sftp error: No media was in remote drive + Sftp error: No media was in remote drive + + + + Failed to save config to disk + Failed to save config to disk + + + + OpenVPN config missing + OpenVPN config missing + + + + OpenVPN management server error + OpenVPN management server error + + + + OpenVPN executable missing + OpenVPN executable missing + + + + ShadowSocks (ss-local) executable missing + ShadowSocks (ss-local) executable missing + + + + Cloak (ck-client) executable missing + Cloak (ck-client) executable missing + + + + Amnezia helper service error + Amnezia helper service error + + + + OpenSSL failed + OpenSSL failed + + + + Can't connect: another VPN connection is active + Can't connect: another VPN connection is active + + + + Can't setup OpenVPN TAP network adapter + Can't setup OpenVPN TAP network adapter + + + + VPN pool error: no available addresses + VPN pool error: no available addresses + + + + The config does not contain any containers and credentiaks for connecting to the server + The config does not contain any containers and credentiaks for connecting to the server + + + + Internal error + Internal error + + + + IPsec + IPsec + + + + IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. +One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. +While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. + +* Available in the AmneziaVPN only on Windows +* Low power consumption, on mobile devices +* Minimal configuration +* Recognised by DPI analysis systems +* Works over UDP network protocol, ports 500 and 4500. + IKEv2 в сочетании с уровнем шифрования IPSec это современный и стабильный протокол VPN. +Он может быстро переключаться между сетями и устройствами, что делает его особенно адаптивным в динамичных сетевых средах. +Несмотря на сочетание безопасности, стабильности и скорости, необходимо отметить, что IKEv2 легко обнаруживается и подвержен блокировке. + +* Доступно в AmneziaVPN только для Windows. +* Низкое энергопотребление, на мобильных устройствах +* Минимальная конфигурация +* Распознается системами DPI-анализа +* Работает по сетевому протоколу UDP, порты 500 и 4500. + + + + DNS Service + DNS Сервис + + + + Sftp file sharing service + Сервис обмена файлами Sftp + + + + + Website in Tor network + Веб-сайт в сети Tor + + + + Amnezia DNS + Amnezia DNS + + + + OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its own security protocol with SSL/TLS for key exchange. + OpenVPN - популярный VPN-протокол, с гибкой настройкой. Имеет собственный протокол безопасности с SSL/TLS для обмена ключами. + + + + ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but is recognised by analysis systems in some highly censored regions. + ShadowSocks - маскирует VPN-трафик под обычный веб-трафик, но распознается системами анализа в некоторых регионах с высоким уровнем цензуры. + + + + OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probbing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. + OpenVPN over Cloak - OpenVPN с маскировкой VPN под web-трафик и защитой от обнаружения active-probbing. Подходит для регионов с самым высоким уровнем цензуры. + + + + WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. + WireGuard - Популярный VPN-протокол с высокой производительностью, высокой скоростью и низким энергопотреблением. Для регионов с низким уровнем цензуры. + + + + AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. + AmneziaWG - Специальный протокол от Amnezia, основанный на протоколе WireGuard. Он такой же быстрый, как WireGuard, но очень устойчив к блокировкам. Рекомендуется для регионов с высоким уровнем цензуры. + + + + IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. + IKEv2 Современный стабильный протокол, немного быстрее других восстанавливает соединение после потери сигнала. Имеет нативную поддержку последних версиий Android и iOS. + + + + Deploy a WordPress site on the Tor network in two clicks. + Разверните сайт на WordPress в сети Tor в два клика. + + + + Replace the current DNS server with your own. This will increase your privacy level. + Замените DNS-сервер на Amnezia DNS. Это повысит уровень конфиденциальности. + + + + Creates a file vault on your server to securely store and transfer files. + Создайте на сервере файловое хранилище для безопасного хранения и передачи файлов. + + + + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. + +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI analysis systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + OpenVPN однин из самых популярных и проверенных временем VPN-протоколов. +В нем используется уникальный протокол безопасности, опирающийся на протокол SSL/TLS для шифрования и обмена ключами. Кроме того, поддержка OpenVPN множества методов аутентификации делает его универсальным и адаптируемым к широкому спектру устройств и операционных систем. Благодаря открытому исходному коду OpenVPN подвергается тщательному анализу со стороны мирового сообщества, что постоянно повышает его безопасность. Благодаря оптимальному соотношению производительности, безопасности и совместимости OpenVPN остается лучшим выбором как для частных лиц, так и для компаний, заботящихся о конфиденциальности. + +* Доступность AmneziaVPN для всех платформ +* Нормальное энергопотребление на мобильных устройствах +* Гибкая настройка под нужды пользователя для работы с различными операционными системами и устройствами +* Распознается системами DPI-анализа и поэтому подвержен блокировке +* Может работать по сетевым протоколам TCP и UDP. + + + + Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. + +* Available in the AmneziaVPN only on desktop platforms +* Normal power consumption on mobile devices + +* Configurable encryption protocol +* Detectable by some DPI systems +* Works over TCP network protocol. + Shadowsocks, создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению. Однако некоторые системы анализа трафика все же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG, или OpenVPN over Cloak. + +* Доступен в AmneziaVPN только на ПК ноутбуках. +* Настраиваемый протокол шифрования +* Обнаруживается некоторыми DPI-системами +* Работает по сетевому протоколу TCP. + + + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for blocking protection. + +OpenVPN provides a secure VPN connection by encrypting all Internet traffic between the client and the server. + +Cloak protects OpenVPN from detection and blocking. + +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected + +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +If there is a extreme level of Internet censorship in your region, we advise you to use only OpenVPN over Cloak from the first connection + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by DPI analysis systems +* Works over TCP network protocol, 443 port. + + OpenVPN over Cloak - это комбинация протокола OpenVPN и плагина Cloak, разработанного специально для защиты от блокировок. + +OpenVPN обеспечивает безопасное VPN-соединение за счет шифрования всего интернет-трафика между клиентом и сервером. + +Cloak защищает OpenVPN от обнаружения и блокировок. + +Cloak может изменять метаданные пакетов. Он полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает его очень устойчивым к обнаружению + +Сразу же после получения первого пакета данных Cloak проверяет подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под поддельный сайт, и ваш VPN становится невидимым для аналитических систем. + +Если в вашем регионе существует экстремальный уровень цензуры в Интернете, мы советуем вам при первом подключении использовать только OpenVPN через Cloak + +* Доступность AmneziaVPN на всех платформах +* Высокое энергопотребление на мобильных устройствах +* Гибкие настройки +* Не распознается системами DPI-анализа +* Работает по сетевому протоколу TCP, 443 порт. + + + + + A relatively new popular VPN protocol with a simplified architecture. +Provides stable VPN connection, high performance on all devices. Uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + WireGuard - относительно новый популярный VPN-протокол с упрощенной архитектурой. +Обеспечивает стабильное VPN-соединение, высокую производительность на всех устройствах. Использует жестко заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных. +WireGuard очень восприимчив к блокированию из-за особенностей сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче выявляются и, соответственно, блокируются современными системами глубокой проверки пакетов (DPI) и другими средствами сетевого мониторинга. + +* Доступность AmneziaVPN для всех платформ +* Низкое энергопотребление +* Минимальное количество настроек +* Легко распознается системами DPI-анализа, подвержен блокировке +* Работает по сетевому протоколу UDP. + + + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by DPI analysis systems, resistant to blocking +* Works over UDP network protocol. + AmneziaWG - усовершенствованная версия популярного VPN-протокола Wireguard. AmneziaWG опирается на фундамент, заложенный WireGuard, сохраняя упрощенную архитектуру и высокопроизводительные возможности работы на разных устройствах. +Хотя WireGuard известен своей эффективностью, у него были проблемы с обнаружением из-за характерных сигнатур пакетов. AmneziaWG решает эту проблему за счет использования более совершенных методов обфускации, благодаря чему его трафик сливается с обычным интернет-трафиком. +Таким образом, AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение. + +* Доступность AmneziaVPN на всех платформах +* Низкое энергопотребление +* Минимальное количество настроек +* Не распознается системами DPI-анализа, устойчив к блокировке +* Работает по сетевому протоколу UDP. + + + AmneziaWG container + AmneziaWG протокол + + + + Sftp file sharing service - is secure FTP service + Сервис обмена файлами Sftp - безопасный FTP-сервис + + + + Sftp service + Сервис SFTP + + + + Entry not found + Entry not found + + + + Access to keychain denied + Access to keychain denied + + + + No keyring daemon + No keyring daemon + + + + Already unlocked + Already unlocked + + + + No such keyring + No such keyring + + + + Bad arguments + Bad arguments + + + + I/O error + I/O error + + + + Cancelled + Cancelled + + + + Keyring already exists + Keyring already exists + + + + No match + No match + + + + Unknown error + Unknown error + + + + error 0x%1: %2 + error 0x%1: %2 + + + + WireGuard Configuration Highlighter + + + + + &Randomize colors + + + + + SelectLanguageDrawer + + + Choose language + انتخاب زبان + + + + Settings + + + Server #1 + Server #1 + + + + + Server + Server + + + + SettingsController + + + Software version + نسخه نرم‎افزار + + + + All settings have been reset to default values + تمام تنظیمات به مقادیر پیش فرض ریست شد + + + + Cached profiles cleared + پروفایل ذخیره شده پاک شد + + + + Backup file is corrupted + فایل بک‎آپ خراب شده است + + + + ShareConnectionDrawer + + + + Save AmneziaVPN config + ذخیره تنظیمات AmneziaVPN + + + + Share + اشتراک‎گذاری + + + + Copy + کپی + + + + Copied + کپی شد + + + + Show connection settings + نمایش تنظیمات ارتباط + + + + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" + برای خواندن QR Code در نرم‎افزار AmneziaVPN "اضافه کردن سرور" -> "من داده برای اتصال دارم" -> "QR Code، کلید یا فایل تنظیمات" + + + + SitesController + + + Hostname not look like ip adress or domain name + فرمت هاست شبیه آدرس آی‎پی یا نام دامنه نیست + + + + New site added: %1 + سایت جدید اضافه‎شد: %1 + + + + Site removed: %1 + سایت حذف شد: %1 + + + + Can't open file: %1 + فایل باز نشد: %1 + + + + Failed to parse JSON data from file: %1 + مشکل در تحلیل داده‎های JSON در فایل: %1 + + + + The JSON data is not an array in file: %1 + داده‎های JSON در فایل به صورت آرایه نیستند: %1 + + + + Import completed + بارگذاری کامل شد + + + + Export completed + خروجی گرفتن کامل شد + + + + SystemTrayNotificationHandler + + + + Show + نمایش + + + + + Connect + اتصال + + + + + Disconnect + قطع ارتباط + + + + + Visit Website + بازدید وب‎سایت + + + + + Quit + خروج + + + + TextFieldWithHeaderType + + + The field can't be empty + Поле не может быть пустым + + + + VpnConnection + + + Mbps + Mbps + + + + VpnProtocol + + + Unknown + ناشناخته + + + + Disconnected + قطع شده + + + + Preparing + درحال آماده‎سازی + + + + Connecting... + برقراری ارتباط... + + + + Connected + وصل شد + + + + Disconnecting... + در حال قطع شدن... + + + + Reconnecting... + برقراری ارتباط دوباره... + + + + Error + خطا + + + + amnezia::ContainerProps + + + Low + پایین + + + + Medium or High + متوسط یا بالا + + + + Extreme + شدید + + + + I just want to increase the level of my privacy. + من فقط میخواهم سطح حریم شخصی خودم را بالا ببرم + + + + I want to bypass censorship. This option recommended in most cases. + من میخواهم از سانسور عبور کنم. این گزینه در اکثر موارد توصیه می‎‌شود + + + + Most VPN protocols are blocked. Recommended if other options are not working. + اکثر پروتکل‎های وی‎پی‎ان مسدود شده‎اند. در مواردی که بقیه گزینه‎ها کار نمی‎کنند توصی می‎شود. + + + High + Высокий + + + Medium + Средний + + + Many foreign websites and VPN providers are blocked + Многие иностранные сайты и VPN-провайдеры заблокированы + + + Some foreign sites are blocked, but VPN providers are not blocked + Некоторые иностранные сайты заблокированы, но VPN-провайдеры не блокируются + + + I just want to increase the level of privacy + Хочу просто повысить уровень приватности + + + + main2 + + + Private key passphrase + عبارت کلید خصوصی + + + + Save + ذخیره + + + diff --git a/client/ui/models/languageModel.cpp b/client/ui/models/languageModel.cpp index b860b9da..c3552ea7 100644 --- a/client/ui/models/languageModel.cpp +++ b/client/ui/models/languageModel.cpp @@ -44,6 +44,7 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan case LanguageSettings::AvailableLanguageEnum::English: strLanguage = "English"; break; case LanguageSettings::AvailableLanguageEnum::Russian: strLanguage = "Русский"; break; case LanguageSettings::AvailableLanguageEnum::China_cn: strLanguage = "\347\256\200\344\275\223\344\270\255\346\226\207"; break; + case LanguageSettings::AvailableLanguageEnum::Persian: strLanguage = "فارسی"; break; default: break; } @@ -57,6 +58,7 @@ void LanguageModel::changeLanguage(const LanguageSettings::AvailableLanguageEnum case LanguageSettings::AvailableLanguageEnum::English: emit updateTranslations(QLocale::English); break; case LanguageSettings::AvailableLanguageEnum::Russian: emit updateTranslations(QLocale::Russian); break; case LanguageSettings::AvailableLanguageEnum::China_cn: emit updateTranslations(QLocale::Chinese); break; + case LanguageSettings::AvailableLanguageEnum::Persian: emit updateTranslations(QLocale::Persian); break; default: emit updateTranslations(QLocale::English); break; } } @@ -68,6 +70,7 @@ int LanguageModel::getCurrentLanguageIndex() case QLocale::English: return static_cast(LanguageSettings::AvailableLanguageEnum::English); break; case QLocale::Russian: return static_cast(LanguageSettings::AvailableLanguageEnum::Russian); break; case QLocale::Chinese: return static_cast(LanguageSettings::AvailableLanguageEnum::China_cn); break; + case QLocale::Persian: return static_cast(LanguageSettings::AvailableLanguageEnum::Persian); break; default: return static_cast(LanguageSettings::AvailableLanguageEnum::English); break; } } diff --git a/client/ui/models/languageModel.h b/client/ui/models/languageModel.h index c8879a34..8a55263c 100644 --- a/client/ui/models/languageModel.h +++ b/client/ui/models/languageModel.h @@ -12,7 +12,8 @@ namespace LanguageSettings enum class AvailableLanguageEnum { English, Russian, - China_cn + China_cn, + Persian }; Q_ENUM_NS(AvailableLanguageEnum) From 9681bea237af405b7557da742b54cdda0b03d09f Mon Sep 17 00:00:00 2001 From: pokamest Date: Wed, 20 Dec 2023 12:14:06 +0000 Subject: [PATCH 32/33] Update ts files --- client/translations/amneziavpn_fa_IR.ts | 388 +++++++++++++++------- client/translations/amneziavpn_ru.ts | 424 ++++++++++++++++-------- client/translations/amneziavpn_zh_CN.ts | 388 +++++++++++++++------- 3 files changed, 810 insertions(+), 390 deletions(-) diff --git a/client/translations/amneziavpn_fa_IR.ts b/client/translations/amneziavpn_fa_IR.ts index 5c6ad424..7471208b 100644 --- a/client/translations/amneziavpn_fa_IR.ts +++ b/client/translations/amneziavpn_fa_IR.ts @@ -8,9 +8,8 @@ Раздельное туннелирование для "Wireguard" не реализовано,опция отключена - Split tunneling for %1 is not implemented, the option was disabled - جداسازی ترافیک برای %1 پیاده سازی نشده، این گزینه غیرفعال است + جداسازی ترافیک برای %1 پیاده سازی نشده، این گزینه غیرفعال است @@ -27,46 +26,59 @@ وی‎پی‎ان متصل است + + ApiController + + + Error when retrieving configuration from cloud server + + + ConnectionController - + VPN Protocols is not installed. Please install VPN container at first پروتکل وی‎پی‎ان نصب نشده است لطفا کانتینر وی‎پی‎ان را نصب کنید - + Connection... ارتباط - + Connected متصل - + Settings updated successfully, Reconnnection... تنظیمات به روز رسانی شد در حال اتصال دوباره... - + + Settings updated successfully + + + + Reconnection... در حال اتصال دوباره... - - - + + + Connect اتصال - + Disconnection... قطع ارتباط... @@ -115,7 +127,7 @@ ExportController - + Access error! خطای دسترسی! @@ -128,7 +140,7 @@ امکان تغییر پروتکل در هنگام متصل بودن وجود ندارد - + The selected protocol is not supported on the current platform پروتکل انتخاب شده بر روی این پلتفرم پشتیبانی نمی‎‎شود @@ -140,7 +152,7 @@ ImportController - + Scanned %1 of %2. ارزیابی %1 از %2. @@ -148,58 +160,58 @@ InstallController - - + + %1 installed successfully. %1 با موفقیت نصب شد - - + + %1 is already installed on the server. %1 در حال حاضر بر روی سرور نصب شده است - + Added containers that were already installed on the server کانتینرهایی که بر روی سرور موجود بودند اضافه شدند - + Already installed containers were found on the server. All installed containers have been added to the application کانتینرهای نصب شده بر روی سرور شناسایی شدند. تمام کانتینترهای نصب شده به نرم افزار اضافه شدند - + Settings updated successfully تنظیمات با موفقیت به‎روز‎رسانی شدند - + Server '%1' was removed سرور %1 حذف شد - + All containers from server '%1' have been removed تمام کانتینترها از سرور %1 حذف شدند - + %1 has been removed from the server '%2' %1 از سرور %2 حذف شد - + Please login as the user لطفا به عنوان کاربر وارد شوید - + Server added successfully سرور با موفقیت اضافه شد @@ -267,17 +279,17 @@ Already installed containers were found on the server. All installed containers PageHome - + VPN protocol پروتکل وی‎پی‎ان - + Servers سرورها - + Unable change server while there is an active connection امکان تغییر سرور در هنگام متصل بودن وجود ندارد @@ -377,28 +389,28 @@ Already installed containers were found on the server. All installed containers PageProtocolCloakSettings - + Cloak settings تنظیمات Cloak - + Disguised as traffic from پنهان کردن به عنوان ترافیک از - + Port پورت - - + + Cipher رمزگذاری - + Save and Restart Amnezia ذخیره و راه اندازی دوباره Amnezia @@ -653,23 +665,23 @@ Already installed containers were found on the server. All installed containers PageProtocolShadowSocksSettings - + ShadowSocks settings تنظیمات ShadowSocks - + Port پورت - - + + Cipher رمزگذاری - + Save and Restart Amnezia ذخیره و راه اندازی دوباره Amnezia @@ -1643,18 +1655,17 @@ It's okay as long as it's from someone you trust. Password / SSH private key - + Continue ادامه - All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties - تمام داده‎هایی که شما وارد می‎کنید به شدت محرمانه‎ است و با Amnezia یا هر شخص ثالث دیگری به اشتراک گذاشته نمی‎شود + تمام داده‎هایی که شما وارد می‎کنید به شدت محرمانه‎ است و با Amnezia یا هر شخص ثالث دیگری به اشتراک گذاشته نمی‎شود - + Enter the address in the format 255.255.255.255:88 آدرس را با فرمت 255.255.255.255:88 وارد کنید @@ -1669,17 +1680,22 @@ and will not be shared or disclosed to the Amnezia or any third parties سرور خود را پیکربندی کنید - + + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties + + + + Ip address cannot be empty آدرس آی‎پی نمی‎تواند خالی باشد - + Login cannot be empty نام‎کاربری نمی‎تواند خالی باشد - + Password/private key cannot be empty پسورد یا کلید خصوصی نمی‎تواند خالی باشد @@ -1715,7 +1731,7 @@ and will not be shared or disclosed to the Amnezia or any third parties PageSetupWizardInstalling - + The server has already been added to the application سرور در حال حاضر به نرم‎افزار اضافه شده است @@ -1728,28 +1744,33 @@ and will not be shared or disclosed to the Amnezia or any third parties занят установкой других протоколов или сервисов. Установка Amnesia - + Amnezia has detected that your server is currently برنامه Amnezia تشخیص داده است که سرور در حال حاضر - + busy installing other software. Amnezia installation مشغول نصب نرم‎افزار دیگری است. نصب Amnezia - + will pause until the server finishes installing other software متوقف شده تا زمانی که سرور نصب نرم‎افزار دیگر را تمام کند - + Installing در حال نصب + + + Cancel installation + + - + Usually it takes no more than 5 minutes معمولا بیش از 5 دقیقه طول نمی‎کشد @@ -1816,22 +1837,22 @@ and will not be shared or disclosed to the Amnezia or any third parties تنظیمات از فایل بک‎آپ بازیابی شدند - + Free service for creating a personal VPN on your server. سرویس رایگان برای ایجاد وی‎پی‎ان شخصی بر روی سرور خودتان. - + Helps you access blocked content without revealing your privacy, even to VPN providers. به شما کمک می‎کند که بدون فاش کردن حریم شخصی خودتان حتی برای ارائه دهنده وی‎پی‎ان به محتوای مسدود شده دسترسی پیدا کنید. - + I have the data to connect من داده برای اتصال دارم - + I have nothing من هیچی ندارم @@ -1895,12 +1916,12 @@ and will not be shared or disclosed to the Amnezia or any third parties PageShare - + OpenVpn native format فرمت محلی OpenVPN - + WireGuard native format فرمت محلی WireGuard @@ -1909,7 +1930,7 @@ and will not be shared or disclosed to the Amnezia or any third parties VPN-Доступ - + Connection ارتباط @@ -1922,84 +1943,222 @@ and will not be shared or disclosed to the Amnezia or any third parties Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки. - - + + Server سرور - Accessing - در حال دسترسی به + در حال دسترسی به - File with accessing settings to - فایل شامل تنظیمات دسترسی به + فایل شامل تنظیمات دسترسی به - + + Config revoked + + + + Connection to ارتباط با - + File with connection settings to فایل شامل تنظیمات ارتباط با - + Save OpenVPN config ذخیره تنظیمات OpenVPN - + Save WireGuard config ذخیره تنظیمات WireGuard - + + Save ShadowSocks config + + + + + Save Cloak config + + + + For the AmneziaVPN app برای نرم‎افزار AmneziaVPN - + + ShadowSocks native format + + + + + Cloak native format + + + + Share VPN Access به اشتراک گذاشتن دسترسی وی‎پی‎ان - - Full access - دسترسی کامل + + Share full access to the server and VPN + - + + Use for your own devices, or share with those you trust to manage the server. + + + + + + Users + + + + + User name + + + + + Search + + + + + Rename + + + + + Client name + + + + + Save + ذخیره + + + + Revoke + + + + + Revoke the config for a user - + + + + + The user will no longer be able to connect to your server. + + + + + Continue + + + + + Cancel + کنسل + + + Full access + دسترسی کامل + + + Share VPN access without the ability to manage the server به اشتراک گذاشتن دسترسی وی‎پی‎ان بدون امکان مدیریت سرور - Share access to server management. The user with whom you share full access to the server will be able to add and remove any protocols and services to the server, as well as change settings. - به اشتراک گذاری دسترسی به مدیریت سرور. کاربری که دسترسی کامل سرور با او به اشتراک گذاشته می‎شود می‎تواند پروتکل‌‎ها و سرویس‎ها را در سرور حذف یا اضافه کند و یا تنظیمات سرور را تغییر دهد. + به اشتراک گذاری دسترسی به مدیریت سرور. کاربری که دسترسی کامل سرور با او به اشتراک گذاشته می‎شود می‎تواند پروتکل‌‎ها و سرویس‎ها را در سرور حذف یا اضافه کند و یا تنظیمات سرور را تغییر دهد. - - + + Protocol پروتکل - - + + Connection format فرمت ارتباط - + + Share اشتراک‎گذاری + + PageShareFullAccess + + + Full access to the server and VPN + + + + + We recommend that you use full access to the server only for your own additional devices. + + + + + + If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. + + + + + + Server + + + + + Accessing + در حال دسترسی به + + + + File with accessing settings to + فایل شامل تنظیمات دسترسی به + + + + Share + اشتراک‎گذاری + + + + Connection to + ارتباط با + + + + File with connection settings to + فایل شامل تنظیمات ارتباط با + + PopupType @@ -2386,67 +2545,70 @@ and will not be shared or disclosed to the Amnezia or any third parties Sftp error: No media was in remote drive - - Failed to save config to disk - Failed to save config to disk + + The config does not contain any containers and credentials for connecting to the server + - + Failed to save config to disk + Failed to save config to disk + + + OpenVPN config missing OpenVPN config missing - + OpenVPN management server error OpenVPN management server error - + OpenVPN executable missing OpenVPN executable missing - + ShadowSocks (ss-local) executable missing ShadowSocks (ss-local) executable missing - + Cloak (ck-client) executable missing Cloak (ck-client) executable missing - + Amnezia helper service error Amnezia helper service error - + OpenSSL failed OpenSSL failed - + Can't connect: another VPN connection is active Can't connect: another VPN connection is active - + Can't setup OpenVPN TAP network adapter Can't setup OpenVPN TAP network adapter - + VPN pool error: no available addresses VPN pool error: no available addresses - The config does not contain any containers and credentiaks for connecting to the server - The config does not contain any containers and credentiaks for connecting to the server + The config does not contain any containers and credentiaks for connecting to the server - + Internal error Internal error @@ -2733,16 +2895,6 @@ This means that AmneziaWG keeps the fast performance of the original while addin error 0x%1: %2 error 0x%1: %2 - - - WireGuard Configuration Highlighter - - - - - &Randomize colors - - SelectLanguageDrawer @@ -2774,17 +2926,17 @@ This means that AmneziaWG keeps the fast performance of the original while addin نسخه نرم‎افزار - + All settings have been reset to default values تمام تنظیمات به مقادیر پیش فرض ریست شد - + Cached profiles cleared پروفایل ذخیره شده پاک شد - + Backup file is corrupted فایل بک‎آپ خراب شده است @@ -2809,16 +2961,22 @@ This means that AmneziaWG keeps the fast performance of the original while addin + Copied کپی شد - + + Copy config string + + + + Show connection settings نمایش تنظیمات ارتباط - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" برای خواندن QR Code در نرم‎افزار AmneziaVPN "اضافه کردن سرور" -> "من داده برای اتصال دارم" -> "QR Code، کلید یا فایل تنظیمات" @@ -2910,7 +3068,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin VpnConnection - + Mbps Mbps diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts index 283bf0c2..4f24e540 100644 --- a/client/translations/amneziavpn_ru.ts +++ b/client/translations/amneziavpn_ru.ts @@ -8,9 +8,8 @@ Раздельное туннелирование для "Wireguard" не реализовано,опция отключена - Split tunneling for %1 is not implemented, the option was disabled - Раздельное туннелирование для %1 не реализовано, опция отключена + Раздельное туннелирование для %1 не реализовано, опция отключена @@ -28,17 +27,9 @@ - CloudController + ApiController - - Error when retrieving configuration from cloud server - - - - - CloudController - - + Error when retrieving configuration from cloud server @@ -46,43 +37,47 @@ ConnectionController - - + VPN Protocols is not installed. Please install VPN container at first VPN протоколы не установлены. Пожалуйста, установите протокол - + Connection... Подключение... - + Connected Подключено - + Settings updated successfully, Reconnnection... Настройки успешно обновлены. Подключение... - + + Settings updated successfully + Настройки успешно обновлены + + + Reconnection... Переподключение... - - - + + + Connect Подключиться - + Disconnection... Отключение... @@ -131,7 +126,7 @@ ExportController - + Access error! Ошибка доступа! @@ -144,7 +139,7 @@ Невозможно изменить протокол при активном соединении - + The selected protocol is not supported on the current platform Выбранный протокол не поддерживается на данном устройстве @@ -156,7 +151,7 @@ ImportController - + Scanned %1 of %2. Отсканировано %1 из%2. @@ -164,52 +159,57 @@ InstallController - - + + %1 installed successfully. %1 успешно установлен. - - + + %1 is already installed on the server. %1 уже установлен на сервер. - - + + +Added containers that were already installed on the server + + + + Already installed containers were found on the server. All installed containers have been added to the application На сервере обнаружены установленные протоколы и сервисы, все они добавлены в приложение - + Settings updated successfully Настройки успешно обновлены - + Server '%1' was removed Сервер '%1' был удален - + All containers from server '%1' have been removed Все протоклы и сервисы были удалены с сервера '%1' - + %1 has been removed from the server '%2' %1 был удален с сервера '%2' - + Please login as the user Пожалуйста, войдите в систему от имени пользователя - + Server added successfully Сервер успешно добавлен @@ -277,17 +277,17 @@ Already installed containers were found on the server. All installed containers PageHome - + VPN protocol VPN протокол - + Servers Серверы - + Unable change server while there is an active connection Невозможно изменить сервер при активном соединении @@ -387,28 +387,28 @@ Already installed containers were found on the server. All installed containers PageProtocolCloakSettings - + Cloak settings Настройки Cloak - + Disguised as traffic from Замаскировать трафик под - + Port Порт - - + + Cipher Шифрование - + Save and Restart Amnezia Сохранить и перезагрузить Amnezia @@ -663,23 +663,23 @@ Already installed containers were found on the server. All installed containers PageProtocolShadowSocksSettings - + ShadowSocks settings Настройки ShadowSocks - + Port Порт - - + + Cipher Шифрование - + Save and Restart Amnezia Сохранить и перезагрузить Amnezia @@ -1011,17 +1011,17 @@ Already installed containers were found on the server. All installed containers + Allow application screenshots + + + + Auto start - - Launch the application every time - - - - - starts + + Launch the application every time the device is starts @@ -1653,18 +1653,17 @@ It's okay as long as it's from someone you trust. Password / SSH private key - + Continue Продолжить - All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties - Все данные, которые вы вводите, останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или каким-либо третьим сторонам + Все данные, которые вы вводите, останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или каким-либо третьим сторонам - + Enter the address in the format 255.255.255.255:88 Введите адрес в формате 255.255.255.255:88 @@ -1679,17 +1678,22 @@ and will not be shared or disclosed to the Amnezia or any third parties Настроить ваш сервер - + + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties + + + + Ip address cannot be empty Поле Ip address не может быть пустым - + Login cannot be empty Поле Login не может быть пустым - + Password/private key cannot be empty Поле Password/private key не может быть пустым @@ -1725,7 +1729,7 @@ and will not be shared or disclosed to the Amnezia or any third parties PageSetupWizardInstalling - + The server has already been added to the application Сервер уже был добавлен в приложение @@ -1738,28 +1742,33 @@ and will not be shared or disclosed to the Amnezia or any third parties занят установкой других протоколов или сервисов. Установка Amnesia - + Amnezia has detected that your server is currently Amnezia обнаружила, что ваш сервер в настоящее время - + busy installing other software. Amnezia installation занят установкой другого программного обеспечения. Установка Amnezia - + will pause until the server finishes installing other software будет приостановлена до тех пор, пока сервер не завершит установку - + Installing Установка + + + Cancel installation + + - + Usually it takes no more than 5 minutes Обычно это занимает не более 5 минут @@ -1826,22 +1835,22 @@ and will not be shared or disclosed to the Amnezia or any third parties Восстановление настроек из бэкап файла - + Free service for creating a personal VPN on your server. Простое и бесплатное приложение для запуска self-hosted VPN с высокими требованиями к приватности. - + Helps you access blocked content without revealing your privacy, even to VPN providers. Помогает получить доступ к заблокированному контенту, не раскрывая вашу конфиденциальность даже провайдерам VPN. - + I have the data to connect У меня есть данные для подключения - + I have nothing У меня ничего нет @@ -1905,12 +1914,12 @@ and will not be shared or disclosed to the Amnezia or any third parties PageShare - + OpenVpn native format OpenVpn нативный формат - + WireGuard native format WireGuard нативный формат @@ -1919,7 +1928,7 @@ and will not be shared or disclosed to the Amnezia or any third parties VPN-Доступ - + Connection Соединение @@ -1932,78 +1941,214 @@ and will not be shared or disclosed to the Amnezia or any third parties Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки. - - + + Server Сервер - Accessing - Доступ + Доступ - + + Config revoked + + + + Connection to Подключение к - + File with connection settings to Файл с настройками доступа к - + Save OpenVPN config Сохранить OpenVPN config - + Save WireGuard config Сохранить WireGuard config - + + Save ShadowSocks config + + + + + Save Cloak config + + + + For the AmneziaVPN app Для AmneziaVPN - + + ShadowSocks native format + + + + + Cloak native format + + + + Share VPN Access Поделиться VPN - - Full access - Полный доступ + + Share full access to the server and VPN + - + + Use for your own devices, or share with those you trust to manage the server. + + + + + + Users + + + + + User name + + + + + Search + + + + + Rename + + + + + Client name + + + + + Save + Сохранить + + + + Revoke + + + + + Revoke the config for a user - + + + + + The user will no longer be able to connect to your server. + + + + + Continue + Продолжить + + + + Cancel + Отменить + + + Full access + Полный доступ + + + Share VPN access without the ability to manage the server Поделиться доступом к VPN, без возможности управления сервером - - Protocols - - - - + + Protocol Протокол - - + + Connection format Формат подключения - + + Share Поделиться + + PageShareFullAccess + + + Full access to the server and VPN + + + + + We recommend that you use full access to the server only for your own additional devices. + + + + + + If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. + + + + + + Server + + + + + Accessing + Доступ + + + + File with accessing settings to + + + + + Share + Поделиться + + + + Connection to + Подключение к + + + + File with connection settings to + Файл с настройками доступа к + + PopupType @@ -2390,67 +2535,66 @@ and will not be shared or disclosed to the Amnezia or any third parties Sftp error: No media was in remote drive - - Failed to save config to disk - Failed to save config to disk + + The config does not contain any containers and credentials for connecting to the server + - + Failed to save config to disk + Failed to save config to disk + + + OpenVPN config missing OpenVPN config missing - + OpenVPN management server error OpenVPN management server error - + OpenVPN executable missing OpenVPN executable missing - + ShadowSocks (ss-local) executable missing ShadowSocks (ss-local) executable missing - + Cloak (ck-client) executable missing Cloak (ck-client) executable missing - + Amnezia helper service error Amnezia helper service error - + OpenSSL failed OpenSSL failed - + Can't connect: another VPN connection is active Can't connect: another VPN connection is active - + Can't setup OpenVPN TAP network adapter Can't setup OpenVPN TAP network adapter - + VPN pool error: no available addresses VPN pool error: no available addresses - - The config does not contain any containers and credentiaks for connecting to the server - - - - + Internal error Internal error @@ -2737,26 +2881,6 @@ This means that AmneziaWG keeps the fast performance of the original while addin error 0x%1: %2 error 0x%1: %2 - - - WireGuard Configuration Highlighter - - - - - &Randomize colors - - - - - WireGuard Configuration Highlighter - - - - - &Randomize colors - - SelectLanguageDrawer @@ -2788,17 +2912,17 @@ This means that AmneziaWG keeps the fast performance of the original while addin Версия ПО - + All settings have been reset to default values Все настройки были сброшены к значению "По умолчанию" - + Cached profiles cleared Кэш профиля очищен - + Backup file is corrupted Backup файл поврежден @@ -2823,16 +2947,22 @@ This means that AmneziaWG keeps the fast performance of the original while addin + Copied Скопировано - - Show content + + Copy config string - + + Show connection settings + + + + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" Для считывания QR-кода в приложении Amnezia выберите "Добавить сервер" → "У меня есть данные для подключения" → "QR-код, ключ или файл настроек" @@ -2924,7 +3054,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin VpnConnection - + Mbps Mbps diff --git a/client/translations/amneziavpn_zh_CN.ts b/client/translations/amneziavpn_zh_CN.ts index c8422faa..e63344d8 100644 --- a/client/translations/amneziavpn_zh_CN.ts +++ b/client/translations/amneziavpn_zh_CN.ts @@ -7,11 +7,6 @@ Split tunneling for WireGuard is not implemented, the option was disabled 未启用选项,还未实现基于WireGuard协议的VPN分离 - - - Split tunneling for %1 is not implemented, the option was disabled - - AndroidController @@ -28,9 +23,9 @@ - CloudController + ApiController - + Error when retrieving configuration from cloud server @@ -39,43 +34,48 @@ ConnectionController - - - + + + Connect 连接 - + VPN Protocols is not installed. Please install VPN container at first 请先安装VPN协议 - + Connection... 连接中 - + Connected 已连接 - + Reconnection... 重连中 - + Disconnection... 断开中 - + Settings updated successfully, Reconnnection... 配置已更新,重连中 + + + Settings updated successfully + 配置更新成功 + ConnectionTypeSelectionDrawer @@ -133,7 +133,7 @@ ExportController - + Access error! 访问错误 @@ -146,7 +146,7 @@ 已建立连接时无法更改服务器配置 - + The selected protocol is not supported on the current platform 当前平台不支持所选协议 @@ -158,7 +158,7 @@ ImportController - + Scanned %1 of %2. 扫描 %1 of %2. @@ -174,47 +174,47 @@ 已安装在服务器上 - - + + %1 installed successfully. %1 安装成功。 - - + + %1 is already installed on the server. 服务器上已经安装 %1。 - + Added containers that were already installed on the server 添加已安装在服务器上的容器 - + Already installed containers were found on the server. All installed containers have been added to the application 在服务上发现已经安装协议并添加至应用 - + Settings updated successfully 配置更新成功 - + Server '%1' was removed 已移除服务器 '%1' - + All containers from server '%1' have been removed 服务器 '%1' 的所有容器已移除 - + %1 has been removed from the server '%2' %1 已从服务器 '%2' 上移除 @@ -235,12 +235,12 @@ Already installed containers were found on the server. All installed containers 协议已从 - + Please login as the user 请以用户身份登录 - + Server added successfully 增加服务器成功 @@ -308,17 +308,17 @@ Already installed containers were found on the server. All installed containers PageHome - + VPN protocol VPN协议 - + Servers 服务器 - + Unable change server while there is an active connection 已建立连接时无法更改服务器配置 @@ -418,28 +418,28 @@ Already installed containers were found on the server. All installed containers PageProtocolCloakSettings - + Cloak settings Cloak 配置 - + Disguised as traffic from 伪装流量为 - + Port 端口 - - + + Cipher 加密算法 - + Save and Restart Amnezia 保存并重启Amnezia @@ -710,23 +710,23 @@ Already installed containers were found on the server. All installed containers PageProtocolShadowSocksSettings - + ShadowSocks settings ShadowSocks 配置 - + Port 端口 - - + + Cipher 加密算法 - + Save and Restart Amnezia 保存并重启Amnezia @@ -1765,34 +1765,38 @@ It's okay as long as it's from someone you trust. 密码 或 私钥 - + Continue 继续 - + + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties + + + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties - 您输入的所有数据将严格保密 + 您输入的所有数据将严格保密 不会向 Amnezia 或任何第三方分享或披露 - + Ip address cannot be empty IP不能为空 - + Enter the address in the format 255.255.255.255:88 按照这种格式输入 255.255.255.255:88 - + Login cannot be empty 账号不能为空 - + Password/private key cannot be empty 密码或私钥不能为空 @@ -1829,25 +1833,30 @@ and will not be shared or disclosed to the Amnezia or any third parties PageSetupWizardInstalling - + Usually it takes no more than 5 minutes 通常不超过5分钟 - + The server has already been added to the application 服务器已添加到应用软件中 - + Amnezia has detected that your server is currently Amnezia 检测到您的服务器当前 - + busy installing other software. Amnezia installation 正安装其他软件。Amnezia安装 + + + Cancel installation + + Amnesia has detected that your server is currently Amnezia 检测到您的服务器当前 @@ -1857,12 +1866,12 @@ and will not be shared or disclosed to the Amnezia or any third parties 正安装其他软件。Amnezia安装 - + will pause until the server finishes installing other software 将暂停,直到其他软件安装完成。 - + Installing 安装中 @@ -1929,22 +1938,22 @@ and will not be shared or disclosed to the Amnezia or any third parties 从备份文件还原配置 - + Free service for creating a personal VPN on your server. 在您的服务器上架设私人免费VPN服务。 - + Helps you access blocked content without revealing your privacy, even to VPN providers. 帮助您访问受限内容,保护您的隐私,即使是VPN提供商也无法获取。 - + I have the data to connect 我有连接配置 - + I have nothing 我没有 @@ -2008,58 +2017,137 @@ and will not be shared or disclosed to the Amnezia or any third parties PageShare - + Save OpenVPN config 保存OpenVPN配置 - + Save WireGuard config 保存WireGuard配置 - + + Save ShadowSocks config + + + + + Save Cloak config + + + + For the AmneziaVPN app AmneziaVPN 应用 - + OpenVpn native format OpenVPN原生格式 - + WireGuard native format WireGuard原生格式 - + + ShadowSocks native format + + + + + Cloak native format + + + + Share VPN Access 共享 VPN 访问 - + + Share full access to the server and VPN + + + + + Use for your own devices, or share with those you trust to manage the server. + + + + + + Users + + + + Share VPN access without the ability to manage the server 共享 VPN 访问,无需管理服务器 - + + Search + + + + + Rename + + + + + Client name + + + + + Save + 保存 + + + + Revoke + + + + + Revoke the config for a user - + + + + + The user will no longer be able to connect to your server. + + + + + Continue + 继续 + + + + Cancel + 取消 + + Share access to server management. The user with whom you share full access to the server will be able to add and remove any protocols and services to the server, as well as change settings. - 共享服务器管理访问权限。与您共享服务器全部访问权限的用户将可以添加和删除服务器上的任何协议和服务,以及更改设置。 + 共享服务器管理访问权限。与您共享服务器全部访问权限的用户将可以添加和删除服务器上的任何协议和服务,以及更改设置。 VPN Access 访问VPN - + Connection 连接 - Full access - 完全访问 + 完全访问 VPN access without the ability to manage the server @@ -2082,23 +2170,21 @@ and will not be shared or disclosed to the Amnezia or any third parties 服务器 - - + + Server 服务器 - Accessing - 访问 + 访问 - File with accessing settings to - 访问配置文件的内容为: + 访问配置文件的内容为: - + File with connection settings to 连接配置文件的内容为: @@ -2107,28 +2193,89 @@ and will not be shared or disclosed to the Amnezia or any third parties 协议 - - + + Protocol 协议 - + Connection to 连接到 - - + + Config revoked + + + + + User name + + + + + Connection format 连接格式 - + + Share 共享 + + PageShareFullAccess + + + Full access to the server and VPN + + + + + We recommend that you use full access to the server only for your own additional devices. + + + + + + If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. + + + + + + Server + 服务器 + + + + Accessing + 访问 + + + + File with accessing settings to + 访问配置文件的内容为: + + + + Share + 共享 + + + + Connection to + 连接到 + + + + File with connection settings to + 连接配置文件的内容为: + + PopupType @@ -2520,71 +2667,70 @@ and will not be shared or disclosed to the Amnezia or any third parties Sftp 错误: 远程驱动器中没有媒介 - Failed to save config to disk - 配置保存到磁盘失败 + 配置保存到磁盘失败 - + OpenVPN config missing OpenVPN配置丢失 - + OpenVPN management server error OpenVPN 管理服务器错误 - + OpenVPN executable missing OpenVPN 可执行文件丢失 - + ShadowSocks (ss-local) executable missing ShadowSocks (ss-local) 执行文件丢失 - + Cloak (ck-client) executable missing Cloak (ck-client) 执行文件丢失 - + Amnezia helper service error Amnezia 服务连接失败 - + OpenSSL failed OpenSSL错误 - + Can't connect: another VPN connection is active 无法连接:另一个VPN连接处于活跃状态 - + Can't setup OpenVPN TAP network adapter 无法设置 OpenVPN TAP 网络适配器 - + VPN pool error: no available addresses VPN 池错误:没有可用地址 - + The config does not contain any containers and credentials for connecting to the server The config does not contain any containers and credentiaks for connecting to the server - 该配置不包含任何用于连接到服务器的容器和凭据。 + 该配置不包含任何用于连接到服务器的容器和凭据。 - + Internal error 内部错误 @@ -2883,26 +3029,6 @@ While it offers a blend of security, stability, and speed, it's essential t error 0x%1: %2 错误 0x%1: %2 - - - WireGuard Configuration Highlighter - - - - - &Randomize colors - - - - - WireGuard Configuration Highlighter - - - - - &Randomize colors - - SelectLanguageDrawer @@ -2934,17 +3060,17 @@ While it offers a blend of security, stability, and speed, it's essential t 软件版本 - + Backup file is corrupted 备份文件已损坏 - + All settings have been reset to default values 所配置恢复为默认值 - + Cached profiles cleared 缓存的配置文件已清除 @@ -2969,11 +3095,17 @@ While it offers a blend of security, stability, and speed, it's essential t + Copied 已拷贝 - + + Copy config string + + + + Show connection settings 显示连接配置 @@ -2982,7 +3114,7 @@ While it offers a blend of security, stability, and speed, it's essential t 展示内容 - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" 要应用二维码到 Amnezia,请底部工具栏点击“+”→“连接方式”→“二维码、授权码或配置文件” @@ -3074,7 +3206,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps From c27d999c41117125c3abe95d07304288e292c6b8 Mon Sep 17 00:00:00 2001 From: pokamest Date: Wed, 20 Dec 2023 08:39:43 -0800 Subject: [PATCH 33/33] build_macos.sh fix --- deploy/build_macos.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/build_macos.sh b/deploy/build_macos.sh index 13214d6d..0d64a9ec 100755 --- a/deploy/build_macos.sh +++ b/deploy/build_macos.sh @@ -146,7 +146,7 @@ if [ "${MAC_CERT_PW+x}" ]; then fi echo "Building DMG installer..." -hdiutil create -volname AmneziaVPN -srcfolder $BUILD_DIR/installer/$APP_NAME.app -ov -format UDZO $DMG_FILENAME +hdiutil create -size 120mb -volname AmneziaVPN -srcfolder $BUILD_DIR/installer/$APP_NAME.app -ov -format UDZO $DMG_FILENAME if [ "${MAC_CERT_PW+x}" ]; then echo "Signing DMG installer..."