Compare commits

...

22 Commits

Author SHA1 Message Date
dranik d1e8fc5f89 merge dev 2026-05-18 15:04:57 +03:00
yp 277b295fd8 feat: add mtproxy(#2370)
* Feat: Add MtProxy (Telegram)

* add path files

* refactor: move logic from ui to core

---------

Co-authored-by: vkamn <vk@amnezia.org>
2026-05-18 19:52:58 +08:00
dranik 39e4c52fb3 merge last integrate-mtproxy 2026-05-18 14:27:14 +03:00
lunardunno 8c33779fc3 chore: Install recommends for apt (#2596) 2026-05-18 13:56:57 +08:00
lunardunno f0299ca9fe chore: authentication prompt in Ubuntu 26.04 (#2603)
Handling the password prompt in Ubuntu 26.04
2026-05-18 11:55:07 +08:00
vkamn 72196484a7 Merge branch 'dev' of github-amnezia:amnezia-vpn/amnezia-client into HEAD 2026-05-15 21:53:30 +08:00
vkamn 05ce813c23 refactor: move logic from ui to core 2026-05-15 21:43:47 +08:00
vkamn 0945366587 Merge branch 'dev' of github-amnezia:amnezia-vpn/amnezia-client into integrate-mtproxy 2026-05-15 21:33:04 +08:00
MrMirDan c7b1c2809f fix: app buttons clicked instead of buttons in context menu (#2200)
* fix: app buttons clicked instead of buttons in context menu

* update: using MouseArea instead of changing popupType

* fix(cursor): fixed cursor type at opened context menu

---------

Co-authored-by: Mitternacht822 <sb@amnezia.org>
2026-05-15 21:02:09 +08:00
MrMirDan c9ed0baf3b fix: app freezes when revoke awg/wg client during active connection (#2211)
* block configs revoke during connection

* update: check that current config is active

* update: notification text
2026-05-15 21:01:39 +08:00
yp 2a3e3126ac feat: regional country codes (#2567)
Co-authored-by: vkamn <vk@amnezia.org>
2026-05-15 15:44:58 +08:00
MrMirDan 98771027b7 fix: vless switch between dividers (#2600) 2026-05-15 14:58:23 +08:00
MrMirDan 0433e03bdc fix: amnezia free card button hovers when card enabled (#2602) 2026-05-15 14:58:11 +08:00
yp cb48667b91 fix: bug when saving after canceling the save action (#2568)
Co-authored-by: vkamn <vk@amnezia.org>
2026-05-15 14:57:44 +08:00
yp d0a1af0381 refactor: deactivate api config before remove (#2569)
Co-authored-by: vkamn <vk@amnezia.org>
2026-05-15 14:56:09 +08:00
Yaroslav Gurov fd0c773918 fix: change artifact names (#2589) 2026-05-15 12:36:38 +08:00
vkamn 06372c8fd7 refactor: remove serverConfig struct (#2595)
* refactor: remove serverConfig struct

* refactor: add warnings for api v1 configs

* refactor: moved the server type definition to a separate namespace

* refactor: simplified gateway stacks

* fix: fixed server description

* fix: fixed postAsync reply usage

* fix: fixed validateConfig call

* fix: fixed server name in notifications

* fix: fixed initPrepareConfigHandler for lagacy configs
2026-05-15 12:33:36 +08:00
dranik b7c502e7ed remove old path 2026-05-04 23:25:56 +03:00
dranik 4b8ddf7236 fixed secret & enum 2026-05-04 23:17:57 +03:00
dranik a0145475f2 Feat: Add Telemt (MtProxy) 2026-05-04 22:58:26 +03:00
dranik 9f3359e1e8 add path files 2026-05-04 19:27:13 +03:00
dranik 485d0c848a Feat: Add MtProxy (Telegram) 2026-05-04 19:16:48 +03:00
188 changed files with 10483 additions and 3137 deletions
-1
View File
@@ -1,6 +1,5 @@
<RCC> <RCC>
<qresource prefix="/client_scripts"> <qresource prefix="/client_scripts">
<file>linux_installer.sh</file>
<file>mac_installer.sh</file> <file>mac_installer.sh</file>
</qresource> </qresource>
</RCC> </RCC>
-29
View File
@@ -1,29 +0,0 @@
#!/bin/bash
EXTRACT_DIR="$1"
INSTALLER_PATH="$2"
# Create and clean extract directory
rm -rf "$EXTRACT_DIR"
mkdir -p "$EXTRACT_DIR"
# Extract TAR archive
tar -xf "$INSTALLER_PATH" -C "$EXTRACT_DIR"
if [ $? -ne 0 ]; then
echo 'Failed to extract TAR archive'
exit 1
fi
# Find and run installer
INSTALLER=$(find "$EXTRACT_DIR" -type f -executable)
if [ -z "$INSTALLER" ]; then
echo 'Installer not found'
exit 1
fi
"$INSTALLER"
EXIT_CODE=$?
# Cleanup
rm -rf "$EXTRACT_DIR"
exit $EXIT_CODE
+7 -1
View File
@@ -15,7 +15,6 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/utils/constants/protocolConstants.h ${CLIENT_ROOT_DIR}/core/utils/constants/protocolConstants.h
${CLIENT_ROOT_DIR}/core/utils/constants/apiKeys.h ${CLIENT_ROOT_DIR}/core/utils/constants/apiKeys.h
${CLIENT_ROOT_DIR}/core/utils/constants/apiConstants.h ${CLIENT_ROOT_DIR}/core/utils/constants/apiConstants.h
${CLIENT_ROOT_DIR}/core/utils/api/apiEnums.h
${CLIENT_ROOT_DIR}/core/utils/errorStrings.h ${CLIENT_ROOT_DIR}/core/utils/errorStrings.h
${CLIENT_ROOT_DIR}/core/utils/selfhosted/scriptsRegistry.h ${CLIENT_ROOT_DIR}/core/utils/selfhosted/scriptsRegistry.h
${CLIENT_ROOT_DIR}/core/utils/qrCodeUtils.h ${CLIENT_ROOT_DIR}/core/utils/qrCodeUtils.h
@@ -36,6 +35,8 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/installers/torInstaller.h ${CLIENT_ROOT_DIR}/core/installers/torInstaller.h
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.h ${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.h
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.h ${CLIENT_ROOT_DIR}/core/installers/socks5Installer.h
${CLIENT_ROOT_DIR}/core/installers/mtProxyInstaller.h
${CLIENT_ROOT_DIR}/core/installers/telemtInstaller.h
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.h ${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.h
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.h ${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.h
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.h ${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.h
@@ -111,6 +112,8 @@ set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/core/installers/torInstaller.cpp ${CLIENT_ROOT_DIR}/core/installers/torInstaller.cpp
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.cpp ${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.cpp
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.cpp ${CLIENT_ROOT_DIR}/core/installers/socks5Installer.cpp
${CLIENT_ROOT_DIR}/core/installers/mtProxyInstaller.cpp
${CLIENT_ROOT_DIR}/core/installers/telemtInstaller.cpp
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.cpp ${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.cpp
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.cpp ${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.cpp
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.cpp ${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.cpp
@@ -138,6 +141,7 @@ set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp ${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.cpp ${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.cpp
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.cpp ${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.cpp
${CLIENT_ROOT_DIR}/core/utils/serverConfigUtils.cpp
${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.cpp ${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.cpp
${CLIENT_ROOT_DIR}/core/utils/utilities.cpp ${CLIENT_ROOT_DIR}/core/utils/utilities.cpp
${CLIENT_ROOT_DIR}/core/utils/managementServer.cpp ${CLIENT_ROOT_DIR}/core/utils/managementServer.cpp
@@ -201,12 +205,14 @@ file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/ui/models/*.h ${CLIENT_ROOT_DIR}/ui/models/*.h
${CLIENT_ROOT_DIR}/ui/models/protocols/*.h ${CLIENT_ROOT_DIR}/ui/models/protocols/*.h
${CLIENT_ROOT_DIR}/ui/models/services/*.h ${CLIENT_ROOT_DIR}/ui/models/services/*.h
${CLIENT_ROOT_DIR}/ui/models/utils/*.h
${CLIENT_ROOT_DIR}/ui/models/api/*.h ${CLIENT_ROOT_DIR}/ui/models/api/*.h
) )
file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
${CLIENT_ROOT_DIR}/ui/models/*.cpp ${CLIENT_ROOT_DIR}/ui/models/*.cpp
${CLIENT_ROOT_DIR}/ui/models/protocols/*.cpp ${CLIENT_ROOT_DIR}/ui/models/protocols/*.cpp
${CLIENT_ROOT_DIR}/ui/models/services/*.cpp ${CLIENT_ROOT_DIR}/ui/models/services/*.cpp
${CLIENT_ROOT_DIR}/ui/models/utils/*.cpp
${CLIENT_ROOT_DIR}/ui/models/api/*.cpp ${CLIENT_ROOT_DIR}/ui/models/api/*.cpp
) )
+62 -21
View File
@@ -1,51 +1,93 @@
#include "newsController.h" #include "newsController.h"
#include "core/controllers/gatewayController.h" #include "core/controllers/gatewayController.h"
#include "core/utils/api/apiEnums.h" #include "core/repositories/secureServersRepository.h"
#include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h" #include "core/utils/constants/apiConstants.h"
#include "core/utils/constants/configKeys.h"
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
#include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QSet>
#include <QSharedPointer> #include <QSharedPointer>
using namespace amnezia; using namespace amnezia;
NewsController::NewsController(SecureAppSettingsRepository* appSettingsRepository, NewsController::NewsController(SecureAppSettingsRepository *appSettingsRepository,
ServersController* serversController) SecureServersRepository *serversRepository)
: m_appSettingsRepository(appSettingsRepository), m_serversController(serversController) : m_appSettingsRepository(appSettingsRepository),
m_serversRepository(serversRepository)
{ {
} }
QJsonObject NewsController::getServicesList() const
{
if (!m_serversRepository) {
return {};
}
QSet<QString> userCountryCodes;
QSet<QString> serviceTypes;
const QVector<QString> ids = m_serversRepository->orderedServerIds();
for (const QString &id : ids) {
const auto apiV2 = m_serversRepository->apiV2Config(id);
if (!apiV2.has_value()) {
continue;
}
if (!apiV2->apiConfig.userCountryCode.isEmpty()) {
userCountryCodes.insert(apiV2->apiConfig.userCountryCode);
}
const QString serviceType = apiV2->serviceType();
if (!serviceType.isEmpty()) {
serviceTypes.insert(serviceType);
}
}
if (userCountryCodes.isEmpty() && serviceTypes.isEmpty()) {
return {};
}
QJsonObject json;
QJsonArray userCountryCodesArray;
for (const QString &code : userCountryCodes) {
userCountryCodesArray.append(code);
}
json[apiDefs::key::userCountryCode] = userCountryCodesArray;
QJsonArray serviceTypesArray;
for (const QString &type : serviceTypes) {
serviceTypesArray.append(type);
}
json[apiDefs::key::serviceType] = serviceTypesArray;
return json;
}
QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews() QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews()
{ {
if (!m_serversController) { if (!m_serversRepository) {
qWarning() << "ServersController is null, skip fetchNews"; qWarning() << "SecureServersRepository is null, skip fetchNews";
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::InternalError, QJsonArray())); return QtFuture::makeReadyFuture(qMakePair(ErrorCode::InternalError, QJsonArray()));
} }
const auto stacks = m_serversController->gatewayStacks(); const QJsonObject services = getServicesList();
if (stacks.isEmpty()) { if (services.isEmpty()) {
qDebug() << "No Gateway stacks, skip fetchNews"; qDebug() << "No Gateway stacks, skip fetchNews";
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::NoError, QJsonArray())); return QtFuture::makeReadyFuture(qMakePair(ErrorCode::NoError, QJsonArray()));
} }
auto gatewayController = QSharedPointer<GatewayController>::create( auto gatewayController = QSharedPointer<GatewayController>::create(
m_appSettingsRepository->getGatewayEndpoint(), m_appSettingsRepository->getGatewayEndpoint(),
m_appSettingsRepository->isDevGatewayEnv(), m_appSettingsRepository->isDevGatewayEnv(),
apiDefs::requestTimeoutMsecs, apiDefs::requestTimeoutMsecs,
m_appSettingsRepository->isStrictKillSwitchEnabled()); m_appSettingsRepository->isStrictKillSwitchEnabled());
QJsonObject payload; QJsonObject payload;
payload.insert("locale", m_appSettingsRepository->getAppLanguage().name().split("_").first()); payload.insert("locale", m_appSettingsRepository->getAppLanguage().name().split("_").first());
const QJsonObject stacksJson = stacks.toJson(); if (services.contains(apiDefs::key::userCountryCode)) {
if (stacksJson.contains(apiDefs::key::userCountryCode)) { payload.insert(apiDefs::key::userCountryCode, services.value(apiDefs::key::userCountryCode));
payload.insert(apiDefs::key::userCountryCode, stacksJson.value(apiDefs::key::userCountryCode));
} }
if (stacksJson.contains(apiDefs::key::serviceType)) { if (services.contains(apiDefs::key::serviceType)) {
payload.insert(apiDefs::key::serviceType, stacksJson.value(apiDefs::key::serviceType)); payload.insert(apiDefs::key::serviceType, services.value(apiDefs::key::serviceType));
} }
auto future = gatewayController->postAsync(QString("%1v1/news"), payload); auto future = gatewayController->postAsync(QString("%1v1/news"), payload);
@@ -69,4 +111,3 @@ QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews()
return qMakePair(ErrorCode::NoError, newsArray); return qMakePair(ErrorCode::NoError, newsArray);
}); });
} }
+6 -4
View File
@@ -3,26 +3,28 @@
#include <QFuture> #include <QFuture>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject>
#include <QPair> #include <QPair>
#include "core/utils/errorCodes.h" #include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h" #include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h" #include "core/utils/commonStructs.h"
#include "core/repositories/secureAppSettingsRepository.h" #include "core/repositories/secureAppSettingsRepository.h"
#include "core/controllers/serversController.h" #include "core/repositories/secureServersRepository.h"
class NewsController class NewsController
{ {
public: public:
explicit NewsController(SecureAppSettingsRepository* appSettingsRepository, explicit NewsController(SecureAppSettingsRepository* appSettingsRepository,
ServersController* serversController); SecureServersRepository* serversRepository);
QFuture<QPair<ErrorCode, QJsonArray>> fetchNews(); QFuture<QPair<ErrorCode, QJsonArray>> fetchNews();
private: private:
QJsonObject getServicesList() const;
SecureAppSettingsRepository* m_appSettingsRepository; SecureAppSettingsRepository* m_appSettingsRepository;
ServersController* m_serversController; SecureServersRepository* m_serversRepository;
}; };
#endif // NEWSCONTROLLER_H #endif // NEWSCONTROLLER_H
@@ -11,7 +11,7 @@
#include <limits> #include <limits>
#include "core/controllers/gatewayController.h" #include "core/controllers/gatewayController.h"
#include "core/utils/api/apiEnums.h" #include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h" #include "core/utils/constants/apiConstants.h"
#include "version.h" #include "version.h"
@@ -16,7 +16,7 @@
#include "core/utils/containerEnum.h" #include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h" #include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h" #include "core/utils/protocolEnum.h"
#include "core/utils/api/apiEnums.h" #include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h" #include "core/utils/constants/apiConstants.h"
#include "core/utils/api/apiUtils.h" #include "core/utils/api/apiUtils.h"
@@ -26,7 +26,6 @@
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h" #include "core/utils/constants/protocolConstants.h"
#include "version.h" #include "version.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/models/api/apiConfig.h" #include "core/models/api/apiConfig.h"
@@ -196,7 +195,7 @@ void SubscriptionController::updateApiConfigInJson(QJsonObject &serverConfigJson
apiConfig[apiDefs::key::serviceProtocol] = serviceProtocol; apiConfig[apiDefs::key::serviceProtocol] = serviceProtocol;
apiConfig[apiDefs::key::userCountryCode] = userCountryCode; apiConfig[apiDefs::key::userCountryCode] = userCountryCode;
if (serverConfigJson.value(configKey::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) { if (serverConfigJson.value(configKey::configVersion).toInt() == serverConfigUtils::ConfigSource::AmneziaGateway) {
QJsonObject responseObj = QJsonDocument::fromJson(apiResponseBody).object(); QJsonObject responseObj = QJsonDocument::fromJson(apiResponseBody).object();
if (responseObj.contains(apiDefs::key::supportedProtocols)) { if (responseObj.contains(apiDefs::key::supportedProtocols)) {
apiConfig.insert(apiDefs::key::supportedProtocols, responseObj.value(apiDefs::key::supportedProtocols).toArray()); apiConfig.insert(apiDefs::key::supportedProtocols, responseObj.value(apiDefs::key::supportedProtocols).toArray());
@@ -217,8 +216,7 @@ ErrorCode SubscriptionController::executeRequest(const QString &endpoint, const
} }
ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCountryCode, const QString &serviceType, ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData, const QString &serviceProtocol, const ProtocolData &protocolData)
ServerConfig &serverConfig)
{ {
GatewayRequestData gatewayRequestData { QSysInfo::productType(), GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION), QString(APP_VERSION),
@@ -247,20 +245,18 @@ ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCo
updateApiConfigInJson(serverConfigJson, serviceType, serviceProtocol, userCountryCode, responseBody); updateApiConfigInJson(serverConfigJson, serviceType, serviceProtocol, userCountryCode, responseBody);
ServerConfig serverConfigModel = ServerConfig::fromJson(serverConfigJson); if (serverConfigJson.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError; return ErrorCode::InternalError;
} }
m_serversRepository->addServer(serverConfigModel); ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(serverConfigJson);
serverConfig = serverConfigModel; m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(),
serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson()));
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCountryCode, const QString &serviceType, ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &email, const QString &serviceProtocol, const QString &email)
ServerConfig &serverConfig)
{ {
const QString trimmedEmail = email.trimmed(); const QString trimmedEmail = email.trimmed();
if (trimmedEmail.isEmpty()) { if (trimmedEmail.isEmpty()) {
@@ -306,16 +302,19 @@ ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCoun
} }
QJsonObject configObject = QJsonDocument::fromJson(configBytes).object(); QJsonObject configObject = QJsonDocument::fromJson(configBytes).object();
ServerConfig serverConfigModel = ServerConfig::fromJson(configObject); if (configObject.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
m_serversRepository->addServer(serverConfigModel); return ErrorCode::InternalError;
serverConfig = serverConfigModel; }
ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(configObject);
m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(),
serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson()));
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType, ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData, const QString &serviceProtocol, const ProtocolData &protocolData,
const QString &transactionId, bool isTestPurchase, const QString &transactionId, bool isTestPurchase,
ServerConfig &serverConfig,
int *duplicateServerIndex) int *duplicateServerIndex)
{ {
GatewayRequestData gatewayRequestData { QSysInfo::productType(), GatewayRequestData gatewayRequestData { QSysInfo::productType(),
@@ -351,15 +350,8 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC
// Check if server with this VPN key already exists // Check if server with this VPN key already exists
for (int i = 0; i < m_serversRepository->serversCount(); ++i) { for (int i = 0; i < m_serversRepository->serversCount(); ++i) {
ServerConfig existingServerConfig = m_serversRepository->server(i); const auto apiV2 = m_serversRepository->apiV2Config(m_serversRepository->serverIdAt(i));
QString existingVpnKey; QString existingVpnKey = apiV2.has_value() ? apiV2->vpnKey() : QString();
if (existingServerConfig.isApiV1()) {
const ApiV1ServerConfig* apiV1 = existingServerConfig.as<ApiV1ServerConfig>();
existingVpnKey = apiV1 ? apiV1->vpnKey() : QString();
} else if (existingServerConfig.isApiV2()) {
const ApiV2ServerConfig* apiV2 = existingServerConfig.as<ApiV2ServerConfig>();
existingVpnKey = apiV2 ? apiV2->vpnKey() : QString();
}
existingVpnKey.replace(QStringLiteral("vpn://"), QString()); existingVpnKey.replace(QStringLiteral("vpn://"), QString());
if (!existingVpnKey.isEmpty() && existingVpnKey == normalizedKey) { if (!existingVpnKey.isEmpty() && existingVpnKey == normalizedKey) {
if (duplicateServerIndex) { if (duplicateServerIndex) {
@@ -385,38 +377,28 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC
quint16 crc = qChecksum(QJsonDocument(configObject).toJson()); quint16 crc = qChecksum(QJsonDocument(configObject).toJson());
ServerConfig serverConfigModel = ServerConfig::fromJson(configObject); if (configObject.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError; return ErrorCode::InternalError;
} }
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>(); ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(configObject);
if (!apiV2) { ApiV2ServerConfig* apiV2 = &apiV2ServerConfig;
return ErrorCode::InternalError;
}
apiV2->apiConfig.vpnKey = normalizedKey; apiV2->apiConfig.vpnKey = normalizedKey;
apiV2->apiConfig.isTestPurchase = isTestPurchase; apiV2->apiConfig.isTestPurchase = isTestPurchase;
apiV2->apiConfig.isInAppPurchase = true; apiV2->apiConfig.isInAppPurchase = true;
apiV2->apiConfig.subscriptionExpiredByServer = false; apiV2->apiConfig.subscriptionExpiredByServer = false;
apiV2->crc = crc; apiV2->crc = crc;
m_serversRepository->addServer(serverConfigModel); m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(),
serverConfig = serverConfigModel; serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson()));
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, const QString &newCountryCode, bool isConnectEvent) ErrorCode SubscriptionController::updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, bool isConnectEvent)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
return ErrorCode::InternalError; return ErrorCode::InternalError;
} }
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase; const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
@@ -445,12 +427,10 @@ ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, cons
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody, isTestPurchase); ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody, isTestPurchase);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
if (errorCode == ErrorCode::ApiSubscriptionExpiredError && !apiV2->apiConfig.isInAppPurchase) { if (errorCode == ErrorCode::ApiSubscriptionExpiredError && !apiV2->apiConfig.isInAppPurchase) {
ServerConfig expiredServerConfig = serverConfigModel; ApiV2ServerConfig expiredApiV2 = *apiV2;
ApiV2ServerConfig *expiredApiV2 = expiredServerConfig.as<ApiV2ServerConfig>(); expiredApiV2.apiConfig.subscriptionExpiredByServer = true;
if (expiredApiV2) { m_serversRepository->editServer(serverId, expiredApiV2.toJson(),
expiredApiV2->apiConfig.subscriptionExpiredByServer = true; serverConfigUtils::configTypeFromJson(expiredApiV2.toJson()));
m_serversRepository->editServer(serverIndex, expiredServerConfig);
}
} }
return errorCode; return errorCode;
} }
@@ -463,16 +443,12 @@ ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, cons
updateApiConfigInJson(serverConfigJson, apiV2->apiConfig.serviceType, serviceProtocol, apiV2->apiConfig.userCountryCode, responseBody); updateApiConfigInJson(serverConfigJson, apiV2->apiConfig.serviceType, serviceProtocol, apiV2->apiConfig.userCountryCode, responseBody);
ServerConfig newServerConfigModel = ServerConfig::fromJson(serverConfigJson); if (serverConfigJson.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
if (!newServerConfigModel.isApiV2()) {
return ErrorCode::InternalError; return ErrorCode::InternalError;
} }
ApiV2ServerConfig* newApiV2 = newServerConfigModel.as<ApiV2ServerConfig>(); ApiV2ServerConfig newApiV2Config = ApiV2ServerConfig::fromJson(serverConfigJson);
if (!newApiV2) { ApiV2ServerConfig* newApiV2 = &newApiV2Config;
return ErrorCode::InternalError;
}
newApiV2->apiConfig.vpnKey = apiV2->apiConfig.vpnKey; newApiV2->apiConfig.vpnKey = apiV2->apiConfig.vpnKey;
newApiV2->apiConfig.isTestPurchase = apiV2->apiConfig.isTestPurchase; newApiV2->apiConfig.isTestPurchase = apiV2->apiConfig.isTestPurchase;
@@ -487,20 +463,15 @@ ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, cons
newApiV2->nameOverriddenByUser = true; newApiV2->nameOverriddenByUser = true;
} }
m_serversRepository->editServer(serverIndex, newServerConfigModel); m_serversRepository->editServer(serverId, newApiV2Config.toJson(),
serverConfigUtils::configTypeFromJson(newApiV2Config.toJson()));
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode SubscriptionController::deactivateDevice(int serverIndex) ErrorCode SubscriptionController::deactivateDevice(const QString &serverId)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
if (!serverConfigModel.isApiV2()) {
return ErrorCode::NoError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
return ErrorCode::NoError; return ErrorCode::NoError;
} }
@@ -528,23 +499,16 @@ ErrorCode SubscriptionController::deactivateDevice(int serverIndex)
return errorCode; return errorCode;
} }
serverConfigModel.visit([](auto& arg) { apiV2->containers.clear();
arg.containers.clear(); m_serversRepository->editServer(serverId, apiV2->toJson(),
}); serverConfigUtils::configTypeFromJson(apiV2->toJson()));
m_serversRepository->editServer(serverIndex, serverConfigModel);
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode SubscriptionController::deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode) ErrorCode SubscriptionController::deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
if (!serverConfigModel.isApiV2()) {
return ErrorCode::NoError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
return ErrorCode::NoError; return ErrorCode::NoError;
} }
@@ -573,25 +537,18 @@ ErrorCode SubscriptionController::deactivateExternalDevice(int serverIndex, cons
} }
if (uuid == m_appSettingsRepository->getInstallationUuid(true)) { if (uuid == m_appSettingsRepository->getInstallationUuid(true)) {
serverConfigModel.visit([](auto& arg) { apiV2->containers.clear();
arg.containers.clear(); m_serversRepository->editServer(serverId, apiV2->toJson(),
}); serverConfigUtils::configTypeFromJson(apiV2->toJson()));
m_serversRepository->editServer(serverIndex, serverConfigModel);
} }
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode SubscriptionController::exportNativeConfig(int serverIndex, const QString &serverCountryCode, QString &nativeConfig) ErrorCode SubscriptionController::exportNativeConfig(const QString &serverId, const QString &serverCountryCode, QString &nativeConfig)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
return ErrorCode::InternalError; return ErrorCode::InternalError;
} }
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase; const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
@@ -624,16 +581,10 @@ ErrorCode SubscriptionController::exportNativeConfig(int serverIndex, const QStr
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode SubscriptionController::revokeNativeConfig(int serverIndex, const QString &serverCountryCode) ErrorCode SubscriptionController::revokeNativeConfig(const QString &serverId, const QString &serverCountryCode)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
return ErrorCode::InternalError; return ErrorCode::InternalError;
} }
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase; const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
@@ -661,126 +612,54 @@ ErrorCode SubscriptionController::revokeNativeConfig(int serverIndex, const QStr
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode SubscriptionController::updateServiceFromTelegram(int serverIndex) ErrorCode SubscriptionController::prepareVpnKeyExport(const QString &serverId, QString &vpnKey)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
if (!serverConfigModel.isApiV1()) {
return ErrorCode::InternalError;
}
const ApiV1ServerConfig* apiV1 = serverConfigModel.as<ApiV1ServerConfig>();
if (!apiV1) {
return ErrorCode::InternalError;
}
QString serviceProtocol = apiV1->protocol;
ProtocolData protocolData = generateProtocolData(serviceProtocol);
QString installationUuid = m_appSettingsRepository->getInstallationUuid(true);
GatewayController gatewayController(m_appSettingsRepository->getGatewayEndpoint(), m_appSettingsRepository->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_appSettingsRepository->isStrictKillSwitchEnabled());
QJsonObject apiPayload;
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
apiPayload[apiDefs::key::uuid] = installationUuid;
apiPayload[apiDefs::key::osVersion] = QSysInfo::productType();
apiPayload[apiDefs::key::appVersion] = QString(APP_VERSION);
apiPayload[configKey::accessToken] = apiV1->apiKey;
apiPayload[apiDefs::key::apiEndpoint] = apiV1->apiEndpoint;
QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
QJsonObject serverConfigJson;
errorCode = extractServerConfigJsonFromResponse(responseBody, serviceProtocol, protocolData, serverConfigJson);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
ServerConfig newServerConfigModel = ServerConfig::fromJson(serverConfigJson);
if (!newServerConfigModel.isApiV1()) {
return ErrorCode::InternalError;
}
ApiV1ServerConfig* newApiV1 = newServerConfigModel.as<ApiV1ServerConfig>();
if (!newApiV1) {
return ErrorCode::InternalError;
}
newApiV1->apiKey = apiV1->apiKey;
newApiV1->apiEndpoint = apiV1->apiEndpoint;
newApiV1->crc = apiV1->crc;
m_serversRepository->editServer(serverIndex, newServerConfigModel);
return ErrorCode::NoError;
}
ErrorCode SubscriptionController::prepareVpnKeyExport(int serverIndex, QString &vpnKey)
{
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
if (serverConfigModel.isApiV1()) {
const ApiV1ServerConfig* apiV1 = serverConfigModel.as<ApiV1ServerConfig>();
vpnKey = apiV1 ? apiV1->vpnKey() : QString();
} else if (serverConfigModel.isApiV2()) {
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
vpnKey = apiV2 ? apiV2->vpnKey() : QString();
if (vpnKey.isEmpty()) {
QJsonObject serverJson = serverConfigModel.toJson();
vpnKey = apiUtils::getPremiumV2VpnKey(serverJson);
if (vpnKey.isEmpty()) {
return ErrorCode::ApiConfigEmptyError;
}
apiV2->apiConfig.vpnKey = vpnKey;
m_serversRepository->editServer(serverIndex, serverConfigModel);
}
} else {
return ErrorCode::ApiConfigEmptyError; return ErrorCode::ApiConfigEmptyError;
} }
vpnKey = apiV2->vpnKey();
if (vpnKey.isEmpty()) {
vpnKey = apiUtils::getPremiumV2VpnKey(apiV2->toJson());
if (vpnKey.isEmpty()) {
return ErrorCode::ApiConfigEmptyError;
}
apiV2->apiConfig.vpnKey = vpnKey;
m_serversRepository->editServer(serverId, apiV2->toJson(),
serverConfigUtils::configTypeFromJson(apiV2->toJson()));
}
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode SubscriptionController::validateAndUpdateConfig(int serverIndex, bool hasInstalledContainers) ErrorCode SubscriptionController::validateAndUpdateConfig(const QString &serverId, bool hasInstalledContainers)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); if (!m_serversRepository->apiV2Config(serverId).has_value()) {
apiDefs::ConfigSource configSource;
if (serverConfigModel.isApiV1()) {
configSource = apiDefs::ConfigSource::Telegram;
} else if (serverConfigModel.isApiV2()) {
configSource = apiDefs::ConfigSource::AmneziaGateway;
} else {
return ErrorCode::NoError; return ErrorCode::NoError;
} }
if (configSource == apiDefs::ConfigSource::Telegram && !hasInstalledContainers) { if (!hasInstalledContainers) {
removeApiConfig(serverIndex); return updateServiceFromGateway(serverId, "", true);
return updateServiceFromTelegram(serverIndex);
} else if (configSource == apiDefs::ConfigSource::AmneziaGateway && !hasInstalledContainers) {
return updateServiceFromGateway(serverIndex, "", true);
} else if (configSource && isApiKeyExpired(serverIndex)) {
qDebug() << "attempt to update api config by expires_at event";
if (configSource == apiDefs::ConfigSource::AmneziaGateway) {
return updateServiceFromGateway(serverIndex, "", true);
} else {
removeApiConfig(serverIndex);
return updateServiceFromTelegram(serverIndex);
}
} }
if (isApiKeyExpired(serverId)) {
qDebug() << "attempt to update api config by expires_at event";
return updateServiceFromGateway(serverId, "", true);
}
return ErrorCode::NoError; return ErrorCode::NoError;
} }
void SubscriptionController::removeApiConfig(int serverIndex) void SubscriptionController::removeApiConfig(const QString &serverId)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
return;
}
#if defined(Q_OS_IOS) || defined(MACOS_NE) #if defined(Q_OS_IOS) || defined(MACOS_NE)
QString description = serverConfigModel.description(); QString description = apiV2->description;
QString hostName = serverConfigModel.hostName(); QString hostName = apiV2->hostName;
QString vpncName = QString("%1 (%2) %3") QString vpncName = QString("%1 (%2) %3")
.arg(description) .arg(description)
.arg(hostName) .arg(hostName)
@@ -789,34 +668,42 @@ void SubscriptionController::removeApiConfig(int serverIndex)
AmneziaVPN::removeVPNC(vpncName.toStdString()); AmneziaVPN::removeVPNC(vpncName.toStdString());
#endif #endif
serverConfigModel.visit([](auto& arg) { apiV2->dns1.clear();
arg.dns1.clear(); apiV2->dns2.clear();
arg.dns2.clear(); apiV2->containers.clear();
arg.containers.clear(); apiV2->hostName.clear();
arg.hostName.clear(); apiV2->defaultContainer = DockerContainer::None;
arg.defaultContainer = DockerContainer::None; apiV2->apiConfig.publicKey = ApiConfig::PublicKeyInfo{};
});
if (serverConfigModel.isApiV2()) { m_serversRepository->editServer(serverId, apiV2->toJson(),
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>(); serverConfigUtils::configTypeFromJson(apiV2->toJson()));
if (apiV2) {
apiV2->apiConfig.publicKey = ApiConfig::PublicKeyInfo{};
}
}
m_serversRepository->editServer(serverIndex, serverConfigModel);
} }
bool SubscriptionController::isApiKeyExpired(int serverIndex) const bool SubscriptionController::removeServer(const QString &serverId)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); if (serverId.isEmpty()) {
if (!serverConfigModel.isApiV2()) {
return false; return false;
} }
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>(); if (!m_serversRepository->apiV2Config(serverId).has_value()) {
if (!apiV2) { qWarning().noquote() << "SubscriptionController::removeServer: not an Api V2 server, id" << serverId;
return false;
}
const ErrorCode revokeError = deactivateDevice(serverId);
if (revokeError != ErrorCode::NoError && revokeError != ErrorCode::ApiNotFoundError) {
qWarning().noquote() << "SubscriptionController::removeServer: deactivateDevice failed (error"
<< static_cast<int>(revokeError) << "); removing locally anyway.";
}
m_serversRepository->removeServer(serverId);
return true;
}
bool SubscriptionController::isApiKeyExpired(const QString &serverId) const
{
auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
return false; return false;
} }
const QString expiresAt = apiV2->apiConfig.publicKey.expiresAt; const QString expiresAt = apiV2->apiConfig.publicKey.expiresAt;
@@ -833,31 +720,24 @@ bool SubscriptionController::isApiKeyExpired(int serverIndex) const
return false; return false;
} }
void SubscriptionController::setCurrentProtocol(int serverIndex, const QString &protocolName) void SubscriptionController::setCurrentProtocol(const QString &serverId, const QString &protocolName)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (serverConfigModel.isApiV2()) { if (apiV2.has_value()) {
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>(); apiV2->apiConfig.serviceProtocol = protocolName;
if (apiV2) { m_serversRepository->editServer(serverId, apiV2->toJson(),
apiV2->apiConfig.serviceProtocol = protocolName; serverConfigUtils::configTypeFromJson(apiV2->toJson()));
}
m_serversRepository->editServer(serverIndex, serverConfigModel);
} }
} }
bool SubscriptionController::isVlessProtocol(int serverIndex) const bool SubscriptionController::isVlessProtocol(const QString &serverId) const
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (serverConfigModel.isApiV2()) { return apiV2.has_value() && apiV2->serviceProtocol() == "vless";
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
return apiV2 && apiV2->serviceProtocol() == "vless";
}
return false;
} }
ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCountryCode, const QString &serviceType, ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &productId, const QString &serviceProtocol, const QString &productId,
ServerConfig &serverConfig,
int *duplicateServerIndex) int *duplicateServerIndex)
{ {
#if defined(Q_OS_IOS) || defined(MACOS_NE) #if defined(Q_OS_IOS) || defined(MACOS_NE)
@@ -891,13 +771,12 @@ ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCou
ProtocolData protocolData = generateProtocolData(serviceProtocol); ProtocolData protocolData = generateProtocolData(serviceProtocol);
return importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData, return importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData,
originalTransactionId, isTestPurchase, serverConfig, duplicateServerIndex); originalTransactionId, isTestPurchase, duplicateServerIndex);
#else #else
Q_UNUSED(userCountryCode); Q_UNUSED(userCountryCode);
Q_UNUSED(serviceType); Q_UNUSED(serviceType);
Q_UNUSED(serviceProtocol); Q_UNUSED(serviceProtocol);
Q_UNUSED(productId); Q_UNUSED(productId);
Q_UNUSED(serverConfig);
return ErrorCode::ApiPurchaseError; return ErrorCode::ApiPurchaseError;
#endif #endif
} }
@@ -956,10 +835,9 @@ SubscriptionController::AppStoreRestoreResult SubscriptionController::processApp
<< "originalTransactionId =" << originalTransactionId << "productId =" << transactionProductId; << "originalTransactionId =" << originalTransactionId << "productId =" << transactionProductId;
ProtocolData protocolData = generateProtocolData(serviceProtocol); ProtocolData protocolData = generateProtocolData(serviceProtocol);
ServerConfig serverConfig;
int currentDuplicateServerIndex = -1; int currentDuplicateServerIndex = -1;
ErrorCode errorCode = importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData, ErrorCode errorCode = importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData,
originalTransactionId, isTestPurchase, serverConfig, originalTransactionId, isTestPurchase,
&currentDuplicateServerIndex); &currentDuplicateServerIndex);
if (errorCode == ErrorCode::ApiConfigAlreadyAdded) { if (errorCode == ErrorCode::ApiConfigAlreadyAdded) {
@@ -991,16 +869,10 @@ SubscriptionController::AppStoreRestoreResult SubscriptionController::processApp
#endif #endif
} }
ErrorCode SubscriptionController::getAccountInfo(int serverIndex, QJsonObject &accountInfo) ErrorCode SubscriptionController::getAccountInfo(const QString &serverId, QJsonObject &accountInfo)
{ {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
return ErrorCode::InternalError; return ErrorCode::InternalError;
} }
bool isTestPurchase = apiV2->apiConfig.isTestPurchase; bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
@@ -1030,20 +902,13 @@ ErrorCode SubscriptionController::getAccountInfo(int serverIndex, QJsonObject &a
return ErrorCode::NoError; return ErrorCode::NoError;
} }
QFuture<QPair<ErrorCode, QString>> SubscriptionController::getRenewalLink(int serverIndex) QFuture<QPair<ErrorCode, QString>> SubscriptionController::getRenewalLink(const QString &serverId)
{ {
auto promise = QSharedPointer<QPromise<QPair<ErrorCode, QString>>>::create(); auto promise = QSharedPointer<QPromise<QPair<ErrorCode, QString>>>::create();
promise->start(); promise->start();
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!serverConfigModel.isApiV2()) { if (!apiV2.has_value()) {
promise->addResult(qMakePair(ErrorCode::InternalError, QString()));
promise->finish();
return promise->future();
}
const ApiV2ServerConfig *apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
promise->addResult(qMakePair(ErrorCode::InternalError, QString())); promise->addResult(qMakePair(ErrorCode::InternalError, QString()));
promise->finish(); promise->finish();
return promise->future(); return promise->future();
@@ -12,7 +12,6 @@
#include "core/utils/commonStructs.h" #include "core/utils/commonStructs.h"
#include "core/repositories/secureServersRepository.h" #include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h" #include "core/repositories/secureAppSettingsRepository.h"
#include "core/models/serverConfig.h"
class ServersController; class ServersController;
@@ -48,44 +47,40 @@ public:
ProtocolData generateProtocolData(const QString &protocol); ProtocolData generateProtocolData(const QString &protocol);
void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload); void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload);
ErrorCode fillServerConfig(const QJsonObject &serverConfigJson, ServerConfig &serverConfig);
ErrorCode importServiceFromGateway(const QString &userCountryCode, const QString &serviceType, ErrorCode importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData, const QString &serviceProtocol, const ProtocolData &protocolData);
ServerConfig &serverConfig);
ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType, ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &email, const QString &serviceProtocol, const QString &email);
ServerConfig &serverConfig);
ErrorCode importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType, ErrorCode importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData, const QString &serviceProtocol, const ProtocolData &protocolData,
const QString &transactionId, bool isTestPurchase, const QString &transactionId, bool isTestPurchase,
ServerConfig &serverConfig,
int *duplicateServerIndex = nullptr); int *duplicateServerIndex = nullptr);
ErrorCode updateServiceFromGateway(int serverIndex, const QString &newCountryCode, bool isConnectEvent); ErrorCode updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, bool isConnectEvent);
ErrorCode deactivateDevice(int serverIndex); ErrorCode deactivateDevice(const QString &serverId);
ErrorCode deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode); ErrorCode deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode);
ErrorCode exportNativeConfig(int serverIndex, const QString &serverCountryCode, QString &nativeConfig); ErrorCode exportNativeConfig(const QString &serverId, const QString &serverCountryCode, QString &nativeConfig);
ErrorCode revokeNativeConfig(int serverIndex, const QString &serverCountryCode); ErrorCode revokeNativeConfig(const QString &serverId, const QString &serverCountryCode);
ErrorCode updateServiceFromTelegram(int serverIndex); ErrorCode prepareVpnKeyExport(const QString &serverId, QString &vpnKey);
ErrorCode prepareVpnKeyExport(int serverIndex, QString &vpnKey); ErrorCode validateAndUpdateConfig(const QString &serverId, bool hasInstalledContainers);
ErrorCode validateAndUpdateConfig(int serverIndex, bool hasInstalledContainers); void removeApiConfig(const QString &serverId);
void removeApiConfig(int serverIndex); bool removeServer(const QString &serverId);
void setCurrentProtocol(int serverIndex, const QString &protocolName); void setCurrentProtocol(const QString &serverId, const QString &protocolName);
bool isVlessProtocol(int serverIndex) const; bool isVlessProtocol(const QString &serverId) const;
ErrorCode getAccountInfo(int serverIndex, QJsonObject &accountInfo); ErrorCode getAccountInfo(const QString &serverId, QJsonObject &accountInfo);
QFuture<QPair<ErrorCode, QString>> getRenewalLink(int serverIndex); QFuture<QPair<ErrorCode, QString>> getRenewalLink(const QString &serverId);
struct AppStoreRestoreResult struct AppStoreRestoreResult
{ {
@@ -98,7 +93,6 @@ public:
ErrorCode processAppStorePurchase(const QString &userCountryCode, const QString &serviceType, ErrorCode processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &productId, const QString &serviceProtocol, const QString &productId,
ServerConfig &serverConfig,
int *duplicateServerIndex = nullptr); int *duplicateServerIndex = nullptr);
AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType, AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
@@ -106,7 +100,7 @@ public:
private: private:
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false); ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false);
bool isApiKeyExpired(int serverIndex) const; bool isApiKeyExpired(const QString &serverId) const;
ErrorCode extractServerConfigJsonFromResponse(const QByteArray &apiResponseBody, const QString &protocol, ErrorCode extractServerConfigJsonFromResponse(const QByteArray &apiResponseBody, const QString &protocol,
const ProtocolData &protocolData, QJsonObject &serverConfigJson); const ProtocolData &protocolData, QJsonObject &serverConfigJson);
@@ -9,11 +9,11 @@
#include "core/utils/constants/protocolConstants.h" #include "core/utils/constants/protocolConstants.h"
#include "core/utils/utilities.h" #include "core/utils/utilities.h"
#include "core/utils/networkUtilities.h" #include "core/utils/networkUtilities.h"
#include "core/utils/serverConfigUtils.h"
#include "version.h" #include "version.h"
#include "core/utils/containerEnum.h" #include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h" #include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h" #include "core/utils/protocolEnum.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/models/protocolConfig.h" #include "core/models/protocolConfig.h"
@@ -51,7 +51,7 @@ void ConnectionController::setConnectionState(Vpn::ConnectionState state)
} }
} }
ErrorCode ConnectionController::prepareConnection(int serverIndex, ErrorCode ConnectionController::prepareConnection(const QString &serverId,
QJsonObject& vpnConfiguration, QJsonObject& vpnConfiguration,
DockerContainer& container) DockerContainer& container)
{ {
@@ -59,35 +59,98 @@ ErrorCode ConnectionController::prepareConnection(int serverIndex,
return ErrorCode::AmneziaServiceNotRunning; return ErrorCode::AmneziaServiceNotRunning;
} }
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); ContainerConfig containerConfigModel;
container = serverConfigModel.defaultContainer(); QPair<QString, QString> dns;
QString hostName;
QString description;
int configVersion = 0;
bool isApiConfig = false;
const auto kind = m_serversRepository->serverKind(serverId);
switch (kind) {
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
if (!cfg.has_value()) return ErrorCode::InternalError;
container = cfg->defaultContainer;
containerConfigModel = cfg->containerConfig(container);
dns = { cfg->dns1, cfg->dns2 };
hostName = cfg->hostName;
description = cfg->description;
break;
}
case serverConfigUtils::ConfigType::SelfHostedUser: {
const auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
if (!cfg.has_value()) return ErrorCode::InternalError;
container = cfg->defaultContainer;
containerConfigModel = cfg->containerConfig(container);
dns = { cfg->dns1, cfg->dns2 };
hostName = cfg->hostName;
description = cfg->description;
break;
}
case serverConfigUtils::ConfigType::Native: {
const auto cfg = m_serversRepository->nativeConfig(serverId);
if (!cfg.has_value()) return ErrorCode::InternalError;
container = cfg->defaultContainer;
containerConfigModel = cfg->containerConfig(container);
dns = { cfg->dns1, cfg->dns2 };
hostName = cfg->hostName;
description = cfg->description;
break;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
case serverConfigUtils::ConfigType::AmneziaFreeV3:
case serverConfigUtils::ConfigType::ExternalPremium: {
const auto cfg = m_serversRepository->apiV2Config(serverId);
if (!cfg.has_value()) return ErrorCode::InternalError;
container = cfg->defaultContainer;
containerConfigModel = cfg->containerConfig(container);
dns = { cfg->dns1, cfg->dns2 };
hostName = cfg->hostName;
description = cfg->description;
configVersion = serverConfigUtils::ConfigSource::AmneziaGateway;
isApiConfig = true;
break;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
case serverConfigUtils::ConfigType::AmneziaFreeV2:
return ErrorCode::InternalError;
case serverConfigUtils::ConfigType::Invalid:
default:
return ErrorCode::InternalError;
}
if (!isContainerSupported(container)) { if (!isContainerSupported(container)) {
return ErrorCode::NotSupportedOnThisPlatform; return ErrorCode::NotSupportedOnThisPlatform;
} }
if (dns.first.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.first)) {
if (m_appSettingsRepository->useAmneziaDns()) {
dns.first = protocols::dns::amneziaDnsIp;
} else {
dns.first = m_appSettingsRepository->primaryDns();
}
}
if (dns.second.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.second)) {
dns.second = m_appSettingsRepository->secondaryDns();
}
ContainerConfig containerConfigModel = m_serversRepository->containerConfig(serverIndex, container); vpnConfiguration = createConnectionConfiguration(dns, isApiConfig, hostName, description, configVersion,
containerConfigModel, container);
auto dns = serverConfigModel.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
vpnConfiguration = createConnectionConfiguration(dns, serverConfigModel, containerConfigModel, container);
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode ConnectionController::openConnection(int serverIndex) ErrorCode ConnectionController::openConnection(const QString &serverId)
{ {
QJsonObject vpnConfiguration; QJsonObject vpnConfiguration;
DockerContainer container; DockerContainer container;
ErrorCode errorCode = prepareConnection(serverIndex, vpnConfiguration, container); ErrorCode errorCode = prepareConnection(serverId, vpnConfiguration, container);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return errorCode; return errorCode;
} }
emit openConnectionRequested(serverIndex, container, vpnConfiguration); emit openConnectionRequested(serverId, container, vpnConfiguration);
return ErrorCode::NoError; return ErrorCode::NoError;
} }
@@ -120,7 +183,10 @@ ErrorCode ConnectionController::lastConnectionError() const
} }
QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QString, QString> &dns, QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QString, QString> &dns,
const ServerConfig &serverConfig, bool isApiConfig,
const QString &hostName,
const QString &description,
int configVersion,
const ContainerConfig &containerConfig, const ContainerConfig &containerConfig,
DockerContainer container) DockerContainer container)
{ {
@@ -134,7 +200,7 @@ QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QStr
ConnectionSettings connectionSettings = { ConnectionSettings connectionSettings = {
{ dns.first, dns.second }, { dns.first, dns.second },
serverConfig.isApiConfig(), isApiConfig,
{ {
m_appSettingsRepository->isSitesSplitTunnelingEnabled(), m_appSettingsRepository->isSitesSplitTunnelingEnabled(),
m_appSettingsRepository->routeMode() m_appSettingsRepository->routeMode()
@@ -160,10 +226,9 @@ QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QStr
vpnConfiguration[configKey::dns1] = dns.first; vpnConfiguration[configKey::dns1] = dns.first;
vpnConfiguration[configKey::dns2] = dns.second; vpnConfiguration[configKey::dns2] = dns.second;
vpnConfiguration[configKey::hostName] = serverConfig.hostName(); vpnConfiguration[configKey::hostName] = hostName;
vpnConfiguration[configKey::description] = serverConfig.description(); vpnConfiguration[configKey::description] = description;
vpnConfiguration[configKey::configVersion] = configVersion;
vpnConfiguration[configKey::configVersion] = serverConfig.configVersion();
return vpnConfiguration; return vpnConfiguration;
} }
@@ -30,11 +30,11 @@ public:
QObject* parent = nullptr); QObject* parent = nullptr);
~ConnectionController() = default; ~ConnectionController() = default;
ErrorCode prepareConnection(int serverIndex, ErrorCode prepareConnection(const QString &serverId,
QJsonObject& vpnConfiguration, QJsonObject& vpnConfiguration,
DockerContainer& container); DockerContainer& container);
ErrorCode openConnection(int serverIndex); ErrorCode openConnection(const QString &serverId);
void closeConnection(); void closeConnection();
@@ -50,7 +50,10 @@ public:
void setConnectionState(Vpn::ConnectionState state); void setConnectionState(Vpn::ConnectionState state);
QJsonObject createConnectionConfiguration(const QPair<QString, QString> &dns, QJsonObject createConnectionConfiguration(const QPair<QString, QString> &dns,
const ServerConfig &serverConfig, bool isApiConfig,
const QString &hostName,
const QString &description,
int configVersion,
const ContainerConfig &containerConfig, const ContainerConfig &containerConfig,
DockerContainer container); DockerContainer container);
@@ -60,7 +63,7 @@ public:
signals: signals:
void connectionStateChanged(Vpn::ConnectionState state); void connectionStateChanged(Vpn::ConnectionState state);
void openConnectionRequested(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration); void openConnectionRequested(const QString &serverId, DockerContainer container, const QJsonObject &vpnConfiguration);
void closeConnectionRequested(); void closeConnectionRequested();
void setConnectionStateRequested(Vpn::ConnectionState state); void setConnectionStateRequested(Vpn::ConnectionState state);
void killSwitchModeChangedRequested(bool enabled); void killSwitchModeChangedRequested(bool enabled);
+23 -6
View File
@@ -8,7 +8,6 @@
#include "core/controllers/selfhosted/installController.h" #include "core/controllers/selfhosted/installController.h"
#include "core/controllers/selfhosted/importController.h" #include "core/controllers/selfhosted/importController.h"
#include "core/controllers/coreSignalHandlers.h" #include "core/controllers/coreSignalHandlers.h"
#include "core/models/serverConfig.h"
#include "logger.h" #include "logger.h"
#include "secureQSettings.h" #include "secureQSettings.h"
@@ -101,6 +100,12 @@ void CoreController::initModels()
m_socks5ConfigModel = new Socks5ProxyConfigModel(this); m_socks5ConfigModel = new Socks5ProxyConfigModel(this);
setQmlContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel); setQmlContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel);
m_mtProxyConfigModel = new MtProxyConfigModel(this);
setQmlContextProperty("MtProxyConfigModel", m_mtProxyConfigModel);
m_telemtConfigModel = new TelemtConfigModel(this);
setQmlContextProperty("TelemtConfigModel", m_telemtConfigModel);
m_clientManagementModel = new ClientManagementModel(this); m_clientManagementModel = new ClientManagementModel(this);
setQmlContextProperty("ClientManagementModel", m_clientManagementModel); setQmlContextProperty("ClientManagementModel", m_clientManagementModel);
@@ -145,7 +150,7 @@ void CoreController::initCoreControllers()
m_allowedDnsController = new AllowedDnsController(m_appSettingsRepository); m_allowedDnsController = new AllowedDnsController(m_appSettingsRepository);
m_servicesCatalogController = new ServicesCatalogController(m_appSettingsRepository); m_servicesCatalogController = new ServicesCatalogController(m_appSettingsRepository);
m_subscriptionController = new SubscriptionController(m_serversRepository, m_appSettingsRepository); m_subscriptionController = new SubscriptionController(m_serversRepository, m_appSettingsRepository);
m_newsController = new NewsController(m_appSettingsRepository, m_serversController); m_newsController = new NewsController(m_appSettingsRepository, m_serversRepository);
m_updateController = new UpdateController(m_appSettingsRepository, this); m_updateController = new UpdateController(m_appSettingsRepository, this);
m_installController = new InstallController(m_serversRepository, m_appSettingsRepository, this); m_installController = new InstallController(m_serversRepository, m_appSettingsRepository, this);
@@ -165,12 +170,12 @@ void CoreController::initControllers()
setQmlContextProperty("FocusController", m_focusController); setQmlContextProperty("FocusController", m_focusController);
} }
m_installUiController = new InstallUiController(m_installController, m_serversController, m_settingsController, m_protocolsModel, m_usersController, m_installUiController = new InstallUiController(m_installController, m_serversController, m_settingsController, m_protocolsModel, m_usersController,
m_awgConfigModel, m_wireGuardConfigModel, m_openVpnConfigModel, m_xrayConfigModel, m_torConfigModel, m_awgConfigModel, m_wireGuardConfigModel, m_openVpnConfigModel, m_xrayConfigModel, m_torConfigModel,
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
m_ikev2ConfigModel, m_ikev2ConfigModel,
#endif #endif
m_sftpConfigModel, m_socks5ConfigModel, this); m_sftpConfigModel, m_socks5ConfigModel, m_mtProxyConfigModel, m_telemtConfigModel, this);
setQmlContextProperty("InstallController", m_installUiController); setQmlContextProperty("InstallController", m_installUiController);
m_importController = new ImportUiController(m_importCoreController, this); m_importController = new ImportUiController(m_importCoreController, this);
@@ -203,6 +208,10 @@ void CoreController::initControllers()
m_systemController = new SystemController(this); m_systemController = new SystemController(this);
setQmlContextProperty("SystemController", m_systemController); setQmlContextProperty("SystemController", m_systemController);
m_networkReachabilityController = new NetworkReachabilityController(this);
m_engine->rootContext()->setContextProperty("NetworkReachabilityController", m_networkReachabilityController);
m_engine->rootContext()->setContextProperty("NetworkReachability", m_networkReachabilityController);
m_servicesCatalogUiController = new ServicesCatalogUiController(m_servicesCatalogController, m_apiServicesModel, this); m_servicesCatalogUiController = new ServicesCatalogUiController(m_servicesCatalogController, m_apiServicesModel, this);
setQmlContextProperty("ServicesCatalogUiController", m_servicesCatalogUiController); setQmlContextProperty("ServicesCatalogUiController", m_servicesCatalogUiController);
@@ -262,9 +271,12 @@ void CoreController::initSignalHandlers()
{ {
m_signalHandlers = new CoreSignalHandlers(this, this); m_signalHandlers = new CoreSignalHandlers(this, this);
m_signalHandlers->initAllHandlers(); m_signalHandlers->initAllHandlers();
// Trigger initial update after handlers are connected // Trigger initial update after handlers are connected
m_serversUiController->updateModel(); m_serversUiController->updateModel();
if (m_serversUiController->hasServersFromGatewayApi()) {
m_apiNewsUiController->fetchNews(false);
}
} }
void CoreController::updateTranslator(const QLocale &locale) void CoreController::updateTranslator(const QLocale &locale)
@@ -322,11 +334,16 @@ PageController* CoreController::pageController() const
void CoreController::openConnectionByIndex(int serverIndex) void CoreController::openConnectionByIndex(int serverIndex)
{ {
const QString serverId =
m_serversUiController ? m_serversUiController->getServerId(serverIndex) : QString();
if (serverId.isEmpty()) {
return;
}
if (m_serversModel) { if (m_serversModel) {
m_serversModel->setProcessedServerIndex(serverIndex); m_serversModel->setProcessedServerIndex(serverIndex);
} }
if (m_serversController) { if (m_serversController) {
m_serversController->setDefaultServerIndex(serverIndex); m_serversController->setDefaultServer(serverId);
} }
m_connectionUiController->toggleConnection(); m_connectionUiController->toggleConnection();
} }
+7 -2
View File
@@ -28,6 +28,7 @@
#include "ui/controllers/languageUiController.h" #include "ui/controllers/languageUiController.h"
#include "ui/controllers/updateUiController.h" #include "ui/controllers/updateUiController.h"
#include "ui/controllers/api/servicesCatalogUiController.h" #include "ui/controllers/api/servicesCatalogUiController.h"
#include "ui/controllers/networkReachabilityController.h"
#include "core/controllers/serversController.h" #include "core/controllers/serversController.h"
#include "core/controllers/selfhosted/usersController.h" #include "core/controllers/selfhosted/usersController.h"
@@ -69,6 +70,9 @@
#include "ui/models/serversModel.h" #include "ui/models/serversModel.h"
#include "ui/models/services/sftpConfigModel.h" #include "ui/models/services/sftpConfigModel.h"
#include "ui/models/services/socks5ProxyConfigModel.h" #include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/services/mtProxyConfigModel.h"
#include "ui/models/services/telemtConfigModel.h"
#include "ui/models/ipSplitTunnelingModel.h" #include "ui/models/ipSplitTunnelingModel.h"
#include "ui/models/newsModel.h" #include "ui/models/newsModel.h"
@@ -84,7 +88,6 @@ class TestDefaultServerChange;
class TestServerEdgeCases; class TestServerEdgeCases;
class TestSignalOrder; class TestSignalOrder;
class TestServersModelSync; class TestServersModelSync;
class TestGatewayStacks;
class TestComplexOperations; class TestComplexOperations;
class TestSettingsSignals; class TestSettingsSignals;
class TestUiServersModelAndController; class TestUiServersModelAndController;
@@ -101,7 +104,6 @@ class CoreController : public QObject
friend class TestServerEdgeCases; friend class TestServerEdgeCases;
friend class TestSignalOrder; friend class TestSignalOrder;
friend class TestServersModelSync; friend class TestServersModelSync;
friend class TestGatewayStacks;
friend class TestComplexOperations; friend class TestComplexOperations;
friend class TestSettingsSignals; friend class TestSettingsSignals;
friend class TestUiServersModelAndController; friend class TestUiServersModelAndController;
@@ -158,6 +160,7 @@ private:
ServersUiController* m_serversUiController; ServersUiController* m_serversUiController;
IpSplitTunnelingUiController* m_ipSplitTunnelingUiController; IpSplitTunnelingUiController* m_ipSplitTunnelingUiController;
SystemController* m_systemController; SystemController* m_systemController;
NetworkReachabilityController* m_networkReachabilityController;
AppSplitTunnelingUiController* m_appSplitTunnelingUiController; AppSplitTunnelingUiController* m_appSplitTunnelingUiController;
AllowedDnsUiController* m_allowedDnsUiController; AllowedDnsUiController* m_allowedDnsUiController;
LanguageUiController* m_languageUiController; LanguageUiController* m_languageUiController;
@@ -210,6 +213,8 @@ private:
#endif #endif
SftpConfigModel* m_sftpConfigModel; SftpConfigModel* m_sftpConfigModel;
Socks5ProxyConfigModel* m_socks5ConfigModel; Socks5ProxyConfigModel* m_socks5ConfigModel;
MtProxyConfigModel* m_mtProxyConfigModel;
TelemtConfigModel* m_telemtConfigModel;
CoreSignalHandlers* m_signalHandlers; CoreSignalHandlers* m_signalHandlers;
}; };
+73 -57
View File
@@ -7,6 +7,7 @@
#include "core/utils/routeModes.h" #include "core/utils/routeModes.h"
#include "core/controllers/coreController.h" #include "core/controllers/coreController.h"
#include "core/repositories/secureServersRepository.h" #include "core/repositories/secureServersRepository.h"
#include "core/utils/serverConfigUtils.h"
#include "core/repositories/secureAppSettingsRepository.h" #include "core/repositories/secureAppSettingsRepository.h"
#include "vpnConnection.h" #include "vpnConnection.h"
#include "ui/controllers/qml/pageController.h" #include "ui/controllers/qml/pageController.h"
@@ -65,7 +66,6 @@ void CoreSignalHandlers::initAllHandlers()
initImportControllerHandler(); initImportControllerHandler();
initApiCountryModelUpdateHandler(); initApiCountryModelUpdateHandler();
initSubscriptionRefreshHandler(); initSubscriptionRefreshHandler();
initContainerModelUpdateHandler();
initAdminConfigRevokedHandler(); initAdminConfigRevokedHandler();
initPassphraseRequestHandler(); initPassphraseRequestHandler();
initTranslationsUpdatedHandler(); initTranslationsUpdatedHandler();
@@ -78,6 +78,7 @@ void CoreSignalHandlers::initAllHandlers()
initAllowedDnsModelUpdateHandler(); initAllowedDnsModelUpdateHandler();
initAppSplitTunnelingModelUpdateHandler(); initAppSplitTunnelingModelUpdateHandler();
initPrepareConfigHandler(); initPrepareConfigHandler();
initUnsupportedConnectDrawerHandler();
initStrictKillSwitchHandler(); initStrictKillSwitchHandler();
initAndroidSettingsHandler(); initAndroidSettingsHandler();
initAndroidConnectionHandler(); initAndroidConnectionHandler();
@@ -124,11 +125,9 @@ void CoreSignalHandlers::initInstallControllerHandler()
{ {
connect(m_coreController->m_installController, &InstallController::serverIsBusy, m_coreController->m_installUiController, &InstallUiController::serverIsBusy); connect(m_coreController->m_installController, &InstallController::serverIsBusy, m_coreController->m_installUiController, &InstallUiController::serverIsBusy);
connect(m_coreController->m_installUiController, &InstallUiController::cancelInstallation, m_coreController->m_installController, &InstallController::cancelInstallation); connect(m_coreController->m_installUiController, &InstallUiController::cancelInstallation, m_coreController->m_installController, &InstallController::cancelInstallation);
connect(m_coreController->m_installUiController, &InstallUiController::currentContainerUpdated, m_coreController->m_connectionUiController,
&ConnectionUiController::onCurrentContainerUpdated);
connect(m_coreController->m_serversUiController, &ServersUiController::processedServerIndexChanged, connect(m_coreController->m_serversUiController, &ServersUiController::processedServerIndexChanged,
m_coreController->m_installUiController, [this](int index) { m_coreController->m_installUiController, [this](int serverIndex) {
if (index >= 0) { if (serverIndex >= 0) {
m_coreController->m_installUiController->clearProcessedServerCredentials(); m_coreController->m_installUiController->clearProcessedServerCredentials();
} }
}); });
@@ -137,20 +136,20 @@ void CoreSignalHandlers::initInstallControllerHandler()
void CoreSignalHandlers::initExportControllerHandler() void CoreSignalHandlers::initExportControllerHandler()
{ {
connect(m_coreController->m_exportController, &ExportController::appendClientRequested, this, connect(m_coreController->m_exportController, &ExportController::appendClientRequested, this,
[this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) { [this](const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container); m_coreController->m_usersController->appendClient(serverId, clientId, clientName, container);
}); });
connect(m_coreController->m_exportController, &ExportController::updateClientsRequested, this, connect(m_coreController->m_exportController, &ExportController::updateClientsRequested, this,
[this](int serverIndex, DockerContainer container) { [this](const QString &serverId, DockerContainer container) {
m_coreController->m_usersController->updateClients(serverIndex, container); m_coreController->m_usersController->updateClients(serverId, container);
}); });
connect(m_coreController->m_exportController, &ExportController::revokeClientRequested, this, connect(m_coreController->m_exportController, &ExportController::revokeClientRequested, this,
[this](int serverIndex, int row, DockerContainer container) { [this](const QString &serverId, int row, DockerContainer container) {
m_coreController->m_usersController->revokeClient(serverIndex, row, container); m_coreController->m_usersController->revokeClient(serverId, row, container);
}); });
connect(m_coreController->m_exportController, &ExportController::renameClientRequested, this, connect(m_coreController->m_exportController, &ExportController::renameClientRequested, this,
[this](int serverIndex, int row, const QString &clientName, DockerContainer container) { [this](const QString &serverId, int row, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->renameClient(serverIndex, row, clientName, container); m_coreController->m_usersController->renameClient(serverId, row, clientName, container);
}); });
} }
@@ -159,9 +158,12 @@ void CoreSignalHandlers::initImportControllerHandler()
connect(m_coreController->m_importCoreController, &ImportController::importFinished, this, [this]() { connect(m_coreController->m_importCoreController, &ImportController::importFinished, this, [this]() {
if (!m_coreController->m_connectionController->isConnected()) { if (!m_coreController->m_connectionController->isConnected()) {
int newServerIndex = m_coreController->m_serversController->getServersCount() - 1; int newServerIndex = m_coreController->m_serversController->getServersCount() - 1;
m_coreController->m_serversController->setDefaultServerIndex(newServerIndex); const QString serverId = m_coreController->m_serversController->getServerId(newServerIndex);
if (!serverId.isEmpty()) {
m_coreController->m_serversController->setDefaultServer(serverId);
}
if (m_coreController->m_serversUiController) { if (m_coreController->m_serversUiController) {
m_coreController->m_serversUiController->setProcessedServerIndex(newServerIndex); m_coreController->m_serversUiController->setProcessedServerId(serverId);
} }
} }
}); });
@@ -170,21 +172,18 @@ void CoreSignalHandlers::initImportControllerHandler()
void CoreSignalHandlers::initApiCountryModelUpdateHandler() void CoreSignalHandlers::initApiCountryModelUpdateHandler()
{ {
connect(m_coreController->m_serversUiController, &ServersUiController::updateApiCountryModel, this, [this]() { connect(m_coreController->m_serversUiController, &ServersUiController::updateApiCountryModel, this, [this]() {
int processedIndex = m_coreController->m_serversUiController->getProcessedServerIndex(); const QString processedServerId = m_coreController->m_serversUiController->getProcessedServerId();
if (processedIndex < 0 || processedIndex >= m_coreController->m_serversRepository->serversCount()) { if (processedServerId.isEmpty()) {
return; return;
} }
ServerConfig server = m_coreController->m_serversRepository->server(processedIndex);
QJsonArray availableCountries; QJsonArray availableCountries;
QString serverCountryCode; QString serverCountryCode;
if (server.isApiV2()) { const auto apiV2 = m_coreController->m_serversRepository->apiV2Config(processedServerId);
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>(); if (apiV2.has_value()) {
if (apiV2) { availableCountries = apiV2->apiConfig.availableCountries;
availableCountries = apiV2->apiConfig.availableCountries; serverCountryCode = apiV2->apiConfig.serverCountryCode;
serverCountryCode = apiV2->apiConfig.serverCountryCode;
}
} }
m_coreController->m_apiCountryModel->updateModel(availableCountries, serverCountryCode); m_coreController->m_apiCountryModel->updateModel(availableCountries, serverCountryCode);
@@ -194,18 +193,9 @@ void CoreSignalHandlers::initApiCountryModelUpdateHandler()
void CoreSignalHandlers::initSubscriptionRefreshHandler() void CoreSignalHandlers::initSubscriptionRefreshHandler()
{ {
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::subscriptionRefreshNeeded, this, [this]() { connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::subscriptionRefreshNeeded, this, [this]() {
const int defaultServerIndex = m_coreController->m_serversController->getDefaultServerIndex(); const QString defaultServerId = m_coreController->m_serversController->getDefaultServerId();
if (defaultServerIndex >= 0) { if (!defaultServerId.isEmpty()) {
m_coreController->m_subscriptionUiController->getAccountInfo(defaultServerIndex, false); m_coreController->m_subscriptionUiController->getAccountInfo(defaultServerId, false);
}
});
}
void CoreSignalHandlers::initContainerModelUpdateHandler()
{
connect(m_coreController->m_serversController, &ServersController::gatewayStacksExpanded, this, [this]() {
if (m_coreController->m_serversUiController->hasServersFromGatewayApi()) {
m_coreController->m_apiNewsUiController->fetchNews(false);
} }
}); });
} }
@@ -213,17 +203,17 @@ void CoreSignalHandlers::initContainerModelUpdateHandler()
void CoreSignalHandlers::initAdminConfigRevokedHandler() void CoreSignalHandlers::initAdminConfigRevokedHandler()
{ {
connect(m_coreController->m_installController, &InstallController::clientRevocationRequested, this, connect(m_coreController->m_installController, &InstallController::clientRevocationRequested, this,
[this](int serverIndex, const ContainerConfig &containerConfig, DockerContainer container) { [this](const QString &serverId, const ContainerConfig &containerConfig, DockerContainer container) {
m_coreController->m_usersController->revokeClient(serverIndex, containerConfig, container); m_coreController->m_usersController->revokeClient(serverId, containerConfig, container);
}); });
connect(m_coreController->m_installController, &InstallController::clientAppendRequested, this, connect(m_coreController->m_installController, &InstallController::clientAppendRequested, this,
[this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) { [this](const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container); m_coreController->m_usersController->appendClient(serverId, clientId, clientName, container);
}); });
connect(m_coreController->m_usersController, &UsersController::adminConfigRevoked, m_coreController->m_serversController, connect(m_coreController->m_usersController, &UsersController::adminConfigRevoked, m_coreController->m_installController,
&ServersController::clearCachedProfile); &InstallController::clearCachedProfile);
} }
void CoreSignalHandlers::initPassphraseRequestHandler() void CoreSignalHandlers::initPassphraseRequestHandler()
@@ -251,7 +241,8 @@ void CoreSignalHandlers::initLanguageHandler()
void CoreSignalHandlers::initAutoConnectHandler() void CoreSignalHandlers::initAutoConnectHandler()
{ {
if (m_coreController->m_settingsUiController->isAutoConnectEnabled() && m_coreController->m_serversController->getDefaultServerIndex() >= 0) { if (m_coreController->m_settingsUiController->isAutoConnectEnabled()
&& !m_coreController->m_serversController->getDefaultServerId().isEmpty()) {
QTimer::singleShot(1000, this, [this]() { m_coreController->m_connectionUiController->openConnection(); }); QTimer::singleShot(1000, this, [this]() { m_coreController->m_connectionUiController->openConnection(); });
} }
} }
@@ -271,16 +262,20 @@ void CoreSignalHandlers::initServersModelUpdateHandler()
m_coreController->m_serversUiController, &ServersUiController::updateModel); m_coreController->m_serversUiController, &ServersUiController::updateModel);
connect(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged, connect(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged,
m_coreController->m_serversUiController, &ServersUiController::onDefaultServerChanged); m_coreController->m_serversUiController, &ServersUiController::onDefaultServerChanged);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded, connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded, this,
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks); [this](const QString &serverId) {
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited, if (m_coreController->m_serversRepository->apiV2Config(serverId).has_value()) {
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks); m_coreController->m_apiNewsUiController->fetchNews(false);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved, }
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks); });
connect(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished, connect(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished, this, [this]() {
m_coreController->m_serversUiController, &ServersUiController::updateModel); m_coreController->m_serversUiController->updateModel();
if (m_coreController->m_serversUiController->hasServersFromGatewayApi()) {
m_coreController->m_apiNewsUiController->fetchNews(false);
}
});
} }
void CoreSignalHandlers::initClientManagementModelUpdateHandler() void CoreSignalHandlers::initClientManagementModelUpdateHandler()
@@ -315,7 +310,19 @@ void CoreSignalHandlers::initPrepareConfigHandler()
connect(m_coreController->m_connectionUiController, &ConnectionUiController::prepareConfig, this, [this]() { connect(m_coreController->m_connectionUiController, &ConnectionUiController::prepareConfig, this, [this]() {
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Preparing); m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Preparing);
m_coreController->m_subscriptionUiController->validateConfig(); const QString serverId = m_coreController->m_serversController->getDefaultServerId();
if (serverId.isEmpty()) {
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
const serverConfigUtils::ConfigType kind = m_coreController->m_serversRepository->serverKind(serverId);
if (serverConfigUtils::isApiV2Subscription(kind) || serverConfigUtils::isLegacyApiSubscription(kind)) {
m_coreController->m_subscriptionUiController->validateConfig();
} else {
m_coreController->m_installUiController->validateConfig();
}
}); });
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::configValidated, this, [this](bool isValid) { connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::configValidated, this, [this](bool isValid) {
@@ -324,7 +331,7 @@ void CoreSignalHandlers::initPrepareConfigHandler()
return; return;
} }
m_coreController->m_installUiController->validateConfig(); m_coreController->m_connectionUiController->openConnection();
}); });
connect(m_coreController->m_installUiController, &InstallUiController::configValidated, this, [this](bool isValid) { connect(m_coreController->m_installUiController, &InstallUiController::configValidated, this, [this](bool isValid) {
@@ -337,6 +344,12 @@ void CoreSignalHandlers::initPrepareConfigHandler()
}); });
} }
void CoreSignalHandlers::initUnsupportedConnectDrawerHandler()
{
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::unsupportedConnectDrawerRequested,
m_coreController->m_pageController, &PageController::unsupportedConnectDrawerRequested);
}
void CoreSignalHandlers::initStrictKillSwitchHandler() void CoreSignalHandlers::initStrictKillSwitchHandler()
{ {
connect(m_coreController->m_settingsUiController, &SettingsUiController::strictKillSwitchEnabledChanged, m_coreController->m_connectionController, connect(m_coreController->m_settingsUiController, &SettingsUiController::strictKillSwitchEnabledChanged, m_coreController->m_connectionController,
@@ -348,7 +361,10 @@ void CoreSignalHandlers::initAndroidSettingsHandler()
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs); connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled); connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer); connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved, this,
[](const QString &/*serverId*/, int removedIndex) {
AndroidController::instance()->resetLastServer(removedIndex);
});
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); }); connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
#endif #endif
} }
+1 -1
View File
@@ -21,7 +21,6 @@ private:
void initImportControllerHandler(); void initImportControllerHandler();
void initApiCountryModelUpdateHandler(); void initApiCountryModelUpdateHandler();
void initSubscriptionRefreshHandler(); void initSubscriptionRefreshHandler();
void initContainerModelUpdateHandler();
void initAdminConfigRevokedHandler(); void initAdminConfigRevokedHandler();
void initPassphraseRequestHandler(); void initPassphraseRequestHandler();
void initTranslationsUpdatedHandler(); void initTranslationsUpdatedHandler();
@@ -34,6 +33,7 @@ private:
void initAllowedDnsModelUpdateHandler(); void initAllowedDnsModelUpdateHandler();
void initAppSplitTunnelingModelUpdateHandler(); void initAppSplitTunnelingModelUpdateHandler();
void initPrepareConfigHandler(); void initPrepareConfigHandler();
void initUnsupportedConnectDrawerHandler();
void initStrictKillSwitchHandler(); void initStrictKillSwitchHandler();
void initAndroidSettingsHandler(); void initAndroidSettingsHandler();
void initAndroidConnectionHandler(); void initAndroidConnectionHandler();
@@ -239,7 +239,7 @@ QFuture<QPair<ErrorCode, QByteArray>> GatewayController::postAsync(const QString
connect(reply, &QNetworkReply::sslErrors, [sslErrors](const QList<QSslError> &errors) { *sslErrors = errors; }); connect(reply, &QNetworkReply::sslErrors, [sslErrors](const QList<QSslError> &errors) { *sslErrors = errors; });
connect(reply, &QNetworkReply::finished, reply, [promise, sslErrors, encRequestData, endpoint, apiPayload, reply, this]() mutable { connect(reply, &QNetworkReply::finished, this, [promise, sslErrors, encRequestData, endpoint, apiPayload, reply, this]() mutable {
QByteArray encryptedResponseBody = reply->readAll(); QByteArray encryptedResponseBody = reply->readAll();
QString replyErrorString = reply->errorString(); QString replyErrorString = reply->errorString();
auto replyError = reply->error(); auto replyError = reply->error();
@@ -5,14 +5,13 @@
#include "core/configurators/configuratorBase.h" #include "core/configurators/configuratorBase.h"
#include "core/utils/selfhosted/sshSession.h" #include "core/utils/selfhosted/sshSession.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/qrCodeUtils.h" #include "core/utils/qrCodeUtils.h"
#include "core/utils/serialization/serialization.h" #include "core/utils/serialization/serialization.h"
#include "core/utils/protocolEnum.h" #include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h" #include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h" #include "core/utils/constants/protocolConstants.h"
#include "core/models/serverConfig.h" #include "core/models/selfhosted/selfHostedAdminServerConfig.h"
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/models/protocolConfig.h" #include "core/models/protocolConfig.h"
@@ -27,18 +26,20 @@ ExportController::ExportController(SecureServersRepository* serversRepository,
{ {
} }
ExportController::ExportResult ExportController::generateFullAccessConfig(int serverIndex) ExportController::ExportResult ExportController::generateFullAccessConfig(const QString &serverId)
{ {
ExportResult result; ExportResult result;
ServerConfig serverConfig = m_serversRepository->server(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
serverConfig.visit([](auto& arg) { if (!adminConfig.has_value()) {
for (auto it = arg.containers.begin(); it != arg.containers.end(); ++it) { result.errorCode = ErrorCode::InternalError;
it.value().protocolConfig.clearClientConfig(); return result;
} }
}); for (auto it = adminConfig->containers.begin(); it != adminConfig->containers.end(); ++it) {
it.value().protocolConfig.clearClientConfig();
}
QJsonObject serverJson = serverConfig.toJson(); QJsonObject serverJson = adminConfig->toJson();
QByteArray compressedConfig = QJsonDocument(serverJson).toJson(); QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
compressedConfig = qCompress(compressedConfig, 8); compressedConfig = qCompress(compressedConfig, 8);
result.config = generateVpnUrl(compressedConfig); result.config = generateVpnUrl(compressedConfig);
@@ -47,13 +48,22 @@ ExportController::ExportResult ExportController::generateFullAccessConfig(int se
return result; return result;
} }
ExportController::ExportResult ExportController::generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName) ExportController::ExportResult ExportController::generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName)
{ {
ExportResult result; ExportResult result;
DockerContainer container = static_cast<DockerContainer>(containerIndex); DockerContainer container = static_cast<DockerContainer>(containerIndex);
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container); if (!adminConfig.has_value()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
const ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
ContainerConfig containerConfig = adminConfig->containerConfig(container);
if (ContainerUtils::containerService(container) != ServiceType::Other) { if (ContainerUtils::containerService(container) != ServiceType::Other) {
SshSession sshSession; SshSession sshSession;
@@ -74,35 +84,25 @@ ExportController::ExportResult ExportController::generateConnectionConfig(int se
QString clientId = newProtocolConfig.clientId(); QString clientId = newProtocolConfig.clientId();
if (!clientId.isEmpty()) { if (!clientId.isEmpty()) {
emit appendClientRequested(serverIndex, clientId, clientName, container); emit appendClientRequested(serverId, clientId, clientName, container);
} }
} }
ServerConfig serverConfig = m_serversRepository->server(serverIndex); const QPair<QString, QString> dns = adminConfig->getDnsPair(m_appSettingsRepository->useAmneziaDns(),
serverConfig.visit([container, containerConfig](auto& arg) { m_appSettingsRepository->primaryDns(),
arg.containers.clear(); m_appSettingsRepository->secondaryDns());
arg.containers[container] = containerConfig;
arg.defaultContainer = container;
});
if (serverConfig.isSelfHosted()) { adminConfig->containers.clear();
SelfHostedServerConfig* selfHosted = serverConfig.as<SelfHostedServerConfig>(); adminConfig->containers[container] = containerConfig;
if (selfHosted) { adminConfig->defaultContainer = container;
selfHosted->userName.reset(); adminConfig->userName.clear();
selfHosted->password.reset(); adminConfig->password.clear();
selfHosted->port.reset(); adminConfig->port = 0;
}
}
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(), adminConfig->dns1 = dns.first;
m_appSettingsRepository->primaryDns(), adminConfig->dns2 = dns.second;
m_appSettingsRepository->secondaryDns());
serverConfig.visit([&dns](auto& arg) {
arg.dns1 = dns.first;
arg.dns2 = dns.second;
});
QJsonObject serverJson = serverConfig.toJson(); QJsonObject serverJson = adminConfig->toJson();
QByteArray compressedConfig = QJsonDocument(serverJson).toJson(); QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
compressedConfig = qCompress(compressedConfig, 8); compressedConfig = qCompress(compressedConfig, 8);
result.config = generateVpnUrl(compressedConfig); result.config = generateVpnUrl(compressedConfig);
@@ -111,7 +111,7 @@ ExportController::ExportResult ExportController::generateConnectionConfig(int se
return result; return result;
} }
ExportController::NativeConfigResult ExportController::generateNativeConfig(int serverIndex, DockerContainer container, ExportController::NativeConfigResult ExportController::generateNativeConfig(const QString &serverId, DockerContainer container,
const ContainerConfig &containerConfig, const ContainerConfig &containerConfig,
const QString &clientName) const QString &clientName)
{ {
@@ -123,11 +123,19 @@ ExportController::NativeConfigResult ExportController::generateNativeConfig(int
Proto protocol = ContainerUtils::defaultProtocol(container); Proto protocol = ContainerUtils::defaultProtocol(container);
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
ServerConfig serverConfig = m_serversRepository->server(serverIndex); if (!adminConfig.has_value()) {
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(), result.errorCode = ErrorCode::InternalError;
m_appSettingsRepository->primaryDns(), return result;
m_appSettingsRepository->secondaryDns()); }
const ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
const QPair<QString, QString> dns = adminConfig->getDnsPair(m_appSettingsRepository->useAmneziaDns(),
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
ContainerConfig modifiedContainerConfig = containerConfig; ContainerConfig modifiedContainerConfig = containerConfig;
modifiedContainerConfig.container = container; modifiedContainerConfig.container = container;
@@ -157,20 +165,25 @@ ExportController::NativeConfigResult ExportController::generateNativeConfig(int
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) { if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
QString clientId = newProtocolConfig.clientId(); QString clientId = newProtocolConfig.clientId();
if (!clientId.isEmpty()) { if (!clientId.isEmpty()) {
emit appendClientRequested(serverIndex, clientId, clientName, container); emit appendClientRequested(serverId, clientId, clientName, container);
} }
} }
return result; return result;
} }
ExportController::ExportResult ExportController::generateOpenVpnConfig(int serverIndex, const QString &clientName) ExportController::ExportResult ExportController::generateOpenVpnConfig(const QString &serverId, const QString &clientName)
{ {
ExportResult result; ExportResult result;
DockerContainer container = DockerContainer::OpenVpn; DockerContainer container = DockerContainer::OpenVpn;
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
ContainerConfig containerConfig = adminConfig->containerConfig(container);
auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName); auto nativeResult = generateNativeConfig(serverId, container, containerConfig, clientName);
if (nativeResult.errorCode != ErrorCode::NoError) { if (nativeResult.errorCode != ErrorCode::NoError) {
result.errorCode = nativeResult.errorCode; result.errorCode = nativeResult.errorCode;
return result; return result;
@@ -185,13 +198,18 @@ ExportController::ExportResult ExportController::generateOpenVpnConfig(int serve
return result; return result;
} }
ExportController::ExportResult ExportController::generateWireGuardConfig(int serverIndex, const QString &clientName) ExportController::ExportResult ExportController::generateWireGuardConfig(const QString &serverId, const QString &clientName)
{ {
ExportResult result; ExportResult result;
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::WireGuard); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
ContainerConfig containerConfig = adminConfig->containerConfig(DockerContainer::WireGuard);
auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::WireGuard, containerConfig, clientName); auto nativeResult = generateNativeConfig(serverId, DockerContainer::WireGuard, containerConfig, clientName);
if (nativeResult.errorCode != ErrorCode::NoError) { if (nativeResult.errorCode != ErrorCode::NoError) {
result.errorCode = nativeResult.errorCode; result.errorCode = nativeResult.errorCode;
return result; return result;
@@ -206,7 +224,7 @@ ExportController::ExportResult ExportController::generateWireGuardConfig(int ser
return result; return result;
} }
ExportController::ExportResult ExportController::generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName) ExportController::ExportResult ExportController::generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName)
{ {
ExportResult result; ExportResult result;
@@ -215,9 +233,14 @@ ExportController::ExportResult ExportController::generateAwgConfig(int serverInd
result.errorCode = ErrorCode::InternalError; result.errorCode = ErrorCode::InternalError;
return result; return result;
} }
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
ContainerConfig containerConfig = adminConfig->containerConfig(container);
auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName); auto nativeResult = generateNativeConfig(serverId, container, containerConfig, clientName);
if (nativeResult.errorCode != ErrorCode::NoError) { if (nativeResult.errorCode != ErrorCode::NoError) {
result.errorCode = nativeResult.errorCode; result.errorCode = nativeResult.errorCode;
return result; return result;
@@ -233,13 +256,18 @@ ExportController::ExportResult ExportController::generateAwgConfig(int serverInd
} }
ExportController::ExportResult ExportController::generateXrayConfig(int serverIndex, const QString &clientName) ExportController::ExportResult ExportController::generateXrayConfig(const QString &serverId, const QString &clientName)
{ {
ExportResult result; ExportResult result;
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::Xray); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
ContainerConfig containerConfig = adminConfig->containerConfig(DockerContainer::Xray);
auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::Xray, containerConfig, clientName); auto nativeResult = generateNativeConfig(serverId, DockerContainer::Xray, containerConfig, clientName);
if (nativeResult.errorCode != ErrorCode::NoError) { if (nativeResult.errorCode != ErrorCode::NoError) {
result.errorCode = nativeResult.errorCode; result.errorCode = nativeResult.errorCode;
return result; return result;
@@ -302,22 +330,22 @@ ExportController::ExportResult ExportController::generateXrayConfig(int serverIn
return result; return result;
} }
void ExportController::updateClientManagementModel(int serverIndex, int containerIndex) void ExportController::updateClientManagementModel(const QString &serverId, int containerIndex)
{ {
DockerContainer container = static_cast<DockerContainer>(containerIndex); DockerContainer container = static_cast<DockerContainer>(containerIndex);
emit updateClientsRequested(serverIndex, container); emit updateClientsRequested(serverId, container);
} }
void ExportController::revokeConfig(int row, int serverIndex, int containerIndex) void ExportController::revokeConfig(int row, const QString &serverId, int containerIndex)
{ {
DockerContainer container = static_cast<DockerContainer>(containerIndex); DockerContainer container = static_cast<DockerContainer>(containerIndex);
emit revokeClientRequested(serverIndex, row, container); emit revokeClientRequested(serverId, row, container);
} }
void ExportController::renameClient(int row, const QString &clientName, int serverIndex, int containerIndex) void ExportController::renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex)
{ {
DockerContainer container = static_cast<DockerContainer>(containerIndex); DockerContainer container = static_cast<DockerContainer>(containerIndex);
emit renameClientRequested(serverIndex, row, clientName, container); emit renameClientRequested(serverId, row, clientName, container);
} }
QString ExportController::generateVpnUrl(const QByteArray &compressedConfig) QString ExportController::generateVpnUrl(const QByteArray &compressedConfig)
@@ -37,23 +37,23 @@ public:
SecureAppSettingsRepository* appSettingsRepository, SecureAppSettingsRepository* appSettingsRepository,
QObject *parent = nullptr); QObject *parent = nullptr);
ExportResult generateFullAccessConfig(int serverIndex); ExportResult generateFullAccessConfig(const QString &serverId);
ExportResult generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName); ExportResult generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName);
ExportResult generateOpenVpnConfig(int serverIndex, const QString &clientName); ExportResult generateOpenVpnConfig(const QString &serverId, const QString &clientName);
ExportResult generateWireGuardConfig(int serverIndex, const QString &clientName); ExportResult generateWireGuardConfig(const QString &serverId, const QString &clientName);
ExportResult generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName); ExportResult generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName);
ExportResult generateXrayConfig(int serverIndex, const QString &clientName); ExportResult generateXrayConfig(const QString &serverId, const QString &clientName);
signals: signals:
void appendClientRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container); void appendClientRequested(const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container);
void updateClientsRequested(int serverIndex, DockerContainer container); void updateClientsRequested(const QString &serverId, DockerContainer container);
void revokeClientRequested(int serverIndex, int row, DockerContainer container); void revokeClientRequested(const QString &serverId, int row, DockerContainer container);
void renameClientRequested(int serverIndex, int row, const QString &clientName, DockerContainer container); void renameClientRequested(const QString &serverId, int row, const QString &clientName, DockerContainer container);
public slots: public slots:
void updateClientManagementModel(int serverIndex, int containerIndex); void updateClientManagementModel(const QString &serverId, int containerIndex);
void revokeConfig(int row, int serverIndex, int containerIndex); void revokeConfig(int row, const QString &serverId, int containerIndex);
void renameClient(int row, const QString &clientName, int serverIndex, int containerIndex); void renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex);
private: private:
struct NativeConfigResult struct NativeConfigResult
@@ -62,7 +62,7 @@ private:
QJsonObject jsonNativeConfig; QJsonObject jsonNativeConfig;
}; };
NativeConfigResult generateNativeConfig(int serverIndex, DockerContainer container, NativeConfigResult generateNativeConfig(const QString &serverId, DockerContainer container,
const ContainerConfig &containerConfig, const ContainerConfig &containerConfig,
const QString &clientName); const QString &clientName);
@@ -16,7 +16,7 @@
#include "core/utils/containerEnum.h" #include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h" #include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h" #include "core/utils/protocolEnum.h"
#include "core/utils/api/apiEnums.h" #include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h" #include "core/utils/constants/apiConstants.h"
#include "core/utils/api/apiUtils.h" #include "core/utils/api/apiUtils.h"
@@ -27,7 +27,6 @@
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h" #include "core/utils/constants/protocolConstants.h"
#include "core/utils/qrCodeUtils.h" #include "core/utils/qrCodeUtils.h"
#include "core/models/serverConfig.h"
using namespace amnezia; using namespace amnezia;
using namespace ProtocolUtils; using namespace ProtocolUtils;
@@ -208,12 +207,18 @@ ImportController::ImportResult ImportController::extractConfigFromData(const QSt
case ConfigTypes::Amnezia: { case ConfigTypes::Amnezia: {
result.config = QJsonDocument::fromJson(config.toUtf8()).object(); result.config = QJsonDocument::fromJson(config.toUtf8()).object();
if (apiUtils::isServerFromApi(result.config)) { if (serverConfigUtils::isServerFromApi(result.config)) {
auto apiConfig = result.config.value(apiDefs::key::apiConfig).toObject(); auto apiConfig = result.config.value(apiDefs::key::apiConfig).toObject();
apiConfig[apiDefs::key::vpnKey] = data; apiConfig[apiDefs::key::vpnKey] = data;
result.config[apiDefs::key::apiConfig] = apiConfig; result.config[apiDefs::key::apiConfig] = apiConfig;
} }
if (serverConfigUtils::isLegacyApiSubscription(serverConfigUtils::configTypeFromJson(result.config))) {
result.errorCode = ErrorCode::LegacyApiV1NotSupportedError;
result.config = {};
return result;
}
processAmneziaConfig(result.config); processAmneziaConfig(result.config);
if (!result.config.empty()) { if (!result.config.empty()) {
checkForMaliciousStrings(result.config, result.maliciousWarningText); checkForMaliciousStrings(result.config, result.maliciousWarningText);
@@ -381,18 +386,29 @@ void ImportController::importConfig(const QJsonObject &config)
credentials.secretData = config.value(configKey::password).toString(); credentials.secretData = config.value(configKey::password).toString();
if (credentials.isValid() || config.contains(configKey::containers)) { if (credentials.isValid() || config.contains(configKey::containers)) {
ServerConfig serverConfig = ServerConfig::fromJson(config); m_serversRepository->addServer(QString(), config, serverConfigUtils::configTypeFromJson(config));
m_serversRepository->addServer(serverConfig);
emit importFinished(); emit importFinished();
} else if (config.contains(configKey::configVersion)) { } else if (config.contains(configKey::configVersion)) {
quint16 crc = qChecksum(QJsonDocument(config).toJson()); quint16 crc = qChecksum(QJsonDocument(config).toJson());
if (m_serversRepository->hasServerWithCrc(crc)) { bool hasServerWithCrc = false;
const QVector<QString> ids = m_serversRepository->orderedServerIds();
for (const QString &id : ids) {
const auto apiV2 = m_serversRepository->apiV2Config(id);
if (!apiV2.has_value()) {
continue;
}
if (static_cast<quint16>(apiV2->crc) == crc) {
hasServerWithCrc = true;
break;
}
}
if (hasServerWithCrc) {
emit importErrorOccurred(ErrorCode::ApiConfigAlreadyAdded, true); emit importErrorOccurred(ErrorCode::ApiConfigAlreadyAdded, true);
} else { } else {
QJsonObject configWithCrc = config; QJsonObject configWithCrc = config;
configWithCrc.insert(configKey::crc, crc); configWithCrc.insert(configKey::crc, crc);
ServerConfig serverConfig = ServerConfig::fromJson(configWithCrc); m_serversRepository->addServer(QString(), configWithCrc, serverConfigUtils::configTypeFromJson(configWithCrc));
m_serversRepository->addServer(serverConfig);
emit importFinished(); emit importFinished();
} }
} else { } else {
@@ -19,6 +19,8 @@
#include "core/installers/openvpnInstaller.h" #include "core/installers/openvpnInstaller.h"
#include "core/installers/sftpInstaller.h" #include "core/installers/sftpInstaller.h"
#include "core/installers/socks5Installer.h" #include "core/installers/socks5Installer.h"
#include "core/installers/mtProxyInstaller.h"
#include "core/installers/telemtInstaller.h"
#include "core/installers/torInstaller.h" #include "core/installers/torInstaller.h"
#include "core/installers/wireguardInstaller.h" #include "core/installers/wireguardInstaller.h"
#include "core/installers/xrayInstaller.h" #include "core/installers/xrayInstaller.h"
@@ -33,8 +35,8 @@
#include "core/protocols/protocolUtils.h" #include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h" #include "core/utils/constants/protocolConstants.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/models/protocols/mtProxyProtocolConfig.h"
#include "core/models/protocols/awgProtocolConfig.h" #include "core/models/protocols/awgProtocolConfig.h"
#include "ui/models/protocols/wireguardConfigModel.h" #include "ui/models/protocols/wireguardConfigModel.h"
#include "core/utils/utilities.h" #include "core/utils/utilities.h"
@@ -54,6 +56,21 @@ using namespace ProtocolUtils;
namespace namespace
{ {
Logger logger("InstallController"); Logger logger("InstallController");
bool dockerDaemonContainerMissing(const QString &out, const QString &containerDockerName)
{
if (!out.contains(QLatin1String("Error response from daemon"), Qt::CaseInsensitive)) {
return false;
}
if (out.contains(QLatin1String("No such container"), Qt::CaseInsensitive)
&& out.contains(containerDockerName, Qt::CaseInsensitive)) {
return true;
}
if (out.size() < 700 && out.contains(QLatin1String("is not running"), Qt::CaseInsensitive)) {
return true;
}
return false;
}
} }
InstallController::InstallController(SecureServersRepository *serversRepository, InstallController::InstallController(SecureServersRepository *serversRepository,
@@ -129,15 +146,36 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
return startupContainerWorker(credentials, container, config, sshSession); return startupContainerWorker(credentials, container, config, sshSession);
} }
ErrorCode InstallController::updateContainer(int serverIndex, DockerContainer container, const ContainerConfig &oldConfig, ErrorCode InstallController::updateContainer(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig,
ContainerConfig &newConfig) ContainerConfig &newConfig)
{ {
if (!isUpdateDockerContainerRequired(container, oldConfig, newConfig)) { if (!isUpdateDockerContainerRequired(container, oldConfig, newConfig)) {
m_serversRepository->setContainerConfig(serverIndex, container, newConfig); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
if (container == DockerContainer::MtProxy) {
ServerCredentials credentials = adminConfig->credentials();
SshSession sshSession(this);
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
} else if (container == DockerContainer::Telemt) {
ServerCredentials credentials = adminConfig->credentials();
SshSession sshSession(this);
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
}
adminConfig->updateContainerConfig(container, newConfig);
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
SshSession sshSession(this); SshSession sshSession(this);
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig); bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
@@ -154,42 +192,56 @@ ErrorCode InstallController::updateContainer(int serverIndex, DockerContainer co
} }
if (errorCode == ErrorCode::NoError) { if (errorCode == ErrorCode::NoError) {
clearCachedProfile(serverIndex, container); if (container == DockerContainer::MtProxy) {
m_serversRepository->setContainerConfig(serverIndex, container, newConfig); MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
} else if (container == DockerContainer::Telemt) {
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
}
clearCachedProfile(serverId, container);
adminConfig->updateContainerConfig(container, newConfig);
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
} }
return errorCode; return errorCode;
} }
void InstallController::clearCachedProfile(int serverIndex, DockerContainer container) void InstallController::clearCachedProfile(const QString &serverId, DockerContainer container)
{ {
if (ContainerUtils::containerService(container) == ServiceType::Other) { if (ContainerUtils::containerService(container) == ServiceType::Other) {
return; return;
} }
ContainerConfig containerConfigModel = m_serversRepository->containerConfig(serverIndex, container); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
m_serversRepository->clearLastConnectionConfig(serverIndex, container); return;
emit clientRevocationRequested(serverIndex, containerConfigModel, container);
}
ErrorCode InstallController::validateAndPrepareConfig(int serverIndex)
{
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
if (serverConfigModel.isApiConfig()) {
return ErrorCode::NoError;
} }
DockerContainer container = serverConfigModel.defaultContainer(); adminConfig->clearCachedClientProfile(container);
const ContainerConfig containerConfigModel = adminConfig->containerConfig(container);
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
emit clientRevocationRequested(serverId, containerConfigModel, container);
}
ErrorCode InstallController::validateAndPrepareConfig(const QString &serverId)
{
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
DockerContainer container = adminConfig->defaultContainer;
if (container == DockerContainer::None) { if (container == DockerContainer::None) {
return ErrorCode::NoInstalledContainersError; return ErrorCode::NoInstalledContainersError;
} }
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container); ContainerConfig containerConfig = adminConfig->containerConfig(container);
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
SshSession sshSession; SshSession sshSession;
auto isProtocolConfigExists = [](const ContainerConfig &cfg) { auto isProtocolConfigExists = [](const ContainerConfig &cfg) {
@@ -198,20 +250,21 @@ ErrorCode InstallController::validateAndPrepareConfig(int serverIndex)
if (!isProtocolConfigExists(containerConfig)) { if (!isProtocolConfigExists(containerConfig)) {
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName()); QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
ErrorCode errorCode = processContainerForAdmin(container, containerConfig, credentials, sshSession, serverIndex, clientName); ErrorCode errorCode = processContainerForAdmin(container, containerConfig, credentials, sshSession, serverId, clientName);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return errorCode; return errorCode;
} }
m_serversRepository->setContainerConfig(serverIndex, container, containerConfig); adminConfig->updateContainerConfig(container, containerConfig);
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
} }
return ErrorCode::NoError; return ErrorCode::NoError;
} }
void InstallController::validateConfig(int serverIndex) void InstallController::validateConfig(const QString &serverId)
{ {
QFuture<ErrorCode> future = QtConcurrent::run([this, serverIndex]() { QFuture<ErrorCode> future = QtConcurrent::run([this, serverId]() {
return validateAndPrepareConfig(serverIndex); return validateAndPrepareConfig(serverId);
}); });
auto *watcher = new QFutureWatcher<ErrorCode>(this); auto *watcher = new QFutureWatcher<ErrorCode>(this);
@@ -230,6 +283,21 @@ void InstallController::validateConfig(int serverIndex)
watcher->setFuture(future); watcher->setFuture(future);
} }
void InstallController::addEmptyServer(const ServerCredentials &credentials)
{
SelfHostedAdminServerConfig serverConfig;
serverConfig.hostName = credentials.hostName;
serverConfig.userName = credentials.userName;
serverConfig.password = credentials.secretData;
serverConfig.port = credentials.port;
serverConfig.description = m_appSettingsRepository->nextAvailableServerName();
serverConfig.displayName = serverConfig.description.isEmpty() ? serverConfig.hostName : serverConfig.description;
serverConfig.defaultContainer = DockerContainer::None;
m_serversRepository->addServer(QString(), serverConfig.toJson(),
serverConfigUtils::ConfigType::SelfHostedAdmin);
}
ErrorCode InstallController::prepareContainerConfig(DockerContainer container, const ServerCredentials &credentials, ContainerConfig &containerConfig, SshSession &sshSession) ErrorCode InstallController::prepareContainerConfig(DockerContainer container, const ServerCredentials &credentials, ContainerConfig &containerConfig, SshSession &sshSession)
{ {
if (!ContainerUtils::isSupportedByCurrentPlatform(container)) { if (!ContainerUtils::isSupportedByCurrentPlatform(container)) {
@@ -257,7 +325,7 @@ ErrorCode InstallController::prepareContainerConfig(DockerContainer container, c
return ErrorCode::NoError; return ErrorCode::NoError;
} }
void InstallController::adminAppendRequested(int serverIndex, DockerContainer container, void InstallController::adminAppendRequested(const QString &serverId, DockerContainer container,
const ContainerConfig &containerConfig, const QString &clientName) const ContainerConfig &containerConfig, const QString &clientName)
{ {
if (ContainerUtils::containerService(container) == ServiceType::Other if (ContainerUtils::containerService(container) == ServiceType::Other
@@ -266,13 +334,13 @@ void InstallController::adminAppendRequested(int serverIndex, DockerContainer co
} }
QString clientId = containerConfig.protocolConfig.clientId(); QString clientId = containerConfig.protocolConfig.clientId();
if (!clientId.isEmpty()) { if (!clientId.isEmpty()) {
emit clientAppendRequested(serverIndex, clientId, clientName, container); emit clientAppendRequested(serverId, clientId, clientName, container);
} }
} }
ErrorCode InstallController::processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig, ErrorCode InstallController::processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig,
const ServerCredentials &credentials, SshSession &sshSession, const ServerCredentials &credentials, SshSession &sshSession,
int serverIndex, const QString &clientName) const QString &serverId, const QString &clientName)
{ {
if (ContainerUtils::isSupportedByCurrentPlatform(container)) { if (ContainerUtils::isSupportedByCurrentPlatform(container)) {
ErrorCode errorCode = prepareContainerConfig(container, credentials, containerConfig, sshSession); ErrorCode errorCode = prepareContainerConfig(container, credentials, containerConfig, sshSession);
@@ -280,7 +348,7 @@ ErrorCode InstallController::processContainerForAdmin(DockerContainer container,
return errorCode; return errorCode;
} }
} }
adminAppendRequested(serverIndex, container, containerConfig, clientName); adminAppendRequested(serverId, container, containerConfig, clientName);
return ErrorCode::NoError; return ErrorCode::NoError;
} }
@@ -372,9 +440,24 @@ ErrorCode InstallController::configureContainerWorker(const ServerCredentials &c
sshSession.replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container), baseVars), sshSession.replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container), baseVars),
cbReadStdOut, cbReadStdErr); cbReadStdOut, cbReadStdErr);
if (e != ErrorCode::NoError) {
return e;
}
if (dockerDaemonContainerMissing(stdOut, ContainerUtils::containerToString(container))) {
qDebug() << "configureContainerWorker: Docker daemon reports container missing/stopped, output:" << stdOut;
return ErrorCode::ServerContainerMissingError;
}
updateContainerConfigAfterInstallation(container, config, stdOut); updateContainerConfigAfterInstallation(container, config, stdOut);
return e; if (container == DockerContainer::MtProxy) {
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, config);
} else if (container == DockerContainer::Telemt) {
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, config);
}
return ErrorCode::NoError;
} }
ErrorCode InstallController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession) ErrorCode InstallController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession)
@@ -527,6 +610,79 @@ bool InstallController::isReinstallContainerRequired(DockerContainer container,
} }
} }
if (container == DockerContainer::MtProxy) {
const auto *oldMt = oldConfig.getMtProxyProtocolConfig();
const auto *newMt = newConfig.getMtProxyProtocolConfig();
if (oldMt && newMt) {
const QString oldPort =
oldMt->port.isEmpty() ? QString(protocols::mtProxy::defaultPort) : oldMt->port;
const QString newPort =
newMt->port.isEmpty() ? QString(protocols::mtProxy::defaultPort) : newMt->port;
if (oldPort != newPort) {
return true;
}
const QString oldTransport = oldMt->transportMode.isEmpty() ? QString(
protocols::mtProxy::transportModeStandard)
: oldMt->transportMode;
const QString newTransport = newMt->transportMode.isEmpty() ? QString(
protocols::mtProxy::transportModeStandard)
: newMt->transportMode;
if (oldTransport != newTransport) {
return true;
}
if (oldMt->tlsDomain != newMt->tlsDomain) {
return true;
}
}
}
if (container == DockerContainer::Telemt) {
const auto *oldT = oldConfig.getTelemtProtocolConfig();
const auto *newT = newConfig.getTelemtProtocolConfig();
if (oldT && newT) {
const QString oldPort =
oldT->port.isEmpty() ? QString(protocols::telemt::defaultPort) : oldT->port;
const QString newPort =
newT->port.isEmpty() ? QString(protocols::telemt::defaultPort) : newT->port;
if (oldPort != newPort) {
return true;
}
const QString oldTransport = oldT->transportMode.isEmpty()
? QString(protocols::telemt::transportModeStandard)
: oldT->transportMode;
const QString newTransport = newT->transportMode.isEmpty()
? QString(protocols::telemt::transportModeStandard)
: newT->transportMode;
if (oldTransport != newTransport) {
return true;
}
if (oldT->tlsDomain != newT->tlsDomain) {
return true;
}
if (oldT->maskEnabled != newT->maskEnabled) {
return true;
}
if (oldT->tlsEmulation != newT->tlsEmulation) {
return true;
}
if (oldT->useMiddleProxy != newT->useMiddleProxy) {
return true;
}
if (oldT->tag != newT->tag) {
return true;
}
const QString oldUser = oldT->userName.isEmpty()
? QString::fromUtf8(protocols::telemt::defaultUserName)
: oldT->userName;
const QString newUser = newT->userName.isEmpty()
? QString::fromUtf8(protocols::telemt::defaultUserName)
: newT->userName;
if (oldUser != newUser) {
return true;
}
}
}
if (container == DockerContainer::Socks5Proxy) { if (container == DockerContainer::Socks5Proxy) {
return true; return true;
} }
@@ -618,7 +774,7 @@ ErrorCode InstallController::isUserInSudo(const ServerCredentials &credentials,
return ErrorCode::ServerUserDirectoryNotAccessible; return ErrorCode::ServerUserDirectoryNotAccessible;
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on")) if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
return ErrorCode::ServerUserNotAllowedInSudoers; return ErrorCode::ServerUserNotAllowedInSudoers;
if (stdOut.contains("password is required")) if (stdOut.contains("password is required") || stdOut.contains("authentication is required"))
return ErrorCode::ServerUserPasswordRequired; return ErrorCode::ServerUserPasswordRequired;
return error; return error;
@@ -688,9 +844,16 @@ ErrorCode InstallController::setupServerFirewall(const ServerCredentials &creden
amnezia::genBaseVars(credentials, DockerContainer::None, QString(), QString()))); amnezia::genBaseVars(credentials, DockerContainer::None, QString(), QString())));
} }
ErrorCode InstallController::rebootServer(int serverIndex) ErrorCode InstallController::rebootServer(const QString &serverId)
{ {
auto credentials = m_serversRepository->serverCredentials(serverIndex); const auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
SshSession sshSession(this); SshSession sshSession(this);
QString script = QString("sudo reboot"); QString script = QString("sudo reboot");
@@ -709,27 +872,38 @@ ErrorCode InstallController::rebootServer(int serverIndex)
return sshSession.runScript(credentials, script, cbReadStdOut, cbReadStdErr); return sshSession.runScript(credentials, script, cbReadStdOut, cbReadStdErr);
} }
ErrorCode InstallController::removeAllContainers(int serverIndex) ErrorCode InstallController::removeAllContainers(const QString &serverId)
{ {
auto credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
SshSession sshSession(this); SshSession sshSession(this);
ErrorCode errorCode = sshSession.runScript(credentials, amnezia::scriptData(SharedScriptType::remove_all_containers)); ErrorCode errorCode = sshSession.runScript(credentials, amnezia::scriptData(SharedScriptType::remove_all_containers));
if (errorCode == ErrorCode::NoError) { if (errorCode == ErrorCode::NoError) {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); adminConfig->containers.clear();
serverConfigModel.visit([](auto& arg) { adminConfig->defaultContainer = DockerContainer::None;
arg.containers.clear(); m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
arg.defaultContainer = DockerContainer::None;
});
m_serversRepository->editServer(serverIndex, serverConfigModel);
} }
return errorCode; return errorCode;
} }
ErrorCode InstallController::removeContainer(int serverIndex, DockerContainer container) ErrorCode InstallController::removeContainer(const QString &serverId, DockerContainer container)
{ {
auto credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
SshSession sshSession(this); SshSession sshSession(this);
ErrorCode errorCode = sshSession.runScript( ErrorCode errorCode = sshSession.runScript(
credentials, credentials,
@@ -737,11 +911,10 @@ ErrorCode InstallController::removeContainer(int serverIndex, DockerContainer co
amnezia::genBaseVars(credentials, container, QString(), QString()))); amnezia::genBaseVars(credentials, container, QString(), QString())));
if (errorCode == ErrorCode::NoError) { if (errorCode == ErrorCode::NoError) {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); QMap<DockerContainer, ContainerConfig> containers = adminConfig->containers;
QMap<DockerContainer, ContainerConfig> containers = serverConfigModel.containers();
containers.remove(container); containers.remove(container);
DockerContainer defaultContainer = serverConfigModel.defaultContainer(); DockerContainer defaultContainer = adminConfig->defaultContainer;
if (defaultContainer == container) { if (defaultContainer == container) {
if (containers.isEmpty()) { if (containers.isEmpty()) {
defaultContainer = DockerContainer::None; defaultContainer = DockerContainer::None;
@@ -749,12 +922,10 @@ ErrorCode InstallController::removeContainer(int serverIndex, DockerContainer co
defaultContainer = containers.begin().key(); defaultContainer = containers.begin().key();
} }
} }
serverConfigModel.visit([&containers, defaultContainer](auto& arg) { adminConfig->containers = containers;
arg.containers = containers; adminConfig->defaultContainer = defaultContainer;
arg.defaultContainer = defaultContainer; m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
});
m_serversRepository->editServer(serverIndex, serverConfigModel);
} }
return errorCode; return errorCode;
@@ -772,6 +943,8 @@ QScopedPointer<InstallerBase> InstallController::createInstaller(DockerContainer
case DockerContainer::TorWebSite: return QScopedPointer<InstallerBase>(new TorInstaller(this)); case DockerContainer::TorWebSite: return QScopedPointer<InstallerBase>(new TorInstaller(this));
case DockerContainer::Sftp: return QScopedPointer<InstallerBase>(new SftpInstaller(this)); case DockerContainer::Sftp: return QScopedPointer<InstallerBase>(new SftpInstaller(this));
case DockerContainer::Socks5Proxy: return QScopedPointer<InstallerBase>(new Socks5Installer(this)); case DockerContainer::Socks5Proxy: return QScopedPointer<InstallerBase>(new Socks5Installer(this));
case DockerContainer::MtProxy: return QScopedPointer<InstallerBase>(new MtProxyInstaller(this));
case DockerContainer::Telemt: return QScopedPointer<InstallerBase>(new TelemtInstaller(this));
default: return QScopedPointer<InstallerBase>(new InstallerBase(this)); default: return QScopedPointer<InstallerBase>(new InstallerBase(this));
} }
} }
@@ -810,14 +983,35 @@ bool InstallController::isUpdateDockerContainerRequired(DockerContainer containe
return false; return false;
} }
} }
} else if (container == DockerContainer::MtProxy) {
const auto *oldMt = oldConfig.getMtProxyProtocolConfig();
const auto *newMt = newConfig.getMtProxyProtocolConfig();
if (!oldMt || !newMt) {
return true;
}
return !oldMt->equalsDockerDeploymentSettings(*newMt);
} else if (container == DockerContainer::Telemt) {
const auto *oldT = oldConfig.getTelemtProtocolConfig();
const auto *newT = newConfig.getTelemtProtocolConfig();
if (!oldT || !newT) {
return true;
}
return !oldT->equalsDockerDeploymentSettings(*newT);
} }
return true; return true;
} }
ErrorCode InstallController::scanServerForInstalledContainers(int serverIndex) ErrorCode InstallController::scanServerForInstalledContainers(const QString &serverId)
{ {
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
SshSession sshSession(this); SshSession sshSession(this);
QMap<DockerContainer, ContainerConfig> installedContainers; QMap<DockerContainer, ContainerConfig> installedContainers;
@@ -826,8 +1020,7 @@ ErrorCode InstallController::scanServerForInstalledContainers(int serverIndex)
return errorCode; return errorCode;
} }
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); QMap<DockerContainer, ContainerConfig> containers = adminConfig->containers;
QMap<DockerContainer, ContainerConfig> containers = serverConfigModel.containers();
bool hasNewContainers = false; bool hasNewContainers = false;
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName()); QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
@@ -835,29 +1028,25 @@ ErrorCode InstallController::scanServerForInstalledContainers(int serverIndex)
if (!containers.contains(iterator.key())) { if (!containers.contains(iterator.key())) {
ContainerConfig containerConfig = iterator.value(); ContainerConfig containerConfig = iterator.value();
errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession, errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession,
serverIndex, clientName); serverId, clientName);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return errorCode; return errorCode;
} }
containers.insert(iterator.key(), containerConfig); containers.insert(iterator.key(), containerConfig);
hasNewContainers = true; hasNewContainers = true;
DockerContainer defaultContainer = serverConfigModel.defaultContainer(); DockerContainer defaultContainer = adminConfig->defaultContainer;
if (defaultContainer == DockerContainer::None if (defaultContainer == DockerContainer::None
&& ContainerUtils::containerService(iterator.key()) != ServiceType::Other && ContainerUtils::containerService(iterator.key()) != ServiceType::Other
&& ContainerUtils::isSupportedByCurrentPlatform(iterator.key())) { && ContainerUtils::isSupportedByCurrentPlatform(iterator.key())) {
serverConfigModel.visit([iterator](auto& arg) { adminConfig->defaultContainer = iterator.key();
arg.defaultContainer = iterator.key();
});
} }
} }
} }
if (hasNewContainers) { if (hasNewContainers) {
serverConfigModel.visit([&containers](auto& arg) { adminConfig->containers = containers;
arg.containers = containers; m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
});
m_serversRepository->editServer(serverIndex, serverConfigModel);
} }
return ErrorCode::NoError; return ErrorCode::NoError;
@@ -899,7 +1088,7 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials,
preparedContainers.insert(container, containerConfig); preparedContainers.insert(container, containerConfig);
} }
SelfHostedServerConfig serverConfig; SelfHostedAdminServerConfig serverConfig;
serverConfig.hostName = credentials.hostName; serverConfig.hostName = credentials.hostName;
serverConfig.userName = credentials.userName; serverConfig.userName = credentials.userName;
serverConfig.password = credentials.secretData; serverConfig.password = credentials.secretData;
@@ -912,21 +1101,29 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials,
serverConfig.defaultContainer = container; serverConfig.defaultContainer = container;
m_serversRepository->addServer(ServerConfig(serverConfig)); serverConfig.displayName = serverConfig.description.isEmpty() ? serverConfig.hostName : serverConfig.description;
int serverIndex = m_serversRepository->serversCount() - 1; const QString newServerId = m_serversRepository->addServer(QString(), serverConfig.toJson(),
serverConfigUtils::ConfigType::SelfHostedAdmin);
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName()); QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
for (auto iterator = preparedContainers.begin(); iterator != preparedContainers.end(); iterator++) { for (auto iterator = preparedContainers.begin(); iterator != preparedContainers.end(); iterator++) {
adminAppendRequested(serverIndex, iterator.key(), iterator.value(), clientName); adminAppendRequested(newServerId, iterator.key(), iterator.value(), clientName);
} }
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode InstallController::installContainer(int serverIndex, DockerContainer container, int port, ErrorCode InstallController::installContainer(const QString &serverId, DockerContainer container, int port,
TransportProto transportProto, bool &wasContainerInstalled) TransportProto transportProto, bool &wasContainerInstalled)
{ {
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
SshSession sshSession(this); SshSession sshSession(this);
QMap<DockerContainer, ContainerConfig> installedContainers; QMap<DockerContainer, ContainerConfig> installedContainers;
@@ -949,15 +1146,17 @@ ErrorCode InstallController::installContainer(int serverIndex, DockerContainer c
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName()); QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) { for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
ContainerConfig existingConfigModel = m_serversRepository->containerConfig(serverIndex, iterator.key()); ContainerConfig existingConfigModel = adminConfig->containerConfig(iterator.key());
if (existingConfigModel.container == DockerContainer::None) { if (existingConfigModel.container == DockerContainer::None) {
ContainerConfig containerConfig = iterator.value(); ContainerConfig containerConfig = iterator.value();
errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession, errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession,
serverIndex, clientName); serverId, clientName);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return errorCode; return errorCode;
} }
m_serversRepository->setContainerConfig(serverIndex, iterator.key(), containerConfig); adminConfig->updateContainerConfig(iterator.key(), containerConfig);
m_serversRepository->editServer(serverId, adminConfig->toJson(),
serverConfigUtils::ConfigType::SelfHostedAdmin);
} }
} }
@@ -993,7 +1192,15 @@ bool InstallController::isServerAlreadyExists(const ServerCredentials &credentia
{ {
int serversCount = m_serversRepository->serversCount(); int serversCount = m_serversRepository->serversCount();
for (int i = 0; i < serversCount; i++) { for (int i = 0; i < serversCount; i++) {
const ServerCredentials existingCredentials = m_serversRepository->serverCredentials(i); const QString existingServerId = m_serversRepository->serverIdAt(i);
const auto adminConfig = m_serversRepository->selfHostedAdminConfig(existingServerId);
if (!adminConfig.has_value()) {
continue;
}
const ServerCredentials existingCredentials = adminConfig->credentials();
if (!existingCredentials.isValid()) {
continue;
}
if (credentials.hostName == existingCredentials.hostName && credentials.port == existingCredentials.port) { if (credentials.hostName == existingCredentials.hostName && credentials.port == existingCredentials.port) {
existingServerIndex = i; existingServerIndex = i;
return true; return true;
@@ -1093,6 +1300,56 @@ void InstallController::updateContainerConfigAfterInstallation(DockerContainer c
onion.replace("\n", ""); onion.replace("\n", "");
torProtocolConfig->serverConfig.site = onion; torProtocolConfig->serverConfig.site = onion;
} }
} else if (container == DockerContainer::MtProxy) {
if (auto* mtProxyConfig = containerConfig.getMtProxyProtocolConfig()) {
qDebug() << "amnezia mtproxy" << stdOut;
static const QRegularExpression reSecret(
QStringLiteral(R"(\[\*\]\s+Secret:\s+([0-9a-fA-F]{32}))"),
QRegularExpression::CaseInsensitiveOption);
static const QRegularExpression reTgLink(QStringLiteral(R"(\[\*\]\s+tg://\s+link:\s+(tg://proxy\?[^\s]+))"));
static const QRegularExpression reTmeLink(
QStringLiteral(R"(\[\*\]\s+t\.me\s+link:\s+(https://t\.me/proxy\?[^\s]+))"));
const QRegularExpressionMatch mSecret = reSecret.match(stdOut);
const QRegularExpressionMatch mTgLink = reTgLink.match(stdOut);
const QRegularExpressionMatch mTmeLink = reTmeLink.match(stdOut);
if (mSecret.hasMatch()) {
mtProxyConfig->secret = mSecret.captured(1);
}
if (mTgLink.hasMatch()) {
mtProxyConfig->tgLink = mTgLink.captured(1);
}
if (mTmeLink.hasMatch()) {
mtProxyConfig->tmeLink = mTmeLink.captured(1);
}
}
} else if (container == DockerContainer::Telemt) {
if (auto *telemtConfig = containerConfig.getTelemtProtocolConfig()) {
qDebug() << "amnezia-telemt configure stdout" << stdOut;
static const QRegularExpression reSecret(
QStringLiteral(R"(\[\*\]\s+Secret:\s+([0-9a-fA-F]{32}))"),
QRegularExpression::CaseInsensitiveOption);
static const QRegularExpression reTgLink(QStringLiteral(R"(\[\*\]\s+tg://\s+link:\s+(tg://proxy\?[^\s]+))"));
static const QRegularExpression reTmeLink(
QStringLiteral(R"(\[\*\]\s+t\.me\s+link:\s+(https://t\.me/proxy\?[^\s]+))"));
const QRegularExpressionMatch mSecret = reSecret.match(stdOut);
const QRegularExpressionMatch mTgLink = reTgLink.match(stdOut);
const QRegularExpressionMatch mTmeLink = reTmeLink.match(stdOut);
if (mSecret.hasMatch()) {
telemtConfig->secret = mSecret.captured(1);
}
if (mTgLink.hasMatch()) {
telemtConfig->tgLink = mTgLink.captured(1);
}
if (mTmeLink.hasMatch()) {
telemtConfig->tmeLink = mTmeLink.captured(1);
}
}
} }
} }
@@ -1177,3 +1434,126 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
return ErrorCode::NoError; return ErrorCode::NoError;
} }
ErrorCode InstallController::setDockerContainerEnabledState(const QString &serverId, DockerContainer container, bool enabled)
{
if (container != DockerContainer::MtProxy && container != DockerContainer::Telemt) {
return ErrorCode::InternalError;
}
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
const QString containerName = ContainerUtils::containerToString(container);
SshSession sshSession(this);
const QString script = enabled ? QStringLiteral("sudo docker start %1").arg(containerName)
: QStringLiteral("sudo docker stop %1").arg(containerName);
const ErrorCode runError = sshSession.runScript(credentials, script);
if (runError != ErrorCode::NoError) {
return runError;
}
ContainerConfig currentConfig = adminConfig->containerConfig(container);
bool persist = false;
if (auto *mtConfig = currentConfig.getMtProxyProtocolConfig()) {
mtConfig->isEnabled = enabled;
persist = true;
} else if (auto *telemtConfig = currentConfig.getTelemtProtocolConfig()) {
telemtConfig->isEnabled = enabled;
persist = true;
}
if (persist) {
adminConfig->updateContainerConfig(container, currentConfig);
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
}
return ErrorCode::NoError;
}
ErrorCode InstallController::queryDockerContainerStatus(const QString &serverId, DockerContainer container, int &statusOut)
{
statusOut = 3;
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
const QString containerName = ContainerUtils::containerToString(container);
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data;
return ErrorCode::NoError;
};
SshSession sshSession(this);
const QString script = QStringLiteral(
"sudo docker inspect --format '{{.State.Status}}' %1 2>/dev/null || echo 'not_found'")
.arg(containerName);
const ErrorCode errorCode = sshSession.runScript(credentials, script, cbReadStdOut);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
const QString status = stdOut.trimmed();
if (status == QLatin1String("running")) {
statusOut = 1;
} else if (status == QLatin1String("not_found") || status.isEmpty()) {
statusOut = 0;
} else if (status == QLatin1String("exited") || status == QLatin1String("created")
|| status == QLatin1String("paused")) {
statusOut = 2;
} else {
statusOut = 3;
}
return ErrorCode::NoError;
}
ErrorCode InstallController::queryMtProxyDiagnostics(const QString &serverId, DockerContainer container, int listenPort,
MtProxyContainerDiagnostics &out)
{
out = {};
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
SshSession sshSession(this);
return MtProxyInstaller::queryDiagnostics(sshSession, credentials, container, listenPort, out);
}
QString InstallController::fetchDockerContainerSecret(const QString &serverId, DockerContainer container)
{
if (container != DockerContainer::MtProxy && container != DockerContainer::Telemt) {
return {};
}
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return {};
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return {};
}
const QString containerName = ContainerUtils::containerToString(container);
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data;
return ErrorCode::NoError;
};
SshSession sshSession(this);
const QString path = QStringLiteral("/data/secret");
const QString cmd = QStringLiteral("sudo docker exec %1 cat %2").arg(containerName, path);
const ErrorCode errorCode = sshSession.runScript(credentials, cmd, cbReadStdOut);
if (errorCode != ErrorCode::NoError) {
return {};
}
const QString secret = stdOut.trimmed();
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
return hex32.match(secret).hasMatch() ? secret : QString();
}
@@ -16,6 +16,7 @@
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/repositories/secureServersRepository.h" #include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h" #include "core/repositories/secureAppSettingsRepository.h"
#include "core/installers/mtProxyInstaller.h"
class SshSession; class SshSession;
class InstallerBase; class InstallerBase;
@@ -33,22 +34,32 @@ public:
~InstallController(); ~InstallController();
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false); ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false);
ErrorCode updateContainer(int serverIndex, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig); ErrorCode updateContainer(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig);
ErrorCode rebootServer(int serverIndex); ErrorCode rebootServer(const QString &serverId);
ErrorCode removeAllContainers(int serverIndex); ErrorCode removeAllContainers(const QString &serverId);
ErrorCode removeContainer(int serverIndex, DockerContainer container); ErrorCode removeContainer(const QString &serverId, DockerContainer container);
ErrorCode setDockerContainerEnabledState(const QString &serverId, DockerContainer container, bool enabled);
/// statusOut: 0 = not deployed, 1 = running, 2 = stopped, 3 = error
ErrorCode queryDockerContainerStatus(const QString &serverId, DockerContainer container, int &statusOut);
ErrorCode queryMtProxyDiagnostics(const QString &serverId, DockerContainer container, int listenPort,
MtProxyContainerDiagnostics &out);
QString fetchDockerContainerSecret(const QString &serverId, DockerContainer container);
ContainerConfig generateConfig(DockerContainer container, int port, TransportProto transportProto); ContainerConfig generateConfig(DockerContainer container, int port, TransportProto transportProto);
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, ContainerConfig> &installedContainers, SshSession &sshSession); ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, ContainerConfig> &installedContainers, SshSession &sshSession);
ErrorCode scanServerForInstalledContainers(int serverIndex); ErrorCode scanServerForInstalledContainers(const QString &serverId);
ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config); ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config);
ErrorCode installServer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ErrorCode installServer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto,
bool &wasContainerInstalled); bool &wasContainerInstalled);
ErrorCode installContainer(int serverIndex, DockerContainer container, int port, TransportProto transportProto, ErrorCode installContainer(const QString &serverId, DockerContainer container, int port, TransportProto transportProto,
bool &wasContainerInstalled); bool &wasContainerInstalled);
bool isUpdateDockerContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig); bool isUpdateDockerContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
@@ -62,11 +73,13 @@ public:
void cancelInstallation(); void cancelInstallation();
void clearCachedProfile(int serverIndex, DockerContainer container); void clearCachedProfile(const QString &serverId, DockerContainer container);
ErrorCode validateAndPrepareConfig(int serverIndex); ErrorCode validateAndPrepareConfig(const QString &serverId);
void validateConfig(int serverIndex); void validateConfig(const QString &serverId);
void addEmptyServer(const ServerCredentials &credentials);
signals: signals:
void configValidated(bool isValid); void configValidated(bool isValid);
@@ -74,8 +87,8 @@ signals:
void serverIsBusy(const bool isBusy); void serverIsBusy(const bool isBusy);
void cancelInstallationRequested(); void cancelInstallationRequested();
void clientRevocationRequested(int serverIndex, const ContainerConfig &containerConfig, DockerContainer container); void clientRevocationRequested(const QString &serverId, const ContainerConfig &containerConfig, DockerContainer container);
void clientAppendRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container); void clientAppendRequested(const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container);
private: private:
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container, SshSession &sshSession); ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container, SshSession &sshSession);
@@ -95,9 +108,9 @@ private:
ErrorCode processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig, ErrorCode processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig,
const ServerCredentials &credentials, SshSession &sshSession, const ServerCredentials &credentials, SshSession &sshSession,
int serverIndex, const QString &clientName); const QString &serverId, const QString &clientName);
void adminAppendRequested(int serverIndex, DockerContainer container, void adminAppendRequested(const QString &serverId, DockerContainer container,
const ContainerConfig &containerConfig, const QString &clientName); const ContainerConfig &containerConfig, const QString &clientName);
static void updateContainerConfigAfterInstallation(DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut); static void updateContainerConfigAfterInstallation(DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut);
@@ -114,4 +127,3 @@ private:
}; };
#endif // INSTALLCONTROLLER_H #endif // INSTALLCONTROLLER_H
@@ -14,7 +14,6 @@
#include "core/protocols/protocolUtils.h" #include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h" #include "core/utils/constants/protocolConstants.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
using namespace amnezia; using namespace amnezia;
@@ -292,11 +291,18 @@ ErrorCode UsersController::getXrayClients(const DockerContainer container, const
return error; return error;
} }
ErrorCode UsersController::updateClients(int serverIndex, const DockerContainer container) ErrorCode UsersController::updateClients(const QString &serverId, const DockerContainer container)
{ {
ErrorCode error = ErrorCode::NoError; ErrorCode error = ErrorCode::NoError;
SshSession sshSession; SshSession sshSession;
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable"); QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
if (container == DockerContainer::OpenVpn) { if (container == DockerContainer::OpenVpn) {
@@ -381,20 +387,27 @@ ErrorCode UsersController::updateClients(int serverIndex, const DockerContainer
} }
ErrorCode UsersController::appendClient(int serverIndex, const QString &clientId, const QString &clientName, const DockerContainer container) ErrorCode UsersController::appendClient(const QString &serverId, const QString &clientId, const QString &clientName, const DockerContainer container)
{ {
ErrorCode error = ErrorCode::NoError; ErrorCode error = ErrorCode::NoError;
SshSession sshSession; SshSession sshSession;
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
error = updateClients(serverIndex, container); error = updateClients(serverId, container);
if (error != ErrorCode::NoError) { if (error != ErrorCode::NoError) {
return error; return error;
} }
int existingIndex = clientIndexById(clientId, m_clientsTable); int existingIndex = clientIndexById(clientId, m_clientsTable);
if (existingIndex >= 0) { if (existingIndex >= 0) {
return renameClient(serverIndex, existingIndex, clientName, container, true); return renameClient(serverId, existingIndex, clientName, container, true);
} }
QJsonObject client; QJsonObject client;
@@ -426,7 +439,7 @@ ErrorCode UsersController::appendClient(int serverIndex, const QString &clientId
return error; return error;
} }
ErrorCode UsersController::renameClient(int serverIndex, const int row, const QString &clientName, ErrorCode UsersController::renameClient(const QString &serverId, const int row, const QString &clientName,
const DockerContainer container, bool addTimeStamp) const DockerContainer container, bool addTimeStamp)
{ {
if (row < 0 || row >= m_clientsTable.size()) { if (row < 0 || row >= m_clientsTable.size()) {
@@ -434,7 +447,14 @@ ErrorCode UsersController::renameClient(int serverIndex, const int row, const QS
} }
SshSession sshSession; SshSession sshSession;
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
auto client = m_clientsTable.at(row).toObject(); auto client = m_clientsTable.at(row).toObject();
auto userData = client[configKey::userData].toObject(); auto userData = client[configKey::userData].toObject();
@@ -470,7 +490,7 @@ ErrorCode UsersController::renameClient(int serverIndex, const int row, const QS
} }
ErrorCode UsersController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials, ErrorCode UsersController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex, SshSession* sshSession, QJsonArray &clientsTable) SshSession* sshSession, QJsonArray &clientsTable)
{ {
if (row < 0 || row >= clientsTable.size()) { if (row < 0 || row >= clientsTable.size()) {
return ErrorCode::InternalError; return ErrorCode::InternalError;
@@ -689,14 +709,21 @@ ErrorCode UsersController::revokeXray(const int row,
return error; return error;
} }
ErrorCode UsersController::revokeClient(int serverIndex, const int index, const DockerContainer container) ErrorCode UsersController::revokeClient(const QString &serverId, const int index, const DockerContainer container)
{ {
if (index < 0 || index >= m_clientsTable.size()) { if (index < 0 || index >= m_clientsTable.size()) {
return ErrorCode::InternalError; return ErrorCode::InternalError;
} }
SshSession sshSession; SshSession sshSession;
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
QString clientId = m_clientsTable.at(index).toObject().value(configKey::clientId).toString(); QString clientId = m_clientsTable.at(index).toObject().value(configKey::clientId).toString();
ErrorCode errorCode = ErrorCode::NoError; ErrorCode errorCode = ErrorCode::NoError;
@@ -704,7 +731,7 @@ ErrorCode UsersController::revokeClient(int serverIndex, const int index, const
switch(container) switch(container)
{ {
case DockerContainer::OpenVpn: { case DockerContainer::OpenVpn: {
errorCode = revokeOpenVpn(index, container, credentials, serverIndex, &sshSession, m_clientsTable); errorCode = revokeOpenVpn(index, container, credentials, &sshSession, m_clientsTable);
break; break;
} }
case DockerContainer::WireGuard: case DockerContainer::WireGuard:
@@ -724,12 +751,15 @@ ErrorCode UsersController::revokeClient(int serverIndex, const int index, const
} }
if (errorCode == ErrorCode::NoError) { if (errorCode == ErrorCode::NoError) {
ServerConfig serverConfig = m_serversRepository->server(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
ContainerConfig containerCfg = m_serversRepository->containerConfig(serverIndex, container); if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ContainerConfig containerCfg = adminConfig->containerConfig(container);
QString containerClientId = containerCfg.protocolConfig.clientId(); QString containerClientId = containerCfg.protocolConfig.clientId();
if (!clientId.isEmpty() && !containerClientId.isEmpty() && containerClientId.contains(clientId)) { if (!clientId.isEmpty() && !containerClientId.isEmpty() && containerClientId.contains(clientId)) {
emit adminConfigRevoked(serverIndex, container); emit adminConfigRevoked(serverId, container);
} }
emit clientRevoked(index); emit clientRevoked(index);
@@ -739,13 +769,20 @@ ErrorCode UsersController::revokeClient(int serverIndex, const int index, const
return errorCode; return errorCode;
} }
ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container) ErrorCode UsersController::revokeClient(const QString &serverId, const ContainerConfig &containerConfig, const DockerContainer container)
{ {
SshSession sshSession; SshSession sshSession;
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
ErrorCode errorCode = ErrorCode::NoError; ErrorCode errorCode = ErrorCode::NoError;
errorCode = updateClients(serverIndex, container); errorCode = updateClients(serverId, container);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return errorCode; return errorCode;
} }
@@ -778,7 +815,7 @@ ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig &
switch (container) switch (container)
{ {
case DockerContainer::OpenVpn: { case DockerContainer::OpenVpn: {
errorCode = revokeOpenVpn(row, container, credentials, serverIndex, &sshSession, m_clientsTable); errorCode = revokeOpenVpn(row, container, credentials, &sshSession, m_clientsTable);
break; break;
} }
case DockerContainer::WireGuard: case DockerContainer::WireGuard:
@@ -797,7 +834,7 @@ ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig &
} }
if (errorCode == ErrorCode::NoError) { if (errorCode == ErrorCode::NoError) {
emit adminConfigRevoked(serverIndex, container); emit adminConfigRevoked(serverId, container);
emit clientRevoked(row); emit clientRevoked(row);
emit clientsUpdated(m_clientsTable); emit clientsUpdated(m_clientsTable);
} }
@@ -37,21 +37,21 @@ signals:
void clientAdded(const QJsonObject &client); void clientAdded(const QJsonObject &client);
void clientRenamed(int row, const QString &newName); void clientRenamed(int row, const QString &newName);
void clientRevoked(int row); void clientRevoked(int row);
void adminConfigRevoked(int serverIndex, DockerContainer container); void adminConfigRevoked(const QString &serverId, DockerContainer container);
public slots: public slots:
ErrorCode updateClients(int serverIndex, const DockerContainer container); ErrorCode updateClients(const QString &serverId, const DockerContainer container);
ErrorCode appendClient(int serverIndex, const QString &clientId, const QString &clientName, const DockerContainer container); ErrorCode appendClient(const QString &serverId, const QString &clientId, const QString &clientName, const DockerContainer container);
ErrorCode renameClient(int serverIndex, const int row, const QString &userName, const DockerContainer container, bool addTimeStamp = false); ErrorCode renameClient(const QString &serverId, const int row, const QString &userName, const DockerContainer container, bool addTimeStamp = false);
ErrorCode revokeClient(int serverIndex, const int index, const DockerContainer container); ErrorCode revokeClient(const QString &serverId, const int index, const DockerContainer container);
ErrorCode revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container); ErrorCode revokeClient(const QString &serverId, const ContainerConfig &containerConfig, const DockerContainer container);
private: private:
bool isClientExists(const QString &clientId, const QJsonArray &clientsTable); bool isClientExists(const QString &clientId, const QJsonArray &clientsTable);
int clientIndexById(const QString &clientId, const QJsonArray &clientsTable); int clientIndexById(const QString &clientId, const QJsonArray &clientsTable);
void migration(const QByteArray &clientsTableString, QJsonArray &clientsTable); void migration(const QByteArray &clientsTableString, QJsonArray &clientsTable);
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials, const int serverIndex, ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, QJsonArray &clientsTable); SshSession* sshSession, QJsonArray &clientsTable);
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials, ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, QJsonArray &clientsTable); SshSession* sshSession, QJsonArray &clientsTable);
@@ -73,4 +73,3 @@ private:
}; };
#endif // USERSCONTROLLER_H #endif // USERSCONTROLLER_H
+349 -140
View File
@@ -1,81 +1,268 @@
#include "serversController.h" #include "serversController.h"
#include "core/utils/networkUtilities.h" #include "core/utils/serverConfigUtils.h"
#include "core/utils/api/apiEnums.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/protocolEnum.h" #include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h" #include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/models/serverDescription.h"
#if defined(Q_OS_IOS) || defined(MACOS_NE) #if defined(Q_OS_IOS) || defined(MACOS_NE)
#include <AmneziaVPN-Swift.h> #include <AmneziaVPN-Swift.h>
#endif #endif
ServersController::ServersController(SecureServersRepository* serversRepository, ServersController::ServersController(SecureServersRepository *serversRepository,
SecureAppSettingsRepository* appSettingsRepository, SecureAppSettingsRepository *appSettingsRepository, QObject *parent)
QObject *parent)
: QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository) : QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository)
{ {
recomputeGatewayStacks(); ensureDefaultServerValid();
} }
void ServersController::addServer(const ServerConfig &server) void ServersController::ensureDefaultServerValid()
{ {
m_serversRepository->addServer(server); if (!getServersCount()) {
} return;
}
void ServersController::editServer(int index, const ServerConfig &server)
{ const QString defaultId = getDefaultServerId();
m_serversRepository->editServer(index, server); if (!defaultId.isEmpty() && indexOfServerId(defaultId) >= 0) {
} return;
}
void ServersController::removeServer(int index)
{ const QString firstId = getServerId(0);
m_serversRepository->removeServer(index); if (!firstId.isEmpty()) {
} setDefaultServer(firstId);
void ServersController::setDefaultServerIndex(int index)
{
m_serversRepository->setDefaultServer(index);
}
void ServersController::setDefaultContainer(int serverIndex, DockerContainer container)
{
m_serversRepository->setDefaultContainer(serverIndex, container);
}
void ServersController::updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config)
{
m_serversRepository->setContainerConfig(serverIndex, container, config);
}
void ServersController::clearCachedProfile(int serverIndex, DockerContainer container)
{
m_serversRepository->clearLastConnectionConfig(serverIndex, container);
}
QJsonArray ServersController::getServersArray() const
{
QJsonArray result;
QVector<ServerConfig> servers = m_serversRepository->servers();
for (const ServerConfig& server : servers) {
result.append(server.toJson());
} }
return result;
} }
QVector<ServerConfig> ServersController::getServers() const bool ServersController::renameServer(const QString &serverId, const QString &name)
{ {
return m_serversRepository->servers(); const serverConfigUtils::ConfigType kind = m_serversRepository->serverKind(serverId);
switch (kind) {
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
if (!cfg.has_value()) return false;
cfg->description = name;
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
return true;
}
case serverConfigUtils::ConfigType::SelfHostedUser: {
auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
if (!cfg.has_value()) return false;
cfg->description = name;
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
return true;
}
case serverConfigUtils::ConfigType::Native: {
auto cfg = m_serversRepository->nativeConfig(serverId);
if (!cfg.has_value()) return false;
cfg->description = name;
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
return true;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
case serverConfigUtils::ConfigType::AmneziaFreeV3:
case serverConfigUtils::ConfigType::ExternalPremium: {
auto cfg = m_serversRepository->apiV2Config(serverId);
if (!cfg.has_value()) return false;
cfg->name = name;
cfg->nameOverriddenByUser = true;
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
return true;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
case serverConfigUtils::ConfigType::AmneziaFreeV2:
case serverConfigUtils::ConfigType::Invalid:
default:
return false;
}
} }
ContainerConfig ServersController::getContainerConfig(int serverIndex, DockerContainer container) const void ServersController::removeServer(const QString &serverId)
{ {
return m_serversRepository->containerConfig(serverIndex, container); m_serversRepository->removeServer(serverId);
}
void ServersController::setDefaultServer(const QString &serverId)
{
m_serversRepository->setDefaultServer(serverId);
}
void ServersController::setDefaultContainer(const QString &serverId, DockerContainer container)
{
const serverConfigUtils::ConfigType kind = m_serversRepository->serverKind(serverId);
switch (kind) {
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
if (!cfg.has_value()) return;
cfg->defaultContainer = container;
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
return;
}
case serverConfigUtils::ConfigType::SelfHostedUser: {
auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
if (!cfg.has_value()) return;
cfg->defaultContainer = container;
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
return;
}
case serverConfigUtils::ConfigType::Native: {
auto cfg = m_serversRepository->nativeConfig(serverId);
if (!cfg.has_value()) return;
cfg->defaultContainer = container;
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
return;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
case serverConfigUtils::ConfigType::AmneziaFreeV3:
case serverConfigUtils::ConfigType::ExternalPremium: {
auto cfg = m_serversRepository->apiV2Config(serverId);
if (!cfg.has_value()) return;
cfg->defaultContainer = container;
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
return;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
case serverConfigUtils::ConfigType::AmneziaFreeV2:
case serverConfigUtils::ConfigType::Invalid:
default:
return;
}
}
QVector<ServerDescription> ServersController::buildServerDescriptions(bool isAmneziaDnsEnabled) const
{
QVector<ServerDescription> out;
const QVector<QString> ids = m_serversRepository->orderedServerIds();
out.reserve(ids.size());
for (const QString &id : ids) {
ServerDescription d;
using Kind = serverConfigUtils::ConfigType;
const Kind kind = m_serversRepository->serverKind(id);
switch (kind) {
case Kind::SelfHostedAdmin: {
const auto cfg = m_serversRepository->selfHostedAdminConfig(id);
if (!cfg) {
continue;
}
d = buildServerDescription(*cfg, isAmneziaDnsEnabled);
break;
}
case Kind::SelfHostedUser: {
const auto cfg = m_serversRepository->selfHostedUserConfig(id);
if (!cfg) {
continue;
}
d = buildServerDescription(*cfg, isAmneziaDnsEnabled);
break;
}
case Kind::Native: {
const auto cfg = m_serversRepository->nativeConfig(id);
if (!cfg) {
continue;
}
d = buildServerDescription(*cfg, isAmneziaDnsEnabled);
break;
}
case Kind::AmneziaPremiumV2:
case Kind::AmneziaFreeV3:
case Kind::ExternalPremium: {
const auto cfg = m_serversRepository->apiV2Config(id);
if (!cfg) {
continue;
}
d = buildServerDescription(*cfg, isAmneziaDnsEnabled);
break;
}
case Kind::AmneziaPremiumV1:
case Kind::AmneziaFreeV2: {
const auto cfg = m_serversRepository->legacyApiConfig(id);
if (!cfg) {
continue;
}
d = buildServerDescription(*cfg, isAmneziaDnsEnabled);
break;
}
case Kind::Invalid:
default:
continue;
}
d.serverId = id;
out.append(d);
}
return out;
}
QMap<DockerContainer, ContainerConfig> ServersController::getServerContainersMap(const QString &serverId) const
{
switch (m_serversRepository->serverKind(serverId)) {
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
return cfg.has_value() ? cfg->containers : QMap<DockerContainer, ContainerConfig>{};
}
case serverConfigUtils::ConfigType::SelfHostedUser: {
const auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
return cfg.has_value() ? cfg->containers : QMap<DockerContainer, ContainerConfig>{};
}
case serverConfigUtils::ConfigType::Native: {
const auto cfg = m_serversRepository->nativeConfig(serverId);
return cfg.has_value() ? cfg->containers : QMap<DockerContainer, ContainerConfig>{};
}
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
case serverConfigUtils::ConfigType::AmneziaFreeV3:
case serverConfigUtils::ConfigType::ExternalPremium: {
const auto cfg = m_serversRepository->apiV2Config(serverId);
return cfg.has_value() ? cfg->containers : QMap<DockerContainer, ContainerConfig>{};
}
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
case serverConfigUtils::ConfigType::AmneziaFreeV2: {
const auto cfg = m_serversRepository->legacyApiConfig(serverId);
return cfg.has_value() ? cfg->containers : QMap<DockerContainer, ContainerConfig>{};
}
case serverConfigUtils::ConfigType::Invalid:
default:
return {};
}
}
DockerContainer ServersController::getDefaultContainer(const QString &serverId) const
{
switch (m_serversRepository->serverKind(serverId)) {
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None;
}
case serverConfigUtils::ConfigType::SelfHostedUser: {
const auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None;
}
case serverConfigUtils::ConfigType::Native: {
const auto cfg = m_serversRepository->nativeConfig(serverId);
return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
case serverConfigUtils::ConfigType::AmneziaFreeV3:
case serverConfigUtils::ConfigType::ExternalPremium: {
const auto cfg = m_serversRepository->apiV2Config(serverId);
return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
case serverConfigUtils::ConfigType::AmneziaFreeV2: {
const auto cfg = m_serversRepository->legacyApiConfig(serverId);
return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None;
}
case serverConfigUtils::ConfigType::Invalid:
default:
return DockerContainer::None;
}
}
ContainerConfig ServersController::getContainerConfig(const QString &serverId, DockerContainer container) const
{
return getServerContainersMap(serverId).value(container);
} }
int ServersController::getDefaultServerIndex() const int ServersController::getDefaultServerIndex() const
@@ -83,114 +270,131 @@ int ServersController::getDefaultServerIndex() const
return m_serversRepository->defaultServerIndex(); return m_serversRepository->defaultServerIndex();
} }
QString ServersController::getDefaultServerId() const
{
return m_serversRepository->defaultServerId();
}
int ServersController::getServersCount() const int ServersController::getServersCount() const
{ {
return m_serversRepository->serversCount(); return m_serversRepository->serversCount();
} }
ServerConfig ServersController::getServerConfig(int serverIndex) const QString ServersController::getServerId(int serverIndex) const
{ {
return m_serversRepository->server(serverIndex); return m_serversRepository->serverIdAt(serverIndex);
} }
ServerCredentials ServersController::getServerCredentials(int serverIndex) const int ServersController::indexOfServerId(const QString &serverId) const
{ {
return m_serversRepository->serverCredentials(serverIndex); return m_serversRepository->indexOfServerId(serverId);
} }
QPair<QString, QString> ServersController::getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const QString ServersController::notificationDisplayName(const QString &serverId) const
{ {
ServerConfig serverConfig = m_serversRepository->server(serverIndex); if (serverId.isEmpty()) {
return serverConfig.getDnsPair(isAmneziaDnsEnabled, return {};
m_appSettingsRepository->primaryDns(), }
m_appSettingsRepository->secondaryDns());
}
ServersController::GatewayStacksData ServersController::gatewayStacks() const using Kind = serverConfigUtils::ConfigType;
{ switch (m_serversRepository->serverKind(serverId)) {
return m_gatewayStacks; case Kind::SelfHostedAdmin: {
} if (const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId)) {
if (!cfg->displayName.isEmpty()) {
void ServersController::recomputeGatewayStacks() return cfg->displayName;
{
GatewayStacksData computed;
bool hasNewTags = false;
QVector<ServerConfig> servers = m_serversRepository->servers();
for (const ServerConfig& serverConfig : servers) {
if (serverConfig.isApiV2()) {
const ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
if (!apiV2) continue;
const QString userCountryCode = apiV2->apiConfig.userCountryCode;
const QString serviceType = apiV2->serviceType();
if (!userCountryCode.isEmpty()) {
if (!m_gatewayStacks.userCountryCodes.contains(userCountryCode)) {
hasNewTags = true;
}
computed.userCountryCodes.insert(userCountryCode);
}
if (!serviceType.isEmpty()) {
if (!m_gatewayStacks.serviceTypes.contains(serviceType)) {
hasNewTags = true;
}
computed.serviceTypes.insert(serviceType);
} }
} }
break;
} }
case Kind::SelfHostedUser: {
m_gatewayStacks = std::move(computed); if (const auto cfg = m_serversRepository->selfHostedUserConfig(serverId)) {
if (hasNewTags) { if (!cfg->displayName.isEmpty()) {
emit gatewayStacksExpanded(); return cfg->displayName;
}
}
bool ServersController::GatewayStacksData::operator==(const GatewayStacksData &other) const
{
return userCountryCodes == other.userCountryCodes && serviceTypes == other.serviceTypes;
}
QJsonObject ServersController::GatewayStacksData::toJson() const
{
QJsonObject json;
QJsonArray userCountryCodesArray;
for (const QString &code : userCountryCodes) {
userCountryCodesArray.append(code);
}
json[apiDefs::key::userCountryCode] = userCountryCodesArray;
QJsonArray serviceTypesArray;
for (const QString &type : serviceTypes) {
serviceTypesArray.append(type);
}
json[apiDefs::key::serviceType] = serviceTypesArray;
return json;
}
bool ServersController::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const
{
QVector<ServerConfig> servers = m_serversRepository->servers();
for (const ServerConfig& serverConfig : servers) {
if (serverConfig.isApiV2()) {
const ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
if (!apiV2) return false;
if (apiV2->apiConfig.userCountryCode == userCountryCode
&& apiV2->serviceType() == serviceType
&& apiV2->serviceProtocol() == serviceProtocol) {
return true;
} }
} }
break;
}
case Kind::Native: {
if (const auto cfg = m_serversRepository->nativeConfig(serverId)) {
if (!cfg->displayName.isEmpty()) {
return cfg->displayName;
}
}
break;
}
case Kind::AmneziaPremiumV2:
case Kind::AmneziaFreeV3:
case Kind::ExternalPremium: {
if (const auto cfg = m_serversRepository->apiV2Config(serverId)) {
if (!cfg->displayName.isEmpty()) {
return cfg->displayName;
}
}
break;
}
case Kind::AmneziaPremiumV1:
case Kind::AmneziaFreeV2: {
if (const auto cfg = m_serversRepository->legacyApiConfig(serverId)) {
if (!cfg->displayName.isEmpty()) {
return cfg->displayName;
}
}
break;
}
default:
break;
}
const int idx = indexOfServerId(serverId);
if (idx >= 0) {
return QString::number(idx + 1);
}
return serverId;
}
std::optional<ApiV2ServerConfig> ServersController::apiV2Config(const QString &serverId) const
{
return m_serversRepository->apiV2Config(serverId);
}
std::optional<SelfHostedAdminServerConfig> ServersController::selfHostedAdminConfig(const QString &serverId) const
{
return m_serversRepository->selfHostedAdminConfig(serverId);
}
ServerCredentials ServersController::getServerCredentials(const QString &serverId) const
{
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
if (cfg.has_value()) {
const ServerCredentials creds = cfg->credentials();
if (creds.isValid()) {
return creds;
}
}
return ServerCredentials {};
}
bool ServersController::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol) const
{
const QVector<QString> ids = m_serversRepository->orderedServerIds();
for (const QString &id : ids) {
const auto apiV2 = m_serversRepository->apiV2Config(id);
if (!apiV2.has_value()) {
continue;
}
if (apiV2->apiConfig.userCountryCode == userCountryCode && apiV2->serviceType() == serviceType
&& apiV2->serviceProtocol() == serviceProtocol) {
return true;
}
} }
return false; return false;
} }
bool ServersController::hasInstalledContainers(int serverIndex) const bool ServersController::hasInstalledContainers(const QString &serverId) const
{ {
ServerConfig serverConfig = m_serversRepository->server(serverIndex); const QMap<DockerContainer, ContainerConfig> containers = getServerContainersMap(serverId);
QMap<DockerContainer, ContainerConfig> containers = serverConfig.containers();
for (auto it = containers.begin(); it != containers.end(); ++it) { for (auto it = containers.begin(); it != containers.end(); ++it) {
DockerContainer container = it.key(); DockerContainer container = it.key();
if (ContainerUtils::containerService(container) == ServiceType::Vpn) { if (ContainerUtils::containerService(container) == ServiceType::Vpn) {
@@ -203,3 +407,8 @@ bool ServersController::hasInstalledContainers(int serverIndex) const
return false; return false;
} }
bool ServersController::isLegacyApiV1Server(const QString &serverId) const
{
return !serverId.isEmpty()
&& serverConfigUtils::isLegacyApiSubscription(m_serversRepository->serverKind(serverId));
}
+23 -45
View File
@@ -1,11 +1,11 @@
#ifndef SERVERSCONTROLLER_H #ifndef SERVERSCONTROLLER_H
#define SERVERSCONTROLLER_H #define SERVERSCONTROLLER_H
#include <optional>
#include <QObject> #include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QSet>
#include <QVector> #include <QVector>
#include <QMap>
#include <QPair> #include <QPair>
@@ -17,34 +17,18 @@
#include "core/utils/commonStructs.h" #include "core/utils/commonStructs.h"
#include "core/repositories/secureServersRepository.h" #include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h" #include "core/repositories/secureAppSettingsRepository.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/models/serverDescription.h"
class SshSession; class SshSession;
class InstallController; class InstallController;
using namespace amnezia; using namespace amnezia;
/**
* @brief Core business logic controller for server operations
*
* This controller contains pure business logic for managing servers.
*/
class ServersController : public QObject class ServersController : public QObject
{ {
Q_OBJECT Q_OBJECT
public:
struct GatewayStacksData
{
QSet<QString> userCountryCodes;
QSet<QString> serviceTypes;
bool isEmpty() const { return userCountryCodes.isEmpty() && serviceTypes.isEmpty(); }
bool operator==(const GatewayStacksData &other) const;
QJsonObject toJson() const;
};
public: public:
explicit ServersController(SecureServersRepository* serversRepository, explicit ServersController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository = nullptr, SecureAppSettingsRepository* appSettingsRepository = nullptr,
@@ -52,44 +36,38 @@ public:
~ServersController() = default; ~ServersController() = default;
// Server management // Server management
void addServer(const ServerConfig &server); bool renameServer(const QString &serverId, const QString &name);
void editServer(int index, const ServerConfig &server); void removeServer(const QString &serverId);
void removeServer(int index); void setDefaultServer(const QString &serverId);
void setDefaultServerIndex(int index);
// Container management // Container management
void setDefaultContainer(int serverIndex, DockerContainer container); void setDefaultContainer(const QString &serverId, DockerContainer container);
void updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config);
// Cache management
void clearCachedProfile(int serverIndex, DockerContainer container);
// Getters // Getters
QJsonArray getServersArray() const; QVector<ServerDescription> buildServerDescriptions(bool isAmneziaDnsEnabled) const;
QVector<ServerConfig> getServers() const;
int getDefaultServerIndex() const; int getDefaultServerIndex() const;
QString getDefaultServerId() const;
int getServersCount() const; int getServersCount() const;
ServerConfig getServerConfig(int serverIndex) const; QString getServerId(int serverIndex) const;
ServerCredentials getServerCredentials(int serverIndex) const; int indexOfServerId(const QString &serverId) const;
ContainerConfig getContainerConfig(int serverIndex, DockerContainer container) const; QString notificationDisplayName(const QString &serverId) const;
QPair<QString, QString> getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const; std::optional<ApiV2ServerConfig> apiV2Config(const QString &serverId) const;
std::optional<SelfHostedAdminServerConfig> selfHostedAdminConfig(const QString &serverId) const;
GatewayStacksData gatewayStacks() const; ServerCredentials getServerCredentials(const QString &serverId) const;
QMap<DockerContainer, ContainerConfig> getServerContainersMap(const QString &serverId) const;
DockerContainer getDefaultContainer(const QString &serverId) const;
ContainerConfig getContainerConfig(const QString &serverId, DockerContainer container) const;
// Validation // Validation
bool isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const; bool isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const;
bool hasInstalledContainers(int serverIndex) const; bool hasInstalledContainers(const QString &serverId) const;
bool isLegacyApiV1Server(const QString &serverId) const;
signals:
void gatewayStacksExpanded();
public slots:
void recomputeGatewayStacks();
private: private:
void ensureDefaultServerValid();
SecureServersRepository* m_serversRepository; SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository; SecureAppSettingsRepository* m_appSettingsRepository;
GatewayStacksData m_gatewayStacks;
}; };
#endif // SERVERSCONTROLLER_H #endif // SERVERSCONTROLLER_H
@@ -179,12 +179,9 @@ QString SettingsController::getAppVersion() const
void SettingsController::clearSettings() void SettingsController::clearSettings()
{ {
int serverCount = m_serversRepository->serversCount();
m_appSettingsRepository->clearSettings(); m_appSettingsRepository->clearSettings();
m_serversRepository->setServersArray(QJsonArray()); m_serversRepository->clearServers();
m_serversRepository->setDefaultServer(0);
emit siteSplitTunnelingRouteModeChanged(RouteMode::VpnOnlyForwardSites); emit siteSplitTunnelingRouteModeChanged(RouteMode::VpnOnlyForwardSites);
emit siteSplitTunnelingToggled(false); emit siteSplitTunnelingToggled(false);
+6 -34
View File
@@ -21,14 +21,14 @@ namespace
Logger logger("UpdateController"); Logger logger("UpdateController");
#if defined(Q_OS_WINDOWS) #if defined(Q_OS_WINDOWS)
const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_x64.exe"); const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN-%1-win64.exe");
const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN_installer.exe"; const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN_installer.exe";
#elif defined(Q_OS_MACOS) #elif defined(Q_OS_MACOS)
const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_macos.pkg"); const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN-%1-Darwin.pkg");
const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.pkg"; const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.pkg";
#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) #elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_linux_x64.tar"); const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN-%1-Linux.run");
const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.tar"; const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.run";
#endif #endif
} }
@@ -346,36 +346,10 @@ int UpdateController::runMacInstaller(const QString &installerPath)
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
int UpdateController::runLinuxInstaller(const QString &installerPath) int UpdateController::runLinuxInstaller(const QString &installerPath)
{ {
// Create temporary directory for extraction QFile::setPermissions(installerPath, QFile::permissions(installerPath) | QFile::ExeUser);
QTemporaryDir extractDir;
extractDir.setAutoRemove(false);
if (!extractDir.isValid()) {
logger.error() << "Failed to create temporary directory";
return -1;
}
logger.info() << "Temporary directory created:" << extractDir.path();
// Create script file in the temporary directory
QString scriptPath = extractDir.path() + "/installer.sh";
QFile scriptFile(scriptPath);
if (!scriptFile.open(QIODevice::WriteOnly)) {
logger.error() << "Failed to create script file";
return -1;
}
// Get script content from registry
QString scriptContent = amnezia::scriptData(amnezia::ClientScriptType::linux_installer);
scriptFile.write(scriptContent.toUtf8());
scriptFile.close();
logger.info() << "Script file created:" << scriptPath;
// Make script executable
QFile::setPermissions(scriptPath, QFile::permissions(scriptPath) | QFile::ExeUser);
// Start detached process
qint64 pid; qint64 pid;
bool success = bool success = QProcess::startDetached(installerPath, QStringList(), QString(), &pid);
QProcess::startDetached("/bin/bash", QStringList() << scriptPath << extractDir.path() << installerPath, extractDir.path(), &pid);
if (success) { if (success) {
logger.info() << "Installation process started with PID:" << pid; logger.info() << "Installation process started with PID:" << pid;
@@ -387,5 +361,3 @@ int UpdateController::runLinuxInstaller(const QString &installerPath)
return 0; return 0;
} }
#endif #endif
@@ -0,0 +1,16 @@
#ifndef CONTAINERDIAGNOSTICS_H
#define CONTAINERDIAGNOSTICS_H
namespace amnezia
{
struct ContainerDiagnostics
{
bool available = false;
bool portReachable = false;
virtual ~ContainerDiagnostics() = default;
};
} // namespace amnezia
#endif // CONTAINERDIAGNOSTICS_H
@@ -0,0 +1,18 @@
#ifndef MTPROXYDIAGNOSTICS_H
#define MTPROXYDIAGNOSTICS_H
#include "containerDiagnostics.h"
#include <QString>
namespace amnezia {
struct MtProxyDiagnostics : ContainerDiagnostics {
bool upstreamReachable = false;
int clientsConnected = -1;
QString lastConfigRefresh;
QString statsEndpoint;
};
} // namespace amnezia
#endif // MTPROXYDIAGNOSTICS_H
@@ -0,0 +1,20 @@
#ifndef TELEMTDIAGNOSTICS_H
#define TELEMTDIAGNOSTICS_H
#include "containerDiagnostics.h"
#include <QString>
namespace amnezia
{
struct TelemtDiagnostics : ContainerDiagnostics
{
bool upstreamReachable = false;
int clientsConnected = -1;
QString lastConfigRefresh;
QString statsEndpoint;
};
} // namespace amnezia
#endif // TELEMTDIAGNOSTICS_H
+14
View File
@@ -14,6 +14,8 @@
#include "core/models/protocols/xrayProtocolConfig.h" #include "core/models/protocols/xrayProtocolConfig.h"
#include "core/models/protocols/sftpProtocolConfig.h" #include "core/models/protocols/sftpProtocolConfig.h"
#include "core/models/protocols/socks5ProxyProtocolConfig.h" #include "core/models/protocols/socks5ProxyProtocolConfig.h"
#include "core/models/protocols/mtProxyProtocolConfig.h"
#include "core/models/protocols/telemtProtocolConfig.h"
#include "core/models/protocols/ikev2ProtocolConfig.h" #include "core/models/protocols/ikev2ProtocolConfig.h"
#include "core/models/protocols/torProtocolConfig.h" #include "core/models/protocols/torProtocolConfig.h"
@@ -91,6 +93,18 @@ ContainerConfig InstallerBase::createBaseConfig(DockerContainer container, int p
config.protocolConfig = socks5Config; config.protocolConfig = socks5Config;
break; break;
} }
case Proto::MtProxy: {
MtProxyProtocolConfig mtConfig;
mtConfig.port = portStr;
config.protocolConfig = mtConfig;
break;
}
case Proto::Telemt: {
TelemtProtocolConfig telemtConfig;
telemtConfig.port = portStr;
config.protocolConfig = telemtConfig;
break;
}
case Proto::Ikev2: { case Proto::Ikev2: {
Ikev2ProtocolConfig ikev2Config; Ikev2ProtocolConfig ikev2Config;
config.protocolConfig = ikev2Config; config.protocolConfig = ikev2Config;
+130
View File
@@ -0,0 +1,130 @@
#include "mtProxyInstaller.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/models/containerConfig.h"
#include "core/models/protocols/mtProxyProtocolConfig.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QRegularExpression>
#include <QtGlobal>
using namespace amnezia;
namespace {
constexpr QLatin1String kMtProxyClientJsonPath("/data/amnezia-mtproxy-client.json");
constexpr QLatin1String kMtProxyClientJsonUploadPath("data/amnezia-mtproxy-client.json");
constexpr QLatin1String kMtProxySecretPath("/data/secret");
}
MtProxyInstaller::MtProxyInstaller(QObject *parent)
: InstallerBase(parent) {
}
ErrorCode MtProxyInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
SshSession *sshSession, ContainerConfig &config) {
if (container != DockerContainer::MtProxy || !sshSession) {
return ErrorCode::NoError;
}
MtProxyProtocolConfig *mt = config.getMtProxyProtocolConfig();
if (!mt) {
return ErrorCode::NoError;
}
ErrorCode jsonErr = ErrorCode::NoError;
const QByteArray jsonRaw =
sshSession->getTextFileFromContainer(container, credentials, QString(kMtProxyClientJsonPath), jsonErr);
if (jsonErr == ErrorCode::NoError && !jsonRaw.trimmed().isEmpty()) {
QJsonParseError parseError;
const QJsonDocument doc = QJsonDocument::fromJson(jsonRaw.trimmed(), &parseError);
if (parseError.error == QJsonParseError::NoError && doc.isObject()) {
QJsonObject merged = mt->toJson();
const QJsonObject snap = doc.object();
for (auto it = snap.constBegin(); it != snap.constEnd(); ++it) {
merged.insert(it.key(), it.value());
}
*mt = MtProxyProtocolConfig::fromJson(merged);
}
}
ErrorCode secretErr = ErrorCode::NoError;
const QByteArray secretRaw =
sshSession->getTextFileFromContainer(container, credentials, QString(kMtProxySecretPath), secretErr);
const QString sec = QString::fromUtf8(secretRaw).trimmed();
if (sec.length() == 32) {
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
if (hex32.match(sec).hasMatch()) {
mt->secret = sec;
}
}
return ErrorCode::NoError;
}
ErrorCode MtProxyInstaller::queryDiagnostics(SshSession &sshSession, const ServerCredentials &credentials,
DockerContainer container, int listenPort,
MtProxyContainerDiagnostics &out)
{
out = {};
if (container != DockerContainer::MtProxy && container != DockerContainer::Telemt) {
return ErrorCode::InternalError;
}
const QString containerName = ContainerUtils::containerToString(container);
const QString script =
QStringLiteral(
"PORT_OK=$(sudo docker exec %1 sh -c 'ss -tlnp 2>/dev/null | grep -q :%2 && echo yes || echo no' 2>/dev/null || echo no); "
"TG_OK=$(curl -s --max-time 5 -o /dev/null -w '%%{http_code}' https://core.telegram.org/getProxySecret 2>/dev/null | grep -q '200' && echo yes || echo no); "
"CLIENTS=$(sudo docker exec amnezia-mtproxy sh -c 'curl -s --max-time 3 http://localhost:2398/stats 2>/dev/null | grep -o \"total_special_connections:[0-9]*\" | cut -d: -f2' 2>/dev/null); "
"CONF_TIME=$(sudo docker exec amnezia-mtproxy sh -c 'stat -c \"%%y\" /data/proxy-multi.conf 2>/dev/null | cut -d. -f1' 2>/dev/null || echo unknown); "
"echo \"PORT_OK=${PORT_OK}\"; "
"echo \"TG_OK=${TG_OK}\"; "
"echo \"CLIENTS=${CLIENTS:-0}\"; "
"echo \"CONF_TIME=${CONF_TIME}\"; "
"echo \"STATS=http://localhost:2398/stats\";")
.arg(containerName)
.arg(listenPort);
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data;
return ErrorCode::NoError;
};
const ErrorCode errorCode = sshSession.runScript(credentials, script, cbReadStdOut);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
for (const QString &line : stdOut.split('\n', Qt::SkipEmptyParts)) {
if (line.startsWith(QLatin1String("PORT_OK="))) {
out.portReachable = line.mid(8).trimmed() == QLatin1String("yes");
} else if (line.startsWith(QLatin1String("TG_OK="))) {
out.upstreamReachable = line.mid(6).trimmed() == QLatin1String("yes");
} else if (line.startsWith(QLatin1String("CLIENTS="))) {
out.clientsConnected = line.mid(8).trimmed().toInt();
} else if (line.startsWith(QLatin1String("CONF_TIME="))) {
out.lastConfigRefresh = line.mid(10).trimmed();
} else if (line.startsWith(QLatin1String("STATS="))) {
out.statsEndpoint = line.mid(6).trimmed();
}
}
return ErrorCode::NoError;
}
void MtProxyInstaller::uploadClientSettingsSnapshot(SshSession &sshSession, const ServerCredentials &credentials,
DockerContainer container, const ContainerConfig &config) {
const MtProxyProtocolConfig *mt = config.getMtProxyProtocolConfig();
if (!mt) {
return;
}
const QByteArray payload = QJsonDocument(mt->toJson()).toJson(QJsonDocument::Compact);
const ErrorCode err = sshSession.uploadTextFileToContainer(container, credentials, QString::fromUtf8(payload),
QString(kMtProxyClientJsonUploadPath));
if (err != ErrorCode::NoError) {
qWarning() << "MtProxyInstaller::uploadClientSettingsSnapshot failed" << err;
}
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef MTPROXYINSTALLER_H
#define MTPROXYINSTALLER_H
#include "installerBase.h"
#include <QString>
struct MtProxyContainerDiagnostics {
bool portReachable = false;
bool upstreamReachable = false;
int clientsConnected = -1;
QString lastConfigRefresh;
QString statsEndpoint;
};
class MtProxyInstaller : public InstallerBase {
Q_OBJECT
public:
explicit MtProxyInstaller(QObject *parent = nullptr);
amnezia::ErrorCode
extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
SshSession *sshSession, amnezia::ContainerConfig &config) override;
static void uploadClientSettingsSnapshot(SshSession &sshSession, const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container,
const amnezia::ContainerConfig &config);
static amnezia::ErrorCode queryDiagnostics(SshSession &sshSession, const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container, int listenPort,
MtProxyContainerDiagnostics &out);
};
#endif // MTPROXYINSTALLER_H
@@ -0,0 +1,79 @@
#include "telemtInstaller.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/models/containerConfig.h"
#include "core/models/protocols/telemtProtocolConfig.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QRegularExpression>
#include <QtGlobal>
using namespace amnezia;
namespace {
constexpr QLatin1String kTelemtClientJsonPath("/data/amnezia-telemt-client.json");
constexpr QLatin1String kTelemtClientJsonUploadPath("data/amnezia-telemt-client.json");
constexpr QLatin1String kTelemtSecretPath("/data/secret");
}
TelemtInstaller::TelemtInstaller(QObject *parent) : InstallerBase(parent) {}
ErrorCode TelemtInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
SshSession *sshSession, ContainerConfig &config) {
if (container != DockerContainer::Telemt || !sshSession) {
return ErrorCode::NoError;
}
TelemtProtocolConfig *tc = config.getTelemtProtocolConfig();
if (!tc) {
return ErrorCode::NoError;
}
ErrorCode jsonErr = ErrorCode::NoError;
const QByteArray jsonRaw =
sshSession->getTextFileFromContainer(container, credentials, QString(kTelemtClientJsonPath), jsonErr);
if (jsonErr == ErrorCode::NoError && !jsonRaw.trimmed().isEmpty()) {
QJsonParseError parseError;
const QJsonDocument doc = QJsonDocument::fromJson(jsonRaw.trimmed(), &parseError);
if (parseError.error == QJsonParseError::NoError && doc.isObject()) {
QJsonObject merged = tc->toJson();
const QJsonObject snap = doc.object();
for (auto it = snap.constBegin(); it != snap.constEnd(); ++it) {
merged.insert(it.key(), it.value());
}
*tc = TelemtProtocolConfig::fromJson(merged);
}
}
ErrorCode secretErr = ErrorCode::NoError;
const QByteArray secretRaw =
sshSession->getTextFileFromContainer(container, credentials, QString(kTelemtSecretPath), secretErr);
const QString sec = QString::fromUtf8(secretRaw).trimmed();
if (sec.length() == 32) {
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
if (hex32.match(sec).hasMatch()) {
tc->secret = sec;
}
}
return ErrorCode::NoError;
}
void TelemtInstaller::uploadClientSettingsSnapshot(SshSession &sshSession, const ServerCredentials &credentials,
DockerContainer container, const ContainerConfig &config) {
const TelemtProtocolConfig *tc = config.getTelemtProtocolConfig();
if (!tc) {
return;
}
const QByteArray payload = QJsonDocument(tc->toJson()).toJson(QJsonDocument::Compact);
const ErrorCode err = sshSession.uploadTextFileToContainer(container, credentials, QString::fromUtf8(payload),
QString(kTelemtClientJsonUploadPath));
if (err != ErrorCode::NoError) {
qWarning() << "TelemtInstaller::uploadClientSettingsSnapshot failed" << err;
}
}
+20
View File
@@ -0,0 +1,20 @@
#ifndef TELEMTINSTALLER_H
#define TELEMTINSTALLER_H
#include "installerBase.h"
class TelemtInstaller : public InstallerBase {
Q_OBJECT
public:
explicit TelemtInstaller(QObject *parent = nullptr);
amnezia::ErrorCode
extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
SshSession *sshSession, amnezia::ContainerConfig &config) override;
static void uploadClientSettingsSnapshot(SshSession &sshSession, const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container,
const amnezia::ContainerConfig &config);
};
#endif // TELEMTINSTALLER_H
+1 -1
View File
@@ -6,7 +6,7 @@
#include <QString> #include <QString>
#include <QDateTime> #include <QDateTime>
#include "core/utils/api/apiEnums.h" #include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h" #include "core/utils/constants/apiConstants.h"
@@ -1,140 +0,0 @@
#include "apiV1ServerConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/api/apiUtils.h"
namespace amnezia
{
using namespace ContainerEnumNS;
bool ApiV1ServerConfig::isPremium() const
{
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
return apiEndpoint.contains(premiumV1Endpoint);
}
bool ApiV1ServerConfig::isFree() const
{
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
return apiEndpoint.contains(freeV2Endpoint);
}
QString ApiV1ServerConfig::vpnKey() const
{
QJsonObject json = toJson();
return apiUtils::getPremiumV1VpnKey(json);
}
bool ApiV1ServerConfig::hasContainers() const
{
return !containers.isEmpty();
}
ContainerConfig ApiV1ServerConfig::containerConfig(DockerContainer container) const
{
if (!containers.contains(container)) {
return ContainerConfig{};
}
return containers.value(container);
}
QJsonObject ApiV1ServerConfig::toJson() const
{
QJsonObject obj;
if (!name.isEmpty()) {
obj[configKey::name] = name;
}
if (!description.isEmpty()) {
obj[configKey::description] = description;
}
if (!protocol.isEmpty()) {
obj[apiDefs::key::protocol] = protocol;
}
if (!apiEndpoint.isEmpty()) {
obj[apiDefs::key::apiEndpoint] = apiEndpoint;
}
if (!apiKey.isEmpty()) {
obj[apiDefs::key::apiKey] = apiKey;
}
obj[configKey::configVersion] = configVersion;
if (!hostName.isEmpty()) {
obj[configKey::hostName] = hostName;
}
QJsonArray containersArray;
for (auto it = containers.begin(); it != containers.end(); ++it) {
QJsonObject containerObj = it.value().toJson();
containersArray.append(containerObj);
}
if (!containersArray.isEmpty()) {
obj[configKey::containers] = containersArray;
}
if (defaultContainer != DockerContainer::None) {
obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer);
}
if (!dns1.isEmpty()) {
obj[configKey::dns1] = dns1;
}
if (!dns2.isEmpty()) {
obj[configKey::dns2] = dns2;
}
if (crc > 0) {
obj[configKey::crc] = crc;
}
return obj;
}
ApiV1ServerConfig ApiV1ServerConfig::fromJson(const QJsonObject& json)
{
ApiV1ServerConfig config;
config.name = json.value(configKey::name).toString();
config.description = json.value(configKey::description).toString();
config.protocol = json.value(apiDefs::key::protocol).toString();
config.apiEndpoint = json.value(apiDefs::key::apiEndpoint).toString();
config.apiKey = json.value(apiDefs::key::apiKey).toString();
config.configVersion = json.value(configKey::configVersion).toInt(1);
config.hostName = json.value(configKey::hostName).toString();
QJsonArray containersArray = json.value(configKey::containers).toArray();
for (const QJsonValue& val : containersArray) {
QJsonObject containerObj = val.toObject();
ContainerConfig containerConfig = ContainerConfig::fromJson(containerObj);
QString containerStr = containerObj.value(configKey::container).toString();
DockerContainer container = ContainerUtils::containerFromString(containerStr);
config.containers.insert(container, containerConfig);
}
QString defaultContainerStr = json.value(configKey::defaultContainer).toString();
config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr);
config.dns1 = json.value(configKey::dns1).toString();
config.dns2 = json.value(configKey::dns2).toString();
config.crc = json.value(configKey::crc).toInt(0);
return config;
}
} // namespace amnezia
@@ -1,47 +0,0 @@
#ifndef APIV1SERVERCONFIG_H
#define APIV1SERVERCONFIG_H
#include <QJsonObject>
#include <QMap>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/models/containerConfig.h"
#include "core/utils/api/apiEnums.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
namespace amnezia
{
using namespace ContainerEnumNS;
struct ApiV1ServerConfig {
QString description;
QString hostName;
QMap<DockerContainer, ContainerConfig> containers;
DockerContainer defaultContainer;
QString dns1;
QString dns2;
QString name;
QString protocol;
QString apiEndpoint;
QString apiKey;
int crc;
int configVersion;
bool isPremium() const;
bool isFree() const;
QString vpnKey() const;
bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const;
QJsonObject toJson() const;
static ApiV1ServerConfig fromJson(const QJsonObject& json);
};
} // namespace amnezia
#endif // APIV1SERVERCONFIG_H
@@ -80,6 +80,9 @@ QJsonObject ApiV2ServerConfig::toJson() const
if (!description.isEmpty()) { if (!description.isEmpty()) {
obj[configKey::description] = description; obj[configKey::description] = description;
} }
if (!displayName.isEmpty()) {
obj[configKey::displayName] = displayName;
}
obj[configKey::configVersion] = configVersion; obj[configKey::configVersion] = configVersion;
@@ -131,6 +134,7 @@ ApiV2ServerConfig ApiV2ServerConfig::fromJson(const QJsonObject& json)
config.name = json.value(configKey::name).toString(); config.name = json.value(configKey::name).toString();
config.nameOverriddenByUser = json.value(configKey::nameOverriddenByUser).toBool(false); config.nameOverriddenByUser = json.value(configKey::nameOverriddenByUser).toBool(false);
config.description = json.value(configKey::description).toString(); config.description = json.value(configKey::description).toString();
config.displayName = json.value(configKey::displayName).toString();
config.configVersion = json.value(configKey::configVersion).toInt(2); config.configVersion = json.value(configKey::configVersion).toInt(2);
config.hostName = json.value(configKey::hostName).toString(); config.hostName = json.value(configKey::hostName).toString();
@@ -163,6 +167,10 @@ ApiV2ServerConfig ApiV2ServerConfig::fromJson(const QJsonObject& json)
config.authData = AuthData::fromJson(authDataObj); config.authData = AuthData::fromJson(authDataObj);
} }
if (config.displayName.isEmpty()) {
config.displayName = config.name.isEmpty() ? config.description : config.name;
}
return config; return config;
} }
+2 -1
View File
@@ -10,7 +10,7 @@
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/models/api/apiConfig.h" #include "core/models/api/apiConfig.h"
#include "core/models/api/authData.h" #include "core/models/api/authData.h"
#include "core/utils/api/apiEnums.h" #include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h" #include "core/utils/constants/apiConstants.h"
@@ -21,6 +21,7 @@ using namespace ContainerEnumNS;
struct ApiV2ServerConfig { struct ApiV2ServerConfig {
QString description; QString description;
QString displayName;
QString hostName; QString hostName;
QMap<DockerContainer, ContainerConfig> containers; QMap<DockerContainer, ContainerConfig> containers;
DockerContainer defaultContainer; DockerContainer defaultContainer;
+1 -1
View File
@@ -4,7 +4,7 @@
#include <QJsonObject> #include <QJsonObject>
#include <QString> #include <QString>
#include "core/utils/api/apiEnums.h" #include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h" #include "core/utils/constants/apiConstants.h"
@@ -0,0 +1,43 @@
#include "legacyApiServerConfig.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/configKeys.h"
namespace amnezia
{
bool LegacyApiServerConfig::hasContainers() const
{
return !containers.isEmpty();
}
ContainerConfig LegacyApiServerConfig::containerConfig(DockerContainer container) const
{
if (!containers.contains(container)) {
return ContainerConfig{};
}
return containers.value(container);
}
LegacyApiServerConfig LegacyApiServerConfig::fromJson(const QJsonObject &json)
{
LegacyApiServerConfig config;
config.name = json.value(configKey::name).toString();
config.description = json.value(configKey::description).toString();
config.displayName = json.value(configKey::displayName).toString();
config.hostName = json.value(configKey::hostName).toString();
config.crc = json.value(configKey::crc).toInt(0);
config.configVersion = json.value(configKey::configVersion).toInt(1);
config.apiEndpoint = json.value(apiDefs::key::apiEndpoint).toString();
if (config.displayName.isEmpty()) {
config.displayName = config.name.isEmpty() ? config.description : config.name;
}
return config;
}
} // namespace amnezia
@@ -0,0 +1,38 @@
#ifndef LEGACYAPISERVERCONFIG_H
#define LEGACYAPISERVERCONFIG_H
#include <QJsonObject>
#include <QMap>
#include "core/utils/containerEnum.h"
#include "core/utils/protocolEnum.h"
#include "core/models/containerConfig.h"
namespace amnezia
{
using namespace ContainerEnumNS;
struct LegacyApiServerConfig {
QString description;
QString displayName;
QString hostName;
QMap<DockerContainer, ContainerConfig> containers;
DockerContainer defaultContainer = DockerContainer::None;
QString dns1;
QString dns2;
QString name;
int crc = 0;
int configVersion = 0;
QString apiEndpoint;
bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const;
static LegacyApiServerConfig fromJson(const QJsonObject &json);
};
} // namespace amnezia
#endif // LEGACYAPISERVERCONFIG_H
+20
View File
@@ -113,6 +113,26 @@ const Socks5ProxyProtocolConfig* ContainerConfig::getSocks5ProxyProtocolConfig()
return protocolConfig.as<Socks5ProxyProtocolConfig>(); return protocolConfig.as<Socks5ProxyProtocolConfig>();
} }
MtProxyProtocolConfig* ContainerConfig::getMtProxyProtocolConfig()
{
return protocolConfig.as<MtProxyProtocolConfig>();
}
const MtProxyProtocolConfig* ContainerConfig::getMtProxyProtocolConfig() const
{
return protocolConfig.as<MtProxyProtocolConfig>();
}
TelemtProtocolConfig* ContainerConfig::getTelemtProtocolConfig()
{
return protocolConfig.as<TelemtProtocolConfig>();
}
const TelemtProtocolConfig* ContainerConfig::getTelemtProtocolConfig() const
{
return protocolConfig.as<TelemtProtocolConfig>();
}
Ikev2ProtocolConfig* ContainerConfig::getIkev2ProtocolConfig() Ikev2ProtocolConfig* ContainerConfig::getIkev2ProtocolConfig()
{ {
return protocolConfig.as<Ikev2ProtocolConfig>(); return protocolConfig.as<Ikev2ProtocolConfig>();
+6
View File
@@ -57,6 +57,12 @@ struct ContainerConfig {
Socks5ProxyProtocolConfig* getSocks5ProxyProtocolConfig(); Socks5ProxyProtocolConfig* getSocks5ProxyProtocolConfig();
const Socks5ProxyProtocolConfig* getSocks5ProxyProtocolConfig() const; const Socks5ProxyProtocolConfig* getSocks5ProxyProtocolConfig() const;
MtProxyProtocolConfig* getMtProxyProtocolConfig();
const MtProxyProtocolConfig* getMtProxyProtocolConfig() const;
TelemtProtocolConfig* getTelemtProtocolConfig();
const TelemtProtocolConfig* getTelemtProtocolConfig() const;
Ikev2ProtocolConfig* getIkev2ProtocolConfig(); Ikev2ProtocolConfig* getIkev2ProtocolConfig();
const Ikev2ProtocolConfig* getIkev2ProtocolConfig() const; const Ikev2ProtocolConfig* getIkev2ProtocolConfig() const;
+18
View File
@@ -9,6 +9,8 @@
#include "core/utils/protocolEnum.h" #include "core/utils/protocolEnum.h"
#include "core/models/protocols/ikev2ProtocolConfig.h" #include "core/models/protocols/ikev2ProtocolConfig.h"
#include "core/models/protocols/dnsProtocolConfig.h" #include "core/models/protocols/dnsProtocolConfig.h"
#include "core/models/protocols/mtProxyProtocolConfig.h"
#include "core/models/protocols/telemtProtocolConfig.h"
namespace amnezia namespace amnezia
{ {
@@ -38,6 +40,10 @@ Proto ProtocolConfig::type() const
return Proto::TorWebSite; return Proto::TorWebSite;
} else if constexpr (std::is_same_v<T, DnsProtocolConfig>) { } else if constexpr (std::is_same_v<T, DnsProtocolConfig>) {
return Proto::Dns; return Proto::Dns;
} else if constexpr (std::is_same_v<T, MtProxyProtocolConfig>) {
return Proto::MtProxy;
} else if constexpr (std::is_same_v<T, TelemtProtocolConfig>) {
return Proto::Telemt;
} }
return Proto::Unknown; return Proto::Unknown;
}, data); }, data);
@@ -65,6 +71,10 @@ QString ProtocolConfig::port() const
return QString(); return QString();
} else if constexpr (std::is_same_v<T, DnsProtocolConfig>) { } else if constexpr (std::is_same_v<T, DnsProtocolConfig>) {
return QString(); return QString();
} else if constexpr (std::is_same_v<T, MtProxyProtocolConfig>) {
return arg.port.isEmpty() ? QString(protocols::mtProxy::defaultPort) : arg.port;
} else if constexpr (std::is_same_v<T, TelemtProtocolConfig>) {
return arg.port.isEmpty() ? QString(protocols::telemt::defaultPort) : arg.port;
} }
return QString(); return QString();
}, data); }, data);
@@ -88,6 +98,10 @@ QString ProtocolConfig::transportProto() const
return QString(); return QString();
} else if constexpr (std::is_same_v<T, DnsProtocolConfig>) { } else if constexpr (std::is_same_v<T, DnsProtocolConfig>) {
return QString(); return QString();
} else if constexpr (std::is_same_v<T, MtProxyProtocolConfig>) {
return QStringLiteral("tcp");
} else if constexpr (std::is_same_v<T, TelemtProtocolConfig>) {
return QStringLiteral("tcp");
} }
return QString(); return QString();
}, data); }, data);
@@ -299,6 +313,10 @@ ProtocolConfig ProtocolConfig::fromJson(const QJsonObject& json, Proto type)
return ProtocolConfig{TorProtocolConfig::fromJson(json)}; return ProtocolConfig{TorProtocolConfig::fromJson(json)};
case Proto::Dns: case Proto::Dns:
return ProtocolConfig{DnsProtocolConfig::fromJson(json)}; return ProtocolConfig{DnsProtocolConfig::fromJson(json)};
case Proto::MtProxy:
return ProtocolConfig{MtProxyProtocolConfig::fromJson(json)};
case Proto::Telemt:
return ProtocolConfig{TelemtProtocolConfig::fromJson(json)};
default: default:
return ProtocolConfig{AwgProtocolConfig{}}; return ProtocolConfig{AwgProtocolConfig{}};
} }
+4
View File
@@ -22,6 +22,8 @@
#include "core/models/protocols/ikev2ProtocolConfig.h" #include "core/models/protocols/ikev2ProtocolConfig.h"
#include "core/models/protocols/torProtocolConfig.h" #include "core/models/protocols/torProtocolConfig.h"
#include "core/models/protocols/dnsProtocolConfig.h" #include "core/models/protocols/dnsProtocolConfig.h"
#include "core/models/protocols/mtProxyProtocolConfig.h"
#include "core/models/protocols/telemtProtocolConfig.h"
namespace amnezia namespace amnezia
{ {
@@ -36,6 +38,8 @@ struct ProtocolConfig {
XrayProtocolConfig, XrayProtocolConfig,
SftpProtocolConfig, SftpProtocolConfig,
Socks5ProxyProtocolConfig, Socks5ProxyProtocolConfig,
MtProxyProtocolConfig,
TelemtProtocolConfig,
Ikev2ProtocolConfig, Ikev2ProtocolConfig,
TorProtocolConfig, TorProtocolConfig,
DnsProtocolConfig DnsProtocolConfig
@@ -0,0 +1,147 @@
#include "mtProxyProtocolConfig.h"
#include "../../../core/utils/protocolEnum.h"
#include "../../../core/protocols/protocolUtils.h"
#include "../../../core/utils/constants/configKeys.h"
#include "../../../core/utils/constants/protocolConstants.h"
#include <QJsonArray>
#include <algorithm>
using namespace amnezia;
namespace amnezia {
QJsonObject MtProxyProtocolConfig::toJson() const {
QJsonObject obj;
if (!port.isEmpty()) {
obj[configKey::port] = port;
}
if (!secret.isEmpty()) {
obj[protocols::mtProxy::secretKey] = secret;
}
if (!tag.isEmpty()) {
obj[protocols::mtProxy::tagKey] = tag;
}
if (!tgLink.isEmpty()) {
obj[protocols::mtProxy::tgLinkKey] = tgLink;
}
if (!tmeLink.isEmpty()) {
obj[protocols::mtProxy::tmeLinkKey] = tmeLink;
}
obj[protocols::mtProxy::isEnabledKey] = isEnabled;
if (!publicHost.isEmpty()) {
obj[protocols::mtProxy::publicHostKey] = publicHost;
}
if (!transportMode.isEmpty()) {
obj[protocols::mtProxy::transportModeKey] = transportMode;
}
if (!tlsDomain.isEmpty()) {
obj[protocols::mtProxy::tlsDomainKey] = tlsDomain;
}
if (!additionalSecrets.isEmpty()) {
obj[protocols::mtProxy::additionalSecretsKey] = QJsonArray::fromStringList(additionalSecrets);
}
if (!workersMode.isEmpty()) {
obj[protocols::mtProxy::workersModeKey] = workersMode;
}
if (!workers.isEmpty()) {
obj[protocols::mtProxy::workersKey] = workers;
}
obj[protocols::mtProxy::natEnabledKey] = natEnabled;
if (!natInternalIp.isEmpty()) {
obj[protocols::mtProxy::natInternalIpKey] = natInternalIp;
}
if (!natExternalIp.isEmpty()) {
obj[protocols::mtProxy::natExternalIpKey] = natExternalIp;
}
return obj;
}
MtProxyProtocolConfig MtProxyProtocolConfig::fromJson(const QJsonObject &json) {
MtProxyProtocolConfig config;
config.port = json.value(configKey::port).toString();
config.secret = json.value(protocols::mtProxy::secretKey).toString();
config.tag = json.value(protocols::mtProxy::tagKey).toString();
config.tgLink = json.value(protocols::mtProxy::tgLinkKey).toString();
config.tmeLink = json.value(protocols::mtProxy::tmeLinkKey).toString();
config.isEnabled = json.value(protocols::mtProxy::isEnabledKey).toBool(true);
config.publicHost = json.value(protocols::mtProxy::publicHostKey).toString();
config.transportMode = json.value(protocols::mtProxy::transportModeKey).toString();
config.tlsDomain = json.value(protocols::mtProxy::tlsDomainKey).toString();
for (const auto &v: json.value(protocols::mtProxy::additionalSecretsKey).toArray()) {
const QString s = v.toString();
if (!s.isEmpty()) {
config.additionalSecrets.append(s);
}
}
config.workersMode = json.value(protocols::mtProxy::workersModeKey).toString();
config.workers = json.value(protocols::mtProxy::workersKey).toString();
config.natEnabled = json.value(protocols::mtProxy::natEnabledKey).toBool(false);
config.natInternalIp = json.value(protocols::mtProxy::natInternalIpKey).toString();
config.natExternalIp = json.value(protocols::mtProxy::natExternalIpKey).toString();
return config;
}
bool MtProxyProtocolConfig::equalsDockerDeploymentSettings(const MtProxyProtocolConfig &other) const {
const auto normPort = [](const QString &p) {
return p.isEmpty() ? QString(protocols::mtProxy::defaultPort) : p;
};
const auto normTransport = [](const QString &t) {
return t.isEmpty() ? QString(protocols::mtProxy::transportModeStandard) : t;
};
const auto normWorkersMode = [](const QString &m) {
return m.isEmpty() ? QString(protocols::mtProxy::workersModeAuto) : m;
};
if (normPort(port) != normPort(other.port)) {
return false;
}
if (normTransport(transportMode) != normTransport(other.transportMode)) {
return false;
}
if (tlsDomain != other.tlsDomain) {
return false;
}
if (secret != other.secret) {
return false;
}
if (tag != other.tag) {
return false;
}
if (publicHost != other.publicHost) {
return false;
}
if (normWorkersMode(workersMode) != normWorkersMode(other.workersMode)) {
return false;
}
if (workers != other.workers) {
return false;
}
if (natEnabled != other.natEnabled) {
return false;
}
if (natInternalIp != other.natInternalIp) {
return false;
}
if (natExternalIp != other.natExternalIp) {
return false;
}
if (isEnabled != other.isEnabled) {
return false;
}
QStringList aa = additionalSecrets;
QStringList bb = other.additionalSecrets;
aa.removeAll(QString());
bb.removeAll(QString());
std::sort(aa.begin(), aa.end());
std::sort(bb.begin(), bb.end());
return aa == bb;
}
} // namespace amnezia
@@ -0,0 +1,38 @@
#ifndef MTPROXYPROTOCOLCONFIG_H
#define MTPROXYPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include <QStringList>
namespace amnezia {
struct MtProxyProtocolConfig {
QString port;
QString secret;
QString tag;
QString tgLink;
QString tmeLink;
bool isEnabled = true;
QString publicHost;
QString transportMode;
QString tlsDomain;
QStringList additionalSecrets;
QString workersMode;
QString workers;
bool natEnabled = false;
QString natInternalIp;
QString natExternalIp;
QJsonObject toJson() const;
static MtProxyProtocolConfig fromJson(const QJsonObject &json);
// Port, transport, TLS, secrets, NAT, workers, isEnabled, additionalSecrets (order-independent).
// Ignores tgLink / tmeLink (derived / display).
bool equalsDockerDeploymentSettings(const MtProxyProtocolConfig &other) const;
};
} // namespace amnezia
#endif // MTPROXYPROTOCOLCONFIG_H
@@ -0,0 +1,162 @@
#include "telemtProtocolConfig.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include <QJsonArray>
#include <algorithm>
using namespace amnezia;
QJsonObject TelemtProtocolConfig::toJson() const
{
QJsonObject obj;
if (!port.isEmpty()) {
obj[QString(configKey::port)] = port;
}
if (!secret.isEmpty()) {
obj[protocols::telemt::secretKey] = secret;
}
if (!tag.isEmpty()) {
obj[protocols::telemt::tagKey] = tag;
}
if (!tgLink.isEmpty()) {
obj[protocols::telemt::tgLinkKey] = tgLink;
}
if (!tmeLink.isEmpty()) {
obj[protocols::telemt::tmeLinkKey] = tmeLink;
}
obj[protocols::telemt::isEnabledKey] = isEnabled;
if (!publicHost.isEmpty()) {
obj[protocols::telemt::publicHostKey] = publicHost;
}
if (!transportMode.isEmpty()) {
obj[protocols::telemt::transportModeKey] = transportMode;
}
if (!tlsDomain.isEmpty()) {
obj[protocols::telemt::tlsDomainKey] = tlsDomain;
}
obj[protocols::telemt::maskEnabledKey] = maskEnabled;
obj[protocols::telemt::tlsEmulationKey] = tlsEmulation;
obj[protocols::telemt::useMiddleProxyKey] = useMiddleProxy;
if (!userName.isEmpty()) {
obj[protocols::telemt::userNameKey] = userName;
}
if (!additionalSecrets.isEmpty()) {
obj[protocols::telemt::additionalSecretsKey] = QJsonArray::fromStringList(additionalSecrets);
}
if (!workersMode.isEmpty()) {
obj[protocols::telemt::workersModeKey] = workersMode;
}
if (!workers.isEmpty()) {
obj[protocols::telemt::workersKey] = workers;
}
obj[protocols::telemt::natEnabledKey] = natEnabled;
if (!natInternalIp.isEmpty()) {
obj[protocols::telemt::natInternalIpKey] = natInternalIp;
}
if (!natExternalIp.isEmpty()) {
obj[protocols::telemt::natExternalIpKey] = natExternalIp;
}
return obj;
}
TelemtProtocolConfig TelemtProtocolConfig::fromJson(const QJsonObject &json)
{
TelemtProtocolConfig c;
c.port = json.value(QString(configKey::port)).toString();
c.secret = json.value(protocols::telemt::secretKey).toString();
c.tag = json.value(protocols::telemt::tagKey).toString();
c.tgLink = json.value(protocols::telemt::tgLinkKey).toString();
c.tmeLink = json.value(protocols::telemt::tmeLinkKey).toString();
c.isEnabled = json.value(protocols::telemt::isEnabledKey).toBool(true);
c.publicHost = json.value(protocols::telemt::publicHostKey).toString();
c.transportMode = json.value(protocols::telemt::transportModeKey).toString();
c.tlsDomain = json.value(protocols::telemt::tlsDomainKey).toString();
c.maskEnabled = json.value(protocols::telemt::maskEnabledKey).toBool(true);
c.tlsEmulation = json.value(protocols::telemt::tlsEmulationKey).toBool(false);
c.useMiddleProxy = json.value(protocols::telemt::useMiddleProxyKey).toBool(true);
c.userName = json.value(protocols::telemt::userNameKey).toString();
for (const auto &v : json.value(protocols::telemt::additionalSecretsKey).toArray()) {
const QString s = v.toString();
if (!s.isEmpty()) {
c.additionalSecrets.append(s);
}
}
c.workersMode = json.value(protocols::telemt::workersModeKey).toString();
c.workers = json.value(protocols::telemt::workersKey).toString();
c.natEnabled = json.value(protocols::telemt::natEnabledKey).toBool(false);
c.natInternalIp = json.value(protocols::telemt::natInternalIpKey).toString();
c.natExternalIp = json.value(protocols::telemt::natExternalIpKey).toString();
return c;
}
bool TelemtProtocolConfig::equalsDockerDeploymentSettings(const TelemtProtocolConfig &other) const
{
const auto normPort = [](const QString &p) {
return p.isEmpty() ? QString(protocols::telemt::defaultPort) : p;
};
const auto normTransport = [](const QString &t) {
return t.isEmpty() ? QString(protocols::telemt::transportModeStandard) : t;
};
const auto normWorkersMode = [](const QString &m) {
return m.isEmpty() ? QString(protocols::telemt::workersModeAuto) : m;
};
if (normPort(port) != normPort(other.port)) {
return false;
}
if (normTransport(transportMode) != normTransport(other.transportMode)) {
return false;
}
if (tlsDomain != other.tlsDomain) {
return false;
}
if (secret != other.secret) {
return false;
}
if (tag != other.tag) {
return false;
}
if (publicHost != other.publicHost) {
return false;
}
if (maskEnabled != other.maskEnabled) {
return false;
}
if (tlsEmulation != other.tlsEmulation) {
return false;
}
if (useMiddleProxy != other.useMiddleProxy) {
return false;
}
if (userName != other.userName) {
return false;
}
if (normWorkersMode(workersMode) != normWorkersMode(other.workersMode)) {
return false;
}
if (workers != other.workers) {
return false;
}
if (natEnabled != other.natEnabled) {
return false;
}
if (natInternalIp != other.natInternalIp) {
return false;
}
if (natExternalIp != other.natExternalIp) {
return false;
}
if (isEnabled != other.isEnabled) {
return false;
}
QStringList aa = additionalSecrets;
QStringList bb = other.additionalSecrets;
aa.removeAll(QString());
bb.removeAll(QString());
std::sort(aa.begin(), aa.end());
std::sort(bb.begin(), bb.end());
return aa == bb;
}
@@ -0,0 +1,38 @@
#ifndef TELEMTPROTOCOLCONFIG_H
#define TELEMTPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include <QStringList>
namespace amnezia {
struct TelemtProtocolConfig {
QString port;
QString secret;
QString tag;
QString tgLink;
QString tmeLink;
bool isEnabled = true;
QString publicHost;
QString transportMode;
QString tlsDomain;
bool maskEnabled = true;
bool tlsEmulation = false;
bool useMiddleProxy = true;
QString userName;
QStringList additionalSecrets;
QString workersMode;
QString workers;
bool natEnabled = false;
QString natInternalIp;
QString natExternalIp;
QJsonObject toJson() const;
static TelemtProtocolConfig fromJson(const QJsonObject &json);
bool equalsDockerDeploymentSettings(const TelemtProtocolConfig &other) const;
};
} // namespace amnezia
#endif // TELEMTPROTOCOLCONFIG_H
@@ -35,6 +35,9 @@ QJsonObject NativeServerConfig::toJson() const
if (!description.isEmpty()) { if (!description.isEmpty()) {
obj[configKey::description] = this->description; obj[configKey::description] = this->description;
} }
if (!displayName.isEmpty()) {
obj[configKey::displayName] = displayName;
}
if (!hostName.isEmpty()) { if (!hostName.isEmpty()) {
obj[configKey::hostName] = hostName; obj[configKey::hostName] = hostName;
} }
@@ -67,6 +70,7 @@ NativeServerConfig NativeServerConfig::fromJson(const QJsonObject& json)
NativeServerConfig config; NativeServerConfig config;
config.description = json.value(configKey::description).toString(); config.description = json.value(configKey::description).toString();
config.displayName = json.value(configKey::displayName).toString();
config.hostName = json.value(configKey::hostName).toString(); config.hostName = json.value(configKey::hostName).toString();
QJsonArray containersArray = json.value(configKey::containers).toArray(); QJsonArray containersArray = json.value(configKey::containers).toArray();
@@ -86,6 +90,10 @@ NativeServerConfig NativeServerConfig::fromJson(const QJsonObject& json)
config.dns1 = json.value(configKey::dns1).toString(); config.dns1 = json.value(configKey::dns1).toString();
config.dns2 = json.value(configKey::dns2).toString(); config.dns2 = json.value(configKey::dns2).toString();
if (config.displayName.isEmpty()) {
config.displayName = config.description.isEmpty() ? config.hostName : config.description;
}
return config; return config;
} }
@@ -16,6 +16,7 @@ using namespace ContainerEnumNS;
struct NativeServerConfig { struct NativeServerConfig {
QString description; QString description;
QString displayName;
QString hostName; QString hostName;
QMap<DockerContainer, ContainerConfig> containers; QMap<DockerContainer, ContainerConfig> containers;
DockerContainer defaultContainer; DockerContainer defaultContainer;
@@ -0,0 +1,170 @@
#include "selfHostedAdminServerConfig.h"
#include <QJsonArray>
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/networkUtilities.h"
namespace amnezia
{
using namespace ContainerEnumNS;
bool SelfHostedAdminServerConfig::hasCredentials() const
{
return !userName.isEmpty() && !password.isEmpty() && port > 0;
}
bool SelfHostedAdminServerConfig::isReadOnly() const
{
return !hasCredentials();
}
ServerCredentials SelfHostedAdminServerConfig::credentials() const
{
ServerCredentials creds;
creds.hostName = hostName;
creds.userName = userName;
creds.secretData = password;
creds.port = port;
return creds;
}
bool SelfHostedAdminServerConfig::hasContainers() const
{
return !containers.isEmpty();
}
ContainerConfig SelfHostedAdminServerConfig::containerConfig(DockerContainer container) const
{
if (!containers.contains(container)) {
return ContainerConfig{};
}
return containers.value(container);
}
void SelfHostedAdminServerConfig::updateContainerConfig(DockerContainer container, const ContainerConfig &config)
{
containers[container] = config;
}
void SelfHostedAdminServerConfig::clearCachedClientProfile(DockerContainer container)
{
if (ContainerUtils::containerService(container) == ServiceType::Other) {
return;
}
ContainerConfig cleared = containerConfig(container);
cleared.protocolConfig.clearClientConfig();
containers[container] = cleared;
}
QPair<QString, QString> SelfHostedAdminServerConfig::getDnsPair(bool isAmneziaDnsEnabled, const QString &primaryDns,
const QString &secondaryDns) const
{
QString d1 = dns1;
QString d2 = dns2;
const bool dnsOnServer = containers.contains(DockerContainer::Dns);
if (d1.isEmpty() || !NetworkUtilities::checkIPv4Format(d1)) {
d1 = (isAmneziaDnsEnabled && dnsOnServer) ? protocols::dns::amneziaDnsIp : primaryDns;
}
if (d2.isEmpty() || !NetworkUtilities::checkIPv4Format(d2)) {
d2 = secondaryDns;
}
return { d1, d2 };
}
QJsonObject SelfHostedAdminServerConfig::toJson() const
{
QJsonObject obj;
if (!description.isEmpty()) {
obj[configKey::description] = this->description;
}
if (!displayName.isEmpty()) {
obj[configKey::displayName] = displayName;
}
if (!hostName.isEmpty()) {
obj[configKey::hostName] = hostName;
}
QJsonArray containersArray;
for (auto it = containers.begin(); it != containers.end(); ++it) {
QJsonObject containerObj = it.value().toJson();
containersArray.append(containerObj);
}
if (!containersArray.isEmpty()) {
obj[configKey::containers] = containersArray;
}
if (defaultContainer != DockerContainer::None) {
obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer);
}
if (!dns1.isEmpty()) {
obj[configKey::dns1] = dns1;
}
if (!dns2.isEmpty()) {
obj[configKey::dns2] = dns2;
}
if (!userName.isEmpty()) {
obj[configKey::userName] = userName;
}
if (!password.isEmpty()) {
obj[configKey::password] = password;
}
if (port > 0) {
obj[configKey::port] = port;
}
return obj;
}
SelfHostedAdminServerConfig SelfHostedAdminServerConfig::fromJson(const QJsonObject &json)
{
SelfHostedAdminServerConfig config;
config.description = json.value(configKey::description).toString();
config.displayName = json.value(configKey::displayName).toString();
config.hostName = json.value(configKey::hostName).toString();
QJsonArray containersArray = json.value(configKey::containers).toArray();
for (const QJsonValue &val : containersArray) {
QJsonObject containerObj = val.toObject();
ContainerConfig cc = ContainerConfig::fromJson(containerObj);
QString containerStr = containerObj.value(configKey::container).toString();
DockerContainer container = ContainerUtils::containerFromString(containerStr);
config.containers.insert(container, cc);
}
QString defaultContainerStr = json.value(configKey::defaultContainer).toString();
config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr);
config.dns1 = json.value(configKey::dns1).toString();
config.dns2 = json.value(configKey::dns2).toString();
config.userName = json.value(configKey::userName).toString();
config.password = json.value(configKey::password).toString();
if (json.contains(configKey::port)) {
config.port = json.value(configKey::port).toInt();
} else {
config.port = 0;
}
if (config.displayName.isEmpty()) {
config.displayName = config.description.isEmpty() ? config.hostName : config.description;
}
return config;
}
} // namespace amnezia
@@ -0,0 +1,53 @@
#ifndef SELFHOSTEDADMINSERVERCONFIG_H
#define SELFHOSTEDADMINSERVERCONFIG_H
#include <QJsonObject>
#include <QMap>
#include <QPair>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/models/containerConfig.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
namespace amnezia
{
using namespace ContainerEnumNS;
struct SelfHostedAdminServerConfig {
QString description;
QString displayName;
QString hostName;
QMap<DockerContainer, ContainerConfig> containers;
DockerContainer defaultContainer;
QString dns1;
QString dns2;
QString userName;
QString password;
int port = 0;
bool hasCredentials() const;
bool isReadOnly() const;
ServerCredentials credentials() const;
bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const;
void updateContainerConfig(DockerContainer container, const ContainerConfig &config);
void clearCachedClientProfile(DockerContainer container);
QPair<QString, QString> getDnsPair(bool isAmneziaDnsEnabled, const QString &primaryDns,
const QString &secondaryDns) const;
QJsonObject toJson() const;
static SelfHostedAdminServerConfig fromJson(const QJsonObject &json);
};
} // namespace amnezia
#endif // SELFHOSTEDADMINSERVERCONFIG_H
@@ -1,53 +1,40 @@
#include "selfHostedServerConfig.h" #include "selfHostedUserServerConfig.h"
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument>
#include <stdexcept>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h" #include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h" #include "core/utils/constants/protocolConstants.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
namespace amnezia namespace amnezia
{ {
using namespace ContainerEnumNS; using namespace ContainerEnumNS;
bool SelfHostedServerConfig::hasCredentials() const bool SelfHostedUserServerConfig::hasCredentials() const
{ {
return userName.has_value() && password.has_value() && port.has_value(); return false;
} }
bool SelfHostedServerConfig::isReadOnly() const bool SelfHostedUserServerConfig::isReadOnly() const
{ {
return !hasCredentials(); return true;
} }
std::optional<ServerCredentials> SelfHostedServerConfig::credentials() const std::optional<ServerCredentials> SelfHostedUserServerConfig::credentials() const
{ {
if (!hasCredentials()) { return std::nullopt;
return std::nullopt;
}
ServerCredentials creds;
creds.hostName = hostName;
creds.userName = userName.value();
creds.secretData = password.value();
creds.port = port.value();
return creds;
} }
bool SelfHostedServerConfig::hasContainers() const bool SelfHostedUserServerConfig::hasContainers() const
{ {
return !containers.isEmpty(); return !containers.isEmpty();
} }
ContainerConfig SelfHostedServerConfig::containerConfig(DockerContainer container) const ContainerConfig SelfHostedUserServerConfig::containerConfig(DockerContainer container) const
{ {
if (!containers.contains(container)) { if (!containers.contains(container)) {
return ContainerConfig{}; return ContainerConfig{};
@@ -55,17 +42,20 @@ ContainerConfig SelfHostedServerConfig::containerConfig(DockerContainer containe
return containers.value(container); return containers.value(container);
} }
QJsonObject SelfHostedServerConfig::toJson() const QJsonObject SelfHostedUserServerConfig::toJson() const
{ {
QJsonObject obj; QJsonObject obj;
if (!description.isEmpty()) { if (!description.isEmpty()) {
obj[configKey::description] = this->description; obj[configKey::description] = this->description;
} }
if (!displayName.isEmpty()) {
obj[configKey::displayName] = displayName;
}
if (!hostName.isEmpty()) { if (!hostName.isEmpty()) {
obj[configKey::hostName] = hostName; obj[configKey::hostName] = hostName;
} }
QJsonArray containersArray; QJsonArray containersArray;
for (auto it = containers.begin(); it != containers.end(); ++it) { for (auto it = containers.begin(); it != containers.end(); ++it) {
QJsonObject containerObj = it.value().toJson(); QJsonObject containerObj = it.value().toJson();
@@ -74,67 +64,51 @@ QJsonObject SelfHostedServerConfig::toJson() const
if (!containersArray.isEmpty()) { if (!containersArray.isEmpty()) {
obj[configKey::containers] = containersArray; obj[configKey::containers] = containersArray;
} }
if (defaultContainer != DockerContainer::None) { if (defaultContainer != DockerContainer::None) {
obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer); obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer);
} }
if (!dns1.isEmpty()) { if (!dns1.isEmpty()) {
obj[configKey::dns1] = dns1; obj[configKey::dns1] = dns1;
} }
if (!dns2.isEmpty()) { if (!dns2.isEmpty()) {
obj[configKey::dns2] = dns2; obj[configKey::dns2] = dns2;
} }
if (userName.has_value()) {
obj[configKey::userName] = userName.value();
}
if (password.has_value()) {
obj[configKey::password] = password.value();
}
if (port.has_value()) {
obj[configKey::port] = port.value();
}
return obj; return obj;
} }
SelfHostedServerConfig SelfHostedServerConfig::fromJson(const QJsonObject& json) SelfHostedUserServerConfig SelfHostedUserServerConfig::fromJson(const QJsonObject &json)
{ {
SelfHostedServerConfig config; SelfHostedUserServerConfig config;
config.description = json.value(configKey::description).toString(); config.description = json.value(configKey::description).toString();
config.displayName = json.value(configKey::displayName).toString();
config.hostName = json.value(configKey::hostName).toString(); config.hostName = json.value(configKey::hostName).toString();
QJsonArray containersArray = json.value(configKey::containers).toArray(); QJsonArray containersArray = json.value(configKey::containers).toArray();
for (const QJsonValue& val : containersArray) { for (const QJsonValue &val : containersArray) {
QJsonObject containerObj = val.toObject(); QJsonObject containerObj = val.toObject();
ContainerConfig containerConfig = ContainerConfig::fromJson(containerObj); ContainerConfig cc = ContainerConfig::fromJson(containerObj);
QString containerStr = containerObj.value(configKey::container).toString(); QString containerStr = containerObj.value(configKey::container).toString();
DockerContainer container = ContainerUtils::containerFromString(containerStr); DockerContainer container = ContainerUtils::containerFromString(containerStr);
config.containers.insert(container, containerConfig); config.containers.insert(container, cc);
} }
QString defaultContainerStr = json.value(configKey::defaultContainer).toString(); QString defaultContainerStr = json.value(configKey::defaultContainer).toString();
config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr); config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr);
config.dns1 = json.value(configKey::dns1).toString(); config.dns1 = json.value(configKey::dns1).toString();
config.dns2 = json.value(configKey::dns2).toString(); config.dns2 = json.value(configKey::dns2).toString();
if (json.contains(configKey::userName)) { if (config.displayName.isEmpty()) {
config.userName = json.value(configKey::userName).toString(); config.displayName = config.description.isEmpty() ? config.hostName : config.description;
} }
if (json.contains(configKey::password)) {
config.password = json.value(configKey::password).toString();
}
if (json.contains(configKey::port)) {
config.port = json.value(configKey::port).toInt();
}
return config; return config;
} }
} // namespace amnezia } // namespace amnezia
@@ -1,5 +1,5 @@
#ifndef SELFHOSTEDSERVERCONFIG_H #ifndef SELFHOSTEDUSERSERVERCONFIG_H
#define SELFHOSTEDSERVERCONFIG_H #define SELFHOSTEDUSERSERVERCONFIG_H
#include <QJsonObject> #include <QJsonObject>
#include <QMap> #include <QMap>
@@ -9,8 +9,6 @@
#include "core/utils/containers/containerUtils.h" #include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h" #include "core/utils/protocolEnum.h"
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h" #include "core/utils/commonStructs.h"
namespace amnezia namespace amnezia
@@ -18,28 +16,24 @@ namespace amnezia
using namespace ContainerEnumNS; using namespace ContainerEnumNS;
struct SelfHostedServerConfig { struct SelfHostedUserServerConfig {
QString description; QString description;
QString displayName;
QString hostName; QString hostName;
QMap<DockerContainer, ContainerConfig> containers; QMap<DockerContainer, ContainerConfig> containers;
DockerContainer defaultContainer; DockerContainer defaultContainer;
QString dns1; QString dns1;
QString dns2; QString dns2;
std::optional<QString> userName;
std::optional<QString> password;
std::optional<int> port;
bool hasCredentials() const; bool hasCredentials() const;
bool isReadOnly() const; bool isReadOnly() const;
std::optional<ServerCredentials> credentials() const; std::optional<ServerCredentials> credentials() const;
bool hasContainers() const; bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const; ContainerConfig containerConfig(DockerContainer container) const;
QJsonObject toJson() const; QJsonObject toJson() const;
static SelfHostedServerConfig fromJson(const QJsonObject& json); static SelfHostedUserServerConfig fromJson(const QJsonObject &json);
}; };
} // namespace amnezia } // namespace amnezia
#endif // SELFHOSTEDSERVERCONFIG_H #endif // SELFHOSTEDUSERSERVERCONFIG_H
-234
View File
@@ -1,234 +0,0 @@
#include "serverConfig.h"
#include "core/utils/api/apiUtils.h"
#include "core/utils/networkUtilities.h"
#include "core/models/selfhosted/selfHostedServerConfig.h"
#include "core/models/selfhosted/nativeServerConfig.h"
#include "core/models/api/apiV1ServerConfig.h"
#include "core/models/api/apiV2ServerConfig.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
namespace amnezia
{
using namespace ContainerEnumNS;
QString ServerConfig::description() const
{
return std::visit([](const auto& v) { return v.description; }, data);
}
QString ServerConfig::hostName() const
{
return std::visit([](const auto& v) { return v.hostName; }, data);
}
QString ServerConfig::displayName() const
{
if (isApiV1()) {
const auto *apiV1 = as<ApiV1ServerConfig>();
return apiV1 ? apiV1->name : description();
}
if (isApiV2()) {
const auto *apiV2 = as<ApiV2ServerConfig>();
return apiV2 ? apiV2->name : description();
}
QString name = description();
return name.isEmpty() ? hostName() : name;
}
QMap<DockerContainer, ContainerConfig> ServerConfig::containers() const
{
return std::visit([](const auto& v) { return v.containers; }, data);
}
DockerContainer ServerConfig::defaultContainer() const
{
return std::visit([](const auto& v) { return v.defaultContainer; }, data);
}
QString ServerConfig::dns1() const
{
return std::visit([](const auto& v) { return v.dns1; }, data);
}
QString ServerConfig::dns2() const
{
return std::visit([](const auto& v) { return v.dns2; }, data);
}
bool ServerConfig::hasContainers() const
{
return std::visit([](const auto& v) { return v.hasContainers(); }, data);
}
ContainerConfig ServerConfig::containerConfig(DockerContainer container) const
{
return std::visit([container](const auto& v) { return v.containerConfig(container); }, data);
}
int ServerConfig::crc() const
{
return std::visit([](const auto& v) -> int {
using T = std::decay_t<decltype(v)>;
if constexpr (std::is_same_v<T, ApiV1ServerConfig> ||
std::is_same_v<T, ApiV2ServerConfig>) {
return v.crc;
}
return 0;
}, data);
}
int ServerConfig::configVersion() const
{
return std::visit([](const auto& v) -> int {
using T = std::decay_t<decltype(v)>;
if constexpr (std::is_same_v<T, ApiV1ServerConfig>) {
return apiDefs::ConfigSource::Telegram;
} else if constexpr (std::is_same_v<T, ApiV2ServerConfig>) {
return apiDefs::ConfigSource::AmneziaGateway;
}
return 0; // SelfHostedServerConfig or NativeServerConfig
}, data);
}
bool ServerConfig::isSelfHosted() const
{
return std::holds_alternative<SelfHostedServerConfig>(data);
}
bool ServerConfig::isNative() const
{
return std::holds_alternative<NativeServerConfig>(data);
}
bool ServerConfig::isApiV1() const
{
return std::holds_alternative<ApiV1ServerConfig>(data);
}
bool ServerConfig::isApiV2() const
{
return std::holds_alternative<ApiV2ServerConfig>(data);
}
bool ServerConfig::isApiConfig() const
{
return isApiV1() || isApiV2();
}
QJsonObject ServerConfig::toJson() const
{
return std::visit([](const auto& v) { return v.toJson(); }, data);
}
ServerConfig ServerConfig::fromJson(const QJsonObject& json)
{
apiDefs::ConfigType configType = apiUtils::getConfigType(json);
switch (configType) {
case apiDefs::ConfigType::SelfHosted: {
bool hasThirdPartyConfig = false;
QJsonArray containersArray = json.value(configKey::containers).toArray();
for (const QJsonValue& val : containersArray) {
QJsonObject containerObj = val.toObject();
for (auto it = containerObj.begin(); it != containerObj.end(); ++it) {
QString key = it.key();
if (key != configKey::container) {
QJsonObject protocolObj = it.value().toObject();
if (protocolObj.contains(configKey::isThirdPartyConfig) &&
protocolObj.value(configKey::isThirdPartyConfig).toBool()) {
hasThirdPartyConfig = true;
break;
}
}
}
if (hasThirdPartyConfig) {
break;
}
}
if (hasThirdPartyConfig) {
return ServerConfig{NativeServerConfig::fromJson(json)};
} else {
return ServerConfig{SelfHostedServerConfig::fromJson(json)};
}
}
case apiDefs::ConfigType::AmneziaPremiumV1:
case apiDefs::ConfigType::AmneziaFreeV2:
return ServerConfig{ApiV1ServerConfig::fromJson(json)};
case apiDefs::ConfigType::AmneziaPremiumV2:
case apiDefs::ConfigType::AmneziaFreeV3:
case apiDefs::ConfigType::ExternalPremium:
return ServerConfig{ApiV2ServerConfig::fromJson(json)};
default: {
// Check if any container has isThirdPartyConfig
bool hasThirdPartyConfig = false;
QJsonArray containersArray = json.value(configKey::containers).toArray();
for (const QJsonValue& val : containersArray) {
QJsonObject containerObj = val.toObject();
// Check all protocol keys in the container object
for (auto it = containerObj.begin(); it != containerObj.end(); ++it) {
QString key = it.key();
if (key != configKey::container) {
QJsonObject protocolObj = it.value().toObject();
if (protocolObj.contains(configKey::isThirdPartyConfig) &&
protocolObj.value(configKey::isThirdPartyConfig).toBool()) {
hasThirdPartyConfig = true;
break;
}
}
}
if (hasThirdPartyConfig) {
break;
}
}
if (hasThirdPartyConfig) {
return ServerConfig{NativeServerConfig::fromJson(json)};
} else {
return ServerConfig{SelfHostedServerConfig::fromJson(json)};
}
}
}
}
QPair<QString, QString> ServerConfig::getDnsPair(bool isAmneziaDnsEnabled,
const QString &primaryDns,
const QString &secondaryDns) const
{
QPair<QString, QString> dns;
QMap<DockerContainer, ContainerConfig> serverContainers = containers();
bool isDnsContainerInstalled = false;
for (auto it = serverContainers.begin(); it != serverContainers.end(); ++it) {
if (it.key() == DockerContainer::Dns) {
isDnsContainerInstalled = true;
break;
}
}
dns.first = dns1();
dns.second = dns2();
if (dns.first.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.first)) {
if (isAmneziaDnsEnabled && isDnsContainerInstalled) {
dns.first = protocols::dns::amneziaDnsIp;
} else {
dns.first = primaryDns;
}
}
if (dns.second.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.second)) {
dns.second = secondaryDns;
}
return dns;
}
} // namespace amnezia
-92
View File
@@ -1,92 +0,0 @@
#ifndef SERVERCONFIG_H
#define SERVERCONFIG_H
#include <variant>
#include <QJsonObject>
#include <QMap>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/models/selfhosted/selfHostedServerConfig.h"
#include "core/models/selfhosted/nativeServerConfig.h"
#include "core/models/api/apiV1ServerConfig.h"
#include "core/models/api/apiV2ServerConfig.h"
#include "core/models/containerConfig.h"
namespace amnezia
{
using namespace ContainerEnumNS;
struct ServerConfig {
using Variant = std::variant<
SelfHostedServerConfig,
NativeServerConfig,
ApiV1ServerConfig,
ApiV2ServerConfig
>;
Variant data;
ServerConfig() = default;
ServerConfig(const Variant& v) : data(v) {}
ServerConfig(Variant&& v) : data(std::move(v)) {}
template<typename T, typename = std::enable_if_t<!std::is_same<std::remove_cv_t<std::remove_reference_t<T>>, ServerConfig>::value>>
ServerConfig(const T& v) : data(v) {}
template<typename T, typename = std::enable_if_t<!std::is_same<std::remove_cv_t<std::remove_reference_t<T>>, ServerConfig>::value>>
ServerConfig(T&& v) : data(std::forward<T>(v)) {}
QString description() const;
QString hostName() const;
QString displayName() const;
QMap<DockerContainer, ContainerConfig> containers() const;
DockerContainer defaultContainer() const;
QString dns1() const;
QString dns2() const;
bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const;
int crc() const;
int configVersion() const;
bool isSelfHosted() const;
bool isNative() const;
bool isApiV1() const;
bool isApiV2() const;
bool isApiConfig() const;
template<typename T>
T* as() {
return std::get_if<T>(&data);
}
template<typename T>
const T* as() const {
return std::get_if<T>(&data);
}
QJsonObject toJson() const;
static ServerConfig fromJson(const QJsonObject& json);
template<typename Visitor>
auto visit(Visitor&& visitor) {
return std::visit(std::forward<Visitor>(visitor), data);
}
template<typename Visitor>
auto visit(Visitor&& visitor) const {
return std::visit(std::forward<Visitor>(visitor), data);
}
QPair<QString, QString> getDnsPair(bool isAmneziaDnsEnabled,
const QString &primaryDns,
const QString &secondaryDns) const;
};
} // namespace amnezia
#endif // SERVERCONFIG_H
+187
View File
@@ -0,0 +1,187 @@
#include "serverDescription.h"
#include <QMap>
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/api/apiUtils.h"
#include "core/utils/containers/containerUtils.h"
#include "core/protocols/protocolUtils.h"
#include "core/models/protocols/awgProtocolConfig.h"
using namespace amnezia;
namespace
{
bool computeHasInstalledVpnContainers(const QMap<DockerContainer, ContainerConfig> &containers)
{
for (auto it = containers.begin(); it != containers.end(); ++it) {
const DockerContainer container = it.key();
if (ContainerUtils::containerService(container) == ServiceType::Vpn || container == DockerContainer::SSXray) {
return true;
}
}
return false;
}
template <typename T>
ServerDescription buildBaseDescription(const T &server)
{
ServerDescription row;
row.hostName = server.hostName;
row.defaultContainer = server.defaultContainer;
row.primaryDnsIsAmnezia = (server.dns1 == protocols::dns::amneziaDnsIp);
row.hasInstalledVpnContainers = computeHasInstalledVpnContainers(server.containers);
return row;
}
QString getBaseDescription(const QMap<DockerContainer, ContainerConfig> &containers,
bool isAmneziaDnsEnabled,
bool hasWriteAccess,
bool primaryDnsIsAmnezia)
{
QString description;
if (hasWriteAccess) {
const bool isDnsInstalled = containers.contains(DockerContainer::Dns);
if (isAmneziaDnsEnabled && isDnsInstalled) {
description += QStringLiteral("Amnezia DNS | ");
}
} else if (primaryDnsIsAmnezia) {
description += QStringLiteral("Amnezia DNS | ");
}
return description;
}
QString getProtocolName(DockerContainer defaultContainer, const QMap<DockerContainer, ContainerConfig> &containers)
{
QString containerName = ContainerUtils::containerHumanNames().value(defaultContainer);
QString protocolVersion;
if (ContainerUtils::isAwgContainer(defaultContainer)) {
const auto it = containers.constFind(defaultContainer);
if (it != containers.cend()) {
if (const AwgProtocolConfig *awg = it->getAwgProtocolConfig()) {
protocolVersion = ProtocolUtils::getProtocolVersionString(awg->toJson());
if (defaultContainer == DockerContainer::Awg && !awg->serverConfig.isThirdPartyConfig) {
containerName = QStringLiteral("AmneziaWG Legacy");
}
}
}
}
return containerName + protocolVersion + QStringLiteral(" | ");
}
} // namespace
namespace amnezia
{
ServerDescription buildServerDescription(const SelfHostedAdminServerConfig &server, bool isAmneziaDnsEnabled)
{
ServerDescription row = buildBaseDescription(server);
row.selfHostedSshCredentials.hostName = server.hostName;
row.selfHostedSshCredentials.userName = server.userName;
row.selfHostedSshCredentials.secretData = server.password;
row.selfHostedSshCredentials.port = server.port > 0 ? server.port : 22;
row.hasWriteAccess = !row.selfHostedSshCredentials.userName.isEmpty()
&& !row.selfHostedSshCredentials.secretData.isEmpty();
row.serverName = server.displayName;
row.baseDescription = getBaseDescription(server.containers, isAmneziaDnsEnabled, row.hasWriteAccess, row.primaryDnsIsAmnezia);
const QString protocolName = getProtocolName(server.defaultContainer, server.containers);
row.expandedServerDescription = row.baseDescription + row.hostName;
row.collapsedServerDescription = row.baseDescription + protocolName + row.hostName;
return row;
}
ServerDescription buildServerDescription(const SelfHostedUserServerConfig &server, bool isAmneziaDnsEnabled)
{
ServerDescription row = buildBaseDescription(server);
row.selfHostedSshCredentials.hostName = server.hostName;
row.selfHostedSshCredentials.port = 22;
row.hasWriteAccess = false;
row.serverName = server.displayName;
row.baseDescription = getBaseDescription(server.containers, isAmneziaDnsEnabled, row.hasWriteAccess, row.primaryDnsIsAmnezia);
const QString protocolName = getProtocolName(server.defaultContainer, server.containers);
row.expandedServerDescription = row.baseDescription + row.hostName;
row.collapsedServerDescription = row.baseDescription + protocolName + row.hostName;
return row;
}
ServerDescription buildServerDescription(const NativeServerConfig &server, bool isAmneziaDnsEnabled)
{
ServerDescription row = buildBaseDescription(server);
row.hasWriteAccess = false;
row.serverName = server.displayName;
row.baseDescription = getBaseDescription(server.containers, isAmneziaDnsEnabled, row.hasWriteAccess, row.primaryDnsIsAmnezia);
const QString protocolName = getProtocolName(server.defaultContainer, server.containers);
row.expandedServerDescription = row.baseDescription + row.hostName;
row.collapsedServerDescription = row.baseDescription + protocolName + row.hostName;
return row;
}
ServerDescription buildServerDescription(const LegacyApiServerConfig &server, bool /*isAmneziaDnsEnabled*/)
{
ServerDescription row = buildBaseDescription(server);
row.configVersion = serverConfigUtils::ConfigSource::Telegram;
row.isApiV1 = true;
row.isServerFromGatewayApi = false;
row.hasWriteAccess = false;
row.serverName = server.displayName;
row.baseDescription = server.description;
const QString fullDescriptionForCollapsed = row.baseDescription;
row.collapsedServerDescription = fullDescriptionForCollapsed;
row.expandedServerDescription = fullDescriptionForCollapsed;
return row;
}
ServerDescription buildServerDescription(const ApiV2ServerConfig &server, bool /*isAmneziaDnsEnabled*/)
{
ServerDescription row = buildBaseDescription(server);
row.configVersion = serverConfigUtils::ConfigSource::AmneziaGateway;
row.isApiV2 = true;
row.isServerFromGatewayApi = true;
row.isPremium = server.isPremium() || server.isExternalPremium();
row.hasWriteAccess = false;
row.serverName = server.displayName;
row.baseDescription = server.apiConfig.serverCountryCode.isEmpty() ? server.description : server.apiConfig.serverCountryName;
row.isCountrySelectionAvailable = !server.apiConfig.availableCountries.isEmpty();
row.apiAvailableCountries = server.apiConfig.availableCountries;
row.apiServerCountryCode = server.apiConfig.serverCountryCode;
row.isAdVisible = server.apiConfig.serviceInfo.isAdVisible;
row.adHeader = server.apiConfig.serviceInfo.adHeader;
row.adDescription = server.apiConfig.serviceInfo.adDescription;
row.adEndpoint = server.apiConfig.serviceInfo.adEndpoint;
row.isRenewalAvailable = server.apiConfig.serviceInfo.isRenewalAvailable;
if (!server.apiConfig.isInAppPurchase) {
if (server.apiConfig.subscriptionExpiredByServer) {
row.isSubscriptionExpired = true;
} else if (!server.apiConfig.subscription.endDate.isEmpty()) {
row.isSubscriptionExpired = apiUtils::isSubscriptionExpired(server.apiConfig.subscription.endDate);
row.isSubscriptionExpiringSoon = apiUtils::isSubscriptionExpiringSoon(server.apiConfig.subscription.endDate);
}
}
const QString fullDescriptionForCollapsed = row.baseDescription;
row.collapsedServerDescription = fullDescriptionForCollapsed;
row.expandedServerDescription = fullDescriptionForCollapsed;
return row;
}
} // namespace amnezia
+64
View File
@@ -0,0 +1,64 @@
#ifndef SERVERDESCRIPTION_H
#define SERVERDESCRIPTION_H
#include <QString>
#include <QJsonArray>
#include "core/utils/containerEnum.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/models/selfhosted/selfHostedAdminServerConfig.h"
#include "core/models/selfhosted/selfHostedUserServerConfig.h"
#include "core/models/selfhosted/nativeServerConfig.h"
#include "core/models/api/legacyApiServerConfig.h"
#include "core/models/api/apiV2ServerConfig.h"
namespace amnezia
{
struct ServerDescription
{
QString serverId;
QString serverName;
QString baseDescription;
QString hostName;
int configVersion = 0;
ServerCredentials selfHostedSshCredentials;
bool hasWriteAccess = false;
bool primaryDnsIsAmnezia = false;
DockerContainer defaultContainer = DockerContainer::None;
bool hasInstalledVpnContainers = false;
bool isApiV1 = false;
bool isApiV2 = false;
bool isServerFromGatewayApi = false;
bool isPremium = false;
bool isCountrySelectionAvailable = false;
QJsonArray apiAvailableCountries;
QString apiServerCountryCode;
bool isAdVisible = false;
QString adHeader;
QString adDescription;
QString adEndpoint;
bool isRenewalAvailable = false;
bool isSubscriptionExpired = false;
bool isSubscriptionExpiringSoon = false;
QString collapsedServerDescription;
QString expandedServerDescription;
};
ServerDescription buildServerDescription(const SelfHostedAdminServerConfig &server, bool isAmneziaDnsEnabled);
ServerDescription buildServerDescription(const SelfHostedUserServerConfig &server, bool isAmneziaDnsEnabled);
ServerDescription buildServerDescription(const NativeServerConfig &server, bool isAmneziaDnsEnabled);
ServerDescription buildServerDescription(const LegacyApiServerConfig &server, bool isAmneziaDnsEnabled);
ServerDescription buildServerDescription(const ApiV2ServerConfig &server, bool isAmneziaDnsEnabled);
} // namespace amnezia
#endif
+16 -3
View File
@@ -68,7 +68,10 @@ QMap<Proto, QString> ProtocolUtils::protocolHumanNames()
{ Proto::TorWebSite, "Website in Tor network" }, { Proto::TorWebSite, "Website in Tor network" },
{ Proto::Dns, "DNS Service" }, { Proto::Dns, "DNS Service" },
{ Proto::Sftp, QObject::tr("SFTP service") }, { Proto::Sftp, QObject::tr("SFTP service") },
{ Proto::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } }; { Proto::Socks5Proxy, QObject::tr("SOCKS5 proxy server") },
{ Proto::MtProxy, QObject::tr("MTProxy (Telegram)") },
{ Proto::Telemt, QObject::tr("Telemt (Telegram)") },
};
} }
QMap<Proto, QString> ProtocolUtils::protocolDescriptions() QMap<Proto, QString> ProtocolUtils::protocolDescriptions()
@@ -92,6 +95,8 @@ ServiceType ProtocolUtils::protocolService(Proto p)
case Proto::Dns: return ServiceType::Other; case Proto::Dns: return ServiceType::Other;
case Proto::Sftp: return ServiceType::Other; case Proto::Sftp: return ServiceType::Other;
case Proto::Socks5Proxy: return ServiceType::Other; case Proto::Socks5Proxy: return ServiceType::Other;
case Proto::MtProxy: return ServiceType::Other;
case Proto::Telemt: return ServiceType::Other;
default: return ServiceType::Other; default: return ServiceType::Other;
} }
} }
@@ -104,6 +109,8 @@ int ProtocolUtils::getPortForInstall(Proto p)
case OpenVpn: case OpenVpn:
case Socks5Proxy: case Socks5Proxy:
return QRandomGenerator::global()->bounded(30000, 50000); return QRandomGenerator::global()->bounded(30000, 50000);
case MtProxy:
case Telemt:
default: default:
return defaultPort(p); return defaultPort(p);
} }
@@ -123,6 +130,8 @@ int ProtocolUtils::defaultPort(Proto p)
case Proto::Dns: return 53; case Proto::Dns: return 53;
case Proto::Sftp: return 222; case Proto::Sftp: return 222;
case Proto::Socks5Proxy: return 38080; case Proto::Socks5Proxy: return 38080;
case Proto::MtProxy: return QString(protocols::mtProxy::defaultPort).toInt();
case Proto::Telemt: return QString(protocols::telemt::defaultPort).toInt();
default: return -1; default: return -1;
} }
} }
@@ -141,6 +150,8 @@ bool ProtocolUtils::defaultPortChangeable(Proto p)
case Proto::Dns: return false; case Proto::Dns: return false;
case Proto::Sftp: return true; case Proto::Sftp: return true;
case Proto::Socks5Proxy: return true; case Proto::Socks5Proxy: return true;
case Proto::MtProxy: return true;
case Proto::Telemt: return true;
default: return false; default: return false;
} }
} }
@@ -161,6 +172,8 @@ TransportProto ProtocolUtils::defaultTransportProto(Proto p)
case Proto::Dns: return TransportProto::Udp; case Proto::Dns: return TransportProto::Udp;
case Proto::Sftp: return TransportProto::Tcp; case Proto::Sftp: return TransportProto::Tcp;
case Proto::Socks5Proxy: return TransportProto::Tcp; case Proto::Socks5Proxy: return TransportProto::Tcp;
case Proto::MtProxy: return TransportProto::Tcp;
case Proto::Telemt: return TransportProto::Tcp;
default: return TransportProto::Udp; default: return TransportProto::Udp;
} }
} }
@@ -180,9 +193,10 @@ bool ProtocolUtils::defaultTransportProtoChangeable(Proto p)
case Proto::Dns: return false; case Proto::Dns: return false;
case Proto::Sftp: return false; case Proto::Sftp: return false;
case Proto::Socks5Proxy: return false; case Proto::Socks5Proxy: return false;
case Proto::MtProxy: return false;
case Proto::Telemt: return false;
default: return false; default: return false;
} }
return false;
} }
QString ProtocolUtils::key_proto_config_data(Proto p) QString ProtocolUtils::key_proto_config_data(Proto p)
@@ -208,4 +222,3 @@ QString ProtocolUtils::getProtocolVersionString(const QJsonObject &protocolConfi
if (version == protocols::awg::awgV1_5) return QObject::tr(" (version 1.5)"); if (version == protocols::awg::awgV1_5) return QObject::tr(" (version 1.5)");
return ""; return "";
} }
@@ -2,15 +2,16 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject>
#include <QUuid> #include <QUuid>
#include "core/utils/errorCodes.h" #include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h" #include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h" #include "core/utils/commonStructs.h"
#include "core/utils/api/apiEnums.h" #include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h" #include "core/utils/constants/apiConstants.h"
#include "core/models/serverConfig.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/networkUtilities.h" #include "core/utils/networkUtilities.h"
using namespace amnezia; using namespace amnezia;
@@ -1,26 +1,44 @@
#include "secureServersRepository.h" #include "secureServersRepository.h"
#include <QJsonDocument>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument>
#include <QJsonValue>
#include <QUuid>
#include "core/utils/api/apiEnums.h" #include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
SecureServersRepository::SecureServersRepository(SecureQSettings* settings, QObject *parent) using namespace amnezia;
namespace {
QString readStorageServerId(const QJsonObject &json)
{
return json.value(QString(configKey::storageServerId)).toString().trimmed();
}
QJsonObject withoutStorageServerId(const QJsonObject &json)
{
QJsonObject o = json;
o.remove(QString(configKey::storageServerId));
return o;
}
QJsonObject embedStorageServerId(const QString &serverId, const QJsonObject &payloadSansId)
{
QJsonObject o = payloadSansId;
o.insert(QString(configKey::storageServerId), serverId);
return o;
}
} // namespace
SecureServersRepository::SecureServersRepository(SecureQSettings *settings, QObject *parent)
: QObject(parent), m_settings(settings) : QObject(parent), m_settings(settings)
{ {
QJsonArray arr = QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array(); loadFromStorage();
for (const QJsonValue &val : arr) { persistDefaultServerFields();
m_servers.append(ServerConfig::fromJson(val.toObject()));
}
m_defaultServerIndex = value("Servers/defaultServerIndex", 0).toInt();
} }
QVariant SecureServersRepository::value(const QString &key, const QVariant &defaultValue) const QVariant SecureServersRepository::value(const QString &key, const QVariant &defaultValue) const
@@ -33,216 +51,322 @@ void SecureServersRepository::setValue(const QString &key, const QVariant &value
m_settings->setValue(key, value); m_settings->setValue(key, value);
} }
void SecureServersRepository::clearServerStateMaps()
{
m_serverJsonById.clear();
m_orderedServerIds.clear();
}
QString SecureServersRepository::normalizedOrGeneratedServerId(const QString &candidateId) const
{
const QString trimmed = candidateId.trimmed();
if (!trimmed.isEmpty() && !m_serverJsonById.contains(trimmed)) {
return trimmed;
}
return QUuid::createUuid().toString(QUuid::WithoutBraces);
}
void SecureServersRepository::updateDefaultServerFromStorage()
{
const QString storedDefaultId = value(QStringLiteral("Servers/defaultServerId"), QString()).toString();
if (!storedDefaultId.isEmpty() && m_serverJsonById.contains(storedDefaultId)) {
m_defaultServerId = storedDefaultId;
return;
}
const int storedDefaultIndex = value("Servers/defaultServerIndex", 0).toInt();
if (storedDefaultIndex >= 0 && storedDefaultIndex < m_orderedServerIds.size()) {
m_defaultServerId = m_orderedServerIds.at(storedDefaultIndex);
return;
}
if (!m_orderedServerIds.isEmpty()) {
m_defaultServerId = m_orderedServerIds.first();
return;
}
m_defaultServerId.clear();
}
void SecureServersRepository::persistDefaultServerFields()
{
if (m_orderedServerIds.isEmpty()) {
m_defaultServerId.clear();
} else if (!m_orderedServerIds.contains(m_defaultServerId)) {
m_defaultServerId = m_orderedServerIds.first();
}
setValue("Servers/defaultServerId", m_defaultServerId);
}
void SecureServersRepository::loadFromStorage()
{
clearServerStateMaps();
const QJsonArray serversArray =
QJsonDocument::fromJson(value(QStringLiteral("Servers/serversList"), QByteArray()).toByteArray())
.array();
for (int i = 0; i < serversArray.size(); ++i) {
const QJsonObject json = serversArray.at(i).toObject();
const QString candidateId = readStorageServerId(json);
const QString serverId = normalizedOrGeneratedServerId(candidateId);
const QJsonObject strippedJson = withoutStorageServerId(json);
const serverConfigUtils::ConfigType kind = serverConfigUtils::configTypeFromJson(strippedJson);
if (m_serverJsonById.contains(serverId) || kind == serverConfigUtils::ConfigType::Invalid) {
continue;
}
m_serverJsonById.insert(serverId, embedStorageServerId(serverId, strippedJson));
m_orderedServerIds.append(serverId);
}
updateDefaultServerFromStorage();
}
void SecureServersRepository::syncToStorage() void SecureServersRepository::syncToStorage()
{ {
QJsonArray arr; QJsonArray serversArray;
for (const ServerConfig &cfg : m_servers) {
arr.append(cfg.toJson()); for (const QString &serverId : m_orderedServerIds) {
if (!m_serverJsonById.contains(serverId)) {
continue;
}
serversArray.append(m_serverJsonById.value(serverId));
} }
setValue("Servers/serversList", QJsonDocument(arr).toJson());
setValue("Servers/serversList", QJsonDocument(serversArray).toJson());
persistDefaultServerFields();
} }
void SecureServersRepository::invalidateCache() void SecureServersRepository::invalidateCache()
{ {
m_servers.clear(); loadFromStorage();
QJsonArray arr = QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array();
for (const QJsonValue &val : arr) {
m_servers.append(ServerConfig::fromJson(val.toObject()));
}
m_defaultServerIndex = value("Servers/defaultServerIndex", 0).toInt();
} }
void SecureServersRepository::setServersArray(const QJsonArray &servers) void SecureServersRepository::clearServers()
{ {
m_servers.clear(); clearServerStateMaps();
for (const QJsonValue &val : servers) {
m_servers.append(ServerConfig::fromJson(val.toObject())); m_defaultServerId.clear();
}
syncToStorage(); syncToStorage();
} }
void SecureServersRepository::addServer(const ServerConfig &server) QString SecureServersRepository::addServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind)
{ {
m_servers.append(server); const QString id = normalizedOrGeneratedServerId(serverId);
if (m_serverJsonById.contains(id) || kind == serverConfigUtils::ConfigType::Invalid) {
return id;
}
const QJsonObject strippedJson = withoutStorageServerId(serverJson);
if (serverConfigUtils::configTypeFromJson(strippedJson) != kind) {
return id;
}
m_serverJsonById.insert(id, embedStorageServerId(id, strippedJson));
m_orderedServerIds.append(id);
if (m_defaultServerId.isEmpty()) {
m_defaultServerId = id;
}
syncToStorage(); syncToStorage();
emit serverAdded(server); emit serverAdded(id);
return id;
} }
void SecureServersRepository::editServer(int index, const ServerConfig &server) void SecureServersRepository::editServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind)
{ {
if (index < 0 || index >= m_servers.size()) { if (indexOfServerId(serverId) < 0 || kind == serverConfigUtils::ConfigType::Invalid) {
return; return;
} }
m_servers.replace(index, server); if (!m_serverJsonById.contains(serverId)) {
syncToStorage();
emit serverEdited(index, server);
}
void SecureServersRepository::removeServer(int index)
{
if (index < 0 || index >= m_servers.size()) {
return; return;
} }
int defaultIndex = m_defaultServerIndex;
m_servers.removeAt(index);
if (defaultIndex == index) { const QJsonObject oldJson = m_serverJsonById.value(serverId);
setDefaultServer(0); const serverConfigUtils::ConfigType oldKind = serverConfigUtils::configTypeFromJson(withoutStorageServerId(oldJson));
} else if (defaultIndex > index) {
setDefaultServer(defaultIndex - 1); m_serverJsonById.remove(serverId);
const QJsonObject strippedNew = withoutStorageServerId(serverJson);
if (serverConfigUtils::configTypeFromJson(strippedNew) != kind) {
const QJsonObject strippedOld = withoutStorageServerId(oldJson);
if (oldKind != serverConfigUtils::ConfigType::Invalid && serverConfigUtils::configTypeFromJson(strippedOld) == oldKind) {
m_serverJsonById.insert(serverId, embedStorageServerId(serverId, strippedOld));
}
return;
}
m_serverJsonById.insert(serverId, embedStorageServerId(serverId, strippedNew));
syncToStorage();
emit serverEdited(serverId);
}
void SecureServersRepository::removeServer(const QString &serverId)
{
const int removedIndex = indexOfServerId(serverId);
if (removedIndex < 0) {
return;
}
if (!m_serverJsonById.contains(serverId)) {
return;
} }
if (m_servers.isEmpty()) { const QString previousDefaultId = m_defaultServerId;
setDefaultServer(0); const int previousDefaultIndex = defaultServerIndex();
m_serverJsonById.remove(serverId);
m_orderedServerIds.removeAt(removedIndex);
if (m_orderedServerIds.isEmpty()) {
m_defaultServerId.clear();
} else if (m_defaultServerId == serverId) {
const int fallbackIndex = qMin(removedIndex, m_orderedServerIds.size() - 1);
m_defaultServerId = m_orderedServerIds.at(fallbackIndex);
} else if (!m_orderedServerIds.contains(m_defaultServerId)) {
m_defaultServerId = m_orderedServerIds.first();
}
const int newDefaultIndex = defaultServerIndex();
if (previousDefaultId != m_defaultServerId || previousDefaultIndex != newDefaultIndex) {
emit defaultServerChanged(m_defaultServerId);
} }
syncToStorage(); syncToStorage();
emit serverRemoved(index); emit serverRemoved(serverId, removedIndex);
} }
ServerConfig SecureServersRepository::server(int index) const serverConfigUtils::ConfigType SecureServersRepository::serverKind(const QString &serverId) const
{ {
if (index < 0 || index >= m_servers.size()) { const auto it = m_serverJsonById.constFind(serverId);
return SelfHostedServerConfig{}; if (it == m_serverJsonById.constEnd()) {
return serverConfigUtils::ConfigType::Invalid;
} }
return m_servers.at(index); return serverConfigUtils::configTypeFromJson(withoutStorageServerId(it.value()));
} }
QVector<ServerConfig> SecureServersRepository::servers() const std::optional<SelfHostedAdminServerConfig> SecureServersRepository::selfHostedAdminConfig(const QString &serverId) const
{ {
return m_servers; const auto it = m_serverJsonById.constFind(serverId);
if (it == m_serverJsonById.constEnd()) {
return std::nullopt;
}
const QJsonObject strippedJson = withoutStorageServerId(it.value());
if (serverConfigUtils::configTypeFromJson(strippedJson) != serverConfigUtils::ConfigType::SelfHostedAdmin) {
return std::nullopt;
}
return SelfHostedAdminServerConfig::fromJson(strippedJson);
}
std::optional<SelfHostedUserServerConfig> SecureServersRepository::selfHostedUserConfig(const QString &serverId) const
{
const auto it = m_serverJsonById.constFind(serverId);
if (it == m_serverJsonById.constEnd()) {
return std::nullopt;
}
const QJsonObject strippedJson = withoutStorageServerId(it.value());
if (serverConfigUtils::configTypeFromJson(strippedJson) != serverConfigUtils::ConfigType::SelfHostedUser) {
return std::nullopt;
}
return SelfHostedUserServerConfig::fromJson(strippedJson);
}
std::optional<NativeServerConfig> SecureServersRepository::nativeConfig(const QString &serverId) const
{
const auto it = m_serverJsonById.constFind(serverId);
if (it == m_serverJsonById.constEnd()) {
return std::nullopt;
}
const QJsonObject strippedJson = withoutStorageServerId(it.value());
if (serverConfigUtils::configTypeFromJson(strippedJson) != serverConfigUtils::ConfigType::Native) {
return std::nullopt;
}
return NativeServerConfig::fromJson(strippedJson);
}
std::optional<ApiV2ServerConfig> SecureServersRepository::apiV2Config(const QString &serverId) const
{
const auto it = m_serverJsonById.constFind(serverId);
if (it == m_serverJsonById.constEnd()) {
return std::nullopt;
}
const QJsonObject strippedJson = withoutStorageServerId(it.value());
if (!serverConfigUtils::isApiV2Subscription(serverConfigUtils::configTypeFromJson(strippedJson))) {
return std::nullopt;
}
return ApiV2ServerConfig::fromJson(strippedJson);
}
std::optional<LegacyApiServerConfig> SecureServersRepository::legacyApiConfig(const QString &serverId) const
{
const auto it = m_serverJsonById.constFind(serverId);
if (it == m_serverJsonById.constEnd()) {
return std::nullopt;
}
const QJsonObject strippedJson = withoutStorageServerId(it.value());
if (!serverConfigUtils::isLegacyApiSubscription(serverConfigUtils::configTypeFromJson(strippedJson))) {
return std::nullopt;
}
return LegacyApiServerConfig::fromJson(strippedJson);
} }
int SecureServersRepository::serversCount() const int SecureServersRepository::serversCount() const
{ {
return m_servers.size(); return m_orderedServerIds.size();
}
QString SecureServersRepository::serverIdAt(int index) const
{
if (index < 0 || index >= m_orderedServerIds.size()) {
return QString();
}
return m_orderedServerIds.at(index);
}
QVector<QString> SecureServersRepository::orderedServerIds() const
{
return m_orderedServerIds;
}
int SecureServersRepository::indexOfServerId(const QString &serverId) const
{
return m_orderedServerIds.indexOf(serverId);
} }
int SecureServersRepository::defaultServerIndex() const int SecureServersRepository::defaultServerIndex() const
{ {
return m_defaultServerIndex; if (m_orderedServerIds.isEmpty()) {
return 0;
}
const int idx = m_orderedServerIds.indexOf(m_defaultServerId);
return idx >= 0 ? idx : 0;
} }
void SecureServersRepository::setDefaultServer(int index) QString SecureServersRepository::defaultServerId() const
{ {
if (index < 0) { return m_defaultServerId;
}
void SecureServersRepository::setDefaultServer(const QString &serverId)
{
if (m_orderedServerIds.isEmpty()) {
return; return;
} }
if (m_servers.size() > 0 && index >= m_servers.size()) { if (!m_serverJsonById.contains(serverId)) {
return; return;
} }
if (m_servers.isEmpty() && index != 0) {
if (indexOfServerId(serverId) < 0) {
return; return;
} }
if (m_defaultServerIndex == index) {
if (m_defaultServerId == serverId) {
return; return;
} }
m_defaultServerIndex = index;
setValue("Servers/defaultServerIndex", index);
emit defaultServerChanged(index);
}
void SecureServersRepository::setDefaultContainer(int serverIndex, DockerContainer container) m_defaultServerId = serverId;
{ persistDefaultServerFields();
ServerConfig config = server(serverIndex); emit defaultServerChanged(m_defaultServerId);
config.visit([container](auto& arg) {
arg.defaultContainer = container;
});
editServer(serverIndex, config);
}
ContainerConfig SecureServersRepository::containerConfig(int serverIndex, DockerContainer container) const
{
ServerConfig config = server(serverIndex);
return config.containerConfig(container);
}
void SecureServersRepository::setContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config)
{
ServerConfig serverConfig = server(serverIndex);
serverConfig.visit([container, &config](auto& arg) {
arg.containers[container] = config;
});
editServer(serverIndex, serverConfig);
}
void SecureServersRepository::clearLastConnectionConfig(int serverIndex, DockerContainer container)
{
ServerConfig serverConfig = server(serverIndex);
ContainerConfig containerCfg = serverConfig.containerConfig(container);
containerCfg.protocolConfig.clearClientConfig();
setContainerConfig(serverIndex, container, containerCfg);
}
ServerCredentials SecureServersRepository::serverCredentials(int index) const
{
ServerConfig config = server(index);
if (config.isSelfHosted()) {
const SelfHostedServerConfig* selfHosted = config.as<SelfHostedServerConfig>();
if (!selfHosted) return ServerCredentials();
auto creds = selfHosted->credentials();
if (creds.has_value()) {
return creds.value();
}
}
return ServerCredentials{};
}
bool SecureServersRepository::hasServerWithVpnKey(const QString &vpnKey) const
{
QString normalizedInput = vpnKey.trimmed();
if (normalizedInput.startsWith(QStringLiteral("vpn://"), Qt::CaseInsensitive)) {
normalizedInput = normalizedInput.mid(QStringLiteral("vpn://").size());
}
if (normalizedInput.isEmpty()) {
return false;
}
QVector<ServerConfig> serversList = servers();
for (const ServerConfig& serverConfig : serversList) {
if (serverConfig.isApiV1()) {
const ApiV1ServerConfig* apiV1 = serverConfig.as<ApiV1ServerConfig>();
if (!apiV1) continue;
QString storedKey = apiV1->vpnKey();
if (storedKey.isEmpty()) {
continue;
}
QString normalizedStored = storedKey.trimmed();
if (normalizedStored.startsWith(QStringLiteral("vpn://"), Qt::CaseInsensitive)) {
normalizedStored = normalizedStored.mid(QStringLiteral("vpn://").size());
}
if (normalizedInput == normalizedStored) {
return true;
}
} else if (serverConfig.isApiV2()) {
const ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
if (!apiV2) continue;
QString storedKey = apiV2->vpnKey();
if (storedKey.isEmpty()) {
continue;
}
QString normalizedStored = storedKey.trimmed();
if (normalizedStored.startsWith(QStringLiteral("vpn://"), Qt::CaseInsensitive)) {
normalizedStored = normalizedStored.mid(QStringLiteral("vpn://").size());
}
if (normalizedInput == normalizedStored) {
return true;
}
}
}
return false;
}
bool SecureServersRepository::hasServerWithCrc(quint16 crc) const
{
for (const ServerConfig& serverConfig : m_servers) {
if (static_cast<quint16>(serverConfig.crc()) == crc) {
return true;
}
}
return false;
} }
@@ -1,14 +1,20 @@
#ifndef SECURESERVERSREPOSITORY_H #ifndef SECURESERVERSREPOSITORY_H
#define SECURESERVERSREPOSITORY_H #define SECURESERVERSREPOSITORY_H
#include <QHash>
#include <QJsonObject>
#include <QObject> #include <QObject>
#include <QVector> #include <QVector>
#include <QJsonArray>
#include <QJsonDocument>
#include <QtGlobal> #include <QtGlobal>
#include <optional>
#include "core/models/serverConfig.h" #include "core/models/selfhosted/selfHostedAdminServerConfig.h"
#include "core/models/selfhosted/selfHostedUserServerConfig.h"
#include "core/models/selfhosted/nativeServerConfig.h"
#include "core/models/api/apiV2ServerConfig.h"
#include "core/models/api/legacyApiServerConfig.h"
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/utils/serverConfigUtils.h"
#include "secureQSettings.h" #include "secureQSettings.h"
using namespace amnezia; using namespace amnezia;
@@ -18,47 +24,57 @@ class SecureServersRepository : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit SecureServersRepository(SecureQSettings* settings, QObject *parent = nullptr); explicit SecureServersRepository(SecureQSettings *settings, QObject *parent = nullptr);
QString addServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind);
void editServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind);
void removeServer(const QString &serverId);
serverConfigUtils::ConfigType serverKind(const QString &serverId) const;
std::optional<SelfHostedAdminServerConfig> selfHostedAdminConfig(const QString &serverId) const;
std::optional<SelfHostedUserServerConfig> selfHostedUserConfig(const QString &serverId) const;
std::optional<NativeServerConfig> nativeConfig(const QString &serverId) const;
std::optional<ApiV2ServerConfig> apiV2Config(const QString &serverId) const;
std::optional<LegacyApiServerConfig> legacyApiConfig(const QString &serverId) const;
void addServer(const ServerConfig &server);
void editServer(int index, const ServerConfig &server);
void removeServer(int index);
ServerConfig server(int index) const;
QVector<ServerConfig> servers() const;
int serversCount() const; int serversCount() const;
int indexOfServerId(const QString &serverId) const;
QString serverIdAt(int index) const;
QVector<QString> orderedServerIds() const;
int defaultServerIndex() const; int defaultServerIndex() const;
void setDefaultServer(int index); QString defaultServerId() const;
void setDefaultServer(const QString &serverId);
void setDefaultContainer(int serverIndex, DockerContainer container); void clearServers();
ContainerConfig containerConfig(int serverIndex, DockerContainer container) const;
void setContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config);
void clearLastConnectionConfig(int serverIndex, DockerContainer container);
ServerCredentials serverCredentials(int index) const;
bool hasServerWithVpnKey(const QString &vpnKey) const;
bool hasServerWithCrc(quint16 crc) const;
void setServersArray(const QJsonArray &servers);
void invalidateCache(); void invalidateCache();
signals: signals:
void serverAdded(ServerConfig config); void serverAdded(const QString &serverId);
void serverEdited(int index, ServerConfig config); void serverEdited(const QString &serverId);
void serverRemoved(int index); void serverRemoved(const QString &serverId, int removedIndex);
void defaultServerChanged(int index); void defaultServerChanged(const QString &defaultServerId);
private: private:
void loadFromStorage();
void updateDefaultServerFromStorage();
void persistDefaultServerFields();
QString normalizedOrGeneratedServerId(const QString &candidateId) const;
void syncToStorage(); void syncToStorage();
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; QVariant value(const QString &key, const QVariant &defaultValue) const;
void setValue(const QString &key, const QVariant &value); void setValue(const QString &key, const QVariant &value);
SecureQSettings* m_settings; void clearServerStateMaps();
QVector<ServerConfig> m_servers; SecureQSettings *m_settings;
int m_defaultServerIndex = 0;
QHash<QString, QJsonObject> m_serverJsonById;
QVector<QString> m_orderedServerIds;
QString m_defaultServerId;
}; };
#endif // SECURESERVERSREPOSITORY_H #endif // SECURESERVERSREPOSITORY_H
-25
View File
@@ -1,25 +0,0 @@
#ifndef APIENUMS_H
#define APIENUMS_H
namespace apiDefs
{
enum ConfigType {
AmneziaFreeV2 = 0,
AmneziaFreeV3,
AmneziaPremiumV1,
AmneziaPremiumV2,
AmneziaTrialV2,
SelfHosted,
ExternalPremium,
ExternalTrial
};
enum ConfigSource {
Telegram = 1,
AmneziaGateway
};
}
#endif // APIENUMS_H
+23 -64
View File
@@ -1,6 +1,8 @@
#include "apiUtils.h" #include "apiUtils.h"
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include <QLatin1Char>
#include <QDateTime> #include <QDateTime>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
@@ -75,63 +77,6 @@ bool apiUtils::isSubscriptionExpiringSoon(const QString &subscriptionEndDate, in
return endDate <= nowUtc.addDays(withinDays); return endDate <= nowUtc.addDays(withinDays);
} }
bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
{
auto configVersion = serverConfigObject.value(configKey::configVersion).toInt();
switch (configVersion) {
case apiDefs::ConfigSource::Telegram: return true;
case apiDefs::ConfigSource::AmneziaGateway: return true;
default: return false;
}
}
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
{
auto configVersion = serverConfigObject.value(configKey::configVersion).toInt();
switch (configVersion) {
case apiDefs::ConfigSource::Telegram: {
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString();
if (apiEndpoint.contains(premiumV1Endpoint)) {
return apiDefs::ConfigType::AmneziaPremiumV1;
} else if (apiEndpoint.contains(freeV2Endpoint)) {
return apiDefs::ConfigType::AmneziaFreeV2;
}
};
case apiDefs::ConfigSource::AmneziaGateway: {
constexpr QLatin1String servicePremium("amnezia-premium");
constexpr QLatin1String serviceFree("amnezia-free");
constexpr QLatin1String serviceExternalPremium("external-premium");
constexpr QLatin1String serviceExternalTrial("external-trial");
auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString();
if (serviceType == servicePremium) {
return apiDefs::ConfigType::AmneziaPremiumV2;
} else if (serviceType == serviceFree) {
return apiDefs::ConfigType::AmneziaFreeV3;
} else if (serviceType == serviceExternalPremium) {
return apiDefs::ConfigType::ExternalPremium;
} else if (serviceType == serviceExternalTrial) {
return apiDefs::ConfigType::ExternalTrial;
}
}
default: {
return apiDefs::ConfigType::SelfHosted;
}
};
}
apiDefs::ConfigSource apiUtils::getConfigSource(const QJsonObject &serverConfigObject)
{
return static_cast<apiDefs::ConfigSource>(serverConfigObject.value(configKey::configVersion).toInt());
}
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString, amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
const QNetworkReply::NetworkError &replyError, const int httpStatusCode, const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
const QByteArray &responseBody) const QByteArray &responseBody)
@@ -197,14 +142,14 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject) bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
{ {
static const QSet<apiDefs::ConfigType> premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2, static const QSet<serverConfigUtils::ConfigType> premiumTypes = { serverConfigUtils::ConfigType::AmneziaPremiumV1, serverConfigUtils::ConfigType::AmneziaPremiumV2,
apiDefs::ConfigType::ExternalPremium, apiDefs::ConfigType::ExternalTrial }; serverConfigUtils::ConfigType::ExternalPremium };
return premiumTypes.contains(getConfigType(serverConfigObject)); return premiumTypes.contains(serverConfigUtils::configTypeFromJson(serverConfigObject));
} }
QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject) QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
{ {
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) { if (serverConfigUtils::configTypeFromJson(serverConfigObject) != serverConfigUtils::ConfigType::AmneziaPremiumV1) {
return {}; return {};
} }
@@ -242,9 +187,8 @@ QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject) QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
{ {
auto configType = apiUtils::getConfigType(serverConfigObject); auto configType = serverConfigUtils::configTypeFromJson(serverConfigObject);
if (configType != apiDefs::ConfigType::AmneziaPremiumV2 && configType != apiDefs::ConfigType::ExternalPremium if (configType != serverConfigUtils::ConfigType::AmneziaPremiumV2 && configType != serverConfigUtils::ConfigType::ExternalPremium) {
&& configType != apiDefs::ConfigType::ExternalTrial) {
return {}; return {};
} }
@@ -289,3 +233,18 @@ QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
return vpnKeyText; return vpnKeyText;
} }
QString apiUtils::countryCodeBaseForFlag(const QString &fullCountryCode)
{
const QString trimmed = fullCountryCode.trimmed();
if (trimmed.isEmpty()) {
return QString();
}
const int dashIdx = trimmed.indexOf(QLatin1Char('-'));
const QString base = dashIdx < 0 ? trimmed : trimmed.left(dashIdx);
const QString normalized = base.trimmed();
if (normalized.isEmpty()) {
return QString();
}
return normalized.toUpper();
}
+4 -6
View File
@@ -4,7 +4,7 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QObject> #include <QObject>
#include "core/utils/api/apiEnums.h" #include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h" #include "core/utils/constants/apiConstants.h"
#include "core/utils/errorCodes.h" #include "core/utils/errorCodes.h"
@@ -13,23 +13,21 @@
namespace apiUtils namespace apiUtils
{ {
bool isServerFromApi(const QJsonObject &serverConfigObject);
bool isSubscriptionExpired(const QString &subscriptionEndDate); bool isSubscriptionExpired(const QString &subscriptionEndDate);
bool isSubscriptionExpiringSoon(const QString &subscriptionEndDate, int withinDays = 30); bool isSubscriptionExpiringSoon(const QString &subscriptionEndDate, int withinDays = 30);
bool isPremiumServer(const QJsonObject &serverConfigObject); bool isPremiumServer(const QJsonObject &serverConfigObject);
apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);
apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString, amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
const QNetworkReply::NetworkError &replyError, const int httpStatusCode, const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
const QByteArray &responseBody); const QByteArray &responseBody);
QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject); QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject);
QString getPremiumV2VpnKey(const QJsonObject &serverConfigObject); QString getPremiumV2VpnKey(const QJsonObject &serverConfigObject);
// ISO2-style segment for flagKit assets (e.g. US-WEST -> US). Do not use in API request bodies.
QString countryCodeBaseForFlag(const QString &fullCountryCode);
} }
#endif // APIUTILS_H #endif // APIUTILS_H
+4 -4
View File
@@ -3,9 +3,9 @@
namespace apiDefs namespace apiDefs
{ {
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
} constexpr int requestTimeoutMsecs = 12 * 1000; // 12 secs
} // namespace apiDefs
#endif // APICONSTANTS_H #endif // APICONSTANTS_H
+2 -3
View File
@@ -2,7 +2,6 @@
#define APIKEYS_H #define APIKEYS_H
#include <QLatin1String> #include <QLatin1String>
#include "core/utils/api/apiEnums.h"
namespace apiDefs namespace apiDefs
{ {
@@ -82,7 +81,7 @@ namespace apiDefs
constexpr QLatin1String expiresAt("expires_at"); constexpr QLatin1String expiresAt("expires_at");
constexpr QLatin1String isConnectEvent("is_connect_event"); constexpr QLatin1String isConnectEvent("is_connect_event");
constexpr QLatin1String certificate("certificate"); constexpr QLatin1String certificate("certificate");
} } // namespace key
} } // namespace apiDefs
#endif // APIKEYS_H #endif // APIKEYS_H
+5
View File
@@ -18,6 +18,7 @@ namespace amnezia
constexpr QLatin1String serverIndex("serverIndex"); constexpr QLatin1String serverIndex("serverIndex");
constexpr QLatin1String description("description"); constexpr QLatin1String description("description");
constexpr QLatin1String displayName("displayName");
constexpr QLatin1String name("name"); constexpr QLatin1String name("name");
constexpr QLatin1String cert("cert"); constexpr QLatin1String cert("cert");
constexpr QLatin1String accessToken("api_key"); constexpr QLatin1String accessToken("api_key");
@@ -92,6 +93,8 @@ namespace amnezia
constexpr QLatin1String xray("xray"); constexpr QLatin1String xray("xray");
constexpr QLatin1String ssxray("ssxray"); constexpr QLatin1String ssxray("ssxray");
constexpr QLatin1String socks5proxy("socks5proxy"); constexpr QLatin1String socks5proxy("socks5proxy");
constexpr QLatin1String mtproxy("mtproxy");
constexpr QLatin1String telemt("telemt");
constexpr QLatin1String splitTunnelSites("splitTunnelSites"); constexpr QLatin1String splitTunnelSites("splitTunnelSites");
constexpr QLatin1String splitTunnelType("splitTunnelType"); constexpr QLatin1String splitTunnelType("splitTunnelType");
@@ -121,6 +124,8 @@ namespace amnezia
constexpr QLatin1String latestHandshake("latestHandshake"); constexpr QLatin1String latestHandshake("latestHandshake");
constexpr QLatin1String dataReceived("dataReceived"); constexpr QLatin1String dataReceived("dataReceived");
constexpr QLatin1String dataSent("dataSent"); constexpr QLatin1String dataSent("dataSent");
constexpr QLatin1String storageServerId("storageServerId");
} }
} }
@@ -3,6 +3,7 @@
namespace amnezia namespace amnezia
{ {
namespace protocols namespace protocols
{ {
@@ -174,9 +175,71 @@ namespace amnezia
constexpr char proxyConfigPath[] = "/usr/local/3proxy/conf/3proxy.cfg"; constexpr char proxyConfigPath[] = "/usr/local/3proxy/conf/3proxy.cfg";
} }
namespace mtProxy
{
constexpr char secretKey[] = "mtproxy_secret";
constexpr char tagKey[] = "mtproxy_tag";
constexpr char tgLinkKey[] = "mtproxy_tg_link";
constexpr char tmeLinkKey[] = "mtproxy_tme_link";
constexpr char isEnabledKey[] = "mtproxy_is_enabled";
constexpr char publicHostKey[] = "mtproxy_public_host";
constexpr char transportModeKey[] = "mtproxy_transport_mode";
constexpr char tlsDomainKey[] = "mtproxy_tls_domain";
constexpr char additionalSecretsKey[] = "mtproxy_additional_secrets";
constexpr char workersKey[] = "mtproxy_workers";
constexpr char workersModeKey[] = "mtproxy_workers_mode";
constexpr char natEnabledKey[] = "mtproxy_nat_enabled";
constexpr char natInternalIpKey[] = "mtproxy_nat_internal_ip";
constexpr char natExternalIpKey[] = "mtproxy_nat_external_ip";
constexpr char transportModeStandard[] = "standard";
constexpr char transportModeFakeTLS[] = "faketls";
constexpr char workersModeAuto[] = "auto";
constexpr char workersModeManual[] = "manual";
constexpr char defaultPort[] = "443";
constexpr char defaultWorkers[] = "2";
constexpr int maxWorkers = 32;
constexpr int botTagHexLength = 32;
constexpr char defaultTlsDomain[] = "googletagmanager.com";
}
namespace telemt
{
constexpr char secretKey[] = "telemt_secret";
constexpr char tagKey[] = "telemt_tag";
constexpr char tgLinkKey[] = "telemt_tg_link";
constexpr char tmeLinkKey[] = "telemt_tme_link";
constexpr char isEnabledKey[] = "telemt_is_enabled";
constexpr char publicHostKey[] = "telemt_public_host";
constexpr char transportModeKey[] = "telemt_transport_mode";
constexpr char tlsDomainKey[] = "telemt_tls_domain";
constexpr char maskEnabledKey[] = "telemt_mask_enabled";
constexpr char tlsEmulationKey[] = "telemt_tls_emulation";
constexpr char useMiddleProxyKey[] = "telemt_use_middle_proxy";
constexpr char userNameKey[] = "telemt_user_name";
// Stored for UI only (Telemt server ignores these; same controls as MTProxy page)
constexpr char additionalSecretsKey[] = "telemt_additional_secrets";
constexpr char workersKey[] = "telemt_workers";
constexpr char workersModeKey[] = "telemt_workers_mode";
constexpr char natEnabledKey[] = "telemt_nat_enabled";
constexpr char natInternalIpKey[] = "telemt_nat_internal_ip";
constexpr char natExternalIpKey[] = "telemt_nat_external_ip";
constexpr char transportModeStandard[] = "standard";
constexpr char transportModeFakeTLS[] = "faketls";
constexpr char defaultPort[] = "443";
constexpr char defaultTlsDomain[] = "googletagmanager.com";
constexpr char defaultUserName[] = "amnezia";
constexpr char defaultWorkers[] = "2";
constexpr char workersModeAuto[] = "auto";
constexpr char workersModeManual[] = "manual";
constexpr int maxWorkers = 32;
}
} // namespace protocols } // namespace protocols
} }
#endif // PROTOCOLCONSTANTS_H #endif // PROTOCOLCONSTANTS_H
+3 -1
View File
@@ -23,7 +23,9 @@ namespace amnezia
TorWebSite, TorWebSite,
Dns, Dns,
Sftp, Sftp,
Socks5Proxy Socks5Proxy,
MtProxy,
Telemt,
}; };
Q_ENUM_NS(DockerContainer) Q_ENUM_NS(DockerContainer)
} // namespace ContainerEnumNS } // namespace ContainerEnumNS
@@ -72,7 +72,10 @@ QMap<DockerContainer, QString> ContainerUtils::containerHumanNames()
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") }, { DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
{ DockerContainer::Dns, QObject::tr("AmneziaDNS") }, { DockerContainer::Dns, QObject::tr("AmneziaDNS") },
{ DockerContainer::Sftp, QObject::tr("SFTP file sharing service") }, { DockerContainer::Sftp, QObject::tr("SFTP file sharing service") },
{ DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } }; { DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") },
{ DockerContainer::MtProxy, QObject::tr("MTProxy (Telegram)") },
{ DockerContainer::Telemt, QObject::tr("Telemt (Telegram)") },
};
} }
QMap<DockerContainer, QString> ContainerUtils::containerDescriptions() QMap<DockerContainer, QString> ContainerUtils::containerDescriptions()
@@ -102,7 +105,12 @@ QMap<DockerContainer, QString> ContainerUtils::containerDescriptions()
{ DockerContainer::Sftp, { DockerContainer::Sftp,
QObject::tr("Create a file vault on your server to securely store and transfer files.") }, QObject::tr("Create a file vault on your server to securely store and transfer files.") },
{ DockerContainer::Socks5Proxy, { DockerContainer::Socks5Proxy,
QObject::tr("") } }; QObject::tr("") },
{ DockerContainer::MtProxy,
QObject::tr("Telegram MTProto proxy server") },
{ DockerContainer::Telemt,
QObject::tr("Telegram MTProto proxy (Telemt, Rust)") },
};
} }
QMap<DockerContainer, QString> ContainerUtils::containerDetailedDescriptions() QMap<DockerContainer, QString> ContainerUtils::containerDetailedDescriptions()
@@ -172,7 +180,15 @@ QMap<DockerContainer, QString> ContainerUtils::containerDetailedDescriptions()
"You will be able to access it using\n FileZilla or other SFTP clients, " "You will be able to access it using\n FileZilla or other SFTP clients, "
"as well as mount the disk on your device to access\n it directly from your device.\n\n" "as well as mount the disk on your device to access\n it directly from your device.\n\n"
"For more detailed information, you can\n find it in the support section under \"Create SFTP file storage.\" ") }, "For more detailed information, you can\n find it in the support section under \"Create SFTP file storage.\" ") },
{ DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } { DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") },
{ DockerContainer::MtProxy,
QObject::tr("Telegram MTProto proxy server. "
"Allows Telegram clients to connect through your server "
"using the MTProto protocol. Supports FakeTLS mode for "
"bypassing DPI-based blocking.") },
{ DockerContainer::Telemt,
QObject::tr("Telegram MTProto proxy powered by Telemt (Rust). "
"Supports secure and TLS fronting modes with optional traffic masking.") },
}; };
} }
@@ -197,6 +213,8 @@ Proto ContainerUtils::defaultProtocol(DockerContainer c)
case DockerContainer::Dns: return Proto::Dns; case DockerContainer::Dns: return Proto::Dns;
case DockerContainer::Sftp: return Proto::Sftp; case DockerContainer::Sftp: return Proto::Sftp;
case DockerContainer::Socks5Proxy: return Proto::Socks5Proxy; case DockerContainer::Socks5Proxy: return Proto::Socks5Proxy;
case DockerContainer::MtProxy: return Proto::MtProxy;
case DockerContainer::Telemt: return Proto::Telemt;
default: return Proto::Unknown; default: return Proto::Unknown;
} }
} }
@@ -224,6 +242,8 @@ bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
case DockerContainer::Awg: return true; case DockerContainer::Awg: return true;
case DockerContainer::Xray: return true; case DockerContainer::Xray: return true;
case DockerContainer::SSXray: return true; case DockerContainer::SSXray: return true;
case DockerContainer::MtProxy: return true;
case DockerContainer::Telemt: return true;
default: default:
return false; return false;
} }
@@ -237,7 +257,8 @@ bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
case DockerContainer::Awg: return true; case DockerContainer::Awg: return true;
case DockerContainer::Xray: return true; case DockerContainer::Xray: return true;
case DockerContainer::SSXray: return true; case DockerContainer::SSXray: return true;
return false; case DockerContainer::MtProxy: return true;
case DockerContainer::Telemt: return true;
default: default:
return false; return false;
} }
@@ -256,6 +277,8 @@ bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
case DockerContainer::Awg: return true; case DockerContainer::Awg: return true;
case DockerContainer::Xray: return true; case DockerContainer::Xray: return true;
case DockerContainer::SSXray: return true; case DockerContainer::SSXray: return true;
case DockerContainer::MtProxy: return true;
case DockerContainer::Telemt: return true;
default: return false; default: return false;
} }
@@ -318,6 +341,8 @@ bool ContainerUtils::isShareable(DockerContainer container)
case DockerContainer::Dns: return false; case DockerContainer::Dns: return false;
case DockerContainer::Sftp: return false; case DockerContainer::Sftp: return false;
case DockerContainer::Socks5Proxy: return false; case DockerContainer::Socks5Proxy: return false;
case DockerContainer::MtProxy: return false;
case DockerContainer::Telemt: return false;
default: return true; default: return true;
} }
} }
@@ -346,8 +371,10 @@ int ContainerUtils::installPageOrder(DockerContainer container)
case DockerContainer::Xray: return 3; case DockerContainer::Xray: return 3;
case DockerContainer::Ipsec: return 7; case DockerContainer::Ipsec: return 7;
case DockerContainer::SSXray: return 8; case DockerContainer::SSXray: return 8;
case DockerContainer::MtProxy:
case DockerContainer::Telemt:
return 20;
default: return 0; default: return 0;
} }
} }
+3 -2
View File
@@ -71,10 +71,11 @@ namespace amnezia
// import and install errors // import and install errors
ImportInvalidConfigError = 900, ImportInvalidConfigError = 900,
ImportBackupFileUseRestoreInstead = 903,
RestoreBackupInvalidError = 904,
ImportOpenConfigError = 901, ImportOpenConfigError = 901,
NoInstalledContainersError = 902, NoInstalledContainersError = 902,
ImportBackupFileUseRestoreInstead = 903,
RestoreBackupInvalidError = 904,
LegacyApiV1NotSupportedError = 905,
// Android errors // Android errors
AndroidError = 1000, AndroidError = 1000,
+1
View File
@@ -59,6 +59,7 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break; case (ErrorCode::ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break;
case (ErrorCode::ImportBackupFileUseRestoreInstead): errorMessage = QObject::tr("Backup files cannot be imported here. Use 'Restore from backup' instead."); break; case (ErrorCode::ImportBackupFileUseRestoreInstead): errorMessage = QObject::tr("Backup files cannot be imported here. Use 'Restore from backup' instead."); break;
case (ErrorCode::RestoreBackupInvalidError): errorMessage = QObject::tr("Backup file is corrupted or has invalid format"); break; case (ErrorCode::RestoreBackupInvalidError): errorMessage = QObject::tr("Backup file is corrupted or has invalid format"); break;
case (ErrorCode::LegacyApiV1NotSupportedError): errorMessage = QObject::tr("This legacy Amnezia subscription format is no longer supported"); break;
case (ErrorCode::ImportOpenConfigError): errorMessage = QObject::tr("Unable to open config file"); break; case (ErrorCode::ImportOpenConfigError): errorMessage = QObject::tr("Unable to open config file"); break;
case (ErrorCode::NoInstalledContainersError): errorMessage = QObject::tr("VPN Protocols is not installed.\n Please install VPN container at first"); break; case (ErrorCode::NoInstalledContainersError): errorMessage = QObject::tr("VPN Protocols is not installed.\n Please install VPN container at first"); break;
+3 -1
View File
@@ -30,7 +30,9 @@ namespace amnezia
TorWebSite, TorWebSite,
Dns, Dns,
Sftp, Sftp,
Socks5Proxy Socks5Proxy,
MtProxy,
Telemt,
}; };
Q_ENUM_NS(Proto) Q_ENUM_NS(Proto)
@@ -9,7 +9,6 @@
#include "core/utils/containerEnum.h" #include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h" #include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h" #include "core/utils/protocolEnum.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h" #include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h" #include "core/utils/constants/protocolConstants.h"
@@ -20,6 +19,8 @@
#include "core/models/protocols/xrayProtocolConfig.h" #include "core/models/protocols/xrayProtocolConfig.h"
#include "core/models/protocols/sftpProtocolConfig.h" #include "core/models/protocols/sftpProtocolConfig.h"
#include "core/models/protocols/socks5ProxyProtocolConfig.h" #include "core/models/protocols/socks5ProxyProtocolConfig.h"
#include "core/models/protocols/mtProxyProtocolConfig.h"
#include "core/models/protocols/telemtProtocolConfig.h"
using namespace amnezia; using namespace amnezia;
using namespace ProtocolUtils; using namespace ProtocolUtils;
@@ -38,6 +39,8 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container)
case DockerContainer::Dns: return QLatin1String("dns"); case DockerContainer::Dns: return QLatin1String("dns");
case DockerContainer::Sftp: return QLatin1String("sftp"); case DockerContainer::Sftp: return QLatin1String("sftp");
case DockerContainer::Socks5Proxy: return QLatin1String("socks5_proxy"); case DockerContainer::Socks5Proxy: return QLatin1String("socks5_proxy");
case DockerContainer::MtProxy: return QLatin1String("mtproxy");
case DockerContainer::Telemt: return QLatin1String("telemt");
default: return QString(); default: return QString();
} }
} }
@@ -76,7 +79,6 @@ QString amnezia::scriptName(ProtocolScriptType type)
QString amnezia::scriptName(ClientScriptType type) QString amnezia::scriptName(ClientScriptType type)
{ {
switch (type) { switch (type) {
case ClientScriptType::linux_installer: return QLatin1String("linux_installer.sh");
case ClientScriptType::mac_installer: return QLatin1String("mac_installer.sh"); case ClientScriptType::mac_installer: return QLatin1String("mac_installer.sh");
default: return QString(); default: return QString();
} }
@@ -285,6 +287,86 @@ amnezia::ScriptVars amnezia::genSocks5ProxyVars(const ContainerConfig &container
return vars; return vars;
} }
amnezia::ScriptVars amnezia::genMtProxyVars(const ContainerConfig &containerConfig) {
ScriptVars vars;
if (auto *mtProxyProtocolConfig = containerConfig.getMtProxyProtocolConfig()) {
const MtProxyProtocolConfig &c = *mtProxyProtocolConfig;
vars.append({{"$MTPROXY_PORT", c.port.isEmpty() ? QString(protocols::mtProxy::defaultPort) : c.port}});
vars.append({{"$MTPROXY_SECRET", c.secret}});
vars.append({{"$MTPROXY_TAG", c.tag}});
vars.append({{"$MTPROXY_TRANSPORT_MODE",
c.transportMode.isEmpty() ? QString(protocols::mtProxy::transportModeStandard)
: c.transportMode}});
QString tlsDomain = c.tlsDomain;
if (tlsDomain.isEmpty()) {
tlsDomain = QString(protocols::mtProxy::defaultTlsDomain);
}
vars.append({{"$MTPROXY_TLS_DOMAIN", tlsDomain}});
vars.append({{"$MTPROXY_PUBLIC_HOST", c.publicHost}});
QStringList additionalList;
for (const QString &s: c.additionalSecrets) {
if (!s.isEmpty()) {
additionalList << s;
}
}
vars.append({{"$MTPROXY_ADDITIONAL_SECRETS", additionalList.join(QLatin1Char(','))}});
const QString workersMode = c.workersMode.isEmpty() ? QString(protocols::mtProxy::workersModeAuto)
: c.workersMode;
QString workers;
if (workersMode == QLatin1String(protocols::mtProxy::workersModeManual)) {
workers = c.workers.isEmpty() ? QString(protocols::mtProxy::defaultWorkers) : c.workers;
} else {
const QString transportMode =
c.transportMode.isEmpty() ? QString(protocols::mtProxy::transportModeStandard) : c.transportMode;
workers = (transportMode == QLatin1String(protocols::mtProxy::transportModeFakeTLS)) ? QStringLiteral("0")
: QStringLiteral("2");
}
vars.append({{"$MTPROXY_WORKERS", workers}});
vars.append({{"$MTPROXY_NAT_ENABLED", c.natEnabled ? QStringLiteral("1") : QStringLiteral("0")}});
vars.append({{"$MTPROXY_NAT_INTERNAL_IP", c.natInternalIp}});
vars.append({{"$MTPROXY_NAT_EXTERNAL_IP", c.natExternalIp}});
}
return vars;
}
amnezia::ScriptVars amnezia::genTelemtVars(const ContainerConfig &containerConfig)
{
ScriptVars vars;
if (auto *telemtProtocolConfig = containerConfig.getTelemtProtocolConfig()) {
const TelemtProtocolConfig &c = *telemtProtocolConfig;
const QString transport = c.transportMode.isEmpty() ? QString(protocols::telemt::transportModeStandard)
: c.transportMode;
const bool faketls = (transport == QLatin1String(protocols::telemt::transportModeFakeTLS));
vars.append({ { "$TELEMT_TOML_SECURE", faketls ? QLatin1String("false") : QLatin1String("true") } });
vars.append({ { "$TELEMT_TOML_TLS", faketls ? QLatin1String("true") : QLatin1String("false") } });
vars.append({ { "$TELEMT_PORT", c.port.isEmpty() ? QString(protocols::telemt::defaultPort) : c.port } });
vars.append({ { "$TELEMT_SECRET", c.secret } });
vars.append({ { "$TELEMT_TAG", c.tag } });
QString tlsDomain = c.tlsDomain;
if (tlsDomain.isEmpty()) {
tlsDomain = QString(protocols::telemt::defaultTlsDomain);
}
vars.append({ { "$TELEMT_TLS_DOMAIN", tlsDomain } });
vars.append({ { "$TELEMT_PUBLIC_HOST", c.publicHost } });
vars.append({ { "$TELEMT_USER_NAME",
c.userName.isEmpty() ? QString::fromUtf8(protocols::telemt::defaultUserName) : c.userName } });
vars.append({ { "$TELEMT_USE_MIDDLE_PROXY", c.useMiddleProxy ? QLatin1String("true") : QLatin1String("false") } });
vars.append({ { "$TELEMT_MASK", c.maskEnabled ? QLatin1String("true") : QLatin1String("false") } });
vars.append({ { "$TELEMT_TLS_EMULATION", c.tlsEmulation ? QLatin1String("true") : QLatin1String("false") } });
}
return vars;
}
amnezia::ScriptVars amnezia::genProtocolVarsForContainer(DockerContainer container, const ContainerConfig &containerConfig) amnezia::ScriptVars amnezia::genProtocolVarsForContainer(DockerContainer container, const ContainerConfig &containerConfig)
{ {
ScriptVars vars; ScriptVars vars;
@@ -309,6 +391,12 @@ amnezia::ScriptVars amnezia::genProtocolVarsForContainer(DockerContainer contain
case Proto::Socks5Proxy: case Proto::Socks5Proxy:
vars.append(genSocks5ProxyVars(containerConfig)); vars.append(genSocks5ProxyVars(containerConfig));
break; break;
case Proto::MtProxy:
vars.append(genMtProxyVars(containerConfig));
break;
case Proto::Telemt:
vars.append(genTelemtVars(containerConfig));
break;
default: default:
break; break;
} }
@@ -43,7 +43,6 @@ enum ProtocolScriptType {
enum ClientScriptType { enum ClientScriptType {
// Client-side scripts // Client-side scripts
linux_installer,
mac_installer mac_installer
}; };
@@ -68,6 +67,8 @@ ScriptVars genWireGuardVars(const ContainerConfig &containerConfig);
ScriptVars genAwgVars(const ContainerConfig &containerConfig); ScriptVars genAwgVars(const ContainerConfig &containerConfig);
ScriptVars genSftpVars(const ContainerConfig &containerConfig); ScriptVars genSftpVars(const ContainerConfig &containerConfig);
ScriptVars genSocks5ProxyVars(const ContainerConfig &containerConfig); ScriptVars genSocks5ProxyVars(const ContainerConfig &containerConfig);
ScriptVars genMtProxyVars(const ContainerConfig &containerConfig);
ScriptVars genTelemtVars(const ContainerConfig &containerConfig);
ScriptVars genProtocolVarsForContainer(DockerContainer container, const ContainerConfig &containerConfig); ScriptVars genProtocolVarsForContainer(DockerContainer container, const ContainerConfig &containerConfig);
} }
+3 -3
View File
@@ -56,7 +56,7 @@ namespace libssh {
QEventLoop wait; QEventLoop wait;
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit); connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
watcher.setFuture(future); watcher.setFuture(future);
wait.exec(); wait.exec(QEventLoop::ExcludeUserInputEvents);
int connectionResult = watcher.result(); int connectionResult = watcher.result();
@@ -189,7 +189,7 @@ namespace libssh {
QEventLoop wait; QEventLoop wait;
QObject::connect(this, &Client::writeToChannelFinished, &wait, &QEventLoop::quit); QObject::connect(this, &Client::writeToChannelFinished, &wait, &QEventLoop::quit);
wait.exec(); wait.exec(QEventLoop::ExcludeUserInputEvents);
return watcher.result(); return watcher.result();
} }
@@ -284,7 +284,7 @@ namespace libssh {
QEventLoop wait; QEventLoop wait;
QObject::connect(this, &Client::scpFileCopyFinished, &wait, &QEventLoop::quit); QObject::connect(this, &Client::scpFileCopyFinished, &wait, &QEventLoop::quit);
wait.exec(); wait.exec(QEventLoop::ExcludeUserInputEvents);
closeScpSession(); closeScpSession();
return watcher.result(); return watcher.result();
+2 -2
View File
@@ -103,8 +103,8 @@ ErrorCode SshSession::runContainerScript(const ServerCredentials &credentials, D
if (e) if (e)
return e; return e;
QString runner = const bool useSh = container == DockerContainer::Socks5Proxy || container == DockerContainer::MtProxy || container == DockerContainer::Telemt;
QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, (container == DockerContainer::Socks5Proxy ? "sh" : "bash")); QString runner = QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, useSh ? "sh" : "bash");
e = runScript(credentials, replaceVars(runner, amnezia::genBaseVars(credentials, container, QString(), QString())), cbReadStdOut, cbReadStdErr); e = runScript(credentials, replaceVars(runner, amnezia::genBaseVars(credentials, container, QString(), QString())), cbReadStdOut, cbReadStdErr);
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName); QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
+122
View File
@@ -0,0 +1,122 @@
#include "serverConfigUtils.h"
#include <QJsonArray>
#include <QJsonValue>
#include "core/models/selfhosted/selfHostedAdminServerConfig.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/configKeys.h"
namespace
{
bool hasThirdPartyConfig(const QJsonObject &json)
{
const QJsonArray containersArray = json.value(amnezia::configKey::containers).toArray();
for (const QJsonValue &val : containersArray) {
const QJsonObject containerObj = val.toObject();
for (auto it = containerObj.begin(); it != containerObj.end(); ++it) {
if (it.key() == amnezia::configKey::container) {
continue;
}
const QJsonObject protocolObj = it.value().toObject();
if (protocolObj.contains(amnezia::configKey::isThirdPartyConfig)
&& protocolObj.value(amnezia::configKey::isThirdPartyConfig).toBool()) {
return true;
}
}
}
return false;
}
} // namespace
namespace serverConfigUtils
{
bool isServerFromApi(const QJsonObject &serverConfigObject)
{
const int configVersion = serverConfigObject.value(amnezia::configKey::configVersion).toInt();
switch (configVersion) {
case ConfigSource::Telegram:
case ConfigSource::AmneziaGateway:
return true;
default:
return false;
}
}
ConfigSource getConfigSource(const QJsonObject &serverConfigObject)
{
return static_cast<ConfigSource>(serverConfigObject.value(amnezia::configKey::configVersion).toInt());
}
ConfigType configTypeFromJson(const QJsonObject &serverConfigObject)
{
const int configVersion = serverConfigObject.value(amnezia::configKey::configVersion).toInt();
switch (configVersion) {
case ConfigSource::Telegram: {
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
const QString apiEndpointValue = serverConfigObject.value(apiDefs::key::apiEndpoint).toString();
if (apiEndpointValue.contains(premiumV1Endpoint)) {
return ConfigType::AmneziaPremiumV1;
}
if (apiEndpointValue.contains(freeV2Endpoint)) {
return ConfigType::AmneziaFreeV2;
}
}
[[fallthrough]];
case ConfigSource::AmneziaGateway: {
constexpr QLatin1String servicePremium("amnezia-premium");
constexpr QLatin1String serviceFree("amnezia-free");
constexpr QLatin1String serviceExternalPremium("external-premium");
const QJsonObject apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
const QString serviceTypeStr = apiConfigObject.value(apiDefs::key::serviceType).toString();
if (serviceTypeStr == servicePremium) {
return ConfigType::AmneziaPremiumV2;
}
if (serviceTypeStr == serviceFree) {
return ConfigType::AmneziaFreeV3;
}
if (serviceTypeStr == serviceExternalPremium) {
return ConfigType::ExternalPremium;
}
break;
}
default:
break;
}
if (hasThirdPartyConfig(serverConfigObject)) {
return ConfigType::Native;
}
const amnezia::SelfHostedAdminServerConfig adminProbe =
amnezia::SelfHostedAdminServerConfig::fromJson(serverConfigObject);
return adminProbe.hasCredentials() ? ConfigType::SelfHostedAdmin : ConfigType::SelfHostedUser;
}
bool isLegacyApiSubscription(ConfigType configType)
{
return configType == ConfigType::AmneziaPremiumV1 || configType == ConfigType::AmneziaFreeV2;
}
bool isApiV2Subscription(ConfigType configType)
{
switch (configType) {
case ConfigType::AmneziaPremiumV2:
case ConfigType::AmneziaFreeV3:
case ConfigType::ExternalPremium:
return true;
default:
return false;
}
}
} // namespace serverConfigUtils
+40
View File
@@ -0,0 +1,40 @@
#ifndef SERVERCONFIGUTILS_H
#define SERVERCONFIGUTILS_H
#include <QJsonObject>
namespace serverConfigUtils
{
enum ConfigType {
AmneziaFreeV2 = 0,
AmneziaFreeV3,
AmneziaPremiumV1,
AmneziaPremiumV2,
SelfHosted,
ExternalPremium,
SelfHostedAdmin = 8,
SelfHostedUser,
Native,
Invalid
};
enum ConfigSource {
Telegram = 1,
AmneziaGateway
};
bool isServerFromApi(const QJsonObject &serverConfigObject);
ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
ConfigType configTypeFromJson(const QJsonObject &serverConfigObject);
bool isLegacyApiSubscription(ConfigType configType);
bool isApiV2Subscription(ConfigType configType);
} // namespace serverConfigUtils
#endif // SERVERCONFIGUTILS_H
+3 -1
View File
@@ -977,7 +977,9 @@ bool IosController::shareText(const QStringList& filesToSend) {
} }
#if !MACOS_NE #if !MACOS_NE
UIViewController *qtController = getViewController(); UIViewController *qtController = getViewController();
if (!qtController) return; if (!qtController) {
return false;
}
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil]; UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
#endif #endif
+1 -1
View File
@@ -1,4 +1,4 @@
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";\ if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install --install-recommends"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\ elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\ 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";\
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); silent_inst="-nq install"; check_pkgs="-nq refresh"; docker_pkg="docker"; dist="opensuse";\ elif which zypper > /dev/null 2>&1; then pm=$(which zypper); silent_inst="-nq install"; check_pkgs="-nq refresh"; docker_pkg="docker"; dist="opensuse";\
+9
View File
@@ -0,0 +1,9 @@
FROM amneziavpn/mtproxy:latest
RUN mkdir -p /opt/amnezia /data
RUN printf '#!/bin/sh\ntail -f /dev/null\n' > /opt/amnezia/start.sh && \
chmod a+x /opt/amnezia/start.sh
VOLUME /data
ENTRYPOINT ["/bin/sh", "/opt/amnezia/start.sh"]
CMD [""]
@@ -0,0 +1,60 @@
#!/bin/sh
# Download Telegram config files
curl -s https://core.telegram.org/getProxySecret -o /data/proxy-secret
curl -s https://core.telegram.org/getProxyConfig -o /data/proxy-multi.conf
# Determine secret: env var -> saved file -> generate new
if [ -n "$MTPROXY_SECRET" ]; then
SECRET="$MTPROXY_SECRET"
elif [ -f /data/secret ]; then
SECRET=$(cat /data/secret)
else
SECRET=$(openssl rand -hex 16)
fi
# Validate: must be exactly 32 hex chars
echo "$SECRET" | grep -qE '^[0-9a-fA-F]{32}$' || SECRET=$(openssl rand -hex 16)
# Persist secret for start.sh restarts
echo "$SECRET" > /data/secret
# Detect external IP
IP=$(curl -s --max-time 5 https://api.ipify.org 2>/dev/null)
[ -z "$IP" ] && IP=$(curl -s --max-time 5 https://ifconfig.me 2>/dev/null)
[ -z "$IP" ] && IP=$(curl -s --max-time 5 https://icanhazip.com 2>/dev/null)
# Use custom public host/domain if provided, otherwise fall back to detected IP
if [ -n "$MTPROXY_PUBLIC_HOST" ]; then
LINK_HOST="$MTPROXY_PUBLIC_HOST"
else
LINK_HOST="$IP"
fi
PORT=$MTPROXY_PORT
# Transport mode is substituted by replaceVars — plain variable, no curly braces
TRANSPORT_MODE=$MTPROXY_TRANSPORT_MODE
PADDED_SECRET="dd${SECRET}"
if [ "$TRANSPORT_MODE" = "faketls" ] && [ -n "$MTPROXY_TLS_DOMAIN" ]; then
DOMAIN_HEX=$(echo -n "$MTPROXY_TLS_DOMAIN" | od -A n -t x1 | tr -d ' \n')
FAKETLS_SECRET="ee${SECRET}${DOMAIN_HEX}"
else
FAKETLS_SECRET=""
fi
# Active link secret depends on transport mode
if [ "$TRANSPORT_MODE" = "faketls" ] && [ -n "$FAKETLS_SECRET" ]; then
LINK_SECRET="$FAKETLS_SECRET"
else
LINK_SECRET="$PADDED_SECRET"
fi
# Output stable markers — parsed by updateContainerConfigAfterInstallation()
echo "[*] MTProxy configuration"
echo "[*] Secret: ${SECRET}"
echo "[*] FakeTLS: ${FAKETLS_SECRET}"
echo "[*] tg:// link: tg://proxy?server=${LINK_HOST}&port=${PORT}&secret=${LINK_SECRET}"
echo "[*] t.me link: https://t.me/proxy?server=${LINK_HOST}&port=${PORT}&secret=${LINK_SECRET}"
@@ -0,0 +1,9 @@
# Run container
sudo docker run -d \
--log-driver none \
--restart always \
-p $MTPROXY_PORT:$MTPROXY_PORT/tcp \
-v amnezia-mtproxy-data:/data \
--name $CONTAINER_NAME \
$CONTAINER_NAME
+71
View File
@@ -0,0 +1,71 @@
#!/bin/sh
echo "Container startup"
# Read persisted secret
SECRET=""
if [ -f /data/secret ]; then
SECRET=$(cat /data/secret)
fi
if [ -z "$SECRET" ]; then
echo "ERROR: /data/secret not found — run configure_container first"
tail -f /dev/null
exit 1
fi
# Build tag argument
TAG_ARG=""
if [ -n "$MTPROXY_TAG" ]; then
TAG_ARG="-P $MTPROXY_TAG"
fi
# Build domain argument for FakeTLS mode
DOMAIN_ARG=""
if [ "$MTPROXY_TRANSPORT_MODE" = "faketls" ] && [ -n "$MTPROXY_TLS_DOMAIN" ]; then
DOMAIN_ARG="--domain $MTPROXY_TLS_DOMAIN"
fi
WORKERS=$MTPROXY_WORKERS
STATS_PORT=2398
LISTEN_PORT=$MTPROXY_PORT
NAT_FLAG=""
NAT_VALUE=""
if [ "$MTPROXY_NAT_ENABLED" = "1" ] && [ -n "$MTPROXY_NAT_INTERNAL_IP" ] && [ -n "$MTPROXY_NAT_EXTERNAL_IP" ]; then
NAT_FLAG="--nat-info"
NAT_VALUE="$MTPROXY_NAT_INTERNAL_IP:$MTPROXY_NAT_EXTERNAL_IP"
else
INTERNAL_IP=$(hostname -i 2>/dev/null | awk '{print $1}')
EXTERNAL_IP=$(curl -s --max-time 5 https://api.ipify.org 2>/dev/null)
[ -z "$EXTERNAL_IP" ] && EXTERNAL_IP=$(curl -s --max-time 5 https://ifconfig.me 2>/dev/null)
if [ -n "$INTERNAL_IP" ] && [ -n "$EXTERNAL_IP" ] && [ "$INTERNAL_IP" != "$EXTERNAL_IP" ]; then
NAT_FLAG="--nat-info"
NAT_VALUE="${INTERNAL_IP}:${EXTERNAL_IP}"
fi
fi
# Build additional secrets arguments
ADDITIONAL_SECRETS_ARG=""
if [ -n "$MTPROXY_ADDITIONAL_SECRETS" ]; then
for S in $(echo "$MTPROXY_ADDITIONAL_SECRETS" | tr ',' ' '); do
ADDITIONAL_SECRETS_ARG="$ADDITIONAL_SECRETS_ARG -S $S"
done
fi
# Start proxy (foreground)
exec mtproto-proxy \
-u root \
-p ${STATS_PORT} \
-H ${LISTEN_PORT} \
-S ${SECRET} \
${ADDITIONAL_SECRETS_ARG} \
--aes-pwd /data/proxy-secret \
-M ${WORKERS} \
-C 60000 \
--allow-skip-dh \
${NAT_FLAG:+${NAT_FLAG} ${NAT_VALUE}} \
${TAG_ARG} \
${DOMAIN_ARG} \
/data/proxy-multi.conf
+8 -1
View File
@@ -24,6 +24,14 @@
<file>ipsec/run_container.sh</file> <file>ipsec/run_container.sh</file>
<file>ipsec/start.sh</file> <file>ipsec/start.sh</file>
<file>ipsec/strongswan.profile</file> <file>ipsec/strongswan.profile</file>
<file>mtproxy/configure_container.sh</file>
<file>mtproxy/Dockerfile</file>
<file>mtproxy/run_container.sh</file>
<file>mtproxy/start.sh</file>
<file>telemt/configure_container.sh</file>
<file>telemt/Dockerfile</file>
<file>telemt/run_container.sh</file>
<file>telemt/start.sh</file>
<file>openvpn/configure_container.sh</file> <file>openvpn/configure_container.sh</file>
<file>openvpn/Dockerfile</file> <file>openvpn/Dockerfile</file>
<file>openvpn/run_container.sh</file> <file>openvpn/run_container.sh</file>
@@ -55,4 +63,3 @@
<file>xray/template.json</file> <file>xray/template.json</file>
</qresource> </qresource>
</RCC> </RCC>
+42
View File
@@ -0,0 +1,42 @@
# syntax=docker/dockerfile:1
# Debian-based image with Telemt binary (shell + jq for Amnezia configure scripts).
# Binary from https://github.com/telemt/telemt releases (same pattern as upstream Dockerfile minimal stage).
FROM debian:12-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
binutils \
ca-certificates \
curl \
jq \
openssl \
tar \
&& rm -rf /var/lib/apt/lists/*
# Use machine arch (works with classic `docker build`; TARGETARCH is only set with BuildKit).
RUN set -eux; \
ARCH="$(uname -m)"; \
case "$ARCH" in \
x86_64) ASSET="telemt-x86_64-linux-musl.tar.gz" ;; \
aarch64|arm64) ASSET="telemt-aarch64-linux-musl.tar.gz" ;; \
*) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
esac; \
curl -fL --retry 5 --retry-delay 3 --connect-timeout 10 --max-time 120 \
-o "/tmp/${ASSET}" "https://github.com/telemt/telemt/releases/latest/download/${ASSET}"; \
curl -fL --retry 5 --retry-delay 3 --connect-timeout 10 --max-time 120 \
-o "/tmp/${ASSET}.sha256" "https://github.com/telemt/telemt/releases/latest/download/${ASSET}.sha256"; \
cd /tmp && sha256sum -c "${ASSET}.sha256"; \
tar -xzf "${ASSET}" -C /tmp; \
test -f /tmp/telemt; \
install -m 0755 /tmp/telemt /usr/local/bin/telemt; \
strip --strip-unneeded /usr/local/bin/telemt || true; \
rm -f "/tmp/${ASSET}" "/tmp/${ASSET}.sha256" /tmp/telemt
RUN mkdir -p /opt/amnezia /data
RUN printf '#!/bin/sh\ntail -f /dev/null\n' > /opt/amnezia/start.sh && \
chmod a+x /opt/amnezia/start.sh
VOLUME /data
ENTRYPOINT ["/bin/sh", "/opt/amnezia/start.sh"]
CMD [""]
@@ -0,0 +1,73 @@
#!/bin/sh
# Do not use set -e: Telemt / curl / kill edge cases should not abort the whole configure step.
echo "[*] Amnezia Telemt: configure script start"
mkdir -p /data/tlsfront
# Secret: substituted $TELEMT_SECRET -> saved file -> openssl (same rules as MTProxy configure)
if [ -n "$TELEMT_SECRET" ]; then
SECRET="$TELEMT_SECRET"
elif [ -f /data/secret ]; then
SECRET=$(cat /data/secret)
else
SECRET=$(openssl rand -hex 16)
fi
# Must be exactly 32 hex chars
echo "$SECRET" | grep -qE '^[0-9a-fA-F]{32}$' || SECRET=$(openssl rand -hex 16)
# Build config.toml (other variables substituted on the host by Amnezia before upload)
rm -f /data/config.toml
{
echo "### Amnezia Telemt — generated"
echo "[general]"
echo "use_middle_proxy = $TELEMT_USE_MIDDLE_PROXY"
echo "log_level = \"normal\""
if [ -n "$TELEMT_TAG" ]; then
echo "ad_tag = \"$TELEMT_TAG\""
fi
echo ""
echo "[general.modes]"
echo "classic = false"
echo "secure = $TELEMT_TOML_SECURE"
echo "tls = $TELEMT_TOML_TLS"
echo ""
echo "[general.links]"
echo "show = \"*\""
if [ -n "$TELEMT_PUBLIC_HOST" ]; then
echo "public_host = \"$TELEMT_PUBLIC_HOST\""
fi
echo "public_port = $TELEMT_PORT"
echo ""
echo "[server]"
echo "port = $TELEMT_PORT"
echo ""
echo "[server.api]"
echo "enabled = true"
echo "listen = \"0.0.0.0:9091\""
# Match upstream Telemt default: localhost API only (curl in this script uses 127.0.0.1).
echo "whitelist = [\"127.0.0.0/8\"]"
echo ""
echo "[[server.listeners]]"
echo "ip = \"0.0.0.0\""
echo ""
echo "[censorship]"
echo "tls_domain = \"$TELEMT_TLS_DOMAIN\""
echo "mask = $TELEMT_MASK"
echo "tls_emulation = $TELEMT_TLS_EMULATION"
echo "tls_front_dir = \"/data/tlsfront\""
echo ""
echo "[access.users]"
echo "$TELEMT_USER_NAME = \"$SECRET\""
} > /data/config.toml
echo "$SECRET" > /data/secret
chmod 600 /data/secret 2>/dev/null || true
# Do not start telemt here: a long-lived process + curl loop inside `docker exec` can confuse SSH/Docker
# timing and is unnecessary — start.sh runs telemt after configure. Links can be empty until the service
# is up; the client still parses Secret below.
echo "[*] Telemt configuration"
echo "[*] Secret: $SECRET"
echo "[*] tg:// link: "
echo "[*] t.me link: "
@@ -0,0 +1,9 @@
# Run container (ulimit per Telemt docs — avoids "Too many open files" under load)
sudo docker run -d \
--log-driver none \
--restart always \
--ulimit nofile=65536:65536 \
-p $TELEMT_PORT:$TELEMT_PORT/tcp \
-v amnezia-telemt-data:/data \
--name $CONTAINER_NAME \
$CONTAINER_NAME
+12
View File
@@ -0,0 +1,12 @@
#!/bin/sh
echo "Container startup (Telemt)"
if [ ! -f /data/config.toml ]; then
echo "ERROR: /data/config.toml not found — run configure_container first"
tail -f /dev/null
exit 1
fi
mkdir -p /data/tlsfront
exec /usr/local/bin/telemt /data/config.toml
-10
View File
@@ -95,15 +95,6 @@ target_link_libraries(test_servers_model_sync PRIVATE
test_common test_common
) )
add_executable(test_gateway_stacks
testGatewayStacks.cpp
)
target_link_libraries(test_gateway_stacks PRIVATE
Qt6::Test
test_common
)
add_executable(test_complex_operations add_executable(test_complex_operations
testComplexOperations.cpp testComplexOperations.cpp
) )
@@ -148,7 +139,6 @@ add_test(NAME DefaultServerChangeTest COMMAND test_default_server_change)
add_test(NAME ServerEdgeCasesTest COMMAND test_server_edge_cases) add_test(NAME ServerEdgeCasesTest COMMAND test_server_edge_cases)
add_test(NAME SignalOrderTest COMMAND test_signal_order) add_test(NAME SignalOrderTest COMMAND test_signal_order)
add_test(NAME ServersModelSyncTest COMMAND test_servers_model_sync) add_test(NAME ServersModelSyncTest COMMAND test_servers_model_sync)
add_test(NAME GatewayStacksTest COMMAND test_gateway_stacks)
add_test(NAME ComplexOperationsTest COMMAND test_complex_operations) add_test(NAME ComplexOperationsTest COMMAND test_complex_operations)
add_test(NAME SettingsSignalsTest COMMAND test_settings_signals) add_test(NAME SettingsSignalsTest COMMAND test_settings_signals)
add_test(NAME UiServersModelAndControllerTest COMMAND test_ui_servers_model_and_controller) add_test(NAME UiServersModelAndControllerTest COMMAND test_ui_servers_model_and_controller)
+3 -3
View File
@@ -8,7 +8,7 @@
#include <QSignalSpy> #include <QSignalSpy>
#include "core/controllers/coreController.h" #include "core/controllers/coreController.h"
#include "core/models/serverConfig.h" #include "core/utils/constants/configKeys.h"
#include "vpnConnection.h" #include "vpnConnection.h"
#include "secureQSettings.h" #include "secureQSettings.h"
@@ -117,8 +117,8 @@ private slots:
QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged signal should NOT be emitted (default is already 0)"); QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged signal should NOT be emitted (default is already 0)");
QVERIFY2(m_coreController->m_serversRepository->serversCount() > 0, "Server should be added"); QVERIFY2(m_coreController->m_serversRepository->serversCount() > 0, "Server should be added");
int serverIndex = m_coreController->m_serversRepository->defaultServerIndex(); const QString serverId = m_coreController->m_serversRepository->defaultServerId();
auto exportResult = m_coreController->m_exportController->generateFullAccessConfig(serverIndex); auto exportResult = m_coreController->m_exportController->generateFullAccessConfig(serverId);
QVERIFY2(exportResult.errorCode == ErrorCode::NoError, "Export should succeed"); QVERIFY2(exportResult.errorCode == ErrorCode::NoError, "Export should succeed");
QVERIFY2(!exportResult.config.isEmpty(), "Exported config should not be empty"); QVERIFY2(!exportResult.config.isEmpty(), "Exported config should not be empty");
+15 -16
View File
@@ -5,7 +5,8 @@
#include <QSignalSpy> #include <QSignalSpy>
#include "core/controllers/coreController.h" #include "core/controllers/coreController.h"
#include "core/models/serverConfig.h" #include "core/models/serverDescription.h"
#include "tests/testServerRepositoryHelpers.h"
#include "vpnConnection.h" #include "vpnConnection.h"
#include "secureQSettings.h" #include "secureQSettings.h"
@@ -37,7 +38,7 @@ private slots:
void init() { void init() {
m_settings->clearSettings(); m_settings->clearSettings();
if (m_coreController->m_serversModel) { if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false); m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
} }
} }
@@ -65,35 +66,33 @@ private slots:
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 3, "Should have 3 servers"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 3, "Should have 3 servers");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Default should be index 2"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Default should be index 2");
ServerConfig server0 = m_coreController->m_serversController->getServerConfig(0); amnezia::test::setServerDescription(m_coreController->m_serversRepository,
server0.visit([](auto& arg) { m_coreController->m_serversController->getServerId(0),
arg.description = "Edited First Server"; QStringLiteral("Edited First Server"));
});
m_coreController->m_serversController->editServer(0, server0);
QVERIFY2(serverEditedSpy.count() == 1, "serverEdited should be emitted"); QVERIFY2(serverEditedSpy.count() == 1, "serverEdited should be emitted");
QString editedDesc0 = m_coreController->m_serversRepository->server(0).description(); QString editedDesc0 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(0));
QVERIFY2(editedDesc0 == "Edited First Server", "First server should be edited"); QVERIFY2(editedDesc0 == "Edited First Server", "First server should be edited");
m_coreController->m_serversController->removeServer(1); m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(1));
QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved should be emitted"); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved should be emitted");
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "Should have 2 servers"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "Should have 2 servers");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Default should be index 1 (was 2, removed 1)"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Default should be index 1 (was 2, removed 1)");
m_coreController->m_serversController->setDefaultServerIndex(0); m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(0));
QVERIFY2(defaultServerChangedSpy.count() == 4, "defaultServerChanged should be emitted again"); QVERIFY2(defaultServerChangedSpy.count() == 4, "defaultServerChanged should be emitted again");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default should be index 0"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default should be index 0");
ServerConfig server0After = m_coreController->m_serversController->getServerConfig(0); amnezia::test::setServerDescription(m_coreController->m_serversRepository,
server0After.visit([](auto& arg) { m_coreController->m_serversController->getServerId(0),
arg.description = "Final Edited Server"; QStringLiteral("Final Edited Server"));
});
m_coreController->m_serversController->editServer(0, server0After);
QVERIFY2(serverEditedSpy.count() == 2, "serverEdited should be emitted again"); QVERIFY2(serverEditedSpy.count() == 2, "serverEdited should be emitted again");
QString finalDesc0 = m_coreController->m_serversRepository->server(0).description(); QString finalDesc0 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(0));
QVERIFY2(finalDesc0 == "Final Edited Server", "First server should be edited again"); QVERIFY2(finalDesc0 == "Final Edited Server", "First server should be edited again");
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "Final servers count should be 2"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "Final servers count should be 2");
+17 -14
View File
@@ -5,7 +5,8 @@
#include <QSignalSpy> #include <QSignalSpy>
#include "core/controllers/coreController.h" #include "core/controllers/coreController.h"
#include "core/models/serverConfig.h" #include "core/models/serverDescription.h"
#include "tests/testServerRepositoryHelpers.h"
#include "ui/models/serversModel.h" #include "ui/models/serversModel.h"
#include "vpnConnection.h" #include "vpnConnection.h"
#include "secureQSettings.h" #include "secureQSettings.h"
@@ -39,7 +40,7 @@ private slots:
m_settings->clearSettings(); m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache(); m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) { if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false); m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
} }
} }
@@ -60,9 +61,10 @@ private slots:
QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged); QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged);
m_coreController->m_serversController->setDefaultServerIndex(0); m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(0));
QVERIFY2(defaultServerChangedSpy.count() == 1, "defaultServerChanged signal should be emitted"); QVERIFY2(defaultServerChangedSpy.count() == 1, "defaultServerChanged signal should be emitted");
QVERIFY2(defaultServerChangedSpy.at(0).at(0).toInt() == 0, "defaultServerChanged should emit index 0"); QVERIFY2(defaultServerChangedSpy.at(0).at(0).toString() == m_coreController->m_serversController->getServerId(0),
"defaultServerChanged should emit new default server id");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default server index should be 0"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default server index should be 0");
if (m_coreController->m_serversModel) { if (m_coreController->m_serversModel) {
@@ -70,9 +72,10 @@ private slots:
QVERIFY2(modelDefaultIndex == 0, "Model should reflect default server"); QVERIFY2(modelDefaultIndex == 0, "Model should reflect default server");
} }
m_coreController->m_serversController->setDefaultServerIndex(2); m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(2));
QVERIFY2(defaultServerChangedSpy.count() == 2, "defaultServerChanged signal should be emitted again"); QVERIFY2(defaultServerChangedSpy.count() == 2, "defaultServerChanged signal should be emitted again");
QVERIFY2(defaultServerChangedSpy.at(1).at(0).toInt() == 2, "defaultServerChanged should emit index 2"); QVERIFY2(defaultServerChangedSpy.at(1).at(0).toString() == m_coreController->m_serversController->getServerId(2),
"defaultServerChanged should emit new default server id");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Default server index should be 2"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Default server index should be 2");
} }
@@ -94,28 +97,28 @@ private slots:
QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged); QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged);
QSignalSpy serverRemovedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved); QSignalSpy serverRemovedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved);
m_coreController->m_serversController->removeServer(0); m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0));
QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted");
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "Should have 2 servers"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "Should have 2 servers");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Default should be index 1 (was 2, removed 0)"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Default should be index 1 (was 2, removed 0)");
ServerConfig remainingServer1 = m_coreController->m_serversRepository->server(0); QString desc1 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
ServerConfig remainingServer2 = m_coreController->m_serversRepository->server(1); m_coreController->m_serversRepository->serverIdAt(0));
QString desc1 = remainingServer1.description(); QString desc2 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
QString desc2 = remainingServer2.description(); m_coreController->m_serversRepository->serverIdAt(1));
QVERIFY2(desc1 == "Xray Server", "First remaining server should be Xray"); QVERIFY2(desc1 == "Xray Server", "First remaining server should be Xray");
QVERIFY2(desc2 == "WireGuard Server", "Second remaining server should be WireGuard"); QVERIFY2(desc2 == "WireGuard Server", "Second remaining server should be WireGuard");
defaultServerChangedSpy.clear(); defaultServerChangedSpy.clear();
serverRemovedSpy.clear(); serverRemovedSpy.clear();
m_coreController->m_serversController->removeServer(0); m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0));
QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted");
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 1, "Should have 1 server"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 1, "Should have 1 server");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default should be index 0 (was 1, removed 0)"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default should be index 0 (was 1, removed 0)");
ServerConfig lastServer = m_coreController->m_serversRepository->server(0); QString lastDesc = amnezia::test::serverDescription(m_coreController->m_serversRepository,
QString lastDesc = lastServer.description(); m_coreController->m_serversRepository->serverIdAt(0));
QVERIFY2(lastDesc == "WireGuard Server", "Last server should be WireGuard"); QVERIFY2(lastDesc == "WireGuard Server", "Last server should be WireGuard");
} }
}; };
-75
View File
@@ -1,75 +0,0 @@
#include <QTest>
#include <QJsonDocument>
#include <QJsonObject>
#include <QUuid>
#include <QSignalSpy>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "vpnConnection.h"
#include "secureQSettings.h"
using namespace amnezia;
class TestGatewayStacks : public QObject
{
Q_OBJECT
private:
CoreController* m_coreController;
SecureQSettings* m_settings;
private slots:
void initTestCase() {
QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString();
m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false);
auto vpnConnection = QSharedPointer<VpnConnection>::create(nullptr, nullptr);
m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this);
}
void cleanupTestCase() {
m_settings->clearSettings();
delete m_coreController;
delete m_settings;
}
void init() {
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
}
}
void testGatewayStacksRecomputeOnServerOperations() {
QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA";
QSignalSpy gatewayStacksExpandedSpy(m_coreController->m_serversController, &ServersController::gatewayStacksExpanded);
QSignalSpy serverAddedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded);
QSignalSpy serverEditedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited);
QSignalSpy serverRemovedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved);
auto importResult = m_coreController->m_importCoreController->extractConfigFromData(awgKey);
m_coreController->m_importCoreController->importConfig(importResult.config);
QVERIFY2(serverAddedSpy.count() == 1, "serverAdded signal should be emitted");
QVERIFY2(m_coreController->m_serversController->gatewayStacks().isEmpty(), "Gateway stacks should be empty for self-hosted servers");
ServerConfig serverConfig = m_coreController->m_serversController->getServerConfig(0);
serverConfig.visit([](auto& arg) {
arg.description = "Edited Server";
});
m_coreController->m_serversController->editServer(0, serverConfig);
QVERIFY2(serverEditedSpy.count() == 1, "serverEdited signal should be emitted");
m_coreController->m_serversController->removeServer(0);
QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted");
QVERIFY2(m_coreController->m_serversController->gatewayStacks().isEmpty(), "Gateway stacks should remain empty");
}
};
QTEST_MAIN(TestGatewayStacks)
#include "testGatewayStacks.moc"
+19 -18
View File
@@ -6,7 +6,8 @@
#include <QSignalSpy> #include <QSignalSpy>
#include "core/controllers/coreController.h" #include "core/controllers/coreController.h"
#include "core/models/serverConfig.h" #include "core/models/serverDescription.h"
#include "tests/testServerRepositoryHelpers.h"
#include "vpnConnection.h" #include "vpnConnection.h"
#include "secureQSettings.h" #include "secureQSettings.h"
@@ -40,7 +41,7 @@ private slots:
m_settings->clearSettings(); m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache(); m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) { if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false); m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
} }
} }
@@ -70,8 +71,8 @@ private slots:
} }
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "First server should be default"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "First server should be default");
ServerConfig server1 = m_coreController->m_serversRepository->server(0); QString desc1 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
QString desc1 = server1.description(); m_coreController->m_serversRepository->serverIdAt(0));
QVERIFY2(desc1 == "AWG Server", "First server description should match"); QVERIFY2(desc1 == "AWG Server", "First server description should match");
if (m_coreController->m_serversModel) { if (m_coreController->m_serversModel) {
@@ -92,8 +93,8 @@ private slots:
} }
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Second server should be default"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Second server should be default");
ServerConfig server2 = m_coreController->m_serversRepository->server(1); QString desc2 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
QString desc2 = server2.description(); m_coreController->m_serversRepository->serverIdAt(1));
QVERIFY2(desc2 == "Xray Server", "Second server description should match"); QVERIFY2(desc2 == "Xray Server", "Second server description should match");
if (m_coreController->m_serversModel) { if (m_coreController->m_serversModel) {
@@ -114,8 +115,8 @@ private slots:
} }
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Third server should be default"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Third server should be default");
ServerConfig server3 = m_coreController->m_serversRepository->server(2); QString desc3 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
QString desc3 = server3.description(); m_coreController->m_serversRepository->serverIdAt(2));
QVERIFY2(desc3 == "WireGuard Server", "Third server description should match"); QVERIFY2(desc3 == "WireGuard Server", "Third server description should match");
if (m_coreController->m_serversModel) { if (m_coreController->m_serversModel) {
@@ -147,25 +148,25 @@ private slots:
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "After two imports servers count should be 2"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "After two imports servers count should be 2");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Second server should be default"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Second server should be default");
ServerConfig server0 = m_coreController->m_serversRepository->server(0); QString desc0 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
ServerConfig server1 = m_coreController->m_serversRepository->server(1); m_coreController->m_serversRepository->serverIdAt(0));
QString desc0 = server0.description(); QString desc1 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
QString desc1 = server1.description(); m_coreController->m_serversRepository->serverIdAt(1));
QVERIFY2(desc0 == "AWG Server", "First server description should match"); QVERIFY2(desc0 == "AWG Server", "First server description should match");
QVERIFY2(desc1 == "Xray Server", "Second server description should match"); QVERIFY2(desc1 == "Xray Server", "Second server description should match");
defaultServerChangedSpy.clear(); defaultServerChangedSpy.clear();
serverRemovedSpy.clear(); serverRemovedSpy.clear();
m_coreController->m_serversController->removeServer(0); m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0));
QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted");
QVERIFY2(serverRemovedSpy.at(0).at(0).toInt() == 0, "serverRemoved should emit index 0"); QVERIFY2(serverRemovedSpy.at(0).at(1).toInt() == 0, "serverRemoved should emit removed index 0");
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 1, "After removing first server, servers count should be 1"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 1, "After removing first server, servers count should be 1");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "After removing first server, default index should be 0"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "After removing first server, default index should be 0");
ServerConfig remainingServer = m_coreController->m_serversRepository->server(0); QString remainingDesc = amnezia::test::serverDescription(m_coreController->m_serversRepository,
QString remainingDesc = remainingServer.description(); m_coreController->m_serversRepository->serverIdAt(0));
QVERIFY2(remainingDesc == "Xray Server", "Remaining server should be Xray Server"); QVERIFY2(remainingDesc == "Xray Server", "Remaining server should be Xray Server");
if (m_coreController->m_serversModel) { if (m_coreController->m_serversModel) {
@@ -177,10 +178,10 @@ private slots:
defaultServerChangedSpy.clear(); defaultServerChangedSpy.clear();
serverRemovedSpy.clear(); serverRemovedSpy.clear();
m_coreController->m_serversController->removeServer(0); m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0));
QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted");
QVERIFY2(serverRemovedSpy.at(0).at(0).toInt() == 0, "serverRemoved should emit index 0"); QVERIFY2(serverRemovedSpy.at(0).at(1).toInt() == 0, "serverRemoved should emit removed index 0");
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 0, "After removing last server, servers count should be 0"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 0, "After removing last server, servers count should be 0");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "After removing last server, default index should be 0"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "After removing last server, default index should be 0");
+48 -36
View File
@@ -7,8 +7,8 @@
#include <QDebug> #include <QDebug>
#include "core/controllers/coreController.h" #include "core/controllers/coreController.h"
#include "core/models/serverConfig.h" #include "core/models/serverDescription.h"
#include "core/models/selfhosted/selfHostedServerConfig.h" #include "core/models/selfhosted/selfHostedAdminServerConfig.h"
#include "core/models/containerConfig.h" #include "core/models/containerConfig.h"
#include "core/models/protocols/awgProtocolConfig.h" #include "core/models/protocols/awgProtocolConfig.h"
#include "core/models/protocols/dnsProtocolConfig.h" #include "core/models/protocols/dnsProtocolConfig.h"
@@ -60,21 +60,24 @@ private:
qDebug() << "SSH connection successful. Output:" << sshOutput; qDebug() << "SSH connection successful. Output:" << sshOutput;
} }
void verifyAdminAccess(int serverIndex) { void verifyAdminAccess(int serverIndex)
ServerConfig server = m_coreController->m_serversRepository->server(serverIndex); {
const SelfHostedServerConfig* selfHosted = server.as<SelfHostedServerConfig>(); const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex);
QVERIFY2(selfHosted != nullptr, "Server config should be SelfHostedServerConfig"); const auto adminCfg = m_coreController->m_serversRepository->selfHostedAdminConfig(serverId);
QVERIFY2(adminCfg.has_value(), "Server config should be SelfHostedAdminServerConfig");
const SelfHostedAdminServerConfig &selfHosted = *adminCfg;
QVERIFY2(selfHosted->hasCredentials(), QVERIFY2(selfHosted.hasCredentials(),
"Server should have credentials (admin access)"); "Server should have credentials (admin access)");
QVERIFY2(selfHosted->userName.has_value() && !selfHosted->userName.value().isEmpty(), QVERIFY2(!selfHosted.userName.isEmpty(),
"Server should have userName for admin access"); "Server should have userName for admin access");
QVERIFY2(selfHosted->password.has_value() && !selfHosted->password.value().isEmpty(), QVERIFY2(!selfHosted.password.isEmpty(),
"Server should have password for admin access"); "Server should have password for admin access");
QVERIFY2(!selfHosted->isReadOnly(), QVERIFY2(!selfHosted.isReadOnly(),
"Server should not be read-only (should have admin access)"); "Server should not be read-only (should have admin access)");
if (m_coreController->m_serversModel) { if (m_coreController->m_serversModel) {
@@ -143,7 +146,7 @@ private slots:
void init() { void init() {
m_settings->clearSettings(); m_settings->clearSettings();
if (m_coreController->m_serversModel) { if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false); m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
} }
} }
@@ -177,10 +180,10 @@ private slots:
int serverIndex = m_coreController->m_serversRepository->serversCount() - 1; int serverIndex = m_coreController->m_serversRepository->serversCount() - 1;
qDebug() << "Server with Awg container added at index:" << serverIndex; qDebug() << "Server with Awg container added at index:" << serverIndex;
ServerConfig serverAfterAwg = m_coreController->m_serversRepository->server(serverIndex); const auto adminAfterAwg = m_coreController->m_serversRepository->selfHostedAdminConfig(
QVERIFY2(serverAfterAwg.isSelfHosted(), "Server should be self-hosted"); m_coreController->m_serversRepository->serverIdAt(serverIndex));
const SelfHostedServerConfig* selfHostedAfterAwg = serverAfterAwg.as<SelfHostedServerConfig>(); QVERIFY2(adminAfterAwg.has_value(), "Server should be self-hosted (admin)");
QVERIFY2(selfHostedAfterAwg != nullptr, "Server config should be SelfHostedServerConfig"); const SelfHostedAdminServerConfig *selfHostedAfterAwg = &(*adminAfterAwg);
QVERIFY2(selfHostedAfterAwg->defaultContainer == DockerContainer::Awg, "Default container should be Awg"); QVERIFY2(selfHostedAfterAwg->defaultContainer == DockerContainer::Awg, "Default container should be Awg");
QVERIFY2(selfHostedAfterAwg->containers.contains(DockerContainer::Awg), "Server should have Awg container"); QVERIFY2(selfHostedAfterAwg->containers.contains(DockerContainer::Awg), "Server should have Awg container");
@@ -198,8 +201,9 @@ private slots:
TransportProto dnsTransportProto = TransportProto::Udp; TransportProto dnsTransportProto = TransportProto::Udp;
bool wasDnsInstalled = false; bool wasDnsInstalled = false;
const QString serverIdForOps = m_coreController->m_serversRepository->serverIdAt(serverIndex);
ErrorCode installContainerError = m_coreController->m_installController->installContainer( ErrorCode installContainerError = m_coreController->m_installController->installContainer(
serverIndex, DockerContainer::Dns, dnsPort, dnsTransportProto, wasDnsInstalled); serverIdForOps, DockerContainer::Dns, dnsPort, dnsTransportProto, wasDnsInstalled);
QVERIFY2(installContainerError == ErrorCode::NoError, QVERIFY2(installContainerError == ErrorCode::NoError,
QString("installContainer for Dns should succeed. Error: %1") QString("installContainer for Dns should succeed. Error: %1")
@@ -207,9 +211,10 @@ private slots:
.toUtf8().constData()); .toUtf8().constData());
qDebug() << "Dns container installed:" << wasDnsInstalled; qDebug() << "Dns container installed:" << wasDnsInstalled;
ServerConfig serverAfterDns = m_coreController->m_serversRepository->server(serverIndex); const auto adminAfterDns = m_coreController->m_serversRepository->selfHostedAdminConfig(
const SelfHostedServerConfig* selfHostedAfterDns = serverAfterDns.as<SelfHostedServerConfig>(); m_coreController->m_serversRepository->serverIdAt(serverIndex));
QVERIFY2(selfHostedAfterDns != nullptr, "Server config should be SelfHostedServerConfig"); QVERIFY2(adminAfterDns.has_value(), "Server config should be SelfHostedAdminServerConfig");
const SelfHostedAdminServerConfig *selfHostedAfterDns = &(*adminAfterDns);
QVERIFY2(selfHostedAfterDns->containers.contains(DockerContainer::Awg), "Server should still have Awg container"); QVERIFY2(selfHostedAfterDns->containers.contains(DockerContainer::Awg), "Server should still have Awg container");
QVERIFY2(selfHostedAfterDns->containers.contains(DockerContainer::Dns), "Server should have Dns container"); QVERIFY2(selfHostedAfterDns->containers.contains(DockerContainer::Dns), "Server should have Dns container");
QVERIFY2(selfHostedAfterDns->containers.size() == 2, QVERIFY2(selfHostedAfterDns->containers.size() == 2,
@@ -242,16 +247,18 @@ private slots:
verifySshConnection(credentials); verifySshConnection(credentials);
SelfHostedServerConfig serverConfig; SelfHostedAdminServerConfig serverConfig;
serverConfig.hostName = credentials.hostName; serverConfig.hostName = credentials.hostName;
serverConfig.userName = credentials.userName; serverConfig.userName = credentials.userName;
serverConfig.password = credentials.secretData; serverConfig.password = credentials.secretData;
serverConfig.port = credentials.port; serverConfig.port = credentials.port;
serverConfig.description = m_coreController->m_appSettingsRepository->nextAvailableServerName(); serverConfig.description = m_coreController->m_appSettingsRepository->nextAvailableServerName();
serverConfig.displayName = serverConfig.description.isEmpty() ? serverConfig.hostName : serverConfig.description;
serverConfig.defaultContainer = DockerContainer::None; serverConfig.defaultContainer = DockerContainer::None;
QSignalSpy serverAddedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded); QSignalSpy serverAddedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded);
m_coreController->m_serversController->addServer(ServerConfig(serverConfig)); m_coreController->m_serversRepository->addServer(QString(), serverConfig.toJson(),
serverConfigUtils::ConfigType::SelfHostedAdmin);
QVERIFY2(serverAddedSpy.count() == 1, "serverAdded signal should be emitted"); QVERIFY2(serverAddedSpy.count() == 1, "serverAdded signal should be emitted");
QVERIFY2(m_coreController->m_serversRepository->serversCount() > 0, "Server should be added"); QVERIFY2(m_coreController->m_serversRepository->serversCount() > 0, "Server should be added");
@@ -259,23 +266,25 @@ private slots:
int serverIndex = m_coreController->m_serversRepository->serversCount() - 1; int serverIndex = m_coreController->m_serversRepository->serversCount() - 1;
qDebug() << "Empty server added at index:" << serverIndex; qDebug() << "Empty server added at index:" << serverIndex;
ServerConfig addedServer = m_coreController->m_serversRepository->server(serverIndex); const auto addedAdmin = m_coreController->m_serversRepository->selfHostedAdminConfig(
QVERIFY2(addedServer.isSelfHosted(), "Added server should be self-hosted"); m_coreController->m_serversRepository->serverIdAt(serverIndex));
const SelfHostedServerConfig* selfHosted = addedServer.as<SelfHostedServerConfig>(); QVERIFY2(addedAdmin.has_value(), "Added server should be self-hosted admin");
QVERIFY2(selfHosted != nullptr, "Server config should be SelfHostedServerConfig"); const SelfHostedAdminServerConfig *selfHosted = &(*addedAdmin);
QVERIFY2(selfHosted->containers.isEmpty(), "Server should have no containers initially"); QVERIFY2(selfHosted->containers.isEmpty(), "Server should have no containers initially");
QVERIFY2(selfHosted->defaultContainer == DockerContainer::None, "Default container should be None"); QVERIFY2(selfHosted->defaultContainer == DockerContainer::None, "Default container should be None");
ErrorCode scanError = m_coreController->m_installController->scanServerForInstalledContainers(serverIndex); const QString scanServerId = m_coreController->m_serversRepository->serverIdAt(serverIndex);
ErrorCode scanError = m_coreController->m_installController->scanServerForInstalledContainers(scanServerId);
QVERIFY2(scanError == ErrorCode::NoError, QVERIFY2(scanError == ErrorCode::NoError,
QString("Server scan should succeed. Error: %1") QString("Server scan should succeed. Error: %1")
.arg(static_cast<int>(scanError)) .arg(static_cast<int>(scanError))
.toUtf8().constData()); .toUtf8().constData());
qDebug() << "Server scan completed successfully"; qDebug() << "Server scan completed successfully";
ServerConfig scannedServer = m_coreController->m_serversRepository->server(serverIndex); const auto scannedAdmin = m_coreController->m_serversRepository->selfHostedAdminConfig(
const SelfHostedServerConfig* scannedSelfHosted = scannedServer.as<SelfHostedServerConfig>(); m_coreController->m_serversRepository->serverIdAt(serverIndex));
QVERIFY2(scannedSelfHosted != nullptr, "Scanned server config should be SelfHostedServerConfig"); QVERIFY2(scannedAdmin.has_value(), "Scanned server config should be SelfHostedAdminServerConfig");
const SelfHostedAdminServerConfig *scannedSelfHosted = &(*scannedAdmin);
QMap<DockerContainer, ContainerConfig> containers = scannedSelfHosted->containers; QMap<DockerContainer, ContainerConfig> containers = scannedSelfHosted->containers;
int containersCount = containers.size(); int containersCount = containers.size();
@@ -336,24 +345,27 @@ private slots:
int serverIndex = m_coreController->m_serversRepository->serversCount() - 1; int serverIndex = m_coreController->m_serversRepository->serversCount() - 1;
qDebug() << "Server with Awg container added at index:" << serverIndex; qDebug() << "Server with Awg container added at index:" << serverIndex;
ServerConfig serverBeforeRemoval = m_coreController->m_serversRepository->server(serverIndex); const auto adminBeforeRemoval = m_coreController->m_serversRepository->selfHostedAdminConfig(
const SelfHostedServerConfig* selfHostedBeforeRemoval = serverBeforeRemoval.as<SelfHostedServerConfig>(); m_coreController->m_serversRepository->serverIdAt(serverIndex));
QVERIFY2(selfHostedBeforeRemoval != nullptr, "Server config should be SelfHostedServerConfig"); QVERIFY2(adminBeforeRemoval.has_value(), "Server config should be SelfHostedAdminServerConfig");
const SelfHostedAdminServerConfig *selfHostedBeforeRemoval = &(*adminBeforeRemoval);
QVERIFY2(!selfHostedBeforeRemoval->containers.isEmpty(), "Server should have containers before removal"); QVERIFY2(!selfHostedBeforeRemoval->containers.isEmpty(), "Server should have containers before removal");
QVERIFY2(selfHostedBeforeRemoval->defaultContainer != DockerContainer::None, "Server should have default container before removal"); QVERIFY2(selfHostedBeforeRemoval->defaultContainer != DockerContainer::None, "Server should have default container before removal");
qDebug() << "Containers before removal:" << selfHostedBeforeRemoval->containers.size(); qDebug() << "Containers before removal:" << selfHostedBeforeRemoval->containers.size();
ErrorCode removeError = m_coreController->m_installController->removeAllContainers(serverIndex); const QString removeServerId = m_coreController->m_serversRepository->serverIdAt(serverIndex);
ErrorCode removeError = m_coreController->m_installController->removeAllContainers(removeServerId);
QVERIFY2(removeError == ErrorCode::NoError, QVERIFY2(removeError == ErrorCode::NoError,
QString("removeAllContainers should succeed. Error: %1") QString("removeAllContainers should succeed. Error: %1")
.arg(static_cast<int>(removeError)) .arg(static_cast<int>(removeError))
.toUtf8().constData()); .toUtf8().constData());
qDebug() << "All containers removed successfully"; qDebug() << "All containers removed successfully";
ServerConfig serverAfterRemoval = m_coreController->m_serversRepository->server(serverIndex); const auto adminAfterRemoval = m_coreController->m_serversRepository->selfHostedAdminConfig(
const SelfHostedServerConfig* selfHostedAfterRemoval = serverAfterRemoval.as<SelfHostedServerConfig>(); m_coreController->m_serversRepository->serverIdAt(serverIndex));
QVERIFY2(selfHostedAfterRemoval != nullptr, "Server config should be SelfHostedServerConfig"); QVERIFY2(adminAfterRemoval.has_value(), "Server config should be SelfHostedAdminServerConfig");
const SelfHostedAdminServerConfig *selfHostedAfterRemoval = &(*adminAfterRemoval);
QVERIFY2(selfHostedAfterRemoval->containers.isEmpty(), QVERIFY2(selfHostedAfterRemoval->containers.isEmpty(),
"Server should have no containers after removal"); "Server should have no containers after removal");

Some files were not shown because too many files have changed in this diff Show More