Merge branch 'dev' into refactoring/android
This commit is contained in:
commit
c8d2399db9
69 changed files with 5605 additions and 753 deletions
|
@ -56,6 +56,7 @@ set(CMAKE_AUTORCC ON)
|
||||||
set(AMNEZIAVPN_TS_FILES
|
set(AMNEZIAVPN_TS_FILES
|
||||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
|
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
|
||||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.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)
|
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
|
||||||
|
@ -107,7 +108,7 @@ set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.h
|
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h
|
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.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/protocols_defs.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
|
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
||||||
|
@ -146,7 +147,7 @@ set(SOURCES ${SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
|
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp
|
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.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}/protocols/protocols_defs.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
|
||||||
|
|
|
@ -275,19 +275,16 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
||||||
|
|
||||||
void AmneziaApplication::initModels()
|
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());
|
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
|
||||||
connect(m_vpnConnection.get(), &VpnConnection::newVpnConfigurationCreated, m_containersModel.get(),
|
|
||||||
&ContainersModel::updateContainersConfig);
|
|
||||||
|
|
||||||
m_serversModel.reset(new ServersModel(m_settings, this));
|
m_serversModel.reset(new ServersModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
|
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
|
||||||
connect(m_serversModel.get(), &ServersModel::currentlyProcessedServerIndexChanged, m_containersModel.get(),
|
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(),
|
||||||
&ContainersModel::setCurrentlyProcessedServerIndex);
|
&ContainersModel::updateModel);
|
||||||
connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(),
|
connect(m_serversModel.get(), &ServersModel::defaultContainerChanged, m_containersModel.get(),
|
||||||
&ContainersModel::setCurrentlyProcessedServerIndex);
|
&ContainersModel::setDefaultContainer);
|
||||||
connect(m_containersModel.get(), &ContainersModel::containersModelUpdated, m_serversModel.get(),
|
m_containersModel->setDefaultContainer(m_serversModel->getDefaultContainer()); // make better?
|
||||||
&ServersModel::updateContainersConfig);
|
|
||||||
|
|
||||||
m_languageModel.reset(new LanguageModel(m_settings, this));
|
m_languageModel.reset(new LanguageModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
|
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
|
||||||
|
@ -296,7 +293,7 @@ void AmneziaApplication::initModels()
|
||||||
|
|
||||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
m_sitesModel.reset(new SitesModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
||||||
|
|
||||||
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
|
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
|
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
|
||||||
|
|
||||||
|
@ -322,6 +319,16 @@ void AmneziaApplication::initModels()
|
||||||
|
|
||||||
m_sftpConfigModel.reset(new SftpConfigModel(this));
|
m_sftpConfigModel.reset(new SftpConfigModel(this));
|
||||||
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
|
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, 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()
|
void AmneziaApplication::initControllers()
|
||||||
|
@ -347,18 +354,24 @@ void AmneziaApplication::initControllers()
|
||||||
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
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_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
|
||||||
|
|
||||||
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings));
|
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
|
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(); });
|
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_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||||
|
|
||||||
m_systemController.reset(new SystemController(m_settings));
|
m_systemController.reset(new SystemController(m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
||||||
|
|
||||||
|
m_cloudController.reset(new ApiController(m_serversModel, m_containersModel));
|
||||||
|
m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get());
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "ui/controllers/settingsController.h"
|
#include "ui/controllers/settingsController.h"
|
||||||
#include "ui/controllers/sitesController.h"
|
#include "ui/controllers/sitesController.h"
|
||||||
#include "ui/controllers/systemController.h"
|
#include "ui/controllers/systemController.h"
|
||||||
|
#include "ui/controllers/apiController.h"
|
||||||
#include "ui/models/containers_model.h"
|
#include "ui/models/containers_model.h"
|
||||||
#include "ui/models/languageModel.h"
|
#include "ui/models/languageModel.h"
|
||||||
#include "ui/models/protocols/cloakConfigModel.h"
|
#include "ui/models/protocols/cloakConfigModel.h"
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
#include "ui/models/servers_model.h"
|
#include "ui/models/servers_model.h"
|
||||||
#include "ui/models/services/sftpConfigModel.h"
|
#include "ui/models/services/sftpConfigModel.h"
|
||||||
#include "ui/models/sites_model.h"
|
#include "ui/models/sites_model.h"
|
||||||
|
#include "ui/models/clientManagementModel.h"
|
||||||
|
|
||||||
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
||||||
|
|
||||||
|
@ -94,6 +96,7 @@ private:
|
||||||
QSharedPointer<LanguageModel> m_languageModel;
|
QSharedPointer<LanguageModel> m_languageModel;
|
||||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||||
QSharedPointer<SitesModel> m_sitesModel;
|
QSharedPointer<SitesModel> m_sitesModel;
|
||||||
|
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||||
|
|
||||||
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
||||||
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
||||||
|
@ -118,6 +121,7 @@ private:
|
||||||
QScopedPointer<SettingsController> m_settingsController;
|
QScopedPointer<SettingsController> m_settingsController;
|
||||||
QScopedPointer<SitesController> m_sitesController;
|
QScopedPointer<SitesController> m_sitesController;
|
||||||
QScopedPointer<SystemController> m_systemController;
|
QScopedPointer<SystemController> m_systemController;
|
||||||
|
QScopedPointer<ApiController> m_cloudController;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AMNEZIA_APPLICATION_H
|
#endif // AMNEZIA_APPLICATION_H
|
||||||
|
|
|
@ -3,18 +3,17 @@
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
#include "core/servercontroller.h"
|
#include "core/controllers/serverController.h"
|
||||||
|
|
||||||
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||||
: WireguardConfigurator(settings, true, parent)
|
: WireguardConfigurator(settings, true, parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials,
|
QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
DockerContainer container,
|
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode)
|
||||||
const QJsonObject &containerConfig, 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();
|
QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||||
QString awgConfig = jsonConfig.value(config_key::config).toString();
|
QString awgConfig = jsonConfig.value(config_key::config).toString();
|
||||||
|
|
|
@ -12,7 +12,7 @@ public:
|
||||||
AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
AwgConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||||
|
|
||||||
QString genAwgConfig(const ServerCredentials &credentials, DockerContainer container,
|
QString genAwgConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
|
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AWGCONFIGURATOR_H
|
#endif // AWGCONFIGURATOR_H
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
||||||
#include "core/servercontroller.h"
|
#include "core/controllers/serverController.h"
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
|
|
||||||
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
|
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "core/scripts_registry.h"
|
#include "core/scripts_registry.h"
|
||||||
#include "core/server_defs.h"
|
#include "core/server_defs.h"
|
||||||
#include "core/servercontroller.h"
|
#include "core/controllers/serverController.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent)
|
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "core/scripts_registry.h"
|
#include "core/scripts_registry.h"
|
||||||
#include "core/server_defs.h"
|
#include "core/server_defs.h"
|
||||||
#include "core/servercontroller.h"
|
#include "core/controllers/serverController.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
|
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode *errorCode)
|
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode)
|
||||||
{
|
{
|
||||||
ServerController serverController(m_settings);
|
ServerController serverController(m_settings);
|
||||||
QString config =
|
QString config =
|
||||||
|
@ -113,6 +113,8 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
|
||||||
QJsonObject jConfig;
|
QJsonObject jConfig;
|
||||||
jConfig[config_key::config] = config;
|
jConfig[config_key::config] = config;
|
||||||
|
|
||||||
|
clientId = connData.clientId;
|
||||||
|
|
||||||
return QJsonDocument(jConfig).toJson();
|
return QJsonDocument(jConfig).toJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,13 +133,13 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig)
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
}
|
}
|
||||||
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||||
|
|
||||||
// no redirect-gateway
|
// no redirect-gateway
|
||||||
}
|
}
|
||||||
if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||||
#ifndef Q_OS_ANDROID
|
#ifndef Q_OS_ANDROID
|
||||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||||
#endif
|
#endif
|
||||||
// Prevent ipv6 leak
|
// Prevent ipv6 leak
|
||||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
QString genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
|
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 processConfigWithLocalSettings(QString jsonConfig);
|
||||||
QString processConfigWithExportSettings(QString jsonConfig);
|
QString processConfigWithExportSettings(QString jsonConfig);
|
||||||
|
@ -32,9 +32,9 @@ public:
|
||||||
ErrorCode signCert(DockerContainer container,
|
ErrorCode signCert(DockerContainer container,
|
||||||
const ServerCredentials &credentials, QString clientId);
|
const ServerCredentials &credentials, QString clientId);
|
||||||
|
|
||||||
private:
|
static ConnectionData createCertRequest();
|
||||||
ConnectionData createCertRequest();
|
|
||||||
|
|
||||||
|
private:
|
||||||
ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials,
|
ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials,
|
||||||
DockerContainer container, ErrorCode *errorCode = nullptr);
|
DockerContainer container, ErrorCode *errorCode = nullptr);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "core/servercontroller.h"
|
#include "core/controllers/serverController.h"
|
||||||
|
|
||||||
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
|
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
|
||||||
ConfiguratorBase(settings, parent)
|
ConfiguratorBase(settings, parent)
|
||||||
|
|
|
@ -28,11 +28,11 @@ VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings, QObject *pa
|
||||||
}
|
}
|
||||||
|
|
||||||
QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
|
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) {
|
switch (proto) {
|
||||||
case Proto::OpenVpn:
|
case Proto::OpenVpn:
|
||||||
return openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, errorCode);
|
return openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, clientId, errorCode);
|
||||||
|
|
||||||
case Proto::ShadowSocks:
|
case Proto::ShadowSocks:
|
||||||
return shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, errorCode);
|
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::Cloak: return cloakConfigurator->genCloakConfig(credentials, container, containerConfig, errorCode);
|
||||||
|
|
||||||
case Proto::WireGuard:
|
case Proto::WireGuard:
|
||||||
return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, errorCode);
|
return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, clientId, errorCode);
|
||||||
|
|
||||||
case Proto::Awg:
|
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);
|
case Proto::Ikev2: return ikev2Configurator->genIkev2Config(credentials, container, containerConfig, errorCode);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include "configurator_base.h"
|
#include "configurator_base.h"
|
||||||
#include "core/defs.h"
|
#include "core/defs.h"
|
||||||
|
|
||||||
|
|
||||||
class OpenVpnConfigurator;
|
class OpenVpnConfigurator;
|
||||||
class ShadowSocksConfigurator;
|
class ShadowSocksConfigurator;
|
||||||
class CloakConfigurator;
|
class CloakConfigurator;
|
||||||
|
@ -16,14 +15,15 @@ class SshConfigurator;
|
||||||
class AwgConfigurator;
|
class AwgConfigurator;
|
||||||
|
|
||||||
// Retrieve connection settings from server
|
// Retrieve connection settings from server
|
||||||
class VpnConfigurator : ConfiguratorBase
|
class VpnConfigurator : public ConfiguratorBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
explicit VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||||
|
|
||||||
QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
|
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<QString, QString> getDnsForConfig(int serverIndex);
|
QPair<QString, QString> getDnsForConfig(int serverIndex);
|
||||||
QString &processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
|
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);
|
QString &processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
|
||||||
|
|
||||||
// workaround for containers which is not support normal configuration
|
// workaround for containers which is not support normal configuration
|
||||||
void updateContainerConfigAfterInstallation(DockerContainer container,
|
void updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig,
|
||||||
QJsonObject &containerConfig, const QString &stdOut);
|
const QString &stdOut);
|
||||||
|
|
||||||
std::shared_ptr<OpenVpnConfigurator> openVpnConfigurator;
|
std::shared_ptr<OpenVpnConfigurator> openVpnConfigurator;
|
||||||
std::shared_ptr<ShadowSocksConfigurator> shadowSocksConfigurator;
|
std::shared_ptr<ShadowSocksConfigurator> shadowSocksConfigurator;
|
||||||
|
@ -42,6 +42,10 @@ public:
|
||||||
std::shared_ptr<Ikev2Configurator> ikev2Configurator;
|
std::shared_ptr<Ikev2Configurator> ikev2Configurator;
|
||||||
std::shared_ptr<SshConfigurator> sshConfigurator;
|
std::shared_ptr<SshConfigurator> sshConfigurator;
|
||||||
std::shared_ptr<AwgConfigurator> awgConfigurator;
|
std::shared_ptr<AwgConfigurator> awgConfigurator;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void newVpnConfigCreated(const QString &clientId, const QString &clientName, const DockerContainer container,
|
||||||
|
ServerCredentials credentials);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VPN_CONFIGURATOR_H
|
#endif // VPN_CONFIGURATOR_H
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "core/scripts_registry.h"
|
#include "core/scripts_registry.h"
|
||||||
#include "core/server_defs.h"
|
#include "core/server_defs.h"
|
||||||
#include "core/servercontroller.h"
|
#include "core/controllers/serverController.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode *errorCode)
|
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode)
|
||||||
{
|
{
|
||||||
ServerController serverController(m_settings);
|
ServerController serverController(m_settings);
|
||||||
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
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::psk_key] = connData.pskKey;
|
||||||
jConfig[config_key::server_pub_key] = connData.serverPubKey;
|
jConfig[config_key::server_pub_key] = connData.serverPubKey;
|
||||||
|
|
||||||
|
clientId = connData.clientPubKey;
|
||||||
|
|
||||||
return QJsonDocument(jConfig).toJson();
|
return QJsonDocument(jConfig).toJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
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 processConfigWithLocalSettings(QString config);
|
||||||
QString processConfigWithExportSettings(QString config);
|
QString processConfigWithExportSettings(QString config);
|
||||||
|
|
|
@ -54,11 +54,11 @@ QVector<amnezia::Proto> ContainerProps::protocolsForContainer(amnezia::DockerCon
|
||||||
|
|
||||||
case DockerContainer::ShadowSocks: return { Proto::OpenVpn, Proto::ShadowSocks };
|
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 */ };
|
case DockerContainer::Ipsec: return { Proto::Ikev2 /*, Protocol::L2tp */ };
|
||||||
|
|
||||||
case DockerContainer::Dns: return {};
|
case DockerContainer::Dns: return { Proto::Dns };
|
||||||
|
|
||||||
case DockerContainer::Sftp: return { Proto::Sftp };
|
case DockerContainer::Sftp: return { Proto::Sftp };
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "servercontroller.h"
|
#include "serverController.h"
|
||||||
|
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -24,13 +24,18 @@
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "scripts_registry.h"
|
#include "core/scripts_registry.h"
|
||||||
#include "server_defs.h"
|
#include "core/server_defs.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
#include <configurators/vpn_configurator.h>
|
#include <configurators/vpn_configurator.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
Logger logger("ServerController");
|
||||||
|
}
|
||||||
|
|
||||||
ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings)
|
ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -634,9 +639,9 @@ QString ServerController::checkSshConnection(const ServerCredentials &credential
|
||||||
return stdOut;
|
return stdOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerController::setCancelInstallation(const bool cancel)
|
void ServerController::cancelInstallation()
|
||||||
{
|
{
|
||||||
m_cancelInstallation = cancel;
|
m_cancelInstallation = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
|
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
|
||||||
|
@ -737,6 +742,7 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
||||||
|
|
||||||
ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container)
|
ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container)
|
||||||
{
|
{
|
||||||
|
m_cancelInstallation = false;
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
@ -784,7 +790,6 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
||||||
watcher.setFuture(future);
|
watcher.setFuture(future);
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
m_cancelInstallation = false;
|
|
||||||
emit serverIsBusy(false);
|
emit serverIsBusy(false);
|
||||||
|
|
||||||
return future.result();
|
return future.result();
|
||||||
|
@ -857,6 +862,67 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential
|
||||||
containerConfig[config_key::responsePacketMagicHeader] = serverConfigMap.value(config_key::responsePacketMagicHeader);
|
containerConfig[config_key::responsePacketMagicHeader] = serverConfigMap.value(config_key::responsePacketMagicHeader);
|
||||||
containerConfig[config_key::underloadPacketMagicHeader] = serverConfigMap.value(config_key::underloadPacketMagicHeader);
|
containerConfig[config_key::underloadPacketMagicHeader] = serverConfigMap.value(config_key::underloadPacketMagicHeader);
|
||||||
containerConfig[config_key::transportPacketMagicHeader] = serverConfigMap.value(config_key::transportPacketMagicHeader);
|
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));
|
config.insert(config_key::container, ContainerProps::containerToString(container));
|
|
@ -5,8 +5,8 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "defs.h"
|
#include "core/defs.h"
|
||||||
#include "sshclient.h"
|
#include "core/sshclient.h"
|
||||||
|
|
||||||
class Settings;
|
class Settings;
|
||||||
class VpnConfigurator;
|
class VpnConfigurator;
|
||||||
|
@ -56,7 +56,7 @@ public:
|
||||||
|
|
||||||
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr);
|
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr);
|
||||||
|
|
||||||
void setCancelInstallation(const bool cancel);
|
void cancelInstallation();
|
||||||
|
|
||||||
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey,
|
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey,
|
||||||
const std::function<QString()> &callback);
|
const std::function<QString()> &callback);
|
|
@ -36,7 +36,7 @@ enum ErrorCode
|
||||||
ServerPacketManagerError,
|
ServerPacketManagerError,
|
||||||
|
|
||||||
// Ssh connection errors
|
// Ssh connection errors
|
||||||
SshRequsetDeniedError, SshInterruptedError, SshInternalError,
|
SshRequestDeniedError, SshInterruptedError, SshInternalError,
|
||||||
SshPrivateKeyError, SshPrivateKeyFormatError, SshTimeoutError,
|
SshPrivateKeyError, SshPrivateKeyFormatError, SshTimeoutError,
|
||||||
|
|
||||||
// Ssh sftp errors
|
// Ssh sftp errors
|
||||||
|
@ -47,7 +47,6 @@ enum ErrorCode
|
||||||
SshSftpNoMediaError,
|
SshSftpNoMediaError,
|
||||||
|
|
||||||
// Local errors
|
// Local errors
|
||||||
FailedToSaveConfigData,
|
|
||||||
OpenVpnConfigMissing,
|
OpenVpnConfigMissing,
|
||||||
OpenVpnManagementServerError,
|
OpenVpnManagementServerError,
|
||||||
ConfigMissing,
|
ConfigMissing,
|
||||||
|
@ -67,7 +66,6 @@ enum ErrorCode
|
||||||
|
|
||||||
// 3rd party utils errors
|
// 3rd party utils errors
|
||||||
OpenSslFailed,
|
OpenSslFailed,
|
||||||
OpenVpnExecutableCrashed,
|
|
||||||
ShadowSocksExecutableCrashed,
|
ShadowSocksExecutableCrashed,
|
||||||
CloakExecutableCrashed,
|
CloakExecutableCrashed,
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ QString errorString(ErrorCode code){
|
||||||
case(ServerUserNotInSudo): return QObject::tr("The user does not have permission to use sudo");
|
case(ServerUserNotInSudo): return QObject::tr("The user does not have permission to use sudo");
|
||||||
|
|
||||||
// Libssh errors
|
// 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(SshInterruptedError): return QObject::tr("Ssh request was interrupted");
|
||||||
case(SshInternalError): return QObject::tr("Ssh internal error");
|
case(SshInternalError): return QObject::tr("Ssh internal error");
|
||||||
case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered");
|
case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered");
|
||||||
|
@ -42,7 +42,6 @@ QString errorString(ErrorCode code){
|
||||||
case(SshSftpNoMediaError): return QObject::tr("Sftp error: No media was in remote drive");
|
case(SshSftpNoMediaError): return QObject::tr("Sftp error: No media was in remote drive");
|
||||||
|
|
||||||
// Local errors
|
// Local errors
|
||||||
case (FailedToSaveConfigData): return QObject::tr("Failed to save config to disk");
|
|
||||||
case (OpenVpnConfigMissing): return QObject::tr("OpenVPN config missing");
|
case (OpenVpnConfigMissing): return QObject::tr("OpenVPN config missing");
|
||||||
case (OpenVpnManagementServerError): return QObject::tr("OpenVPN management server error");
|
case (OpenVpnManagementServerError): return QObject::tr("OpenVPN management server error");
|
||||||
|
|
||||||
|
@ -58,7 +57,7 @@ QString errorString(ErrorCode code){
|
||||||
case (OpenVpnTapAdapterError): return QObject::tr("Can't setup OpenVPN TAP network adapter");
|
case (OpenVpnTapAdapterError): return QObject::tr("Can't setup OpenVPN TAP network adapter");
|
||||||
case (AddressPoolError): return QObject::tr("VPN pool error: no available addresses");
|
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):
|
case(InternalError):
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -333,7 +333,7 @@ namespace libssh {
|
||||||
|
|
||||||
switch (errorCode) {
|
switch (errorCode) {
|
||||||
case(SSH_NO_ERROR): return ErrorCode::NoError;
|
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_EINTR): return ErrorCode::SshInterruptedError;
|
||||||
case(SSH_FATAL): return ErrorCode::SshInternalError;
|
case(SSH_FATAL): return ErrorCode::SshInternalError;
|
||||||
default: return ErrorCode::SshInternalError;
|
default: return ErrorCode::SshInternalError;
|
||||||
|
|
6
client/images/controls/close.svg
Normal file
6
client/images/controls/close.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="x, ×, close">
|
||||||
|
<path id="Vector" d="M18 6L6 18" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path id="Vector_2" d="M6 6L18 18" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 374 B |
6
client/images/controls/search.svg
Normal file
6
client/images/controls/search.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="search">
|
||||||
|
<path id="Vector" d="M11 19C15.4183 19 19 15.4183 19 11C19 6.58172 15.4183 3 11 3C6.58172 3 3 6.58172 3 11C3 15.4183 6.58172 19 11 19Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path id="Vector_2" d="M21.0004 20.9984L16.6504 16.6484" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 483 B |
|
@ -21,6 +21,7 @@ namespace amnezia
|
||||||
constexpr char dns2[] = "dns2";
|
constexpr char dns2[] = "dns2";
|
||||||
|
|
||||||
constexpr char description[] = "description";
|
constexpr char description[] = "description";
|
||||||
|
constexpr char name[] = "name";
|
||||||
constexpr char cert[] = "cert";
|
constexpr char cert[] = "cert";
|
||||||
constexpr char config[] = "config";
|
constexpr char config[] = "config";
|
||||||
|
|
||||||
|
@ -79,6 +80,8 @@ namespace amnezia
|
||||||
constexpr char sftp[] = "sftp";
|
constexpr char sftp[] = "sftp";
|
||||||
constexpr char awg[] = "awg";
|
constexpr char awg[] = "awg";
|
||||||
|
|
||||||
|
constexpr char configVersion[] = "config_version";
|
||||||
|
|
||||||
constexpr char splitTunnelSites[] = "splitTunnelSites";
|
constexpr char splitTunnelSites[] = "splitTunnelSites";
|
||||||
constexpr char splitTunnelType[] = "splitTunnelType";
|
constexpr char splitTunnelType[] = "splitTunnelType";
|
||||||
|
|
||||||
|
|
|
@ -222,5 +222,8 @@
|
||||||
<file>server_scripts/awg/configure_container.sh</file>
|
<file>server_scripts/awg/configure_container.sh</file>
|
||||||
<file>server_scripts/awg/run_container.sh</file>
|
<file>server_scripts/awg/run_container.sh</file>
|
||||||
<file>server_scripts/awg/Dockerfile</file>
|
<file>server_scripts/awg/Dockerfile</file>
|
||||||
|
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
|
||||||
|
<file>images/controls/close.svg</file>
|
||||||
|
<file>images/controls/search.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); docker_pkg="docker.io"; dist="debian";\
|
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); docker_pkg="docker"; dist="fedora";\
|
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); docker_pkg="docker"; dist="centos";\
|
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;\
|
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 [ "$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 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 install -yq psmisc; 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 install -yq lsof; 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 update -yq; sudo $pm install -yq $docker_pkg;\
|
if ! command -v docker > /dev/null 2>&1; then \
|
||||||
if [ "$dist" = "fedora" ] || [ "$dist" = "centos" ] || [ "$dist" = "debian" ]; then sudo systemctl enable docker && sudo systemctl start docker; fi;\
|
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
|
||||||
|
sleep 5; sudo systemctl enable --now docker; sleep 5;\
|
||||||
fi;\
|
fi;\
|
||||||
if [ "$dist" = "debian" ]; then \
|
if [ "$(systemctl is-active docker)" != "active" ]; then \
|
||||||
docker_service=$(systemctl list-units --full --all | grep docker.service | grep -v inactive | grep -v dead | grep -v failed);\
|
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
|
||||||
if [ -z "$docker_service" ]; then sudo $pm update -yq; sudo $pm install -yq curl $docker_pkg; fi;\
|
sleep 5; sudo systemctl start docker; sleep 5;\
|
||||||
sleep 3 && sudo systemctl start docker && sleep 3;\
|
|
||||||
fi;\
|
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
|
docker --version
|
||||||
|
|
3185
client/translations/amneziavpn_fa_IR.ts
Normal file
3185
client/translations/amneziavpn_fa_IR.ts
Normal file
File diff suppressed because it is too large
Load diff
|
@ -8,9 +8,8 @@
|
||||||
<translation type="vanished">Раздельное туннелирование для "Wireguard" не реализовано,опция отключена</translation>
|
<translation type="vanished">Раздельное туннелирование для "Wireguard" не реализовано,опция отключена</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../amnezia_application.cpp" line="305"/>
|
|
||||||
<source>Split tunneling for %1 is not implemented, the option was disabled</source>
|
<source>Split tunneling for %1 is not implemented, the option was disabled</source>
|
||||||
<translation>Раздельное туннелирование для %1 не реализовано, опция отключена</translation>
|
<translation type="vanished">Раздельное туннелирование для %1 не реализовано, опция отключена</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -27,45 +26,58 @@
|
||||||
<translation>VPN Подключен</translation>
|
<translation>VPN Подключен</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ApiController</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/controllers/apiController.cpp" line="123"/>
|
||||||
|
<source>Error when retrieving configuration from cloud server</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ConnectionController</name>
|
<name>ConnectionController</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="38"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="35"/>
|
||||||
<source>VPN Protocols is not installed.
|
<source>VPN Protocols is not installed.
|
||||||
Please install VPN container at first</source>
|
Please install VPN container at first</source>
|
||||||
<translation>VPN протоколы не установлены.
|
<translation>VPN протоколы не установлены.
|
||||||
Пожалуйста, установите протокол</translation>
|
Пожалуйста, установите протокол</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="61"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="59"/>
|
||||||
<source>Connection...</source>
|
<source>Connection...</source>
|
||||||
<translation>Подключение...</translation>
|
<translation>Подключение...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="66"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="64"/>
|
||||||
<source>Connected</source>
|
<source>Connected</source>
|
||||||
<translation>Подключено</translation>
|
<translation>Подключено</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="111"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="109"/>
|
||||||
<source>Settings updated successfully, Reconnnection...</source>
|
<source>Settings updated successfully, Reconnnection...</source>
|
||||||
<translation>Настройки успешно обновлены. Подключение...</translation>
|
<translation>Настройки успешно обновлены. Подключение...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="75"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="112"/>
|
||||||
|
<source>Settings updated successfully</source>
|
||||||
|
<translation type="unfinished">Настройки успешно обновлены</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/controllers/connectionController.cpp" line="73"/>
|
||||||
<source>Reconnection...</source>
|
<source>Reconnection...</source>
|
||||||
<translation>Переподключение...</translation>
|
<translation>Переподключение...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.h" line="58"/>
|
<location filename="../ui/controllers/connectionController.h" line="58"/>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="80"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="78"/>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="94"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="92"/>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="100"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="98"/>
|
||||||
<source>Connect</source>
|
<source>Connect</source>
|
||||||
<translation>Подключиться</translation>
|
<translation>Подключиться</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="85"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="83"/>
|
||||||
<source>Disconnection...</source>
|
<source>Disconnection...</source>
|
||||||
<translation>Отключение...</translation>
|
<translation>Отключение...</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -114,7 +126,7 @@
|
||||||
<context>
|
<context>
|
||||||
<name>ExportController</name>
|
<name>ExportController</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/exportController.cpp" line="34"/>
|
<location filename="../ui/controllers/exportController.cpp" line="38"/>
|
||||||
<source>Access error!</source>
|
<source>Access error!</source>
|
||||||
<translation>Ошибка доступа!</translation>
|
<translation>Ошибка доступа!</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -127,7 +139,7 @@
|
||||||
<translation>Невозможно изменить протокол при активном соединении</translation>
|
<translation>Невозможно изменить протокол при активном соединении</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="69"/>
|
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="68"/>
|
||||||
<source>The selected protocol is not supported on the current platform</source>
|
<source>The selected protocol is not supported on the current platform</source>
|
||||||
<translation>Выбранный протокол не поддерживается на данном устройстве</translation>
|
<translation>Выбранный протокол не поддерживается на данном устройстве</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -139,7 +151,7 @@
|
||||||
<context>
|
<context>
|
||||||
<name>ImportController</name>
|
<name>ImportController</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="427"/>
|
<location filename="../ui/controllers/importController.cpp" line="435"/>
|
||||||
<source>Scanned %1 of %2.</source>
|
<source>Scanned %1 of %2.</source>
|
||||||
<translation>Отсканировано %1 из%2.</translation>
|
<translation>Отсканировано %1 из%2.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -147,58 +159,57 @@
|
||||||
<context>
|
<context>
|
||||||
<name>InstallController</name>
|
<name>InstallController</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="143"/>
|
<location filename="../ui/controllers/installController.cpp" line="144"/>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="193"/>
|
<location filename="../ui/controllers/installController.cpp" line="195"/>
|
||||||
<source>%1 installed successfully. </source>
|
<source>%1 installed successfully. </source>
|
||||||
<translation>%1 успешно установлен. </translation>
|
<translation>%1 успешно установлен. </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="145"/>
|
<location filename="../ui/controllers/installController.cpp" line="146"/>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="195"/>
|
<location filename="../ui/controllers/installController.cpp" line="197"/>
|
||||||
<source>%1 is already installed on the server. </source>
|
<source>%1 is already installed on the server. </source>
|
||||||
<translation>%1 уже установлен на сервер. </translation>
|
<translation>%1 уже установлен на сервер. </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="148"/>
|
<location filename="../ui/controllers/installController.cpp" line="149"/>
|
||||||
<source>
|
<source>
|
||||||
Added containers that were already installed on the server</source>
|
Added containers that were already installed on the server</source>
|
||||||
<translation>
|
<translation type="unfinished"></translation>
|
||||||
В приложение добавлены обнаруженные на сервере протоклы и сервисы</translation>
|
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="214"/>
|
<location filename="../ui/controllers/installController.cpp" line="213"/>
|
||||||
<source>
|
<source>
|
||||||
Already installed containers were found on the server. All installed containers have been added to the application</source>
|
Already installed containers were found on the server. All installed containers have been added to the application</source>
|
||||||
<translation>
|
<translation>
|
||||||
На сервере обнаружены установленные протоколы и сервисы, все они добавлены в приложение</translation>
|
На сервере обнаружены установленные протоколы и сервисы, все они добавлены в приложение</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="295"/>
|
<location filename="../ui/controllers/installController.cpp" line="290"/>
|
||||||
<source>Settings updated successfully</source>
|
<source>Settings updated successfully</source>
|
||||||
<translation>Настройки успешно обновлены</translation>
|
<translation>Настройки успешно обновлены</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="310"/>
|
<location filename="../ui/controllers/installController.cpp" line="305"/>
|
||||||
<source>Server '%1' was removed</source>
|
<source>Server '%1' was removed</source>
|
||||||
<translation>Сервер '%1' был удален</translation>
|
<translation>Сервер '%1' был удален</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="320"/>
|
<location filename="../ui/controllers/installController.cpp" line="315"/>
|
||||||
<source>All containers from server '%1' have been removed</source>
|
<source>All containers from server '%1' have been removed</source>
|
||||||
<translation>Все протоклы и сервисы были удалены с сервера '%1'</translation>
|
<translation>Все протоклы и сервисы были удалены с сервера '%1'</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="337"/>
|
<location filename="../ui/controllers/installController.cpp" line="332"/>
|
||||||
<source>%1 has been removed from the server '%2'</source>
|
<source>%1 has been removed from the server '%2'</source>
|
||||||
<translation>%1 был удален с сервера '%2'</translation>
|
<translation>%1 был удален с сервера '%2'</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="483"/>
|
<location filename="../ui/controllers/installController.cpp" line="478"/>
|
||||||
<source>Please login as the user</source>
|
<source>Please login as the user</source>
|
||||||
<translation>Пожалуйста, войдите в систему от имени пользователя</translation>
|
<translation>Пожалуйста, войдите в систему от имени пользователя</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="511"/>
|
<location filename="../ui/controllers/installController.cpp" line="506"/>
|
||||||
<source>Server added successfully</source>
|
<source>Server added successfully</source>
|
||||||
<translation>Сервер успешно добавлен</translation>
|
<translation>Сервер успешно добавлен</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -266,17 +277,17 @@ Already installed containers were found on the server. All installed containers
|
||||||
<context>
|
<context>
|
||||||
<name>PageHome</name>
|
<name>PageHome</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="354"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="317"/>
|
||||||
<source>VPN protocol</source>
|
<source>VPN protocol</source>
|
||||||
<translation>VPN протокол</translation>
|
<translation>VPN протокол</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="398"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="361"/>
|
||||||
<source>Servers</source>
|
<source>Servers</source>
|
||||||
<translation>Серверы</translation>
|
<translation>Серверы</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="490"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="453"/>
|
||||||
<source>Unable change server while there is an active connection</source>
|
<source>Unable change server while there is an active connection</source>
|
||||||
<translation>Невозможно изменить сервер при активном соединении</translation>
|
<translation>Невозможно изменить сервер при активном соединении</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -376,28 +387,28 @@ Already installed containers were found on the server. All installed containers
|
||||||
<context>
|
<context>
|
||||||
<name>PageProtocolCloakSettings</name>
|
<name>PageProtocolCloakSettings</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="74"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="76"/>
|
||||||
<source>Cloak settings</source>
|
<source>Cloak settings</source>
|
||||||
<translation>Настройки Cloak</translation>
|
<translation>Настройки Cloak</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="81"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="83"/>
|
||||||
<source>Disguised as traffic from</source>
|
<source>Disguised as traffic from</source>
|
||||||
<translation>Замаскировать трафик под</translation>
|
<translation>Замаскировать трафик под</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="103"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="105"/>
|
||||||
<source>Port</source>
|
<source>Port</source>
|
||||||
<translation>Порт</translation>
|
<translation>Порт</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="120"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="122"/>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="121"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="123"/>
|
||||||
<source>Cipher</source>
|
<source>Cipher</source>
|
||||||
<translation>Шифрование</translation>
|
<translation>Шифрование</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="159"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="161"/>
|
||||||
<source>Save and Restart Amnezia</source>
|
<source>Save and Restart Amnezia</source>
|
||||||
<translation>Сохранить и перезагрузить Amnezia</translation>
|
<translation>Сохранить и перезагрузить Amnezia</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -652,23 +663,23 @@ Already installed containers were found on the server. All installed containers
|
||||||
<context>
|
<context>
|
||||||
<name>PageProtocolShadowSocksSettings</name>
|
<name>PageProtocolShadowSocksSettings</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="74"/>
|
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="76"/>
|
||||||
<source>ShadowSocks settings</source>
|
<source>ShadowSocks settings</source>
|
||||||
<translation>Настройки ShadowSocks</translation>
|
<translation>Настройки ShadowSocks</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="81"/>
|
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="83"/>
|
||||||
<source>Port</source>
|
<source>Port</source>
|
||||||
<translation>Порт</translation>
|
<translation>Порт</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="98"/>
|
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="100"/>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="99"/>
|
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="101"/>
|
||||||
<source>Cipher</source>
|
<source>Cipher</source>
|
||||||
<translation>Шифрование</translation>
|
<translation>Шифрование</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="137"/>
|
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="139"/>
|
||||||
<source>Save and Restart Amnezia</source>
|
<source>Save and Restart Amnezia</source>
|
||||||
<translation>Сохранить и перезагрузить Amnezia</translation>
|
<translation>Сохранить и перезагрузить Amnezia</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1001,17 +1012,17 @@ Already installed containers were found on the server. All installed containers
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="52"/>
|
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="52"/>
|
||||||
<source>Allow application screenshots</source>
|
<source>Allow application screenshots</source>
|
||||||
<translation>Разрешить скриншоты</translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="72"/>
|
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="72"/>
|
||||||
<source>Auto start</source>
|
<source>Auto start</source>
|
||||||
<translation>Авто-запуск</translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="73"/>
|
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="73"/>
|
||||||
<source>Launch the application every time the device is starts</source>
|
<source>Launch the application every time the device is starts</source>
|
||||||
<translation>Запускать приложение при каждом включении</translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="93"/>
|
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="93"/>
|
||||||
|
@ -1642,18 +1653,17 @@ It's okay as long as it's from someone you trust.</source>
|
||||||
<translation>Password / SSH private key</translation>
|
<translation>Password / SSH private key</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="90"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="94"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>Продолжить</translation>
|
<translation>Продолжить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="115"/>
|
|
||||||
<source>All data you enter will remain strictly confidential
|
<source>All data you enter will remain strictly confidential
|
||||||
and will not be shared or disclosed to the Amnezia or any third parties</source>
|
and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>Все данные, которые вы вводите, останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или каким-либо третьим сторонам</translation>
|
<translation type="vanished">Все данные, которые вы вводите, останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или каким-либо третьим сторонам</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="128"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="132"/>
|
||||||
<source>Enter the address in the format 255.255.255.255:88</source>
|
<source>Enter the address in the format 255.255.255.255:88</source>
|
||||||
<translation>Введите адрес в формате 255.255.255.255:88</translation>
|
<translation>Введите адрес в формате 255.255.255.255:88</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1668,17 +1678,22 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>Настроить ваш сервер</translation>
|
<translation>Настроить ваш сервер</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="125"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="120"/>
|
||||||
|
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="129"/>
|
||||||
<source>Ip address cannot be empty</source>
|
<source>Ip address cannot be empty</source>
|
||||||
<translation>Поле Ip address не может быть пустым</translation>
|
<translation>Поле Ip address не может быть пустым</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="132"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="136"/>
|
||||||
<source>Login cannot be empty</source>
|
<source>Login cannot be empty</source>
|
||||||
<translation>Поле Login не может быть пустым</translation>
|
<translation>Поле Login не может быть пустым</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="136"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="140"/>
|
||||||
<source>Password/private key cannot be empty</source>
|
<source>Password/private key cannot be empty</source>
|
||||||
<translation>Поле Password/private key не может быть пустым</translation>
|
<translation>Поле Password/private key не может быть пустым</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1714,7 +1729,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<context>
|
<context>
|
||||||
<name>PageSetupWizardInstalling</name>
|
<name>PageSetupWizardInstalling</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="65"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="60"/>
|
||||||
<source>The server has already been added to the application</source>
|
<source>The server has already been added to the application</source>
|
||||||
<translation>Сервер уже был добавлен в приложение</translation>
|
<translation>Сервер уже был добавлен в приложение</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1727,28 +1742,33 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation type="vanished">занят установкой других протоколов или сервисов. Установка Amnesia </translation>
|
<translation type="vanished">занят установкой других протоколов или сервисов. Установка Amnesia </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="70"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="66"/>
|
||||||
<source>Amnezia has detected that your server is currently </source>
|
<source>Amnezia has detected that your server is currently </source>
|
||||||
<translation>Amnezia обнаружила, что ваш сервер в настоящее время </translation>
|
<translation>Amnezia обнаружила, что ваш сервер в настоящее время </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="71"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="67"/>
|
||||||
<source>busy installing other software. Amnezia installation </source>
|
<source>busy installing other software. Amnezia installation </source>
|
||||||
<translation>занят установкой другого программного обеспечения. Установка Amnezia </translation>
|
<translation>занят установкой другого программного обеспечения. Установка Amnezia </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="72"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="68"/>
|
||||||
<source>will pause until the server finishes installing other software</source>
|
<source>will pause until the server finishes installing other software</source>
|
||||||
<translation>будет приостановлена до тех пор, пока сервер не завершит установку</translation>
|
<translation>будет приостановлена до тех пор, пока сервер не завершит установку</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="129"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="126"/>
|
||||||
<source>Installing</source>
|
<source>Installing</source>
|
||||||
<translation>Установка</translation>
|
<translation>Установка</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="165"/>
|
||||||
|
<source>Cancel installation</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="21"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="21"/>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="75"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="72"/>
|
||||||
<source>Usually it takes no more than 5 minutes</source>
|
<source>Usually it takes no more than 5 minutes</source>
|
||||||
<translation>Обычно это занимает не более 5 минут</translation>
|
<translation>Обычно это занимает не более 5 минут</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1815,22 +1835,22 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>Восстановление настроек из бэкап файла</translation>
|
<translation>Восстановление настроек из бэкап файла</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="105"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="106"/>
|
||||||
<source>Free service for creating a personal VPN on your server.</source>
|
<source>Free service for creating a personal VPN on your server.</source>
|
||||||
<translation>Простое и бесплатное приложение для запуска self-hosted VPN с высокими требованиями к приватности.</translation>
|
<translation>Простое и бесплатное приложение для запуска self-hosted VPN с высокими требованиями к приватности.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="106"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="107"/>
|
||||||
<source> Helps you access blocked content without revealing your privacy, even to VPN providers.</source>
|
<source> Helps you access blocked content without revealing your privacy, even to VPN providers.</source>
|
||||||
<translation> Помогает получить доступ к заблокированному контенту, не раскрывая вашу конфиденциальность даже провайдерам VPN.</translation>
|
<translation> Помогает получить доступ к заблокированному контенту, не раскрывая вашу конфиденциальность даже провайдерам VPN.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="115"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="116"/>
|
||||||
<source>I have the data to connect</source>
|
<source>I have the data to connect</source>
|
||||||
<translation>У меня есть данные для подключения</translation>
|
<translation>У меня есть данные для подключения</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="135"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="136"/>
|
||||||
<source>I have nothing</source>
|
<source>I have nothing</source>
|
||||||
<translation>У меня ничего нет</translation>
|
<translation>У меня ничего нет</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1894,12 +1914,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<context>
|
<context>
|
||||||
<name>PageShare</name>
|
<name>PageShare</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="91"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="110"/>
|
||||||
<source>OpenVpn native format</source>
|
<source>OpenVpn native format</source>
|
||||||
<translation>OpenVpn нативный формат</translation>
|
<translation>OpenVpn нативный формат</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="96"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="115"/>
|
||||||
<source>WireGuard native format</source>
|
<source>WireGuard native format</source>
|
||||||
<translation>WireGuard нативный формат</translation>
|
<translation>WireGuard нативный формат</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1908,7 +1928,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation type="vanished">VPN-Доступ</translation>
|
<translation type="vanished">VPN-Доступ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="146"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="220"/>
|
||||||
<source>Connection</source>
|
<source>Connection</source>
|
||||||
<translation>Соединение</translation>
|
<translation>Соединение</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1921,84 +1941,214 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation type="vanished">Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки.</translation>
|
<translation type="vanished">Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="190"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="279"/>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="191"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="280"/>
|
||||||
<source>Server</source>
|
<source>Server</source>
|
||||||
<translation>Сервер</translation>
|
<translation>Сервер</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="221"/>
|
|
||||||
<source>Accessing </source>
|
<source>Accessing </source>
|
||||||
<translation>Доступ </translation>
|
<translation type="vanished">Доступ </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="222"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="34"/>
|
||||||
<source>File with accessing settings to </source>
|
<source>Config revoked</source>
|
||||||
<translation>Файл с настройками доступа к </translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="309"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="41"/>
|
||||||
<source>Connection to </source>
|
<source>Connection to </source>
|
||||||
<translation>Подключение к </translation>
|
<translation>Подключение к </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="310"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="42"/>
|
||||||
<source>File with connection settings to </source>
|
<source>File with connection settings to </source>
|
||||||
<translation>Файл с настройками доступа к </translation>
|
<translation>Файл с настройками доступа к </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="48"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="54"/>
|
||||||
<source>Save OpenVPN config</source>
|
<source>Save OpenVPN config</source>
|
||||||
<translation>Сохранить OpenVPN config</translation>
|
<translation>Сохранить OpenVPN config</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="55"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="61"/>
|
||||||
<source>Save WireGuard config</source>
|
<source>Save WireGuard config</source>
|
||||||
<translation>Сохранить WireGuard config</translation>
|
<translation>Сохранить WireGuard config</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="86"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="68"/>
|
||||||
|
<source>Save ShadowSocks config</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="75"/>
|
||||||
|
<source>Save Cloak config</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="105"/>
|
||||||
<source>For the AmneziaVPN app</source>
|
<source>For the AmneziaVPN app</source>
|
||||||
<translation>Для AmneziaVPN</translation>
|
<translation>Для AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="121"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="120"/>
|
||||||
|
<source>ShadowSocks native format</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="125"/>
|
||||||
|
<source>Cloak native format</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="150"/>
|
||||||
<source>Share VPN Access</source>
|
<source>Share VPN Access</source>
|
||||||
<translation>Поделиться VPN</translation>
|
<translation>Поделиться VPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="158"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="178"/>
|
||||||
<source>Full access</source>
|
<source>Share full access to the server and VPN</source>
|
||||||
<translation>Полный доступ</translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="179"/>
|
||||||
|
<source>Use for your own devices, or share with those you trust to manage the server.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="231"/>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="483"/>
|
||||||
|
<source>Users</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="262"/>
|
||||||
|
<source>User name</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="499"/>
|
||||||
|
<source>Search</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="595"/>
|
||||||
|
<source>Rename</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="624"/>
|
||||||
|
<source>Client name</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="632"/>
|
||||||
|
<source>Save</source>
|
||||||
|
<translation type="unfinished">Сохранить</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="660"/>
|
||||||
|
<source>Revoke</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="663"/>
|
||||||
|
<source>Revoke the config for a user - </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="664"/>
|
||||||
|
<source>The user will no longer be able to connect to your server.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="665"/>
|
||||||
|
<source>Continue</source>
|
||||||
|
<translation type="unfinished">Продолжить</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="666"/>
|
||||||
|
<source>Cancel</source>
|
||||||
|
<translation type="unfinished">Отменить</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Full access</source>
|
||||||
|
<translation type="vanished">Полный доступ</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="251"/>
|
||||||
<source>Share VPN access without the ability to manage the server</source>
|
<source>Share VPN access without the ability to manage the server</source>
|
||||||
<translation>Поделиться доступом к VPN, без возможности управления сервером</translation>
|
<translation>Поделиться доступом к VPN, без возможности управления сервером</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="175"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="331"/>
|
||||||
<source>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.</source>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="332"/>
|
||||||
<translation>Поделиться доступом к управлению сервером. Пользователь, с которым вы делитесь полным доступом к серверу, сможет добавлять и удалять любые протоколы и службы на сервере, а также изменять настройки.</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="251"/>
|
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="252"/>
|
|
||||||
<source>Protocol</source>
|
<source>Protocol</source>
|
||||||
<translation>Протокол</translation>
|
<translation>Протокол</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="343"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="428"/>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="344"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="429"/>
|
||||||
<source>Connection format</source>
|
<source>Connection format</source>
|
||||||
<translation>Формат подключения</translation>
|
<translation>Формат подключения</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="382"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="186"/>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="468"/>
|
||||||
<source>Share</source>
|
<source>Share</source>
|
||||||
<translation>Поделиться</translation>
|
<translation>Поделиться</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PageShareFullAccess</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="49"/>
|
||||||
|
<source>Full access to the server and VPN</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="57"/>
|
||||||
|
<source>We recommend that you use full access to the server only for your own additional devices.
|
||||||
|
</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="58"/>
|
||||||
|
<source>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. </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="73"/>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="74"/>
|
||||||
|
<source>Server</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="102"/>
|
||||||
|
<source>Accessing </source>
|
||||||
|
<translation type="unfinished">Доступ </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="103"/>
|
||||||
|
<source>File with accessing settings to </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="122"/>
|
||||||
|
<source>Share</source>
|
||||||
|
<translation type="unfinished">Поделиться</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="126"/>
|
||||||
|
<source>Connection to </source>
|
||||||
|
<translation type="unfinished">Подключение к </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="127"/>
|
||||||
|
<source>File with connection settings to </source>
|
||||||
|
<translation type="unfinished">Файл с настройками доступа к </translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PopupType</name>
|
<name>PopupType</name>
|
||||||
<message>
|
<message>
|
||||||
|
@ -2385,67 +2535,66 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>Sftp error: No media was in remote drive</translation>
|
<translation>Sftp error: No media was in remote drive</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="45"/>
|
<location filename="../core/errorstrings.cpp" line="60"/>
|
||||||
<source>Failed to save config to disk</source>
|
<source>The config does not contain any containers and credentials for connecting to the server</source>
|
||||||
<translation>Failed to save config to disk</translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="46"/>
|
<source>Failed to save config to disk</source>
|
||||||
|
<translation type="vanished">Failed to save config to disk</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../core/errorstrings.cpp" line="45"/>
|
||||||
<source>OpenVPN config missing</source>
|
<source>OpenVPN config missing</source>
|
||||||
<translation>OpenVPN config missing</translation>
|
<translation>OpenVPN config missing</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="47"/>
|
<location filename="../core/errorstrings.cpp" line="46"/>
|
||||||
<source>OpenVPN management server error</source>
|
<source>OpenVPN management server error</source>
|
||||||
<translation>OpenVPN management server error</translation>
|
<translation>OpenVPN management server error</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="50"/>
|
<location filename="../core/errorstrings.cpp" line="49"/>
|
||||||
<source>OpenVPN executable missing</source>
|
<source>OpenVPN executable missing</source>
|
||||||
<translation>OpenVPN executable missing</translation>
|
<translation>OpenVPN executable missing</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="51"/>
|
<location filename="../core/errorstrings.cpp" line="50"/>
|
||||||
<source>ShadowSocks (ss-local) executable missing</source>
|
<source>ShadowSocks (ss-local) executable missing</source>
|
||||||
<translation>ShadowSocks (ss-local) executable missing</translation>
|
<translation>ShadowSocks (ss-local) executable missing</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="52"/>
|
<location filename="../core/errorstrings.cpp" line="51"/>
|
||||||
<source>Cloak (ck-client) executable missing</source>
|
<source>Cloak (ck-client) executable missing</source>
|
||||||
<translation>Cloak (ck-client) executable missing</translation>
|
<translation>Cloak (ck-client) executable missing</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="53"/>
|
<location filename="../core/errorstrings.cpp" line="52"/>
|
||||||
<source>Amnezia helper service error</source>
|
<source>Amnezia helper service error</source>
|
||||||
<translation>Amnezia helper service error</translation>
|
<translation>Amnezia helper service error</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="54"/>
|
<location filename="../core/errorstrings.cpp" line="53"/>
|
||||||
<source>OpenSSL failed</source>
|
<source>OpenSSL failed</source>
|
||||||
<translation>OpenSSL failed</translation>
|
<translation>OpenSSL failed</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="57"/>
|
<location filename="../core/errorstrings.cpp" line="56"/>
|
||||||
<source>Can't connect: another VPN connection is active</source>
|
<source>Can't connect: another VPN connection is active</source>
|
||||||
<translation>Can't connect: another VPN connection is active</translation>
|
<translation>Can't connect: another VPN connection is active</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="58"/>
|
<location filename="../core/errorstrings.cpp" line="57"/>
|
||||||
<source>Can't setup OpenVPN TAP network adapter</source>
|
<source>Can't setup OpenVPN TAP network adapter</source>
|
||||||
<translation>Can't setup OpenVPN TAP network adapter</translation>
|
<translation>Can't setup OpenVPN TAP network adapter</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="59"/>
|
<location filename="../core/errorstrings.cpp" line="58"/>
|
||||||
<source>VPN pool error: no available addresses</source>
|
<source>VPN pool error: no available addresses</source>
|
||||||
<translation>VPN pool error: no available addresses</translation>
|
<translation>VPN pool error: no available addresses</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="61"/>
|
<location filename="../core/errorstrings.cpp" line="64"/>
|
||||||
<source>The config does not contain any containers and credentiaks for connecting to the server</source>
|
|
||||||
<translation>The config does not contain any containers and credentiaks for connecting to the server</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../core/errorstrings.cpp" line="65"/>
|
|
||||||
<source>Internal error</source>
|
<source>Internal error</source>
|
||||||
<translation>Internal error</translation>
|
<translation>Internal error</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -2732,16 +2881,6 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||||
<source>error 0x%1: %2</source>
|
<source>error 0x%1: %2</source>
|
||||||
<translation>error 0x%1: %2</translation>
|
<translation>error 0x%1: %2</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../3rd/wireguard-tools/contrib/highlighter/gui/highlight.cpp" line="39"/>
|
|
||||||
<source>WireGuard Configuration Highlighter</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../3rd/wireguard-tools/contrib/highlighter/gui/highlight.cpp" line="82"/>
|
|
||||||
<source>&Randomize colors</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>SelectLanguageDrawer</name>
|
<name>SelectLanguageDrawer</name>
|
||||||
|
@ -2773,17 +2912,17 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||||
<translation>Версия ПО</translation>
|
<translation>Версия ПО</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/settingsController.cpp" line="136"/>
|
<location filename="../ui/controllers/settingsController.cpp" line="137"/>
|
||||||
<source>All settings have been reset to default values</source>
|
<source>All settings have been reset to default values</source>
|
||||||
<translation>Все настройки были сброшены к значению "По умолчанию"</translation>
|
<translation>Все настройки были сброшены к значению "По умолчанию"</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/settingsController.cpp" line="142"/>
|
<location filename="../ui/controllers/settingsController.cpp" line="143"/>
|
||||||
<source>Cached profiles cleared</source>
|
<source>Cached profiles cleared</source>
|
||||||
<translation>Кэш профиля очищен</translation>
|
<translation>Кэш профиля очищен</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/settingsController.cpp" line="121"/>
|
<location filename="../ui/controllers/settingsController.cpp" line="122"/>
|
||||||
<source>Backup file is corrupted</source>
|
<source>Backup file is corrupted</source>
|
||||||
<translation>Backup файл поврежден</translation>
|
<translation>Backup файл поврежден</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -2808,16 +2947,22 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="111"/>
|
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="111"/>
|
||||||
|
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="135"/>
|
||||||
<source>Copied</source>
|
<source>Copied</source>
|
||||||
<translation>Скопировано</translation>
|
<translation>Скопировано</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="126"/>
|
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="128"/>
|
||||||
<source>Show connection settings</source>
|
<source>Copy config string</source>
|
||||||
<translation>Показать настройки подключения</translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="251"/>
|
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="150"/>
|
||||||
|
<source>Show connection settings</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="280"/>
|
||||||
<source>To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file"</source>
|
<source>To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file"</source>
|
||||||
<translation>Для считывания QR-кода в приложении Amnezia выберите "Добавить сервер" → "У меня есть данные для подключения" → "QR-код, ключ или файл настроек"</translation>
|
<translation>Для считывания QR-кода в приложении Amnezia выберите "Добавить сервер" → "У меня есть данные для подключения" → "QR-код, ключ или файл настроек"</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -2909,7 +3054,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||||
<context>
|
<context>
|
||||||
<name>VpnConnection</name>
|
<name>VpnConnection</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../vpnconnection.cpp" line="406"/>
|
<location filename="../vpnconnection.cpp" line="429"/>
|
||||||
<source>Mbps</source>
|
<source>Mbps</source>
|
||||||
<translation>Mbps</translation>
|
<translation>Mbps</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|
|
@ -7,11 +7,6 @@
|
||||||
<source>Split tunneling for WireGuard is not implemented, the option was disabled</source>
|
<source>Split tunneling for WireGuard is not implemented, the option was disabled</source>
|
||||||
<translation type="vanished">未启用选项,还未实现基于WireGuard协议的VPN分离</translation>
|
<translation type="vanished">未启用选项,还未实现基于WireGuard协议的VPN分离</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../amnezia_application.cpp" line="305"/>
|
|
||||||
<source>Split tunneling for %1 is not implemented, the option was disabled</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>AndroidController</name>
|
<name>AndroidController</name>
|
||||||
|
@ -27,47 +22,60 @@
|
||||||
<translation>VPN已连接</translation>
|
<translation>VPN已连接</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ApiController</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/controllers/apiController.cpp" line="123"/>
|
||||||
|
<source>Error when retrieving configuration from cloud server</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ConnectionController</name>
|
<name>ConnectionController</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.h" line="58"/>
|
<location filename="../ui/controllers/connectionController.h" line="58"/>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="80"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="78"/>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="94"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="92"/>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="100"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="98"/>
|
||||||
<source>Connect</source>
|
<source>Connect</source>
|
||||||
<translation>连接</translation>
|
<translation>连接</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="38"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="35"/>
|
||||||
<source>VPN Protocols is not installed.
|
<source>VPN Protocols is not installed.
|
||||||
Please install VPN container at first</source>
|
Please install VPN container at first</source>
|
||||||
<translation>请先安装VPN协议</translation>
|
<translation>请先安装VPN协议</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="61"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="59"/>
|
||||||
<source>Connection...</source>
|
<source>Connection...</source>
|
||||||
<translation>连接中</translation>
|
<translation>连接中</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="66"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="64"/>
|
||||||
<source>Connected</source>
|
<source>Connected</source>
|
||||||
<translation>已连接</translation>
|
<translation>已连接</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="75"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="73"/>
|
||||||
<source>Reconnection...</source>
|
<source>Reconnection...</source>
|
||||||
<translation>重连中</translation>
|
<translation>重连中</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="85"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="83"/>
|
||||||
<source>Disconnection...</source>
|
<source>Disconnection...</source>
|
||||||
<translation>断开中</translation>
|
<translation>断开中</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/connectionController.cpp" line="111"/>
|
<location filename="../ui/controllers/connectionController.cpp" line="109"/>
|
||||||
<source>Settings updated successfully, Reconnnection...</source>
|
<source>Settings updated successfully, Reconnnection...</source>
|
||||||
<translation>配置已更新,重连中</translation>
|
<translation>配置已更新,重连中</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/controllers/connectionController.cpp" line="112"/>
|
||||||
|
<source>Settings updated successfully</source>
|
||||||
|
<translation type="unfinished">配置更新成功</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ConnectionTypeSelectionDrawer</name>
|
<name>ConnectionTypeSelectionDrawer</name>
|
||||||
|
@ -125,7 +133,7 @@
|
||||||
<context>
|
<context>
|
||||||
<name>ExportController</name>
|
<name>ExportController</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/exportController.cpp" line="34"/>
|
<location filename="../ui/controllers/exportController.cpp" line="38"/>
|
||||||
<source>Access error!</source>
|
<source>Access error!</source>
|
||||||
<translation>访问错误</translation>
|
<translation>访问错误</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -138,7 +146,7 @@
|
||||||
<translation>已建立连接时无法更改服务器配置</translation>
|
<translation>已建立连接时无法更改服务器配置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="69"/>
|
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="68"/>
|
||||||
<source>The selected protocol is not supported on the current platform</source>
|
<source>The selected protocol is not supported on the current platform</source>
|
||||||
<translation>当前平台不支持所选协议</translation>
|
<translation>当前平台不支持所选协议</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -150,7 +158,7 @@
|
||||||
<context>
|
<context>
|
||||||
<name>ImportController</name>
|
<name>ImportController</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="427"/>
|
<location filename="../ui/controllers/importController.cpp" line="435"/>
|
||||||
<source>Scanned %1 of %2.</source>
|
<source>Scanned %1 of %2.</source>
|
||||||
<translation>扫描 %1 of %2.</translation>
|
<translation>扫描 %1 of %2.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -166,47 +174,47 @@
|
||||||
<translation type="obsolete"> 已安装在服务器上</translation>
|
<translation type="obsolete"> 已安装在服务器上</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="143"/>
|
<location filename="../ui/controllers/installController.cpp" line="144"/>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="193"/>
|
<location filename="../ui/controllers/installController.cpp" line="195"/>
|
||||||
<source>%1 installed successfully. </source>
|
<source>%1 installed successfully. </source>
|
||||||
<translation>%1 安装成功。</translation>
|
<translation>%1 安装成功。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="145"/>
|
<location filename="../ui/controllers/installController.cpp" line="146"/>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="195"/>
|
<location filename="../ui/controllers/installController.cpp" line="197"/>
|
||||||
<source>%1 is already installed on the server. </source>
|
<source>%1 is already installed on the server. </source>
|
||||||
<translation>服务器上已经安装 %1。</translation>
|
<translation>服务器上已经安装 %1。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="148"/>
|
<location filename="../ui/controllers/installController.cpp" line="149"/>
|
||||||
<source>
|
<source>
|
||||||
Added containers that were already installed on the server</source>
|
Added containers that were already installed on the server</source>
|
||||||
<translation>添加已安装在服务器上的容器</translation>
|
<translation>添加已安装在服务器上的容器</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="214"/>
|
<location filename="../ui/controllers/installController.cpp" line="213"/>
|
||||||
<source>
|
<source>
|
||||||
Already installed containers were found on the server. All installed containers have been added to the application</source>
|
Already installed containers were found on the server. All installed containers have been added to the application</source>
|
||||||
<translation>
|
<translation>
|
||||||
在服务上发现已经安装协议并添加至应用</translation>
|
在服务上发现已经安装协议并添加至应用</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="295"/>
|
<location filename="../ui/controllers/installController.cpp" line="290"/>
|
||||||
<source>Settings updated successfully</source>
|
<source>Settings updated successfully</source>
|
||||||
<translation>配置更新成功</translation>
|
<translation>配置更新成功</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="310"/>
|
<location filename="../ui/controllers/installController.cpp" line="305"/>
|
||||||
<source>Server '%1' was removed</source>
|
<source>Server '%1' was removed</source>
|
||||||
<translation>已移除服务器 '%1'</translation>
|
<translation>已移除服务器 '%1'</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="320"/>
|
<location filename="../ui/controllers/installController.cpp" line="315"/>
|
||||||
<source>All containers from server '%1' have been removed</source>
|
<source>All containers from server '%1' have been removed</source>
|
||||||
<translation>服务器 '%1' 的所有容器已移除</translation>
|
<translation>服务器 '%1' 的所有容器已移除</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="337"/>
|
<location filename="../ui/controllers/installController.cpp" line="332"/>
|
||||||
<source>%1 has been removed from the server '%2'</source>
|
<source>%1 has been removed from the server '%2'</source>
|
||||||
<translation>%1 已从服务器 '%2' 上移除</translation>
|
<translation>%1 已从服务器 '%2' 上移除</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -227,12 +235,12 @@ Already installed containers were found on the server. All installed containers
|
||||||
<translation type="obsolete"> 协议已从</translation>
|
<translation type="obsolete"> 协议已从</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="483"/>
|
<location filename="../ui/controllers/installController.cpp" line="478"/>
|
||||||
<source>Please login as the user</source>
|
<source>Please login as the user</source>
|
||||||
<translation>请以用户身份登录</translation>
|
<translation>请以用户身份登录</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/installController.cpp" line="511"/>
|
<location filename="../ui/controllers/installController.cpp" line="506"/>
|
||||||
<source>Server added successfully</source>
|
<source>Server added successfully</source>
|
||||||
<translation>增加服务器成功</translation>
|
<translation>增加服务器成功</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -300,17 +308,17 @@ Already installed containers were found on the server. All installed containers
|
||||||
<context>
|
<context>
|
||||||
<name>PageHome</name>
|
<name>PageHome</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="354"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="317"/>
|
||||||
<source>VPN protocol</source>
|
<source>VPN protocol</source>
|
||||||
<translation>VPN协议</translation>
|
<translation>VPN协议</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="398"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="361"/>
|
||||||
<source>Servers</source>
|
<source>Servers</source>
|
||||||
<translation>服务器</translation>
|
<translation>服务器</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageHome.qml" line="490"/>
|
<location filename="../ui/qml/Pages2/PageHome.qml" line="453"/>
|
||||||
<source>Unable change server while there is an active connection</source>
|
<source>Unable change server while there is an active connection</source>
|
||||||
<translation>已建立连接时无法更改服务器配置</translation>
|
<translation>已建立连接时无法更改服务器配置</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -410,28 +418,28 @@ Already installed containers were found on the server. All installed containers
|
||||||
<context>
|
<context>
|
||||||
<name>PageProtocolCloakSettings</name>
|
<name>PageProtocolCloakSettings</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="74"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="76"/>
|
||||||
<source>Cloak settings</source>
|
<source>Cloak settings</source>
|
||||||
<translation>Cloak 配置</translation>
|
<translation>Cloak 配置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="81"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="83"/>
|
||||||
<source>Disguised as traffic from</source>
|
<source>Disguised as traffic from</source>
|
||||||
<translation>伪装流量为</translation>
|
<translation>伪装流量为</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="103"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="105"/>
|
||||||
<source>Port</source>
|
<source>Port</source>
|
||||||
<translation>端口</translation>
|
<translation>端口</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="120"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="122"/>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="121"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="123"/>
|
||||||
<source>Cipher</source>
|
<source>Cipher</source>
|
||||||
<translation>加密算法</translation>
|
<translation>加密算法</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="159"/>
|
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="161"/>
|
||||||
<source>Save and Restart Amnezia</source>
|
<source>Save and Restart Amnezia</source>
|
||||||
<translation>保存并重启Amnezia</translation>
|
<translation>保存并重启Amnezia</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -702,23 +710,23 @@ Already installed containers were found on the server. All installed containers
|
||||||
<context>
|
<context>
|
||||||
<name>PageProtocolShadowSocksSettings</name>
|
<name>PageProtocolShadowSocksSettings</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="74"/>
|
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="76"/>
|
||||||
<source>ShadowSocks settings</source>
|
<source>ShadowSocks settings</source>
|
||||||
<translation>ShadowSocks 配置</translation>
|
<translation>ShadowSocks 配置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="81"/>
|
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="83"/>
|
||||||
<source>Port</source>
|
<source>Port</source>
|
||||||
<translation>端口</translation>
|
<translation>端口</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="98"/>
|
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="100"/>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="99"/>
|
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="101"/>
|
||||||
<source>Cipher</source>
|
<source>Cipher</source>
|
||||||
<translation>加密算法</translation>
|
<translation>加密算法</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="137"/>
|
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="139"/>
|
||||||
<source>Save and Restart Amnezia</source>
|
<source>Save and Restart Amnezia</source>
|
||||||
<translation>保存并重启Amnezia</translation>
|
<translation>保存并重启Amnezia</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1757,34 +1765,38 @@ It's okay as long as it's from someone you trust.</source>
|
||||||
<translation>密码 或 私钥</translation>
|
<translation>密码 或 私钥</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="90"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="94"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>继续</translation>
|
<translation>继续</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="115"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="120"/>
|
||||||
|
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
<source>All data you enter will remain strictly confidential
|
<source>All data you enter will remain strictly confidential
|
||||||
and will not be shared or disclosed to the Amnezia or any third parties</source>
|
and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>您输入的所有数据将严格保密
|
<translation type="vanished">您输入的所有数据将严格保密
|
||||||
不会向 Amnezia 或任何第三方分享或披露</translation>
|
不会向 Amnezia 或任何第三方分享或披露</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="125"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="129"/>
|
||||||
<source>Ip address cannot be empty</source>
|
<source>Ip address cannot be empty</source>
|
||||||
<translation>IP不能为空</translation>
|
<translation>IP不能为空</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="128"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="132"/>
|
||||||
<source>Enter the address in the format 255.255.255.255:88</source>
|
<source>Enter the address in the format 255.255.255.255:88</source>
|
||||||
<translation>按照这种格式输入 255.255.255.255:88</translation>
|
<translation>按照这种格式输入 255.255.255.255:88</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="132"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="136"/>
|
||||||
<source>Login cannot be empty</source>
|
<source>Login cannot be empty</source>
|
||||||
<translation>账号不能为空</translation>
|
<translation>账号不能为空</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="136"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="140"/>
|
||||||
<source>Password/private key cannot be empty</source>
|
<source>Password/private key cannot be empty</source>
|
||||||
<translation>密码或私钥不能为空</translation>
|
<translation>密码或私钥不能为空</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1821,25 +1833,30 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<name>PageSetupWizardInstalling</name>
|
<name>PageSetupWizardInstalling</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="21"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="21"/>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="75"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="72"/>
|
||||||
<source>Usually it takes no more than 5 minutes</source>
|
<source>Usually it takes no more than 5 minutes</source>
|
||||||
<translation>通常不超过5分钟</translation>
|
<translation>通常不超过5分钟</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="65"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="60"/>
|
||||||
<source>The server has already been added to the application</source>
|
<source>The server has already been added to the application</source>
|
||||||
<translation>服务器已添加到应用软件中</translation>
|
<translation>服务器已添加到应用软件中</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="70"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="66"/>
|
||||||
<source>Amnezia has detected that your server is currently </source>
|
<source>Amnezia has detected that your server is currently </source>
|
||||||
<translation>Amnezia 检测到您的服务器当前</translation>
|
<translation>Amnezia 检测到您的服务器当前</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="71"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="67"/>
|
||||||
<source>busy installing other software. Amnezia installation </source>
|
<source>busy installing other software. Amnezia installation </source>
|
||||||
<translation>正安装其他软件。Amnezia安装</translation>
|
<translation>正安装其他软件。Amnezia安装</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="165"/>
|
||||||
|
<source>Cancel installation</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Amnesia has detected that your server is currently </source>
|
<source>Amnesia has detected that your server is currently </source>
|
||||||
<translation type="vanished">Amnezia 检测到您的服务器当前</translation>
|
<translation type="vanished">Amnezia 检测到您的服务器当前</translation>
|
||||||
|
@ -1849,12 +1866,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation type="vanished">正安装其他软件。Amnezia安装</translation>
|
<translation type="vanished">正安装其他软件。Amnezia安装</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="72"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="68"/>
|
||||||
<source>will pause until the server finishes installing other software</source>
|
<source>will pause until the server finishes installing other software</source>
|
||||||
<translation>将暂停,直到其他软件安装完成。</translation>
|
<translation>将暂停,直到其他软件安装完成。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="129"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardInstalling.qml" line="126"/>
|
||||||
<source>Installing</source>
|
<source>Installing</source>
|
||||||
<translation>安装中</translation>
|
<translation>安装中</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -1921,22 +1938,22 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>从备份文件还原配置</translation>
|
<translation>从备份文件还原配置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="105"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="106"/>
|
||||||
<source>Free service for creating a personal VPN on your server.</source>
|
<source>Free service for creating a personal VPN on your server.</source>
|
||||||
<translation>在您的服务器上架设私人免费VPN服务。</translation>
|
<translation>在您的服务器上架设私人免费VPN服务。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="106"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="107"/>
|
||||||
<source> Helps you access blocked content without revealing your privacy, even to VPN providers.</source>
|
<source> Helps you access blocked content without revealing your privacy, even to VPN providers.</source>
|
||||||
<translation>帮助您访问受限内容,保护您的隐私,即使是VPN提供商也无法获取。</translation>
|
<translation>帮助您访问受限内容,保护您的隐私,即使是VPN提供商也无法获取。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="115"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="116"/>
|
||||||
<source>I have the data to connect</source>
|
<source>I have the data to connect</source>
|
||||||
<translation>我有连接配置</translation>
|
<translation>我有连接配置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="135"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="136"/>
|
||||||
<source>I have nothing</source>
|
<source>I have nothing</source>
|
||||||
<translation>我没有</translation>
|
<translation>我没有</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -2000,58 +2017,137 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<context>
|
<context>
|
||||||
<name>PageShare</name>
|
<name>PageShare</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="48"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="54"/>
|
||||||
<source>Save OpenVPN config</source>
|
<source>Save OpenVPN config</source>
|
||||||
<translation>保存OpenVPN配置</translation>
|
<translation>保存OpenVPN配置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="55"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="61"/>
|
||||||
<source>Save WireGuard config</source>
|
<source>Save WireGuard config</source>
|
||||||
<translation>保存WireGuard配置</translation>
|
<translation>保存WireGuard配置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="86"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="68"/>
|
||||||
|
<source>Save ShadowSocks config</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="75"/>
|
||||||
|
<source>Save Cloak config</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="105"/>
|
||||||
<source>For the AmneziaVPN app</source>
|
<source>For the AmneziaVPN app</source>
|
||||||
<translation>AmneziaVPN 应用</translation>
|
<translation>AmneziaVPN 应用</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="91"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="110"/>
|
||||||
<source>OpenVpn native format</source>
|
<source>OpenVpn native format</source>
|
||||||
<translation>OpenVPN原生格式</translation>
|
<translation>OpenVPN原生格式</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="96"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="115"/>
|
||||||
<source>WireGuard native format</source>
|
<source>WireGuard native format</source>
|
||||||
<translation>WireGuard原生格式</translation>
|
<translation>WireGuard原生格式</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="121"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="120"/>
|
||||||
|
<source>ShadowSocks native format</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="125"/>
|
||||||
|
<source>Cloak native format</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="150"/>
|
||||||
<source>Share VPN Access</source>
|
<source>Share VPN Access</source>
|
||||||
<translation>共享 VPN 访问</translation>
|
<translation>共享 VPN 访问</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="178"/>
|
||||||
|
<source>Share full access to the server and VPN</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="179"/>
|
||||||
|
<source>Use for your own devices, or share with those you trust to manage the server.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="231"/>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="483"/>
|
||||||
|
<source>Users</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="251"/>
|
||||||
<source>Share VPN access without the ability to manage the server</source>
|
<source>Share VPN access without the ability to manage the server</source>
|
||||||
<translation>共享 VPN 访问,无需管理服务器</translation>
|
<translation>共享 VPN 访问,无需管理服务器</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="175"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="499"/>
|
||||||
|
<source>Search</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="595"/>
|
||||||
|
<source>Rename</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="624"/>
|
||||||
|
<source>Client name</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="632"/>
|
||||||
|
<source>Save</source>
|
||||||
|
<translation type="unfinished">保存</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="660"/>
|
||||||
|
<source>Revoke</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="663"/>
|
||||||
|
<source>Revoke the config for a user - </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="664"/>
|
||||||
|
<source>The user will no longer be able to connect to your server.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="665"/>
|
||||||
|
<source>Continue</source>
|
||||||
|
<translation type="unfinished">继续</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="666"/>
|
||||||
|
<source>Cancel</source>
|
||||||
|
<translation type="unfinished">取消</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>共享服务器管理访问权限。与您共享服务器全部访问权限的用户将可以添加和删除服务器上的任何协议和服务,以及更改设置。</translation>
|
<translation type="vanished">共享服务器管理访问权限。与您共享服务器全部访问权限的用户将可以添加和删除服务器上的任何协议和服务,以及更改设置。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>VPN Access</source>
|
<source>VPN Access</source>
|
||||||
<translation type="vanished">访问VPN</translation>
|
<translation type="vanished">访问VPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="146"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="220"/>
|
||||||
<source>Connection</source>
|
<source>Connection</source>
|
||||||
<translation>连接</translation>
|
<translation>连接</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="158"/>
|
|
||||||
<source>Full access</source>
|
<source>Full access</source>
|
||||||
<translation>完全访问</translation>
|
<translation type="vanished">完全访问</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>VPN access without the ability to manage the server</source>
|
<source>VPN access without the ability to manage the server</source>
|
||||||
|
@ -2074,23 +2170,21 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation type="obsolete">服务器</translation>
|
<translation type="obsolete">服务器</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="190"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="279"/>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="191"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="280"/>
|
||||||
<source>Server</source>
|
<source>Server</source>
|
||||||
<translation>服务器</translation>
|
<translation>服务器</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="221"/>
|
|
||||||
<source>Accessing </source>
|
<source>Accessing </source>
|
||||||
<translation>访问</translation>
|
<translation type="vanished">访问</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="222"/>
|
|
||||||
<source>File with accessing settings to </source>
|
<source>File with accessing settings to </source>
|
||||||
<translation>访问配置文件的内容为:</translation>
|
<translation type="vanished">访问配置文件的内容为:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="310"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="42"/>
|
||||||
<source>File with connection settings to </source>
|
<source>File with connection settings to </source>
|
||||||
<translation>连接配置文件的内容为:</translation>
|
<translation>连接配置文件的内容为:</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -2099,28 +2193,89 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation type="obsolete">协议</translation>
|
<translation type="obsolete">协议</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="251"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="331"/>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="252"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="332"/>
|
||||||
<source>Protocol</source>
|
<source>Protocol</source>
|
||||||
<translation>协议</translation>
|
<translation>协议</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="309"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="41"/>
|
||||||
<source>Connection to </source>
|
<source>Connection to </source>
|
||||||
<translation>连接到</translation>
|
<translation>连接到</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="343"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="34"/>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="344"/>
|
<source>Config revoked</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="262"/>
|
||||||
|
<source>User name</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="428"/>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="429"/>
|
||||||
<source>Connection format</source>
|
<source>Connection format</source>
|
||||||
<translation>连接格式</translation>
|
<translation>连接格式</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="382"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="186"/>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="468"/>
|
||||||
<source>Share</source>
|
<source>Share</source>
|
||||||
<translation>共享</translation>
|
<translation>共享</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PageShareFullAccess</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="49"/>
|
||||||
|
<source>Full access to the server and VPN</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="57"/>
|
||||||
|
<source>We recommend that you use full access to the server only for your own additional devices.
|
||||||
|
</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="58"/>
|
||||||
|
<source>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. </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="73"/>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="74"/>
|
||||||
|
<source>Server</source>
|
||||||
|
<translation type="unfinished">服务器</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="102"/>
|
||||||
|
<source>Accessing </source>
|
||||||
|
<translation type="unfinished">访问</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="103"/>
|
||||||
|
<source>File with accessing settings to </source>
|
||||||
|
<translation type="unfinished">访问配置文件的内容为:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="122"/>
|
||||||
|
<source>Share</source>
|
||||||
|
<translation type="unfinished">共享</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="126"/>
|
||||||
|
<source>Connection to </source>
|
||||||
|
<translation type="unfinished">连接到</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="127"/>
|
||||||
|
<source>File with connection settings to </source>
|
||||||
|
<translation type="unfinished">连接配置文件的内容为:</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PopupType</name>
|
<name>PopupType</name>
|
||||||
<message>
|
<message>
|
||||||
|
@ -2512,67 +2667,70 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>Sftp 错误: 远程驱动器中没有媒介</translation>
|
<translation>Sftp 错误: 远程驱动器中没有媒介</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="45"/>
|
|
||||||
<source>Failed to save config to disk</source>
|
<source>Failed to save config to disk</source>
|
||||||
<translation>配置保存到磁盘失败</translation>
|
<translation type="vanished">配置保存到磁盘失败</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="46"/>
|
<location filename="../core/errorstrings.cpp" line="45"/>
|
||||||
<source>OpenVPN config missing</source>
|
<source>OpenVPN config missing</source>
|
||||||
<translation>OpenVPN配置丢失</translation>
|
<translation>OpenVPN配置丢失</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="47"/>
|
<location filename="../core/errorstrings.cpp" line="46"/>
|
||||||
<source>OpenVPN management server error</source>
|
<source>OpenVPN management server error</source>
|
||||||
<translation>OpenVPN 管理服务器错误</translation>
|
<translation>OpenVPN 管理服务器错误</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="50"/>
|
<location filename="../core/errorstrings.cpp" line="49"/>
|
||||||
<source>OpenVPN executable missing</source>
|
<source>OpenVPN executable missing</source>
|
||||||
<translation>OpenVPN 可执行文件丢失</translation>
|
<translation>OpenVPN 可执行文件丢失</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="51"/>
|
<location filename="../core/errorstrings.cpp" line="50"/>
|
||||||
<source>ShadowSocks (ss-local) executable missing</source>
|
<source>ShadowSocks (ss-local) executable missing</source>
|
||||||
<translation>ShadowSocks (ss-local) 执行文件丢失</translation>
|
<translation>ShadowSocks (ss-local) 执行文件丢失</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="52"/>
|
<location filename="../core/errorstrings.cpp" line="51"/>
|
||||||
<source>Cloak (ck-client) executable missing</source>
|
<source>Cloak (ck-client) executable missing</source>
|
||||||
<translation>Cloak (ck-client) 执行文件丢失</translation>
|
<translation>Cloak (ck-client) 执行文件丢失</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="53"/>
|
<location filename="../core/errorstrings.cpp" line="52"/>
|
||||||
<source>Amnezia helper service error</source>
|
<source>Amnezia helper service error</source>
|
||||||
<translation>Amnezia 服务连接失败</translation>
|
<translation>Amnezia 服务连接失败</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="54"/>
|
<location filename="../core/errorstrings.cpp" line="53"/>
|
||||||
<source>OpenSSL failed</source>
|
<source>OpenSSL failed</source>
|
||||||
<translation>OpenSSL错误</translation>
|
<translation>OpenSSL错误</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="57"/>
|
<location filename="../core/errorstrings.cpp" line="56"/>
|
||||||
<source>Can't connect: another VPN connection is active</source>
|
<source>Can't connect: another VPN connection is active</source>
|
||||||
<translation>无法连接:另一个VPN连接处于活跃状态</translation>
|
<translation>无法连接:另一个VPN连接处于活跃状态</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="58"/>
|
<location filename="../core/errorstrings.cpp" line="57"/>
|
||||||
<source>Can't setup OpenVPN TAP network adapter</source>
|
<source>Can't setup OpenVPN TAP network adapter</source>
|
||||||
<translation>无法设置 OpenVPN TAP 网络适配器</translation>
|
<translation>无法设置 OpenVPN TAP 网络适配器</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="59"/>
|
<location filename="../core/errorstrings.cpp" line="58"/>
|
||||||
<source>VPN pool error: no available addresses</source>
|
<source>VPN pool error: no available addresses</source>
|
||||||
<translation>VPN 池错误:没有可用地址</translation>
|
<translation>VPN 池错误:没有可用地址</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="61"/>
|
<location filename="../core/errorstrings.cpp" line="60"/>
|
||||||
<source>The config does not contain any containers and credentiaks for connecting to the server</source>
|
<source>The config does not contain any containers and credentials for connecting to the server</source>
|
||||||
<translation>该配置不包含任何用于连接到服务器的容器和凭据。</translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="65"/>
|
<source>The config does not contain any containers and credentiaks for connecting to the server</source>
|
||||||
|
<translation type="vanished">该配置不包含任何用于连接到服务器的容器和凭据。</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../core/errorstrings.cpp" line="64"/>
|
||||||
<source>Internal error</source>
|
<source>Internal error</source>
|
||||||
<translation>内部错误</translation>
|
<translation>内部错误</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -2871,16 +3029,6 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||||
<source>error 0x%1: %2</source>
|
<source>error 0x%1: %2</source>
|
||||||
<translation>错误 0x%1: %2</translation>
|
<translation>错误 0x%1: %2</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../3rd/wireguard-tools/contrib/highlighter/gui/highlight.cpp" line="39"/>
|
|
||||||
<source>WireGuard Configuration Highlighter</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../3rd/wireguard-tools/contrib/highlighter/gui/highlight.cpp" line="82"/>
|
|
||||||
<source>&Randomize colors</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>SelectLanguageDrawer</name>
|
<name>SelectLanguageDrawer</name>
|
||||||
|
@ -2912,17 +3060,17 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||||
<translation>软件版本</translation>
|
<translation>软件版本</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/settingsController.cpp" line="121"/>
|
<location filename="../ui/controllers/settingsController.cpp" line="122"/>
|
||||||
<source>Backup file is corrupted</source>
|
<source>Backup file is corrupted</source>
|
||||||
<translation>备份文件已损坏</translation>
|
<translation>备份文件已损坏</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/settingsController.cpp" line="136"/>
|
<location filename="../ui/controllers/settingsController.cpp" line="137"/>
|
||||||
<source>All settings have been reset to default values</source>
|
<source>All settings have been reset to default values</source>
|
||||||
<translation>所配置恢复为默认值</translation>
|
<translation>所配置恢复为默认值</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/settingsController.cpp" line="142"/>
|
<location filename="../ui/controllers/settingsController.cpp" line="143"/>
|
||||||
<source>Cached profiles cleared</source>
|
<source>Cached profiles cleared</source>
|
||||||
<translation>缓存的配置文件已清除</translation>
|
<translation>缓存的配置文件已清除</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -2947,11 +3095,17 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="111"/>
|
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="111"/>
|
||||||
|
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="135"/>
|
||||||
<source>Copied</source>
|
<source>Copied</source>
|
||||||
<translation>已拷贝</translation>
|
<translation>已拷贝</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="126"/>
|
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="128"/>
|
||||||
|
<source>Copy config string</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="150"/>
|
||||||
<source>Show connection settings</source>
|
<source>Show connection settings</source>
|
||||||
<translation>显示连接配置</translation>
|
<translation>显示连接配置</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -2960,7 +3114,7 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||||
<translation type="obsolete">展示内容</translation>
|
<translation type="obsolete">展示内容</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="251"/>
|
<location filename="../ui/qml/Components/ShareConnectionDrawer.qml" line="280"/>
|
||||||
<source>To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file"</source>
|
<source>To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file"</source>
|
||||||
<translation>要应用二维码到 Amnezia,请底部工具栏点击“+”→“连接方式”→“二维码、授权码或配置文件”</translation>
|
<translation>要应用二维码到 Amnezia,请底部工具栏点击“+”→“连接方式”→“二维码、授权码或配置文件”</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -3052,7 +3206,7 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||||
<context>
|
<context>
|
||||||
<name>VpnConnection</name>
|
<name>VpnConnection</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../vpnconnection.cpp" line="406"/>
|
<location filename="../vpnconnection.cpp" line="429"/>
|
||||||
<source>Mbps</source>
|
<source>Mbps</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
|
|
129
client/ui/controllers/apiController.cpp
Normal file
129
client/ui/controllers/apiController.cpp
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
#include "apiController.h"
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
#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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiController::ApiController(const QSharedPointer<ServersModel> &serversModel,
|
||||||
|
const QSharedPointer<ContainersModel> &containersModel, QObject *parent)
|
||||||
|
: QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ApiController::genPublicKey(const QString &protocol)
|
||||||
|
{
|
||||||
|
if (protocol == configKey::cloak) {
|
||||||
|
return ".";
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ApiController::genCertificateRequest(const QString &protocol)
|
||||||
|
{
|
||||||
|
if (protocol == configKey::cloak) {
|
||||||
|
m_certRequest = OpenVpnConfigurator::createCertRequest();
|
||||||
|
return m_certRequest.request;
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiController::processCloudConfig(const QString &protocol, QString &config)
|
||||||
|
{
|
||||||
|
if (protocol == configKey::cloak) {
|
||||||
|
config.replace("<key>", "<key>\n");
|
||||||
|
config.replace("$OPENVPN_PRIV_KEY", m_certRequest.privKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiController::updateServerConfigFromApi()
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
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
|
||||||
|
|
||||||
|
QString protocol = serverConfig.value(configKey::protocol).toString();
|
||||||
|
|
||||||
|
QJsonObject obj;
|
||||||
|
|
||||||
|
obj[configKey::publicKey] = genPublicKey(protocol);
|
||||||
|
obj[configKey::certificate] = genCertificateRequest(protocol);
|
||||||
|
|
||||||
|
QByteArray requestBody = QJsonDocument(obj).toJson();
|
||||||
|
qDebug() << requestBody;
|
||||||
|
|
||||||
|
QScopedPointer<QNetworkReply> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString configStr = ba;
|
||||||
|
processCloudConfig(protocol, 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));
|
||||||
|
serverConfig.insert(config_key::containers, cloudConfig.value(config_key::containers));
|
||||||
|
serverConfig.insert(config_key::hostName, cloudConfig.value(config_key::hostName));
|
||||||
|
|
||||||
|
auto defaultContainer = cloudConfig.value(config_key::defaultContainer).toString();
|
||||||
|
serverConfig.insert(config_key::defaultContainer, defaultContainer);
|
||||||
|
m_serversModel->editServer(serverConfig);
|
||||||
|
emit m_serversModel->defaultContainerChanged(ContainerProps::containerFromString(defaultContainer));
|
||||||
|
} else {
|
||||||
|
QString err = reply->errorString();
|
||||||
|
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;
|
||||||
|
}
|
36
client/ui/controllers/apiController.h
Normal file
36
client/ui/controllers/apiController.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef APICONTROLLER_H
|
||||||
|
#define APICONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "configurators/openvpn_configurator.h"
|
||||||
|
#include "ui/models/containers_model.h"
|
||||||
|
#include "ui/models/servers_model.h"
|
||||||
|
|
||||||
|
class ApiController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ApiController(const QSharedPointer<ServersModel> &serversModel,
|
||||||
|
const QSharedPointer<ContainersModel> &containersModel, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool updateServerConfigFromApi();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void errorOccurred(const QString &errorMessage);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString genPublicKey(const QString &protocol);
|
||||||
|
QString genCertificateRequest(const QString &protocol);
|
||||||
|
|
||||||
|
void processCloudConfig(const QString &protocol, QString &config);
|
||||||
|
|
||||||
|
QSharedPointer<ServersModel> m_serversModel;
|
||||||
|
QSharedPointer<ContainersModel> m_containersModel;
|
||||||
|
|
||||||
|
OpenVpnConfigurator::ConnectionData m_certRequest;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APICONTROLLER_H
|
|
@ -26,13 +26,10 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
|
||||||
void ConnectionController::openConnection()
|
void ConnectionController::openConnection()
|
||||||
{
|
{
|
||||||
int serverIndex = m_serversModel->getDefaultServerIndex();
|
int serverIndex = m_serversModel->getDefaultServerIndex();
|
||||||
ServerCredentials credentials =
|
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
||||||
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
|
|
||||||
|
|
||||||
DockerContainer container = m_containersModel->getDefaultContainer();
|
DockerContainer container = m_containersModel->getDefaultContainer();
|
||||||
QModelIndex containerModelIndex = m_containersModel->index(container);
|
const QJsonObject &containerConfig = m_containersModel->getContainerConfig(container);
|
||||||
const QJsonObject &containerConfig =
|
|
||||||
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
|
|
||||||
|
|
||||||
if (container == DockerContainer::None) {
|
if (container == DockerContainer::None) {
|
||||||
emit connectionErrorOccurred(tr("VPN Protocols is not installed.\n Please install VPN container at first"));
|
emit connectionErrorOccurred(tr("VPN Protocols is not installed.\n Please install VPN container at first"));
|
||||||
|
@ -40,6 +37,7 @@ void ConnectionController::openConnection()
|
||||||
}
|
}
|
||||||
|
|
||||||
qApp->processEvents();
|
qApp->processEvents();
|
||||||
|
|
||||||
emit connectToVpn(serverIndex, credentials, container, containerConfig);
|
emit connectToVpn(serverIndex, credentials, container, containerConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +108,8 @@ void ConnectionController::onCurrentContainerUpdated()
|
||||||
if (m_isConnected || m_isConnectionInProgress) {
|
if (m_isConnected || m_isConnectionInProgress) {
|
||||||
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, Reconnnection..."));
|
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, Reconnnection..."));
|
||||||
openConnection();
|
openConnection();
|
||||||
|
} else {
|
||||||
|
emit reconnectWithUpdatedContainer(tr("Settings updated successfully"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#include "configurators/cloak_configurator.h"
|
||||||
#include "configurators/openvpn_configurator.h"
|
#include "configurators/openvpn_configurator.h"
|
||||||
|
#include "configurators/shadowsocks_configurator.h"
|
||||||
#include "configurators/wireguard_configurator.h"
|
#include "configurators/wireguard_configurator.h"
|
||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
#include "systemController.h"
|
#include "systemController.h"
|
||||||
|
@ -19,11 +21,13 @@
|
||||||
|
|
||||||
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel,
|
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel,
|
||||||
const QSharedPointer<ContainersModel> &containersModel,
|
const QSharedPointer<ContainersModel> &containersModel,
|
||||||
|
const QSharedPointer<ClientManagementModel> &clientManagementModel,
|
||||||
const std::shared_ptr<Settings> &settings,
|
const std::shared_ptr<Settings> &settings,
|
||||||
const std::shared_ptr<VpnConfigurator> &configurator, QObject *parent)
|
const std::shared_ptr<VpnConfigurator> &configurator, QObject *parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
m_serversModel(serversModel),
|
m_serversModel(serversModel),
|
||||||
m_containersModel(containersModel),
|
m_containersModel(containersModel),
|
||||||
|
m_clientManagementModel(clientManagementModel),
|
||||||
m_settings(settings),
|
m_settings(settings),
|
||||||
m_configurator(configurator)
|
m_configurator(configurator)
|
||||||
{
|
{
|
||||||
|
@ -75,35 +79,40 @@ void ExportController::generateFullAccessConfigAndroid()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ExportController::generateConnectionConfig()
|
void ExportController::generateConnectionConfig(const QString &clientName)
|
||||||
{
|
{
|
||||||
clearPreviousConfig();
|
clearPreviousConfig();
|
||||||
|
|
||||||
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
|
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
|
||||||
ServerCredentials credentials =
|
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
||||||
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
|
|
||||||
|
|
||||||
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
|
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
|
||||||
QModelIndex containerModelIndex = m_containersModel->index(container);
|
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
||||||
QJsonObject containerConfig =
|
|
||||||
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
|
|
||||||
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
||||||
|
|
||||||
ErrorCode errorCode = ErrorCode::NoError;
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
|
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
|
||||||
QJsonObject protocolConfig = m_settings->protocolConfig(serverIndex, container, protocol);
|
QJsonObject protocolConfig = m_settings->protocolConfig(serverIndex, container, protocol);
|
||||||
|
|
||||||
QString vpnConfig =
|
QString clientId;
|
||||||
m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, protocol, &errorCode);
|
QString vpnConfig = m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, protocol,
|
||||||
|
clientId, &errorCode);
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
emit exportErrorOccurred(errorString(errorCode));
|
emit exportErrorOccurred(errorString(errorCode));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
protocolConfig.insert(config_key::last_config, vpnConfig);
|
protocolConfig.insert(config_key::last_config, vpnConfig);
|
||||||
containerConfig.insert(ProtocolProps::protoToString(protocol), protocolConfig);
|
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) {
|
if (!errorCode) {
|
||||||
config.remove(config_key::userName);
|
config.remove(config_key::userName);
|
||||||
config.remove(config_key::password);
|
config.remove(config_key::password);
|
||||||
|
@ -126,23 +135,21 @@ void ExportController::generateConnectionConfig()
|
||||||
emit exportConfigChanged();
|
emit exportConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportController::generateOpenVpnConfig()
|
void ExportController::generateOpenVpnConfig(const QString &clientName)
|
||||||
{
|
{
|
||||||
clearPreviousConfig();
|
clearPreviousConfig();
|
||||||
|
|
||||||
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
|
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
|
||||||
ServerCredentials credentials =
|
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
||||||
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
|
|
||||||
|
|
||||||
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
|
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
|
||||||
QModelIndex containerModelIndex = m_containersModel->index(container);
|
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
||||||
QJsonObject containerConfig =
|
|
||||||
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
|
|
||||||
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
||||||
|
|
||||||
ErrorCode errorCode = ErrorCode::NoError;
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
QString config =
|
QString clientId;
|
||||||
m_configurator->openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, &errorCode);
|
QString config = m_configurator->openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig,
|
||||||
|
clientId, &errorCode);
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
emit exportErrorOccurred(errorString(errorCode));
|
emit exportErrorOccurred(errorString(errorCode));
|
||||||
return;
|
return;
|
||||||
|
@ -155,26 +162,32 @@ void ExportController::generateOpenVpnConfig()
|
||||||
m_config.append(line + "\n");
|
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();
|
emit exportConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportController::generateWireGuardConfig()
|
void ExportController::generateWireGuardConfig(const QString &clientName)
|
||||||
{
|
{
|
||||||
clearPreviousConfig();
|
clearPreviousConfig();
|
||||||
|
|
||||||
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
|
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
|
||||||
ServerCredentials credentials =
|
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
||||||
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
|
|
||||||
|
|
||||||
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
|
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
|
||||||
QModelIndex containerModelIndex = m_containersModel->index(container);
|
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
||||||
QJsonObject containerConfig =
|
|
||||||
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
|
|
||||||
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
||||||
|
|
||||||
|
QString clientId;
|
||||||
ErrorCode errorCode = ErrorCode::NoError;
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
QString config = m_configurator->wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig,
|
QString config = m_configurator->wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig,
|
||||||
&errorCode);
|
clientId, &errorCode);
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
emit exportErrorOccurred(errorString(errorCode));
|
emit exportErrorOccurred(errorString(errorCode));
|
||||||
return;
|
return;
|
||||||
|
@ -187,6 +200,84 @@ void ExportController::generateWireGuardConfig()
|
||||||
m_config.append(line + "\n");
|
m_config.append(line + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_config.toUtf8(), qrcodegen::QrCode::Ecc::LOW);
|
||||||
|
m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
|
||||||
|
|
||||||
|
errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
|
||||||
|
if (errorCode) {
|
||||||
|
emit exportErrorOccurred(errorString(errorCode));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit exportConfigChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportController::generateShadowSocksConfig()
|
||||||
|
{
|
||||||
|
clearPreviousConfig();
|
||||||
|
|
||||||
|
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
|
||||||
|
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
||||||
|
|
||||||
|
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
|
||||||
|
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
||||||
|
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
||||||
|
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
QString config = m_configurator->shadowSocksConfigurator->genShadowSocksConfig(credentials, container,
|
||||||
|
containerConfig, &errorCode);
|
||||||
|
|
||||||
|
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::ShadowSocks, config);
|
||||||
|
QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||||
|
|
||||||
|
QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n");
|
||||||
|
for (const QString &line : lines) {
|
||||||
|
m_config.append(line + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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)));
|
||||||
|
|
||||||
|
emit exportConfigChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportController::generateCloakConfig()
|
||||||
|
{
|
||||||
|
clearPreviousConfig();
|
||||||
|
|
||||||
|
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
|
||||||
|
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
||||||
|
|
||||||
|
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
|
||||||
|
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
emit exportConfigChanged();
|
emit exportConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +286,11 @@ QString ExportController::getConfig()
|
||||||
return m_config;
|
return m_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ExportController::getNativeConfigString()
|
||||||
|
{
|
||||||
|
return m_nativeConfigString;
|
||||||
|
}
|
||||||
|
|
||||||
QList<QString> ExportController::getQrCodes()
|
QList<QString> ExportController::getQrCodes()
|
||||||
{
|
{
|
||||||
return m_qrCodes;
|
return m_qrCodes;
|
||||||
|
@ -205,6 +301,30 @@ void ExportController::exportConfig(const QString &fileName)
|
||||||
SystemController::saveFile(fileName, m_config);
|
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);
|
||||||
|
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<QString> ExportController::generateQrCodeImageSeries(const QByteArray &data)
|
QList<QString> ExportController::generateQrCodeImageSeries(const QByteArray &data)
|
||||||
{
|
{
|
||||||
double k = 850;
|
double k = 850;
|
||||||
|
@ -219,7 +339,7 @@ QList<QString> ExportController::generateQrCodeImageSeries(const QByteArray &dat
|
||||||
QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||||
|
|
||||||
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW);
|
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));
|
chunks.append(svgToBase64(svg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,5 +359,6 @@ int ExportController::getQrCodesCount()
|
||||||
void ExportController::clearPreviousConfig()
|
void ExportController::clearPreviousConfig()
|
||||||
{
|
{
|
||||||
m_config.clear();
|
m_config.clear();
|
||||||
|
m_nativeConfigString.clear();
|
||||||
m_qrCodes.clear();
|
m_qrCodes.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "configurators/vpn_configurator.h"
|
#include "configurators/vpn_configurator.h"
|
||||||
#include "ui/models/containers_model.h"
|
#include "ui/models/containers_model.h"
|
||||||
#include "ui/models/servers_model.h"
|
#include "ui/models/servers_model.h"
|
||||||
|
#include "ui/models/clientManagementModel.h"
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include "platforms/android/authResultReceiver.h"
|
#include "platforms/android/authResultReceiver.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,27 +17,36 @@ class ExportController : public QObject
|
||||||
public:
|
public:
|
||||||
explicit ExportController(const QSharedPointer<ServersModel> &serversModel,
|
explicit ExportController(const QSharedPointer<ServersModel> &serversModel,
|
||||||
const QSharedPointer<ContainersModel> &containersModel,
|
const QSharedPointer<ContainersModel> &containersModel,
|
||||||
|
const QSharedPointer<ClientManagementModel> &clientManagementModel,
|
||||||
const std::shared_ptr<Settings> &settings,
|
const std::shared_ptr<Settings> &settings,
|
||||||
const std::shared_ptr<VpnConfigurator> &configurator, QObject *parent = nullptr);
|
const std::shared_ptr<VpnConfigurator> &configurator, QObject *parent = nullptr);
|
||||||
|
|
||||||
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY exportConfigChanged)
|
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY exportConfigChanged)
|
||||||
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY exportConfigChanged)
|
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY exportConfigChanged)
|
||||||
Q_PROPERTY(QString config READ getConfig NOTIFY exportConfigChanged)
|
Q_PROPERTY(QString config READ getConfig NOTIFY exportConfigChanged)
|
||||||
|
Q_PROPERTY(QString nativeConfigString READ getNativeConfigString NOTIFY exportConfigChanged)
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void generateFullAccessConfig();
|
void generateFullAccessConfig();
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
void generateFullAccessConfigAndroid();
|
void generateFullAccessConfigAndroid();
|
||||||
#endif
|
#endif
|
||||||
void generateConnectionConfig();
|
void generateConnectionConfig(const QString &clientName);
|
||||||
void generateOpenVpnConfig();
|
void generateOpenVpnConfig(const QString &clientName);
|
||||||
void generateWireGuardConfig();
|
void generateWireGuardConfig(const QString &clientName);
|
||||||
|
void generateShadowSocksConfig();
|
||||||
|
void generateCloakConfig();
|
||||||
|
|
||||||
QString getConfig();
|
QString getConfig();
|
||||||
|
QString getNativeConfigString();
|
||||||
QList<QString> getQrCodes();
|
QList<QString> getQrCodes();
|
||||||
|
|
||||||
void exportConfig(const QString &fileName);
|
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);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void generateConfig(int type);
|
void generateConfig(int type);
|
||||||
void exportErrorOccurred(const QString &errorMessage);
|
void exportErrorOccurred(const QString &errorMessage);
|
||||||
|
@ -55,10 +65,12 @@ private:
|
||||||
|
|
||||||
QSharedPointer<ServersModel> m_serversModel;
|
QSharedPointer<ServersModel> m_serversModel;
|
||||||
QSharedPointer<ContainersModel> m_containersModel;
|
QSharedPointer<ContainersModel> m_containersModel;
|
||||||
|
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||||
std::shared_ptr<Settings> m_settings;
|
std::shared_ptr<Settings> m_settings;
|
||||||
std::shared_ptr<VpnConfigurator> m_configurator;
|
std::shared_ptr<VpnConfigurator> m_configurator;
|
||||||
|
|
||||||
QString m_config;
|
QString m_config;
|
||||||
|
QString m_nativeConfigString;
|
||||||
QList<QString> m_qrCodes;
|
QList<QString> m_qrCodes;
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
|
|
|
@ -123,7 +123,9 @@ void ImportController::importConfig()
|
||||||
credentials.userName = m_config.value(config_key::userName).toString();
|
credentials.userName = m_config.value(config_key::userName).toString();
|
||||||
credentials.secretData = m_config.value(config_key::password).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->addServer(m_config);
|
||||||
|
|
||||||
emit importFinished();
|
emit importFinished();
|
||||||
|
@ -147,7 +149,9 @@ QJsonObject ImportController::extractAmneziaConfig(QString &data)
|
||||||
ba = ba_uncompressed;
|
ba = ba_uncompressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return QJsonDocument::fromJson(ba).object();
|
QJsonObject config = QJsonDocument::fromJson(ba).object();
|
||||||
|
|
||||||
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
|
QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <QRandomGenerator>
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
#include "core/servercontroller.h"
|
#include "core/controllers/serverController.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -130,6 +130,7 @@ void InstallController::installServer(DockerContainer container, QJsonObject &co
|
||||||
{
|
{
|
||||||
ServerController serverController(m_settings);
|
ServerController serverController(m_settings);
|
||||||
connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy);
|
connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy);
|
||||||
|
connect(this, &InstallController::cancelInstallation, &serverController, &ServerController::cancelInstallation);
|
||||||
|
|
||||||
QMap<DockerContainer, QJsonObject> installedContainers;
|
QMap<DockerContainer, QJsonObject> installedContainers;
|
||||||
ErrorCode errorCode =
|
ErrorCode errorCode =
|
||||||
|
@ -181,6 +182,7 @@ void InstallController::installContainer(DockerContainer container, QJsonObject
|
||||||
|
|
||||||
ServerController serverController(m_settings);
|
ServerController serverController(m_settings);
|
||||||
connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy);
|
connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy);
|
||||||
|
connect(this, &InstallController::cancelInstallation, &serverController, &ServerController::cancelInstallation);
|
||||||
|
|
||||||
QMap<DockerContainer, QJsonObject> installedContainers;
|
QMap<DockerContainer, QJsonObject> installedContainers;
|
||||||
ErrorCode errorCode = serverController.getAlreadyInstalledContainers(serverCredentials, installedContainers);
|
ErrorCode errorCode = serverController.getAlreadyInstalledContainers(serverCredentials, installedContainers);
|
||||||
|
@ -199,12 +201,9 @@ void InstallController::installContainer(DockerContainer container, QJsonObject
|
||||||
|
|
||||||
if (errorCode == ErrorCode::NoError) {
|
if (errorCode == ErrorCode::NoError) {
|
||||||
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
|
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
|
||||||
auto modelIndex = m_containersModel->index(iterator.key());
|
QJsonObject containerConfig = m_containersModel->getContainerConfig(iterator.key());
|
||||||
QJsonObject containerConfig =
|
|
||||||
qvariant_cast<QJsonObject>(m_containersModel->data(modelIndex, ContainersModel::Roles::ConfigRole));
|
|
||||||
if (containerConfig.isEmpty()) {
|
if (containerConfig.isEmpty()) {
|
||||||
m_containersModel->setData(m_containersModel->index(iterator.key()), iterator.value(),
|
m_serversModel->addContainerConfig(iterator.key(), iterator.value());
|
||||||
ContainersModel::Roles::ConfigRole);
|
|
||||||
if (container != iterator.key()) { // skip the newly installed container
|
if (container != iterator.key()) { // skip the newly installed container
|
||||||
isInstalledContainerAddedToGui = true;
|
isInstalledContainerAddedToGui = true;
|
||||||
}
|
}
|
||||||
|
@ -252,12 +251,9 @@ void InstallController::scanServerForInstalledContainers()
|
||||||
bool isInstalledContainerAddedToGui = false;
|
bool isInstalledContainerAddedToGui = false;
|
||||||
|
|
||||||
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
|
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
|
||||||
auto modelIndex = m_containersModel->index(iterator.key());
|
QJsonObject containerConfig = m_containersModel->getContainerConfig(iterator.key());
|
||||||
QJsonObject containerConfig =
|
|
||||||
qvariant_cast<QJsonObject>(m_containersModel->data(modelIndex, ContainersModel::Roles::ConfigRole));
|
|
||||||
if (containerConfig.isEmpty()) {
|
if (containerConfig.isEmpty()) {
|
||||||
m_containersModel->setData(m_containersModel->index(iterator.key()), iterator.value(),
|
m_serversModel->addContainerConfig(iterator.key(), iterator.value());
|
||||||
ContainersModel::Roles::ConfigRole);
|
|
||||||
isInstalledContainerAddedToGui = true;
|
isInstalledContainerAddedToGui = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,16 +272,15 @@ void InstallController::updateContainer(QJsonObject config)
|
||||||
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
|
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
|
||||||
|
|
||||||
const DockerContainer container = ContainerProps::containerFromString(config.value(config_key::container).toString());
|
const DockerContainer container = ContainerProps::containerFromString(config.value(config_key::container).toString());
|
||||||
auto modelIndex = m_containersModel->index(container);
|
QJsonObject oldContainerConfig = m_containersModel->getContainerConfig(container);
|
||||||
QJsonObject oldContainerConfig =
|
|
||||||
qvariant_cast<QJsonObject>(m_containersModel->data(modelIndex, ContainersModel::Roles::ConfigRole));
|
|
||||||
|
|
||||||
ServerController serverController(m_settings);
|
ServerController serverController(m_settings);
|
||||||
connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy);
|
connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy);
|
||||||
|
connect(this, &InstallController::cancelInstallation, &serverController, &ServerController::cancelInstallation);
|
||||||
|
|
||||||
auto errorCode = serverController.updateContainer(serverCredentials, container, oldContainerConfig, config);
|
auto errorCode = serverController.updateContainer(serverCredentials, container, oldContainerConfig, config);
|
||||||
if (errorCode == ErrorCode::NoError) {
|
if (errorCode == ErrorCode::NoError) {
|
||||||
m_containersModel->setData(modelIndex, config, ContainersModel::Roles::ConfigRole);
|
m_serversModel->updateContainerConfig(container, config);
|
||||||
m_protocolModel->updateModel(config);
|
m_protocolModel->updateModel(config);
|
||||||
|
|
||||||
if ((serverIndex == m_serversModel->getDefaultServerIndex())
|
if ((serverIndex == m_serversModel->getDefaultServerIndex())
|
||||||
|
@ -315,7 +310,7 @@ void InstallController::removeAllContainers()
|
||||||
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
|
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
|
||||||
QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString();
|
QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString();
|
||||||
|
|
||||||
ErrorCode errorCode = m_containersModel->removeAllContainers();
|
ErrorCode errorCode = m_serversModel->removeAllContainers();
|
||||||
if (errorCode == ErrorCode::NoError) {
|
if (errorCode == ErrorCode::NoError) {
|
||||||
emit removeAllContainersFinished(tr("All containers from server '%1' have been removed").arg(serverName));
|
emit removeAllContainersFinished(tr("All containers from server '%1' have been removed").arg(serverName));
|
||||||
return;
|
return;
|
||||||
|
@ -329,12 +324,12 @@ void InstallController::removeCurrentlyProcessedContainer()
|
||||||
QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString();
|
QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString();
|
||||||
|
|
||||||
int container = m_containersModel->getCurrentlyProcessedContainerIndex();
|
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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
emit installationErrorOccurred(errorString(errorCode));
|
emit installationErrorOccurred(errorString(errorCode));
|
||||||
|
|
|
@ -65,6 +65,7 @@ signals:
|
||||||
void passphraseRequestFinished();
|
void passphraseRequestFinished();
|
||||||
|
|
||||||
void serverIsBusy(const bool isBusy);
|
void serverIsBusy(const bool isBusy);
|
||||||
|
void cancelInstallation();
|
||||||
|
|
||||||
void currentContainerUpdated();
|
void currentContainerUpdated();
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,9 @@ namespace PageLoader
|
||||||
PageProtocolWireGuardSettings,
|
PageProtocolWireGuardSettings,
|
||||||
PageProtocolAwgSettings,
|
PageProtocolAwgSettings,
|
||||||
PageProtocolIKev2Settings,
|
PageProtocolIKev2Settings,
|
||||||
PageProtocolRaw
|
PageProtocolRaw,
|
||||||
|
|
||||||
|
PageShareFullAccess
|
||||||
};
|
};
|
||||||
Q_ENUM_NS(PageEnum)
|
Q_ENUM_NS(PageEnum)
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
|
||||||
void SettingsController::toggleAmneziaDns(bool enable)
|
void SettingsController::toggleAmneziaDns(bool enable)
|
||||||
{
|
{
|
||||||
m_settings->setUseAmneziaDns(enable);
|
m_settings->setUseAmneziaDns(enable);
|
||||||
|
emit amneziaDnsToggled(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SettingsController::isAmneziaDnsEnabled()
|
bool SettingsController::isAmneziaDnsEnabled()
|
||||||
|
@ -138,7 +139,7 @@ void SettingsController::clearSettings()
|
||||||
|
|
||||||
void SettingsController::clearCachedProfiles()
|
void SettingsController::clearCachedProfiles()
|
||||||
{
|
{
|
||||||
m_containersModel->clearCachedProfiles();
|
m_serversModel->clearCachedProfiles();
|
||||||
emit changeSettingsFinished(tr("Cached profiles cleared"));
|
emit changeSettingsFinished(tr("Cached profiles cleared"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,8 @@ signals:
|
||||||
|
|
||||||
void importBackupFromOutside(QString filePath);
|
void importBackupFromOutside(QString filePath);
|
||||||
|
|
||||||
|
void amneziaDnsToggled(bool enable);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSharedPointer<ServersModel> m_serversModel;
|
QSharedPointer<ServersModel> m_serversModel;
|
||||||
QSharedPointer<ContainersModel> m_containersModel;
|
QSharedPointer<ContainersModel> m_containersModel;
|
||||||
|
|
|
@ -1,104 +1,373 @@
|
||||||
#include "clientManagementModel.h"
|
#include "clientManagementModel.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
ClientManagementModel::ClientManagementModel(QObject *parent) : QAbstractListModel(parent)
|
#include "core/controllers/serverController.h"
|
||||||
{
|
#include "logger.h"
|
||||||
|
|
||||||
}
|
namespace
|
||||||
|
|
||||||
void ClientManagementModel::clearData()
|
|
||||||
{
|
{
|
||||||
beginResetModel();
|
Logger logger("ClientManagementModel");
|
||||||
m_content.clear();
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientManagementModel::setContent(const QVector<QVariant> &data)
|
namespace configKey {
|
||||||
{
|
constexpr char clientId[] = "clientId";
|
||||||
beginResetModel();
|
constexpr char clientName[] = "clientName";
|
||||||
m_content = data;
|
constexpr char container[] = "container";
|
||||||
endResetModel();
|
constexpr char userData[] = "userData";
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
}
|
||||||
|
|
||||||
|
ClientManagementModel::ClientManagementModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||||
|
: m_settings(settings), QAbstractListModel(parent)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int ClientManagementModel::rowCount(const QModelIndex &parent) const
|
int ClientManagementModel::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent);
|
Q_UNUSED(parent);
|
||||||
return static_cast<int>(m_content.size());
|
return static_cast<int>(m_clientsTable.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ClientManagementModel::data(const QModelIndex &index, int role) const
|
QVariant ClientManagementModel::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid() || index.row() < 0
|
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(m_clientsTable.size())) {
|
||||||
|| index.row() >= static_cast<int>(m_content.size())) {
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == NameRole) {
|
auto client = m_clientsTable.at(index.row()).toObject();
|
||||||
return m_content[index.row()].toJsonObject()["clientName"].toString();
|
auto userData = client.value(configKey::userData).toObject();
|
||||||
} else if (role == OpenVpnCertIdRole) {
|
|
||||||
return m_content[index.row()].toJsonObject()["openvpnCertId"].toString();
|
switch (role) {
|
||||||
} else if (role == OpenVpnCertDataRole) {
|
case ClientNameRole: return userData.value(configKey::clientName).toString();
|
||||||
return m_content[index.row()].toJsonObject()["openvpnCertData"].toString();
|
|
||||||
} else if (role == WireGuardPublicKey) {
|
|
||||||
return m_content[index.row()].toJsonObject()["wireguardPublicKey"].toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
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
|
beginResetModel();
|
||||||
|| index.row() >= static_cast<int>(m_content.size())) {
|
m_clientsTable = QJsonArray();
|
||||||
return;
|
|
||||||
|
ServerController serverController(m_settings);
|
||||||
|
|
||||||
|
ErrorCode error = 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";
|
||||||
|
endResetModel();
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto client = m_content[index.row()].toJsonObject();
|
m_clientsTable = QJsonDocument::fromJson(clientsTableString).array();
|
||||||
if (role == NameRole) {
|
|
||||||
client["clientName"] = data.toString();
|
if (m_clientsTable.isEmpty()) {
|
||||||
} else if (role == OpenVpnCertIdRole) {
|
int count = 0;
|
||||||
client["openvpnCertId"] = data.toString();
|
|
||||||
} else if (role == OpenVpnCertDataRole) {
|
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks
|
||||||
client["openvpnCertData"] = data.toString();
|
|| container == DockerContainer::Cloak) {
|
||||||
} else if (role == WireGuardPublicKey) {
|
error = getOpenVpnClients(serverController, container, credentials, count);
|
||||||
client["wireguardPublicKey"] = data.toString();
|
} else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) {
|
||||||
} else {
|
error = getWireGuardClients(serverController, container, credentials, count);
|
||||||
return;
|
}
|
||||||
}
|
if (error != ErrorCode::NoError) {
|
||||||
if (m_content[index.row()] != client) {
|
endResetModel();
|
||||||
m_content[index.row()] = client;
|
return error;
|
||||||
emit dataChanged(index, index);
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientManagementModel::removeRows(int row)
|
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);
|
||||||
|
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);
|
||||||
|
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(configKey::clientId) && obj[configKey::clientId].toString() == clientId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QString &clientName,
|
||||||
|
const DockerContainer container, ServerCredentials credentials)
|
||||||
|
{
|
||||||
|
ErrorCode 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beginResetModel();
|
||||||
|
QJsonObject client;
|
||||||
|
client[configKey::clientId] = clientId;
|
||||||
|
|
||||||
|
QJsonObject userData;
|
||||||
|
userData[configKey::clientName] = clientName;
|
||||||
|
client[configKey::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);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||||
|
}
|
||||||
|
|
||||||
|
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[configKey::userData].toObject();
|
||||||
|
userData[configKey::clientName] = clientName;
|
||||||
|
client[configKey::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);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||||
|
}
|
||||||
|
|
||||||
|
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(configKey::clientId).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) {
|
||||||
|
logger.error() << "Failed to revoke the certificate";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
beginRemoveRows(QModelIndex(), row, row);
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
m_content.removeAt(row);
|
m_clientsTable.removeAt(row);
|
||||||
endRemoveRows();
|
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) {
|
||||||
|
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||||
|
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) {
|
||||||
|
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(configKey::clientId).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) {
|
||||||
|
logger.error() << "Failed to upload the wg conf file to the server";
|
||||||
|
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) {
|
||||||
|
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||||
|
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) {
|
||||||
|
logger.error() << "Failed to execute the command 'wg syncconf' on the server";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> ClientManagementModel::roleNames() const
|
QHash<int, QByteArray> ClientManagementModel::roleNames() const
|
||||||
{
|
{
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
roles[NameRole] = "clientName";
|
roles[ClientNameRole] = "clientName";
|
||||||
roles[OpenVpnCertIdRole] = "openvpnCertId";
|
|
||||||
roles[OpenVpnCertDataRole] = "openvpnCertData";
|
|
||||||
roles[WireGuardPublicKey] = "wireguardPublicKey";
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,36 +2,48 @@
|
||||||
#define CLIENTMANAGEMENTMODEL_H
|
#define CLIENTMANAGEMENTMODEL_H
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
|
#include <QJsonArray>
|
||||||
|
|
||||||
#include "protocols/protocols_defs.h"
|
#include "core/controllers/serverController.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
class ClientManagementModel : public QAbstractListModel
|
class ClientManagementModel : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum ClientRoles {
|
enum Roles {
|
||||||
NameRole = Qt::UserRole + 1,
|
ClientNameRole = Qt::UserRole + 1,
|
||||||
OpenVpnCertIdRole,
|
|
||||||
OpenVpnCertDataRole,
|
|
||||||
WireGuardPublicKey,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ClientManagementModel(QObject *parent = nullptr);
|
ClientManagementModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||||
|
|
||||||
void clearData();
|
|
||||||
void setContent(const QVector<QVariant> &data);
|
|
||||||
QJsonObject getContent(amnezia::Proto protocol);
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) 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:
|
protected:
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<QVariant> 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);
|
||||||
|
|
||||||
|
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<Settings> m_settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CLIENTMANAGEMENTMODEL_H
|
#endif // CLIENTMANAGEMENTMODEL_H
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#include "containers_model.h"
|
#include "containers_model.h"
|
||||||
|
|
||||||
#include "core/servercontroller.h"
|
#include <QJsonArray>
|
||||||
|
|
||||||
ContainersModel::ContainersModel(std::shared_ptr<Settings> settings, QObject *parent)
|
ContainersModel::ContainersModel(QObject *parent)
|
||||||
: m_settings(settings), QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,37 +13,6 @@ int ContainersModel::rowCount(const QModelIndex &parent) const
|
||||||
return ContainerProps::allContainers().size();
|
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
|
QVariant ContainersModel::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) {
|
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);
|
return data(modelIndex, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContainersModel::setCurrentlyProcessedServerIndex(const int index)
|
void ContainersModel::updateModel(QJsonArray &containers)
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_currentlyProcessedServerIndex = index;
|
m_containers.clear();
|
||||||
m_containers = m_settings->containers(m_currentlyProcessedServerIndex);
|
for (const QJsonValue &val : containers) {
|
||||||
m_defaultContainerIndex = m_settings->defaultContainer(m_currentlyProcessedServerIndex);
|
m_containers.insert(ContainerProps::containerFromString(val.toObject().value(config_key::container).toString()),
|
||||||
|
val.toObject());
|
||||||
|
}
|
||||||
endResetModel();
|
endResetModel();
|
||||||
emit defaultContainerChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContainersModel::setCurrentlyProcessedContainerIndex(int index)
|
void ContainersModel::setDefaultContainer(const int containerIndex)
|
||||||
{
|
{
|
||||||
m_currentlyProcessedContainerIndex = index;
|
m_defaultContainerIndex = static_cast<DockerContainer>(containerIndex);
|
||||||
|
emit dataChanged(index(containerIndex, 0), index(containerIndex, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DockerContainer ContainersModel::getDefaultContainer()
|
DockerContainer ContainersModel::getDefaultContainer()
|
||||||
{
|
{
|
||||||
return m_defaultContainerIndex;
|
return m_defaultContainerIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ContainersModel::getDefaultContainerName()
|
void ContainersModel::setCurrentlyProcessedContainerIndex(int index)
|
||||||
{
|
{
|
||||||
return ContainerProps::containerHumanNames().value(m_defaultContainerIndex);
|
m_currentlyProcessedContainerIndex = index;
|
||||||
}
|
|
||||||
|
|
||||||
void ContainersModel::setDefaultContainer(int index)
|
|
||||||
{
|
|
||||||
auto container = static_cast<DockerContainer>(index);
|
|
||||||
m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container);
|
|
||||||
m_defaultContainerIndex = container;
|
|
||||||
emit defaultContainerChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ContainersModel::getCurrentlyProcessedContainerIndex()
|
int ContainersModel::getCurrentlyProcessedContainerIndex()
|
||||||
|
@ -127,91 +91,9 @@ QString ContainersModel::getCurrentlyProcessedContainerName()
|
||||||
return ContainerProps::containerHumanNames().value(static_cast<DockerContainer>(m_currentlyProcessedContainerIndex));
|
return ContainerProps::containerHumanNames().value(static_cast<DockerContainer>(m_currentlyProcessedContainerIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject ContainersModel::getCurrentlyProcessedContainerConfig()
|
QJsonObject ContainersModel::getContainerConfig(const int containerIndex)
|
||||||
{
|
{
|
||||||
return qvariant_cast<QJsonObject>(data(index(m_currentlyProcessedContainerIndex), ConfigRole));
|
return qvariant_cast<QJsonObject>(data(index(containerIndex), 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<DockerContainer>(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<DockerContainer, QJsonObject> containers = m_settings->containers(serverIndex);
|
|
||||||
return containers.contains(DockerContainer::Dns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContainersModel::isAnyContainerInstalled()
|
bool ContainersModel::isAnyContainerInstalled()
|
||||||
|
@ -228,11 +110,6 @@ bool ContainersModel::isAnyContainerInstalled()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContainersModel::updateContainersConfig()
|
|
||||||
{
|
|
||||||
m_containers = m_settings->containers(m_currentlyProcessedServerIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> ContainersModel::roleNames() const
|
QHash<int, QByteArray> ContainersModel::roleNames() const
|
||||||
{
|
{
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
|
|
|
@ -7,13 +7,12 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
class ContainersModel : public QAbstractListModel
|
class ContainersModel : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ContainersModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
ContainersModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
enum Roles {
|
enum Roles {
|
||||||
NameRole = Qt::UserRole + 1,
|
NameRole = Qt::UserRole + 1,
|
||||||
|
@ -37,37 +36,24 @@ public:
|
||||||
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
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 QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
QVariant data(const int index, int role) const;
|
QVariant data(const int index, int role) const;
|
||||||
|
|
||||||
Q_PROPERTY(QString defaultContainerName READ getDefaultContainerName NOTIFY defaultContainerChanged)
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
void updateModel(QJsonArray &containers);
|
||||||
|
|
||||||
DockerContainer getDefaultContainer();
|
DockerContainer getDefaultContainer();
|
||||||
QString getDefaultContainerName();
|
void setDefaultContainer(const int containerIndex);
|
||||||
void setDefaultContainer(int index);
|
|
||||||
|
|
||||||
void setCurrentlyProcessedServerIndex(const int index);
|
void setCurrentlyProcessedContainerIndex(int containerIndex);
|
||||||
|
|
||||||
void setCurrentlyProcessedContainerIndex(int index);
|
|
||||||
int getCurrentlyProcessedContainerIndex();
|
int getCurrentlyProcessedContainerIndex();
|
||||||
|
|
||||||
QString getCurrentlyProcessedContainerName();
|
QString getCurrentlyProcessedContainerName();
|
||||||
QJsonObject getCurrentlyProcessedContainerConfig();
|
|
||||||
QStringList getAllInstalledServicesName(const int serverIndex);
|
|
||||||
|
|
||||||
ErrorCode removeAllContainers();
|
QJsonObject getContainerConfig(const int containerIndex);
|
||||||
ErrorCode removeCurrentlyProcessedContainer();
|
|
||||||
void clearCachedProfiles();
|
|
||||||
|
|
||||||
bool isAmneziaDnsContainerInstalled();
|
|
||||||
bool isAmneziaDnsContainerInstalled(const int serverIndex);
|
|
||||||
|
|
||||||
bool isAnyContainerInstalled();
|
bool isAnyContainerInstalled();
|
||||||
|
|
||||||
void updateContainersConfig();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
@ -78,11 +64,8 @@ signals:
|
||||||
private:
|
private:
|
||||||
QMap<DockerContainer, QJsonObject> m_containers;
|
QMap<DockerContainer, QJsonObject> m_containers;
|
||||||
|
|
||||||
int m_currentlyProcessedServerIndex;
|
|
||||||
int m_currentlyProcessedContainerIndex;
|
int m_currentlyProcessedContainerIndex;
|
||||||
DockerContainer m_defaultContainerIndex;
|
DockerContainer m_defaultContainerIndex;
|
||||||
|
|
||||||
std::shared_ptr<Settings> m_settings;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONTAINERS_MODEL_H
|
#endif // CONTAINERS_MODEL_H
|
||||||
|
|
|
@ -44,6 +44,7 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan
|
||||||
case LanguageSettings::AvailableLanguageEnum::English: strLanguage = "English"; break;
|
case LanguageSettings::AvailableLanguageEnum::English: strLanguage = "English"; break;
|
||||||
case LanguageSettings::AvailableLanguageEnum::Russian: strLanguage = "Русский"; 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::China_cn: strLanguage = "\347\256\200\344\275\223\344\270\255\346\226\207"; break;
|
||||||
|
case LanguageSettings::AvailableLanguageEnum::Persian: strLanguage = "فارسی"; break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +58,7 @@ void LanguageModel::changeLanguage(const LanguageSettings::AvailableLanguageEnum
|
||||||
case LanguageSettings::AvailableLanguageEnum::English: emit updateTranslations(QLocale::English); break;
|
case LanguageSettings::AvailableLanguageEnum::English: emit updateTranslations(QLocale::English); break;
|
||||||
case LanguageSettings::AvailableLanguageEnum::Russian: emit updateTranslations(QLocale::Russian); break;
|
case LanguageSettings::AvailableLanguageEnum::Russian: emit updateTranslations(QLocale::Russian); break;
|
||||||
case LanguageSettings::AvailableLanguageEnum::China_cn: emit updateTranslations(QLocale::Chinese); 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;
|
default: emit updateTranslations(QLocale::English); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +70,7 @@ int LanguageModel::getCurrentLanguageIndex()
|
||||||
case QLocale::English: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
case QLocale::English: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
||||||
case QLocale::Russian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Russian); break;
|
case QLocale::Russian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Russian); break;
|
||||||
case QLocale::Chinese: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::China_cn); break;
|
case QLocale::Chinese: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::China_cn); break;
|
||||||
|
case QLocale::Persian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Persian); break;
|
||||||
default: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
default: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ namespace LanguageSettings
|
||||||
enum class AvailableLanguageEnum {
|
enum class AvailableLanguageEnum {
|
||||||
English,
|
English,
|
||||||
Russian,
|
Russian,
|
||||||
China_cn
|
China_cn,
|
||||||
|
Persian
|
||||||
};
|
};
|
||||||
Q_ENUM_NS(AvailableLanguageEnum)
|
Q_ENUM_NS(AvailableLanguageEnum)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "servers_model.h"
|
#include "servers_model.h"
|
||||||
|
|
||||||
|
#include "core/controllers/serverController.h"
|
||||||
|
|
||||||
ServersModel::ServersModel(std::shared_ptr<Settings> settings, QObject *parent)
|
ServersModel::ServersModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||||
: m_settings(settings), QAbstractListModel(parent)
|
: m_settings(settings), QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
|
@ -8,6 +10,11 @@ ServersModel::ServersModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||||
m_currentlyProcessedServerIndex = m_defaultServerIndex;
|
m_currentlyProcessedServerIndex = m_defaultServerIndex;
|
||||||
|
|
||||||
connect(this, &ServersModel::defaultServerIndexChanged, this, &ServersModel::defaultServerNameChanged);
|
connect(this, &ServersModel::defaultServerIndexChanged, this, &ServersModel::defaultServerNameChanged);
|
||||||
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int ServersModel::rowCount(const QModelIndex &parent) const
|
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 QJsonObject server = m_servers.at(index.row()).toObject();
|
||||||
|
const auto configVersion = server.value(config_key::configVersion).toInt();
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case NameRole: {
|
case NameRole: {
|
||||||
auto description = server.value(config_key::description).toString();
|
if (configVersion) {
|
||||||
if (description.isEmpty()) {
|
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 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 HostNameRole: return server.value(config_key::hostName).toString();
|
||||||
case CredentialsRole: return QVariant::fromValue(serverCredentials(index.row()));
|
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();
|
QString primaryDns = server.value(config_key::dns1).toString();
|
||||||
return primaryDns == protocols::dns::amneziaDnsIp;
|
return primaryDns == protocols::dns::amneziaDnsIp;
|
||||||
}
|
}
|
||||||
|
case DefaultContainerRole: {
|
||||||
|
return ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
@ -114,6 +133,53 @@ const QString ServersModel::getDefaultServerHostName()
|
||||||
return qvariant_cast<QString>(data(m_defaultServerIndex, HostNameRole));
|
return qvariant_cast<QString>(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()
|
const int ServersModel::getServersCount()
|
||||||
{
|
{
|
||||||
return m_servers.count();
|
return m_servers.count();
|
||||||
|
@ -132,6 +198,7 @@ bool ServersModel::hasServerWithWriteAccess()
|
||||||
void ServersModel::setCurrentlyProcessedServerIndex(const int index)
|
void ServersModel::setCurrentlyProcessedServerIndex(const int index)
|
||||||
{
|
{
|
||||||
m_currentlyProcessedServerIndex = index;
|
m_currentlyProcessedServerIndex = index;
|
||||||
|
updateContainersModel();
|
||||||
emit currentlyProcessedServerIndexChanged(m_currentlyProcessedServerIndex);
|
emit currentlyProcessedServerIndexChanged(m_currentlyProcessedServerIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +212,16 @@ QString ServersModel::getCurrentlyProcessedServerHostName()
|
||||||
return qvariant_cast<QString>(data(m_currentlyProcessedServerIndex, HostNameRole));
|
return qvariant_cast<QString>(data(m_currentlyProcessedServerIndex, HostNameRole));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ServerCredentials ServersModel::getCurrentlyProcessedServerCredentials()
|
||||||
|
{
|
||||||
|
return serverCredentials(m_currentlyProcessedServerIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ServerCredentials ServersModel::getServerCredentials(const int index)
|
||||||
|
{
|
||||||
|
return serverCredentials(index);
|
||||||
|
}
|
||||||
|
|
||||||
bool ServersModel::isDefaultServerCurrentlyProcessed()
|
bool ServersModel::isDefaultServerCurrentlyProcessed()
|
||||||
{
|
{
|
||||||
return m_defaultServerIndex == m_currentlyProcessedServerIndex;
|
return m_defaultServerIndex == m_currentlyProcessedServerIndex;
|
||||||
|
@ -168,6 +245,15 @@ void ServersModel::addServer(const QJsonObject &server)
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServersModel::editServer(const QJsonObject &server)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_settings->editServer(m_currentlyProcessedServerIndex, server);
|
||||||
|
m_servers = m_settings->serversArray();
|
||||||
|
endResetModel();
|
||||||
|
updateContainersModel();
|
||||||
|
}
|
||||||
|
|
||||||
void ServersModel::removeServer()
|
void ServersModel::removeServer()
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
@ -193,23 +279,27 @@ bool ServersModel::isDefaultServerConfigContainsAmneziaDns()
|
||||||
return primaryDns == protocols::dns::amneziaDnsIp;
|
return primaryDns == protocols::dns::amneziaDnsIp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServersModel::updateContainersConfig()
|
|
||||||
{
|
|
||||||
auto server = m_settings->server(m_currentlyProcessedServerIndex);
|
|
||||||
m_servers.replace(m_currentlyProcessedServerIndex, server);
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> ServersModel::roleNames() const
|
QHash<int, QByteArray> ServersModel::roleNames() const
|
||||||
{
|
{
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
|
|
||||||
|
roles[NameRole] = "serverName";
|
||||||
roles[NameRole] = "name";
|
roles[NameRole] = "name";
|
||||||
|
roles[ServerDescriptionRole] = "serverDescription";
|
||||||
|
|
||||||
roles[HostNameRole] = "hostName";
|
roles[HostNameRole] = "hostName";
|
||||||
|
|
||||||
roles[CredentialsRole] = "credentials";
|
roles[CredentialsRole] = "credentials";
|
||||||
roles[CredentialsLoginRole] = "credentialsLogin";
|
roles[CredentialsLoginRole] = "credentialsLogin";
|
||||||
|
|
||||||
roles[IsDefaultRole] = "isDefault";
|
roles[IsDefaultRole] = "isDefault";
|
||||||
roles[IsCurrentlyProcessedRole] = "isCurrentlyProcessed";
|
roles[IsCurrentlyProcessedRole] = "isCurrentlyProcessed";
|
||||||
|
|
||||||
roles[HasWriteAccessRole] = "hasWriteAccess";
|
roles[HasWriteAccessRole] = "hasWriteAccess";
|
||||||
|
|
||||||
roles[ContainsAmneziaDnsRole] = "containsAmneziaDns";
|
roles[ContainsAmneziaDnsRole] = "containsAmneziaDns";
|
||||||
|
|
||||||
|
roles[DefaultContainerRole] = "defaultContainer";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,3 +315,206 @@ ServerCredentials ServersModel::serverCredentials(int index) const
|
||||||
|
|
||||||
return credentials;
|
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<DockerContainer>(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<DockerContainer>(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<DockerContainer>(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<DockerContainer>(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<DockerContainer>(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,21 @@ class ServersModel : public QAbstractListModel
|
||||||
public:
|
public:
|
||||||
enum Roles {
|
enum Roles {
|
||||||
NameRole = Qt::UserRole + 1,
|
NameRole = Qt::UserRole + 1,
|
||||||
|
ServerDescriptionRole,
|
||||||
|
|
||||||
HostNameRole,
|
HostNameRole,
|
||||||
|
|
||||||
CredentialsRole,
|
CredentialsRole,
|
||||||
CredentialsLoginRole,
|
CredentialsLoginRole,
|
||||||
|
|
||||||
IsDefaultRole,
|
IsDefaultRole,
|
||||||
IsCurrentlyProcessedRole,
|
IsCurrentlyProcessedRole,
|
||||||
|
|
||||||
HasWriteAccessRole,
|
HasWriteAccessRole,
|
||||||
ContainsAmneziaDnsRole
|
|
||||||
|
ContainsAmneziaDnsRole,
|
||||||
|
|
||||||
|
DefaultContainerRole
|
||||||
};
|
};
|
||||||
|
|
||||||
ServersModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
ServersModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||||
|
@ -33,6 +41,10 @@ public:
|
||||||
Q_PROPERTY(int defaultIndex READ getDefaultServerIndex WRITE setDefaultServerIndex NOTIFY defaultServerIndexChanged)
|
Q_PROPERTY(int defaultIndex READ getDefaultServerIndex WRITE setDefaultServerIndex NOTIFY defaultServerIndexChanged)
|
||||||
Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerNameChanged)
|
Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerNameChanged)
|
||||||
Q_PROPERTY(QString defaultServerHostName READ getDefaultServerHostName NOTIFY defaultServerIndexChanged)
|
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
|
Q_PROPERTY(int currentlyProcessedIndex READ getCurrentlyProcessedServerIndex WRITE setCurrentlyProcessedServerIndex
|
||||||
NOTIFY currentlyProcessedServerIndexChanged)
|
NOTIFY currentlyProcessedServerIndexChanged)
|
||||||
|
|
||||||
|
@ -41,6 +53,8 @@ public slots:
|
||||||
const int getDefaultServerIndex();
|
const int getDefaultServerIndex();
|
||||||
const QString getDefaultServerName();
|
const QString getDefaultServerName();
|
||||||
const QString getDefaultServerHostName();
|
const QString getDefaultServerHostName();
|
||||||
|
const QString getDefaultServerDescriptionCollapsed();
|
||||||
|
const QString getDefaultServerDescriptionExpanded();
|
||||||
bool isDefaultServerCurrentlyProcessed();
|
bool isDefaultServerCurrentlyProcessed();
|
||||||
|
|
||||||
bool isCurrentlyProcessedServerHasWriteAccess();
|
bool isCurrentlyProcessedServerHasWriteAccess();
|
||||||
|
@ -53,13 +67,34 @@ public slots:
|
||||||
int getCurrentlyProcessedServerIndex();
|
int getCurrentlyProcessedServerIndex();
|
||||||
|
|
||||||
QString getCurrentlyProcessedServerHostName();
|
QString getCurrentlyProcessedServerHostName();
|
||||||
|
const ServerCredentials getCurrentlyProcessedServerCredentials();
|
||||||
|
const ServerCredentials getServerCredentials(const int index);
|
||||||
|
|
||||||
void addServer(const QJsonObject &server);
|
void addServer(const QJsonObject &server);
|
||||||
|
void editServer(const QJsonObject &server);
|
||||||
void removeServer();
|
void removeServer();
|
||||||
|
|
||||||
bool isDefaultServerConfigContainsAmneziaDns();
|
bool isDefaultServerConfigContainsAmneziaDns();
|
||||||
|
bool isAmneziaDnsContainerInstalled(const int serverIndex);
|
||||||
|
|
||||||
void updateContainersConfig();
|
QJsonObject getDefaultServerConfig();
|
||||||
|
|
||||||
|
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:
|
protected:
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
@ -68,9 +103,16 @@ signals:
|
||||||
void currentlyProcessedServerIndexChanged(const int index);
|
void currentlyProcessedServerIndexChanged(const int index);
|
||||||
void defaultServerIndexChanged(const int index);
|
void defaultServerIndexChanged(const int index);
|
||||||
void defaultServerNameChanged();
|
void defaultServerNameChanged();
|
||||||
|
void defaultServerDescriptionChanged();
|
||||||
|
|
||||||
|
void containersUpdated(QJsonArray &containers);
|
||||||
|
void defaultContainerChanged(const int containerIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ServerCredentials serverCredentials(int index) const;
|
ServerCredentials serverCredentials(int index) const;
|
||||||
|
void updateContainersModel();
|
||||||
|
|
||||||
|
QString getDefaultServerDescription(const QJsonObject &server);
|
||||||
|
|
||||||
QJsonArray m_servers;
|
QJsonArray m_servers;
|
||||||
|
|
||||||
|
@ -78,6 +120,8 @@ private:
|
||||||
|
|
||||||
int m_defaultServerIndex;
|
int m_defaultServerIndex;
|
||||||
int m_currentlyProcessedServerIndex;
|
int m_currentlyProcessedServerIndex;
|
||||||
|
|
||||||
|
bool m_isAmneziaDnsEnabled = m_settings->useAmneziaDns();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SERVERSMODEL_H
|
#endif // SERVERSMODEL_H
|
||||||
|
|
|
@ -138,6 +138,10 @@ Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
if (!ApiController.updateServerConfigFromApi()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!ContainersModel.isAnyContainerInstalled()) {
|
if (!ContainersModel.isAnyContainerInstalled()) {
|
||||||
PageController.setTriggeredBtConnectButton(true)
|
PageController.setTriggeredBtConnectButton(true)
|
||||||
|
|
||||||
|
|
|
@ -60,9 +60,8 @@ ListView {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checked) {
|
if (checked) {
|
||||||
isDefault = true
|
ServersModel.setDefaultContainer(proxyContainersModel.mapToSource(index))
|
||||||
|
|
||||||
menuContent.currentIndex = index
|
|
||||||
containersDropDown.menuVisible = false
|
containersDropDown.menuVisible = false
|
||||||
} else {
|
} else {
|
||||||
if (!isSupported && isInstalled) {
|
if (!isSupported && isInstalled) {
|
||||||
|
|
|
@ -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 {
|
BasicButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
|
@ -170,6 +194,12 @@ DrawerType {
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
|
id: nativeConfigString
|
||||||
|
visible: false
|
||||||
|
text: ExportController.nativeConfigString
|
||||||
|
}
|
||||||
|
|
||||||
|
TextArea {
|
||||||
id: configText
|
id: configText
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -213,7 +243,6 @@ DrawerType {
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 2
|
|
||||||
smooth: false
|
smooth: false
|
||||||
|
|
||||||
source: ExportController.qrCodesCount ? ExportController.qrCodes[0] : ""
|
source: ExportController.qrCodesCount ? ExportController.qrCodes[0] : ""
|
||||||
|
|
|
@ -87,6 +87,7 @@ Switch {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
ListItemTitleType {
|
ListItemTitleType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
@ -22,10 +22,6 @@ PageType {
|
||||||
|
|
||||||
property string borderColor: "#2C2D30"
|
property string borderColor: "#2C2D30"
|
||||||
|
|
||||||
property string defaultServerName: ServersModel.defaultServerName
|
|
||||||
property string defaultServerHostName: ServersModel.defaultServerHostName
|
|
||||||
property string defaultContainerName: ContainersModel.defaultContainerName
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: PageController
|
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 {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: buttonContent.state === "expanded"
|
enabled: buttonContent.state === "expanded"
|
||||||
|
@ -267,7 +228,7 @@ PageType {
|
||||||
maximumLineCount: 2
|
maximumLineCount: 2
|
||||||
elide: Qt.ElideRight
|
elide: Qt.ElideRight
|
||||||
|
|
||||||
text: root.defaultServerName
|
text: ServersModel.defaultServerName
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
|
@ -304,6 +265,7 @@ PageType {
|
||||||
Layout.bottomMargin: 44
|
Layout.bottomMargin: 44
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
visible: buttonContent.collapsedVisibility
|
visible: buttonContent.collapsedVisibility
|
||||||
|
text: ServersModel.defaultServerDescriptionCollapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
@ -319,7 +281,7 @@ PageType {
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: root.defaultServerName
|
text: ServersModel.defaultServerName
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
maximumLineCount: 2
|
maximumLineCount: 2
|
||||||
elide: Qt.ElideRight
|
elide: Qt.ElideRight
|
||||||
|
@ -331,6 +293,7 @@ PageType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
verticalAlignment: Qt.AlignVCenter
|
verticalAlignment: Qt.AlignVCenter
|
||||||
|
text: ServersModel.defaultServerDescriptionExpanded
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
@ -349,7 +312,7 @@ PageType {
|
||||||
rootButtonTextTopMargin: 8
|
rootButtonTextTopMargin: 8
|
||||||
rootButtonTextBottomMargin: 8
|
rootButtonTextBottomMargin: 8
|
||||||
|
|
||||||
text: root.defaultContainerName
|
text: ServersModel.defaultContainerName
|
||||||
textColor: "#0E0E11"
|
textColor: "#0E0E11"
|
||||||
headerText: qsTr("VPN protocol")
|
headerText: qsTr("VPN protocol")
|
||||||
headerBackButtonImage: "qrc:/images/controls/arrow-left.svg"
|
headerBackButtonImage: "qrc:/images/controls/arrow-left.svg"
|
||||||
|
@ -468,7 +431,7 @@ PageType {
|
||||||
var description = ""
|
var description = ""
|
||||||
if (hasWriteAccess) {
|
if (hasWriteAccess) {
|
||||||
if (SettingsController.isAmneziaDnsEnabled()
|
if (SettingsController.isAmneziaDnsEnabled()
|
||||||
&& ContainersModel.isAmneziaDnsContainerInstalled(index)) {
|
&& ServersModel.isAmneziaDnsContainerInstalled(index)) {
|
||||||
description += "Amnezia DNS | "
|
description += "Amnezia DNS | "
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -312,9 +312,8 @@ PageType {
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
forceActiveFocus()
|
forceActiveFocus()
|
||||||
PageController.showBusyIndicator(true)
|
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||||
InstallController.updateContainer(AwgConfigModel.getConfig())
|
InstallController.updateContainer(AwgConfigModel.getConfig())
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import QtQuick.Layouts
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
|
||||||
import "./"
|
import "./"
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
|
@ -160,9 +162,8 @@ PageType {
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
forceActiveFocus()
|
forceActiveFocus()
|
||||||
PageController.showBusyIndicator(true)
|
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||||
InstallController.updateContainer(CloakConfigModel.getConfig())
|
InstallController.updateContainer(CloakConfigModel.getConfig())
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -390,9 +390,8 @@ PageType {
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
forceActiveFocus()
|
forceActiveFocus()
|
||||||
PageController.showBusyIndicator(true)
|
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||||
InstallController.updateContainer(OpenVpnConfigModel.getConfig())
|
InstallController.updateContainer(OpenVpnConfigModel.getConfig())
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import QtQuick.Layouts
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
|
||||||
import "./"
|
import "./"
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
|
@ -138,9 +140,8 @@ PageType {
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
forceActiveFocus()
|
forceActiveFocus()
|
||||||
PageController.showBusyIndicator(true)
|
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||||
InstallController.updateContainer(ShadowSocksConfigModel.getConfig())
|
InstallController.updateContainer(ShadowSocksConfigModel.getConfig())
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,8 +66,8 @@ PageType {
|
||||||
|
|
||||||
text: qsTr("Website address")
|
text: qsTr("Website address")
|
||||||
descriptionText: {
|
descriptionText: {
|
||||||
var config = ContainersModel.getCurrentlyProcessedContainerConfig()
|
|
||||||
var containerIndex = ContainersModel.getCurrentlyProcessedContainerIndex()
|
var containerIndex = ContainersModel.getCurrentlyProcessedContainerIndex()
|
||||||
|
var config = ContainersModel.getContainerConfig(containerIndex)
|
||||||
return config[ContainerProps.containerTypeToString(containerIndex)]["site"]
|
return config[ContainerProps.containerTypeToString(containerIndex)]["site"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ PageType {
|
||||||
text: name
|
text: name
|
||||||
descriptionText: {
|
descriptionText: {
|
||||||
var servicesNameString = ""
|
var servicesNameString = ""
|
||||||
var servicesName = ContainersModel.getAllInstalledServicesName(index)
|
var servicesName = ServersModel.getAllInstalledServicesName(index)
|
||||||
for (var i = 0; i < servicesName.length; i++) {
|
for (var i = 0; i < servicesName.length; i++) {
|
||||||
servicesNameString += servicesName[i] + " · "
|
servicesNameString += servicesName[i] + " · "
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ PageType {
|
||||||
regularExpression: InstallController.ipAddressPortRegExp()
|
regularExpression: InstallController.ipAddressPortRegExp()
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextFieldTextChanged: {
|
onFocusChanged: {
|
||||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '');
|
textField.text = textField.text.replace(/^\s+|\s+$/g, '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,10 @@ PageType {
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
hidePassword = !hidePassword
|
hidePassword = !hidePassword
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFocusChanged: {
|
||||||
|
textField.text = textField.text.replace(/^\s+|\s+$/g, '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
|
@ -90,6 +94,7 @@ PageType {
|
||||||
text: qsTr("Continue")
|
text: qsTr("Continue")
|
||||||
|
|
||||||
onClicked: function() {
|
onClicked: function() {
|
||||||
|
forceActiveFocus()
|
||||||
if (!isCredentialsFilled()) {
|
if (!isCredentialsFilled()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -112,8 +117,7 @@ PageType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 12
|
Layout.topMargin: 12
|
||||||
|
|
||||||
text: qsTr("All data you enter will remain strictly confidential
|
text: qsTr("All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties")
|
||||||
and will not be shared or disclosed to the Amnezia or any third parties")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,14 @@ PageType {
|
||||||
|
|
||||||
property bool isTimerRunning: true
|
property bool isTimerRunning: true
|
||||||
property string progressBarText: qsTr("Usually it takes no more than 5 minutes")
|
property string progressBarText: qsTr("Usually it takes no more than 5 minutes")
|
||||||
|
property bool isCancelButtonVisible: false
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: InstallController
|
target: InstallController
|
||||||
|
|
||||||
function onInstallContainerFinished(finishedMessage, isServiceInstall) {
|
function onInstallContainerFinished(finishedMessage, isServiceInstall) {
|
||||||
if (!ConnectionController.isConnected && !isServiceInstall) {
|
if (!ConnectionController.isConnected && !isServiceInstall) {
|
||||||
ContainersModel.setDefaultContainer(ContainersModel.getCurrentlyProcessedContainerIndex())
|
ServersModel.setDefaultContainer(ContainersModel.getCurrentlyProcessedContainerIndex())
|
||||||
}
|
}
|
||||||
|
|
||||||
PageController.closePage() // close installing page
|
PageController.closePage() // close installing page
|
||||||
|
@ -61,11 +62,13 @@ PageType {
|
||||||
|
|
||||||
function onServerIsBusy(isBusy) {
|
function onServerIsBusy(isBusy) {
|
||||||
if (isBusy) {
|
if (isBusy) {
|
||||||
|
root.isCancelButtonVisible = true
|
||||||
root.progressBarText = qsTr("Amnezia has detected that your server is currently ") +
|
root.progressBarText = qsTr("Amnezia has detected that your server is currently ") +
|
||||||
qsTr("busy installing other software. Amnezia installation ") +
|
qsTr("busy installing other software. Amnezia installation ") +
|
||||||
qsTr("will pause until the server finishes installing other software")
|
qsTr("will pause until the server finishes installing other software")
|
||||||
root.isTimerRunning = false
|
root.isTimerRunning = false
|
||||||
} else {
|
} else {
|
||||||
|
root.isCancelButtonVisible = false
|
||||||
root.progressBarText = qsTr("Usually it takes no more than 5 minutes")
|
root.progressBarText = qsTr("Usually it takes no more than 5 minutes")
|
||||||
root.isTimerRunning = true
|
root.isTimerRunning = true
|
||||||
}
|
}
|
||||||
|
@ -150,6 +153,22 @@ PageType {
|
||||||
|
|
||||||
text: root.progressBarText
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ PageType {
|
||||||
target: InstallController
|
target: InstallController
|
||||||
|
|
||||||
function onInstallationErrorOccurred(errorMessage) {
|
function onInstallationErrorOccurred(errorMessage) {
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
PageController.showErrorMessage(errorMessage)
|
PageController.showErrorMessage(errorMessage)
|
||||||
|
|
||||||
var currentPageName = stackView.currentItem.objectName
|
var currentPageName = stackView.currentItem.objectName
|
||||||
|
|
|
@ -24,7 +24,7 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onImportFinished() {
|
function onImportFinished() {
|
||||||
if (ConnectionController.isConnected) {
|
if (!ConnectionController.isConnected) {
|
||||||
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
|
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,15 +18,29 @@ PageType {
|
||||||
|
|
||||||
enum ConfigType {
|
enum ConfigType {
|
||||||
AmneziaConnection,
|
AmneziaConnection,
|
||||||
AmneziaFullAccess,
|
|
||||||
OpenVpn,
|
OpenVpn,
|
||||||
WireGuard
|
WireGuard,
|
||||||
|
ShadowSocks,
|
||||||
|
Cloak
|
||||||
|
}
|
||||||
|
|
||||||
|
signal revokeConfig(int index)
|
||||||
|
onRevokeConfig: function(index) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
ExportController.revokeConfig(index,
|
||||||
|
ContainersModel.getCurrentlyProcessedContainerIndex(),
|
||||||
|
ServersModel.getCurrentlyProcessedServerCredentials())
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
PageController.showNotificationMessage(qsTr("Config revoked"))
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ExportController
|
target: ExportController
|
||||||
|
|
||||||
function onGenerateConfig(type) {
|
function onGenerateConfig(type) {
|
||||||
|
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
|
||||||
|
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
|
||||||
|
|
||||||
shareConnectionDrawer.needCloseButton = false
|
shareConnectionDrawer.needCloseButton = false
|
||||||
|
|
||||||
shareConnectionDrawer.open()
|
shareConnectionDrawer.open()
|
||||||
|
@ -34,28 +48,34 @@ PageType {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(); break;
|
case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(clientNameTextField.textFieldText); break;
|
||||||
case PageShare.ConfigType.AmneziaFullAccess: {
|
|
||||||
if (Qt.platform.os === "android") {
|
|
||||||
ExportController.generateFullAccessConfigAndroid();
|
|
||||||
} else {
|
|
||||||
ExportController.generateFullAccessConfig();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PageShare.ConfigType.OpenVpn: {
|
case PageShare.ConfigType.OpenVpn: {
|
||||||
ExportController.generateOpenVpnConfig();
|
ExportController.generateOpenVpnConfig(clientNameTextField.textFieldText)
|
||||||
shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config")
|
shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config")
|
||||||
shareConnectionDrawer.configExtension = ".ovpn"
|
shareConnectionDrawer.configExtension = ".ovpn"
|
||||||
shareConnectionDrawer.configFileName = "amnezia_for_openvpn"
|
shareConnectionDrawer.configFileName = "amnezia_for_openvpn"
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
case PageShare.ConfigType.WireGuard: {
|
case PageShare.ConfigType.WireGuard: {
|
||||||
ExportController.generateWireGuardConfig();
|
ExportController.generateWireGuardConfig(clientNameTextField.textFieldText)
|
||||||
shareConnectionDrawer.configCaption = qsTr("Save WireGuard config")
|
shareConnectionDrawer.configCaption = qsTr("Save WireGuard config")
|
||||||
shareConnectionDrawer.configExtension = ".conf"
|
shareConnectionDrawer.configExtension = ".conf"
|
||||||
shareConnectionDrawer.configFileName = "amnezia_for_wireguard"
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +93,7 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property string fullConfigServerSelectorText
|
property bool isSearchBarVisible: false
|
||||||
property string connectionServerSelectorText
|
|
||||||
property bool showContent: false
|
property bool showContent: false
|
||||||
property bool shareButtonEnabled: true
|
property bool shareButtonEnabled: true
|
||||||
property list<QtObject> connectionTypesModel: [
|
property list<QtObject> connectionTypesModel: [
|
||||||
|
@ -96,6 +115,16 @@ PageType {
|
||||||
property string name: qsTr("WireGuard native format")
|
property string name: qsTr("WireGuard native format")
|
||||||
property var type: PageShare.ConfigType.WireGuard
|
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 {
|
FlickableType {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
@ -119,6 +148,51 @@ PageType {
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
|
|
||||||
headerText: qsTr("Share VPN Access")
|
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 {
|
Rectangle {
|
||||||
|
@ -147,20 +221,21 @@ PageType {
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
accessTypeSelector.currentIndex = 0
|
accessTypeSelector.currentIndex = 0
|
||||||
serverSelector.text = root.connectionServerSelectorText
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HorizontalRadioButton {
|
HorizontalRadioButton {
|
||||||
checked: root.currentIndex === 1
|
checked: accessTypeSelector.currentIndex === 1
|
||||||
|
|
||||||
implicitWidth: (root.width - 32) / 2
|
implicitWidth: (root.width - 32) / 2
|
||||||
text: qsTr("Full access")
|
text: qsTr("Users")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
accessTypeSelector.currentIndex = 1
|
accessTypeSelector.currentIndex = 1
|
||||||
serverSelector.text = root.fullConfigServerSelectorText
|
PageController.showBusyIndicator(true)
|
||||||
root.shareButtonEnabled = true
|
ExportController.updateClientManagementModel(ContainersModel.getCurrentlyProcessedContainerIndex(),
|
||||||
|
ServersModel.getCurrentlyProcessedServerCredentials())
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,16 +246,30 @@ PageType {
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
Layout.bottomMargin: 24
|
Layout.bottomMargin: 24
|
||||||
|
|
||||||
text: accessTypeSelector.currentIndex === 0 ? qsTr("Share VPN access without the ability to manage the server") :
|
visible: accessTypeSelector.currentIndex === 0
|
||||||
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.")
|
|
||||||
|
text: qsTr("Share VPN access without the ability to manage the server")
|
||||||
color: "#878B91"
|
color: "#878B91"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextFieldWithHeaderType {
|
||||||
|
id: clientNameTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 16
|
||||||
|
|
||||||
|
visible: accessTypeSelector.currentIndex === 0
|
||||||
|
|
||||||
|
headerText: qsTr("User name")
|
||||||
|
textFieldText: "New client"
|
||||||
|
|
||||||
|
checkEmptyText: true
|
||||||
|
}
|
||||||
|
|
||||||
DropDownType {
|
DropDownType {
|
||||||
id: serverSelector
|
id: serverSelector
|
||||||
|
|
||||||
signal severSelectorIndexChanged
|
signal severSelectorIndexChanged
|
||||||
property int currentIndex: 0
|
property int currentIndex: -1
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
@ -207,8 +296,6 @@ PageType {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
currentIndex: 0
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
handler()
|
handler()
|
||||||
|
|
||||||
|
@ -217,22 +304,17 @@ PageType {
|
||||||
serverSelector.severSelectorIndexChanged()
|
serverSelector.severSelectorIndexChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessTypeSelector.currentIndex !== 0) {
|
|
||||||
shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text
|
|
||||||
shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
|
|
||||||
}
|
|
||||||
serverSelector.menuVisible = false
|
serverSelector.menuVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
handler()
|
serverSelectorListView.currentIndex = ServersModel.isDefaultServerHasWriteAccess() ?
|
||||||
serverSelector.severSelectorIndexChanged()
|
proxyServersModel.mapFromSource(ServersModel.defaultIndex) : 0
|
||||||
|
serverSelectorListView.triggerCurrentItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handler() {
|
function handler() {
|
||||||
serverSelector.text = selectedText
|
serverSelector.text = selectedText
|
||||||
root.fullConfigServerSelectorText = selectedText
|
|
||||||
root.connectionServerSelectorText = selectedText
|
|
||||||
ServersModel.currentlyProcessedIndex = proxyServersModel.mapToSource(currentIndex)
|
ServersModel.currentlyProcessedIndex = proxyServersModel.mapToSource(currentIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,8 +323,6 @@ PageType {
|
||||||
DropDownType {
|
DropDownType {
|
||||||
id: protocolSelector
|
id: protocolSelector
|
||||||
|
|
||||||
visible: accessTypeSelector.currentIndex === 0
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
|
||||||
|
@ -280,17 +360,11 @@ PageType {
|
||||||
protocolSelector.menuVisible = false
|
protocolSelector.menuVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (accessTypeSelector.currentIndex === 0) {
|
|
||||||
handler()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: serverSelector
|
target: serverSelector
|
||||||
|
|
||||||
function onSeverSelectorIndexChanged() {
|
function onSeverSelectorIndexChanged() {
|
||||||
protocolSelectorListView.currentIndex = 0
|
protocolSelectorListView.currentIndex = proxyContainersModel.mapFromSource(ServersModel.getDefaultContainer())
|
||||||
protocolSelectorListView.triggerCurrentItem()
|
protocolSelectorListView.triggerCurrentItem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,13 +378,17 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
protocolSelector.text = selectedText
|
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))
|
ContainersModel.setCurrentlyProcessedContainerIndex(proxyContainersModel.mapToSource(currentIndex))
|
||||||
|
|
||||||
fillConnectionTypeModel()
|
fillConnectionTypeModel()
|
||||||
|
|
||||||
|
if (accessTypeSelector.currentIndex === 1) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
ExportController.updateClientManagementModel(ContainersModel.getCurrentlyProcessedContainerIndex(),
|
||||||
|
ServersModel.getCurrentlyProcessedServerCredentials())
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillConnectionTypeModel() {
|
function fillConnectionTypeModel() {
|
||||||
|
@ -322,6 +400,13 @@ PageType {
|
||||||
root.connectionTypesModel.push(openVpnConnectionFormat)
|
root.connectionTypesModel.push(openVpnConnectionFormat)
|
||||||
} else if (index === ContainerProps.containerFromString("amnezia-wireguard")) {
|
} else if (index === ContainerProps.containerFromString("amnezia-wireguard")) {
|
||||||
root.connectionTypesModel.push(wireGuardConnectionFormat)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -378,18 +463,235 @@ PageType {
|
||||||
Layout.topMargin: 40
|
Layout.topMargin: 40
|
||||||
|
|
||||||
enabled: shareButtonEnabled
|
enabled: shareButtonEnabled
|
||||||
|
visible: accessTypeSelector.currentIndex === 0
|
||||||
|
|
||||||
text: qsTr("Share")
|
text: qsTr("Share")
|
||||||
imageSource: "qrc:/images/controls/share-2.svg"
|
imageSource: "qrc:/images/controls/share-2.svg"
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (accessTypeSelector.currentIndex === 0) {
|
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)
|
||||||
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)
|
}
|
||||||
} else {
|
}
|
||||||
ExportController.generateConfig(PageShare.ConfigType.AmneziaFullAccess)
|
|
||||||
|
Header2Type {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 24
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
|
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 {
|
||||||
|
id: clientsListView
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: childrenRect.height
|
||||||
|
|
||||||
|
visible: accessTypeSelector.currentIndex === 1
|
||||||
|
|
||||||
|
model: SortFilterProxyModel {
|
||||||
|
id: proxyClientManagementModel
|
||||||
|
sourceModel: ClientManagementModel
|
||||||
|
filters: RegExpFilter {
|
||||||
|
roleName: "clientName"
|
||||||
|
pattern: ".*" + searchTextField.textFieldText + ".*"
|
||||||
|
caseSensitivity: Qt.CaseInsensitive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
interactive: false
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
implicitWidth: clientsListView.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: clientName
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
clientInfoDrawer.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
DrawerType {
|
||||||
|
id: clientInfoDrawer
|
||||||
|
|
||||||
|
width: root.width
|
||||||
|
height: root.height * 0.5
|
||||||
|
|
||||||
|
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: clientName
|
||||||
|
descriptionText: serverSelector.text
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
clientNameEditor.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: clientNameEditor
|
||||||
|
Layout.fillWidth: true
|
||||||
|
headerText: qsTr("Client name")
|
||||||
|
textFieldText: clientName
|
||||||
|
textField.maximumLength: 30
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Save")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (clientNameEditor.textFieldText !== clientName) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
ExportController.renameClient(index,
|
||||||
|
clientNameEditor.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 - ") + 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()
|
||||||
|
clientInfoDrawer.close()
|
||||||
|
root.revokeConfig(index)
|
||||||
|
}
|
||||||
|
questionDrawer.noButtonFunction = function() {
|
||||||
|
questionDrawer.close()
|
||||||
|
}
|
||||||
|
questionDrawer.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QuestionDrawer {
|
||||||
|
id: questionDrawer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: function(mouse) {
|
||||||
|
forceActiveFocus()
|
||||||
|
mouse.accepted = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
155
client/ui/qml/Pages2/PageShareFullAccess.qml
Normal file
155
client/ui/qml/Pages2/PageShareFullAccess.qml
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -82,6 +82,7 @@ PageType {
|
||||||
target: InstallController
|
target: InstallController
|
||||||
|
|
||||||
function onInstallationErrorOccurred(errorMessage) {
|
function onInstallationErrorOccurred(errorMessage) {
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
PageController.showErrorMessage(errorMessage)
|
PageController.showErrorMessage(errorMessage)
|
||||||
|
|
||||||
var needCloseCurrentPage = false
|
var needCloseCurrentPage = false
|
||||||
|
@ -99,6 +100,7 @@ PageType {
|
||||||
|
|
||||||
function onUpdateContainerFinished(message) {
|
function onUpdateContainerFinished(message) {
|
||||||
PageController.showNotificationMessage(message)
|
PageController.showNotificationMessage(message)
|
||||||
|
PageController.closePage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +109,7 @@ PageType {
|
||||||
|
|
||||||
function onReconnectWithUpdatedContainer(message) {
|
function onReconnectWithUpdatedContainer(message) {
|
||||||
PageController.showNotificationMessage(message)
|
PageController.showNotificationMessage(message)
|
||||||
|
PageController.closePage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <configurators/shadowsocks_configurator.h>
|
#include <configurators/shadowsocks_configurator.h>
|
||||||
#include <configurators/vpn_configurator.h>
|
#include <configurators/vpn_configurator.h>
|
||||||
#include <configurators/wireguard_configurator.h>
|
#include <configurators/wireguard_configurator.h>
|
||||||
#include <core/servercontroller.h>
|
#include "core/controllers/serverController.h"
|
||||||
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
#ifdef AMNEZIA_DESKTOP
|
||||||
#include "core/ipcclient.h"
|
#include "core/ipcclient.h"
|
||||||
|
@ -227,7 +227,8 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, const Ser
|
||||||
configData = lastVpnConfig.value(proto);
|
configData = lastVpnConfig.value(proto);
|
||||||
configData = m_configurator->processConfigWithLocalSettings(serverIndex, container, proto, configData);
|
configData = m_configurator->processConfigWithLocalSettings(serverIndex, container, proto, configData);
|
||||||
} else {
|
} 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) {
|
if (errorCode && *errorCode) {
|
||||||
return "";
|
return "";
|
||||||
|
@ -244,6 +245,8 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, const Ser
|
||||||
protoObject.insert(config_key::last_config, configDataBeforeLocalProcessing);
|
protoObject.insert(config_key::last_config, configDataBeforeLocalProcessing);
|
||||||
m_settings->setProtocolConfig(serverIndex, container, proto, protoObject);
|
m_settings->setProtocolConfig(serverIndex, container, proto, protoObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit m_configurator->newVpnConfigCreated(clientId, "unnamed client", container, credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
return configData;
|
return configData;
|
||||||
|
@ -258,9 +261,7 @@ QJsonObject VpnConnection::createVpnConfiguration(int serverIndex, const ServerC
|
||||||
for (ProtocolEnumNS::Proto proto : ContainerProps::protocolsForContainer(container)) {
|
for (ProtocolEnumNS::Proto proto : ContainerProps::protocolsForContainer(container)) {
|
||||||
QJsonObject vpnConfigData =
|
QJsonObject vpnConfigData =
|
||||||
QJsonDocument::fromJson(createVpnConfigurationForProto(serverIndex, credentials, container,
|
QJsonDocument::fromJson(createVpnConfigurationForProto(serverIndex, credentials, container,
|
||||||
containerConfig, proto, errorCode)
|
containerConfig, proto, errorCode).toUtf8()).object();
|
||||||
.toUtf8())
|
|
||||||
.object();
|
|
||||||
|
|
||||||
if (errorCode && *errorCode) {
|
if (errorCode && *errorCode) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -323,7 +324,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
|
||||||
ErrorCode e = ErrorCode::NoError;
|
ErrorCode e = ErrorCode::NoError;
|
||||||
|
|
||||||
m_vpnConfiguration = createVpnConfiguration(serverIndex, credentials, container, containerConfig, &e);
|
m_vpnConfiguration = createVpnConfiguration(serverIndex, credentials, container, containerConfig, &e);
|
||||||
emit newVpnConfigurationCreated();
|
|
||||||
if (e) {
|
if (e) {
|
||||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -79,8 +79,6 @@ signals:
|
||||||
|
|
||||||
void serviceIsNotReady();
|
void serviceIsNotReady();
|
||||||
|
|
||||||
void newVpnConfigurationCreated();
|
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void onBytesChanged(quint64 receivedBytes, quint64 sentBytes);
|
void onBytesChanged(quint64 receivedBytes, quint64 sentBytes);
|
||||||
void onConnectionStateChanged(Vpn::ConnectionState state);
|
void onConnectionStateChanged(Vpn::ConnectionState state);
|
||||||
|
|
|
@ -146,7 +146,7 @@ if [ "${MAC_CERT_PW+x}" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Building DMG installer..."
|
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
|
if [ "${MAC_CERT_PW+x}" ]; then
|
||||||
echo "Signing DMG installer..."
|
echo "Signing DMG installer..."
|
||||||
|
|
|
@ -76,9 +76,7 @@ function raiseInstallerWindow()
|
||||||
function appProcessIsRunning()
|
function appProcessIsRunning()
|
||||||
{
|
{
|
||||||
if (runningOnWindows()) {
|
if (runningOnWindows()) {
|
||||||
var cmdArgs = ["/FI", "WINDOWTITLE eq " + appName()];
|
var result = installer.execute("tasklist");
|
||||||
var result = installer.execute("tasklist", cmdArgs);
|
|
||||||
|
|
||||||
if ( Number(result[1]) === 0 ) {
|
if ( Number(result[1]) === 0 ) {
|
||||||
if (result[0].indexOf(appExecutableFileName()) !== -1) {
|
if (result[0].indexOf(appExecutableFileName()) !== -1) {
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue