Merge branch 'dev' into refactoring/tests

This commit is contained in:
aiamnezia
2026-05-15 18:45:40 +04:00
148 changed files with 3776 additions and 3074 deletions

3
.gitignore vendored
View File

@@ -10,7 +10,8 @@ deploy/build_64/*
winbuild*.bat
.cache/
.vscode/
.venv/
.cursor/
# Qt-es
/.qmake.cache

View File

@@ -1,6 +1,5 @@
<RCC>
<qresource prefix="/client_scripts">
<file>linux_installer.sh</file>
<file>mac_installer.sh</file>
</qresource>
</RCC>

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

View File

@@ -15,7 +15,6 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/utils/constants/protocolConstants.h
${CLIENT_ROOT_DIR}/core/utils/constants/apiKeys.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/selfhosted/scriptsRegistry.h
${CLIENT_ROOT_DIR}/core/utils/qrCodeUtils.h
@@ -138,6 +137,7 @@ set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.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/utilities.cpp
${CLIENT_ROOT_DIR}/core/utils/managementServer.cpp

View File

@@ -1,51 +1,93 @@
#include "newsController.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/apiConstants.h"
#include "core/utils/constants/configKeys.h"
#include <QtConcurrent/QtConcurrent>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSet>
#include <QSharedPointer>
using namespace amnezia;
NewsController::NewsController(SecureAppSettingsRepository* appSettingsRepository,
ServersController* serversController)
: m_appSettingsRepository(appSettingsRepository), m_serversController(serversController)
NewsController::NewsController(SecureAppSettingsRepository *appSettingsRepository,
SecureServersRepository *serversRepository)
: 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()
{
if (!m_serversController) {
qWarning() << "ServersController is null, skip fetchNews";
if (!m_serversRepository) {
qWarning() << "SecureServersRepository is null, skip fetchNews";
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::InternalError, QJsonArray()));
}
const auto stacks = m_serversController->gatewayStacks();
if (stacks.isEmpty()) {
const QJsonObject services = getServicesList();
if (services.isEmpty()) {
qDebug() << "No Gateway stacks, skip fetchNews";
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::NoError, QJsonArray()));
}
auto gatewayController = QSharedPointer<GatewayController>::create(
m_appSettingsRepository->getGatewayEndpoint(),
m_appSettingsRepository->isDevGatewayEnv(),
apiDefs::requestTimeoutMsecs,
m_appSettingsRepository->isStrictKillSwitchEnabled());
m_appSettingsRepository->getGatewayEndpoint(),
m_appSettingsRepository->isDevGatewayEnv(),
apiDefs::requestTimeoutMsecs,
m_appSettingsRepository->isStrictKillSwitchEnabled());
QJsonObject payload;
payload.insert("locale", m_appSettingsRepository->getAppLanguage().name().split("_").first());
const QJsonObject stacksJson = stacks.toJson();
if (stacksJson.contains(apiDefs::key::userCountryCode)) {
payload.insert(apiDefs::key::userCountryCode, stacksJson.value(apiDefs::key::userCountryCode));
if (services.contains(apiDefs::key::userCountryCode)) {
payload.insert(apiDefs::key::userCountryCode, services.value(apiDefs::key::userCountryCode));
}
if (stacksJson.contains(apiDefs::key::serviceType)) {
payload.insert(apiDefs::key::serviceType, stacksJson.value(apiDefs::key::serviceType));
if (services.contains(apiDefs::key::serviceType)) {
payload.insert(apiDefs::key::serviceType, services.value(apiDefs::key::serviceType));
}
auto future = gatewayController->postAsync(QString("%1v1/news"), payload);
@@ -69,4 +111,3 @@ QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews()
return qMakePair(ErrorCode::NoError, newsArray);
});
}

View File

@@ -3,26 +3,28 @@
#include <QFuture>
#include <QJsonArray>
#include <QJsonObject>
#include <QPair>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "core/controllers/serversController.h"
#include "core/repositories/secureServersRepository.h"
class NewsController
{
public:
explicit NewsController(SecureAppSettingsRepository* appSettingsRepository,
ServersController* serversController);
SecureServersRepository* serversRepository);
QFuture<QPair<ErrorCode, QJsonArray>> fetchNews();
private:
QJsonObject getServicesList() const;
SecureAppSettingsRepository* m_appSettingsRepository;
ServersController* m_serversController;
SecureServersRepository* m_serversRepository;
};
#endif // NEWSCONTROLLER_H

View File

@@ -11,7 +11,7 @@
#include <limits>
#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/apiConstants.h"
#include "version.h"

View File

@@ -16,7 +16,7 @@
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.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/apiConstants.h"
#include "core/utils/api/apiUtils.h"
@@ -26,7 +26,6 @@
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "version.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h"
#include "core/models/api/apiConfig.h"
@@ -196,7 +195,7 @@ void SubscriptionController::updateApiConfigInJson(QJsonObject &serverConfigJson
apiConfig[apiDefs::key::serviceProtocol] = serviceProtocol;
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();
if (responseObj.contains(apiDefs::key::supportedProtocols)) {
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,
const QString &serviceProtocol, const ProtocolData &protocolData,
ServerConfig &serverConfig)
const QString &serviceProtocol, const ProtocolData &protocolData)
{
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
@@ -247,20 +245,18 @@ ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCo
updateApiConfigInJson(serverConfigJson, serviceType, serviceProtocol, userCountryCode, responseBody);
ServerConfig serverConfigModel = ServerConfig::fromJson(serverConfigJson);
if (!serverConfigModel.isApiV2()) {
if (serverConfigJson.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
return ErrorCode::InternalError;
}
m_serversRepository->addServer(serverConfigModel);
serverConfig = serverConfigModel;
ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(serverConfigJson);
m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(),
serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson()));
return ErrorCode::NoError;
}
ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &email,
ServerConfig &serverConfig)
const QString &serviceProtocol, const QString &email)
{
const QString trimmedEmail = email.trimmed();
if (trimmedEmail.isEmpty()) {
@@ -306,16 +302,19 @@ ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCoun
}
QJsonObject configObject = QJsonDocument::fromJson(configBytes).object();
ServerConfig serverConfigModel = ServerConfig::fromJson(configObject);
m_serversRepository->addServer(serverConfigModel);
serverConfig = serverConfigModel;
if (configObject.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
return ErrorCode::InternalError;
}
ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(configObject);
m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(),
serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson()));
return ErrorCode::NoError;
}
ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData,
const QString &transactionId, bool isTestPurchase,
ServerConfig &serverConfig,
int *duplicateServerIndex)
{
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
@@ -351,15 +350,8 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC
// Check if server with this VPN key already exists
for (int i = 0; i < m_serversRepository->serversCount(); ++i) {
ServerConfig existingServerConfig = m_serversRepository->server(i);
QString existingVpnKey;
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();
}
const auto apiV2 = m_serversRepository->apiV2Config(m_serversRepository->serverIdAt(i));
QString existingVpnKey = apiV2.has_value() ? apiV2->vpnKey() : QString();
existingVpnKey.replace(QStringLiteral("vpn://"), QString());
if (!existingVpnKey.isEmpty() && existingVpnKey == normalizedKey) {
if (duplicateServerIndex) {
@@ -385,38 +377,28 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC
quint16 crc = qChecksum(QJsonDocument(configObject).toJson());
ServerConfig serverConfigModel = ServerConfig::fromJson(configObject);
if (!serverConfigModel.isApiV2()) {
if (configObject.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
return ErrorCode::InternalError;
}
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
return ErrorCode::InternalError;
}
ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(configObject);
ApiV2ServerConfig* apiV2 = &apiV2ServerConfig;
apiV2->apiConfig.vpnKey = normalizedKey;
apiV2->apiConfig.isTestPurchase = isTestPurchase;
apiV2->apiConfig.isInAppPurchase = true;
apiV2->apiConfig.subscriptionExpiredByServer = false;
apiV2->crc = crc;
m_serversRepository->addServer(serverConfigModel);
serverConfig = serverConfigModel;
m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(),
serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson()));
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);
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
return ErrorCode::InternalError;
}
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);
if (errorCode != ErrorCode::NoError) {
if (errorCode == ErrorCode::ApiSubscriptionExpiredError && !apiV2->apiConfig.isInAppPurchase) {
ServerConfig expiredServerConfig = serverConfigModel;
ApiV2ServerConfig *expiredApiV2 = expiredServerConfig.as<ApiV2ServerConfig>();
if (expiredApiV2) {
expiredApiV2->apiConfig.subscriptionExpiredByServer = true;
m_serversRepository->editServer(serverIndex, expiredServerConfig);
}
ApiV2ServerConfig expiredApiV2 = *apiV2;
expiredApiV2.apiConfig.subscriptionExpiredByServer = true;
m_serversRepository->editServer(serverId, expiredApiV2.toJson(),
serverConfigUtils::configTypeFromJson(expiredApiV2.toJson()));
}
return errorCode;
}
@@ -463,16 +443,12 @@ ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, cons
updateApiConfigInJson(serverConfigJson, apiV2->apiConfig.serviceType, serviceProtocol, apiV2->apiConfig.userCountryCode, responseBody);
ServerConfig newServerConfigModel = ServerConfig::fromJson(serverConfigJson);
if (!newServerConfigModel.isApiV2()) {
if (serverConfigJson.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
return ErrorCode::InternalError;
}
ApiV2ServerConfig* newApiV2 = newServerConfigModel.as<ApiV2ServerConfig>();
if (!newApiV2) {
return ErrorCode::InternalError;
}
ApiV2ServerConfig newApiV2Config = ApiV2ServerConfig::fromJson(serverConfigJson);
ApiV2ServerConfig* newApiV2 = &newApiV2Config;
newApiV2->apiConfig.vpnKey = apiV2->apiConfig.vpnKey;
newApiV2->apiConfig.isTestPurchase = apiV2->apiConfig.isTestPurchase;
@@ -487,20 +463,15 @@ ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, cons
newApiV2->nameOverriddenByUser = true;
}
m_serversRepository->editServer(serverIndex, newServerConfigModel);
m_serversRepository->editServer(serverId, newApiV2Config.toJson(),
serverConfigUtils::configTypeFromJson(newApiV2Config.toJson()));
return ErrorCode::NoError;
}
ErrorCode SubscriptionController::deactivateDevice(int serverIndex)
ErrorCode SubscriptionController::deactivateDevice(const QString &serverId)
{
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
if (!serverConfigModel.isApiV2()) {
return ErrorCode::NoError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
return ErrorCode::NoError;
}
@@ -528,23 +499,16 @@ ErrorCode SubscriptionController::deactivateDevice(int serverIndex)
return errorCode;
}
serverConfigModel.visit([](auto& arg) {
arg.containers.clear();
});
m_serversRepository->editServer(serverIndex, serverConfigModel);
apiV2->containers.clear();
m_serversRepository->editServer(serverId, apiV2->toJson(),
serverConfigUtils::configTypeFromJson(apiV2->toJson()));
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);
if (!serverConfigModel.isApiV2()) {
return ErrorCode::NoError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
return ErrorCode::NoError;
}
@@ -573,25 +537,18 @@ ErrorCode SubscriptionController::deactivateExternalDevice(int serverIndex, cons
}
if (uuid == m_appSettingsRepository->getInstallationUuid(true)) {
serverConfigModel.visit([](auto& arg) {
arg.containers.clear();
});
m_serversRepository->editServer(serverIndex, serverConfigModel);
apiV2->containers.clear();
m_serversRepository->editServer(serverId, apiV2->toJson(),
serverConfigUtils::configTypeFromJson(apiV2->toJson()));
}
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);
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
return ErrorCode::InternalError;
}
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
@@ -624,16 +581,10 @@ ErrorCode SubscriptionController::exportNativeConfig(int serverIndex, const QStr
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);
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
return ErrorCode::InternalError;
}
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
@@ -661,126 +612,54 @@ ErrorCode SubscriptionController::revokeNativeConfig(int serverIndex, const QStr
return ErrorCode::NoError;
}
ErrorCode SubscriptionController::updateServiceFromTelegram(int serverIndex)
ErrorCode SubscriptionController::prepareVpnKeyExport(const QString &serverId, QString &vpnKey)
{
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
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 {
auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
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;
}
ErrorCode SubscriptionController::validateAndUpdateConfig(int serverIndex, bool hasInstalledContainers)
ErrorCode SubscriptionController::validateAndUpdateConfig(const QString &serverId, bool hasInstalledContainers)
{
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
apiDefs::ConfigSource configSource;
if (serverConfigModel.isApiV1()) {
configSource = apiDefs::ConfigSource::Telegram;
} else if (serverConfigModel.isApiV2()) {
configSource = apiDefs::ConfigSource::AmneziaGateway;
} else {
if (!m_serversRepository->apiV2Config(serverId).has_value()) {
return ErrorCode::NoError;
}
if (configSource == apiDefs::ConfigSource::Telegram && !hasInstalledContainers) {
removeApiConfig(serverIndex);
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 (!hasInstalledContainers) {
return updateServiceFromGateway(serverId, "", true);
}
if (isApiKeyExpired(serverId)) {
qDebug() << "attempt to update api config by expires_at event";
return updateServiceFromGateway(serverId, "", true);
}
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)
QString description = serverConfigModel.description();
QString hostName = serverConfigModel.hostName();
QString description = apiV2->description;
QString hostName = apiV2->hostName;
QString vpncName = QString("%1 (%2) %3")
.arg(description)
.arg(hostName)
@@ -789,34 +668,42 @@ void SubscriptionController::removeApiConfig(int serverIndex)
AmneziaVPN::removeVPNC(vpncName.toStdString());
#endif
serverConfigModel.visit([](auto& arg) {
arg.dns1.clear();
arg.dns2.clear();
arg.containers.clear();
arg.hostName.clear();
arg.defaultContainer = DockerContainer::None;
});
apiV2->dns1.clear();
apiV2->dns2.clear();
apiV2->containers.clear();
apiV2->hostName.clear();
apiV2->defaultContainer = DockerContainer::None;
apiV2->apiConfig.publicKey = ApiConfig::PublicKeyInfo{};
if (serverConfigModel.isApiV2()) {
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (apiV2) {
apiV2->apiConfig.publicKey = ApiConfig::PublicKeyInfo{};
}
}
m_serversRepository->editServer(serverIndex, serverConfigModel);
m_serversRepository->editServer(serverId, apiV2->toJson(),
serverConfigUtils::configTypeFromJson(apiV2->toJson()));
}
bool SubscriptionController::isApiKeyExpired(int serverIndex) const
bool SubscriptionController::removeServer(const QString &serverId)
{
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
if (!serverConfigModel.isApiV2()) {
if (serverId.isEmpty()) {
return false;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
if (!m_serversRepository->apiV2Config(serverId).has_value()) {
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;
}
const QString expiresAt = apiV2->apiConfig.publicKey.expiresAt;
@@ -833,31 +720,24 @@ bool SubscriptionController::isApiKeyExpired(int serverIndex) const
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);
if (serverConfigModel.isApiV2()) {
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (apiV2) {
apiV2->apiConfig.serviceProtocol = protocolName;
}
m_serversRepository->editServer(serverIndex, serverConfigModel);
auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (apiV2.has_value()) {
apiV2->apiConfig.serviceProtocol = protocolName;
m_serversRepository->editServer(serverId, apiV2->toJson(),
serverConfigUtils::configTypeFromJson(apiV2->toJson()));
}
}
bool SubscriptionController::isVlessProtocol(int serverIndex) const
bool SubscriptionController::isVlessProtocol(const QString &serverId) const
{
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
if (serverConfigModel.isApiV2()) {
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
return apiV2 && apiV2->serviceProtocol() == "vless";
}
return false;
auto apiV2 = m_serversRepository->apiV2Config(serverId);
return apiV2.has_value() && apiV2->serviceProtocol() == "vless";
}
ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &productId,
ServerConfig &serverConfig,
int *duplicateServerIndex)
{
#if defined(Q_OS_IOS) || defined(MACOS_NE)
@@ -891,13 +771,12 @@ ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCou
ProtocolData protocolData = generateProtocolData(serviceProtocol);
return importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData,
originalTransactionId, isTestPurchase, serverConfig, duplicateServerIndex);
originalTransactionId, isTestPurchase, duplicateServerIndex);
#else
Q_UNUSED(userCountryCode);
Q_UNUSED(serviceType);
Q_UNUSED(serviceProtocol);
Q_UNUSED(productId);
Q_UNUSED(serverConfig);
return ErrorCode::ApiPurchaseError;
#endif
}
@@ -956,10 +835,9 @@ SubscriptionController::AppStoreRestoreResult SubscriptionController::processApp
<< "originalTransactionId =" << originalTransactionId << "productId =" << transactionProductId;
ProtocolData protocolData = generateProtocolData(serviceProtocol);
ServerConfig serverConfig;
int currentDuplicateServerIndex = -1;
ErrorCode errorCode = importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData,
originalTransactionId, isTestPurchase, serverConfig,
originalTransactionId, isTestPurchase,
&currentDuplicateServerIndex);
if (errorCode == ErrorCode::ApiConfigAlreadyAdded) {
@@ -991,16 +869,10 @@ SubscriptionController::AppStoreRestoreResult SubscriptionController::processApp
#endif
}
ErrorCode SubscriptionController::getAccountInfo(int serverIndex, QJsonObject &accountInfo)
ErrorCode SubscriptionController::getAccountInfo(const QString &serverId, QJsonObject &accountInfo)
{
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError;
}
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
return ErrorCode::InternalError;
}
bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
@@ -1030,20 +902,13 @@ ErrorCode SubscriptionController::getAccountInfo(int serverIndex, QJsonObject &a
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();
promise->start();
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
if (!serverConfigModel.isApiV2()) {
promise->addResult(qMakePair(ErrorCode::InternalError, QString()));
promise->finish();
return promise->future();
}
const ApiV2ServerConfig *apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
if (!apiV2) {
auto apiV2 = m_serversRepository->apiV2Config(serverId);
if (!apiV2.has_value()) {
promise->addResult(qMakePair(ErrorCode::InternalError, QString()));
promise->finish();
return promise->future();

View File

@@ -12,7 +12,6 @@
#include "core/utils/commonStructs.h"
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "core/models/serverConfig.h"
class ServersController;
@@ -48,44 +47,40 @@ public:
ProtocolData generateProtocolData(const QString &protocol);
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,
const QString &serviceProtocol, const ProtocolData &protocolData,
ServerConfig &serverConfig);
const QString &serviceProtocol, const ProtocolData &protocolData);
ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &email,
ServerConfig &serverConfig);
const QString &serviceProtocol, const QString &email);
ErrorCode importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData,
const QString &transactionId, bool isTestPurchase,
ServerConfig &serverConfig,
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);
bool isVlessProtocol(int serverIndex) const;
void setCurrentProtocol(const QString &serverId, const QString &protocolName);
bool isVlessProtocol(const QString &serverId) const;
ErrorCode getAccountInfo(int serverIndex, QJsonObject &accountInfo);
QFuture<QPair<ErrorCode, QString>> getRenewalLink(int serverIndex);
ErrorCode getAccountInfo(const QString &serverId, QJsonObject &accountInfo);
QFuture<QPair<ErrorCode, QString>> getRenewalLink(const QString &serverId);
struct AppStoreRestoreResult
{
@@ -98,7 +93,6 @@ public:
ErrorCode processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &productId,
ServerConfig &serverConfig,
int *duplicateServerIndex = nullptr);
AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
@@ -106,7 +100,7 @@ public:
private:
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,
const ProtocolData &protocolData, QJsonObject &serverConfigJson);

View File

@@ -9,11 +9,11 @@
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/utilities.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/serverConfigUtils.h"
#include "version.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.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,
DockerContainer& container)
{
@@ -59,35 +59,98 @@ ErrorCode ConnectionController::prepareConnection(int serverIndex,
return ErrorCode::AmneziaServiceNotRunning;
}
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
container = serverConfigModel.defaultContainer();
ContainerConfig containerConfigModel;
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)) {
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);
auto dns = serverConfigModel.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
vpnConfiguration = createConnectionConfiguration(dns, serverConfigModel, containerConfigModel, container);
vpnConfiguration = createConnectionConfiguration(dns, isApiConfig, hostName, description, configVersion,
containerConfigModel, container);
return ErrorCode::NoError;
}
ErrorCode ConnectionController::openConnection(int serverIndex)
ErrorCode ConnectionController::openConnection(const QString &serverId)
{
QJsonObject vpnConfiguration;
DockerContainer container;
ErrorCode errorCode = prepareConnection(serverIndex, vpnConfiguration, container);
ErrorCode errorCode = prepareConnection(serverId, vpnConfiguration, container);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
emit openConnectionRequested(serverIndex, container, vpnConfiguration);
emit openConnectionRequested(serverId, container, vpnConfiguration);
return ErrorCode::NoError;
}
@@ -120,7 +183,10 @@ ErrorCode ConnectionController::lastConnectionError() const
}
QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QString, QString> &dns,
const ServerConfig &serverConfig,
bool isApiConfig,
const QString &hostName,
const QString &description,
int configVersion,
const ContainerConfig &containerConfig,
DockerContainer container)
{
@@ -134,7 +200,7 @@ QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QStr
ConnectionSettings connectionSettings = {
{ dns.first, dns.second },
serverConfig.isApiConfig(),
isApiConfig,
{
m_appSettingsRepository->isSitesSplitTunnelingEnabled(),
m_appSettingsRepository->routeMode()
@@ -160,10 +226,9 @@ QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QStr
vpnConfiguration[configKey::dns1] = dns.first;
vpnConfiguration[configKey::dns2] = dns.second;
vpnConfiguration[configKey::hostName] = serverConfig.hostName();
vpnConfiguration[configKey::description] = serverConfig.description();
vpnConfiguration[configKey::configVersion] = serverConfig.configVersion();
vpnConfiguration[configKey::hostName] = hostName;
vpnConfiguration[configKey::description] = description;
vpnConfiguration[configKey::configVersion] = configVersion;
return vpnConfiguration;
}

View File

@@ -30,11 +30,11 @@ public:
QObject* parent = nullptr);
~ConnectionController() = default;
ErrorCode prepareConnection(int serverIndex,
ErrorCode prepareConnection(const QString &serverId,
QJsonObject& vpnConfiguration,
DockerContainer& container);
ErrorCode openConnection(int serverIndex);
ErrorCode openConnection(const QString &serverId);
void closeConnection();
@@ -50,7 +50,10 @@ public:
void setConnectionState(Vpn::ConnectionState state);
QJsonObject createConnectionConfiguration(const QPair<QString, QString> &dns,
const ServerConfig &serverConfig,
bool isApiConfig,
const QString &hostName,
const QString &description,
int configVersion,
const ContainerConfig &containerConfig,
DockerContainer container);
@@ -60,7 +63,7 @@ public:
signals:
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 setConnectionStateRequested(Vpn::ConnectionState state);
void killSwitchModeChangedRequested(bool enabled);

View File

@@ -8,7 +8,6 @@
#include "core/controllers/selfhosted/installController.h"
#include "core/controllers/selfhosted/importController.h"
#include "core/controllers/coreSignalHandlers.h"
#include "core/models/serverConfig.h"
#include "logger.h"
#include "secureQSettings.h"
@@ -145,7 +144,7 @@ void CoreController::initCoreControllers()
m_allowedDnsController = new AllowedDnsController(m_appSettingsRepository);
m_servicesCatalogController = new ServicesCatalogController(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_installController = new InstallController(m_serversRepository, m_appSettingsRepository, this);
@@ -165,7 +164,7 @@ void CoreController::initControllers()
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,
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel,
@@ -262,9 +261,12 @@ void CoreController::initSignalHandlers()
{
m_signalHandlers = new CoreSignalHandlers(this, this);
m_signalHandlers->initAllHandlers();
// Trigger initial update after handlers are connected
m_serversUiController->updateModel();
if (m_serversUiController->hasServersFromGatewayApi()) {
m_apiNewsUiController->fetchNews(false);
}
}
void CoreController::updateTranslator(const QLocale &locale)
@@ -322,11 +324,16 @@ PageController* CoreController::pageController() const
void CoreController::openConnectionByIndex(int serverIndex)
{
const QString serverId =
m_serversUiController ? m_serversUiController->getServerId(serverIndex) : QString();
if (serverId.isEmpty()) {
return;
}
if (m_serversModel) {
m_serversModel->setProcessedServerIndex(serverIndex);
}
if (m_serversController) {
m_serversController->setDefaultServerIndex(serverIndex);
m_serversController->setDefaultServer(serverId);
}
m_connectionUiController->toggleConnection();
}

View File

@@ -82,9 +82,9 @@ class TestAdminSelfHostedExport;
class TestServerEdit;
class TestDefaultServerChange;
class TestServerEdgeCases;
class TestGatewayStacks;
class TestSignalOrder;
class TestServersModelSync;
class TestGatewayStacks;
class TestComplexOperations;
class TestSettingsSignals;
class TestUiServersModelAndController;
@@ -107,9 +107,9 @@ class CoreController : public QObject
friend class TestServerEdit;
friend class TestDefaultServerChange;
friend class TestServerEdgeCases;
friend class TestGatewayStacks;
friend class TestSignalOrder;
friend class TestServersModelSync;
friend class TestGatewayStacks;
friend class TestComplexOperations;
friend class TestSettingsSignals;
friend class TestUiServersModelAndController;

View File

@@ -7,6 +7,7 @@
#include "core/utils/routeModes.h"
#include "core/controllers/coreController.h"
#include "core/repositories/secureServersRepository.h"
#include "core/utils/serverConfigUtils.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "vpnConnection.h"
#include "ui/controllers/qml/pageController.h"
@@ -65,7 +66,6 @@ void CoreSignalHandlers::initAllHandlers()
initImportControllerHandler();
initApiCountryModelUpdateHandler();
initSubscriptionRefreshHandler();
initContainerModelUpdateHandler();
initAdminConfigRevokedHandler();
initPassphraseRequestHandler();
initTranslationsUpdatedHandler();
@@ -78,6 +78,7 @@ void CoreSignalHandlers::initAllHandlers()
initAllowedDnsModelUpdateHandler();
initAppSplitTunnelingModelUpdateHandler();
initPrepareConfigHandler();
initUnsupportedConnectDrawerHandler();
initStrictKillSwitchHandler();
initAndroidSettingsHandler();
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_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,
m_coreController->m_installUiController, [this](int index) {
if (index >= 0) {
m_coreController->m_installUiController, [this](int serverIndex) {
if (serverIndex >= 0) {
m_coreController->m_installUiController->clearProcessedServerCredentials();
}
});
@@ -137,20 +136,20 @@ void CoreSignalHandlers::initInstallControllerHandler()
void CoreSignalHandlers::initExportControllerHandler()
{
connect(m_coreController->m_exportController, &ExportController::appendClientRequested, this,
[this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container);
[this](const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->appendClient(serverId, clientId, clientName, container);
});
connect(m_coreController->m_exportController, &ExportController::updateClientsRequested, this,
[this](int serverIndex, DockerContainer container) {
m_coreController->m_usersController->updateClients(serverIndex, container);
[this](const QString &serverId, DockerContainer container) {
m_coreController->m_usersController->updateClients(serverId, container);
});
connect(m_coreController->m_exportController, &ExportController::revokeClientRequested, this,
[this](int serverIndex, int row, DockerContainer container) {
m_coreController->m_usersController->revokeClient(serverIndex, row, container);
[this](const QString &serverId, int row, DockerContainer container) {
m_coreController->m_usersController->revokeClient(serverId, row, container);
});
connect(m_coreController->m_exportController, &ExportController::renameClientRequested, this,
[this](int serverIndex, int row, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->renameClient(serverIndex, row, clientName, container);
[this](const QString &serverId, int row, const QString &clientName, DockerContainer 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]() {
if (!m_coreController->m_connectionController->isConnected()) {
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) {
m_coreController->m_serversUiController->setProcessedServerIndex(newServerIndex);
m_coreController->m_serversUiController->setProcessedServerId(serverId);
}
}
});
@@ -170,21 +172,18 @@ void CoreSignalHandlers::initImportControllerHandler()
void CoreSignalHandlers::initApiCountryModelUpdateHandler()
{
connect(m_coreController->m_serversUiController, &ServersUiController::updateApiCountryModel, this, [this]() {
int processedIndex = m_coreController->m_serversUiController->getProcessedServerIndex();
if (processedIndex < 0 || processedIndex >= m_coreController->m_serversRepository->serversCount()) {
const QString processedServerId = m_coreController->m_serversUiController->getProcessedServerId();
if (processedServerId.isEmpty()) {
return;
}
ServerConfig server = m_coreController->m_serversRepository->server(processedIndex);
QJsonArray availableCountries;
QString serverCountryCode;
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (apiV2) {
availableCountries = apiV2->apiConfig.availableCountries;
serverCountryCode = apiV2->apiConfig.serverCountryCode;
}
const auto apiV2 = m_coreController->m_serversRepository->apiV2Config(processedServerId);
if (apiV2.has_value()) {
availableCountries = apiV2->apiConfig.availableCountries;
serverCountryCode = apiV2->apiConfig.serverCountryCode;
}
m_coreController->m_apiCountryModel->updateModel(availableCountries, serverCountryCode);
@@ -194,18 +193,9 @@ void CoreSignalHandlers::initApiCountryModelUpdateHandler()
void CoreSignalHandlers::initSubscriptionRefreshHandler()
{
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::subscriptionRefreshNeeded, this, [this]() {
const int defaultServerIndex = m_coreController->m_serversController->getDefaultServerIndex();
if (defaultServerIndex >= 0) {
m_coreController->m_subscriptionUiController->getAccountInfo(defaultServerIndex, 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);
const QString defaultServerId = m_coreController->m_serversController->getDefaultServerId();
if (!defaultServerId.isEmpty()) {
m_coreController->m_subscriptionUiController->getAccountInfo(defaultServerId, false);
}
});
}
@@ -213,17 +203,17 @@ void CoreSignalHandlers::initContainerModelUpdateHandler()
void CoreSignalHandlers::initAdminConfigRevokedHandler()
{
connect(m_coreController->m_installController, &InstallController::clientRevocationRequested, this,
[this](int serverIndex, const ContainerConfig &containerConfig, DockerContainer container) {
m_coreController->m_usersController->revokeClient(serverIndex, containerConfig, container);
[this](const QString &serverId, const ContainerConfig &containerConfig, DockerContainer container) {
m_coreController->m_usersController->revokeClient(serverId, containerConfig, container);
});
connect(m_coreController->m_installController, &InstallController::clientAppendRequested, this,
[this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container);
[this](const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container) {
m_coreController->m_usersController->appendClient(serverId, clientId, clientName, container);
});
connect(m_coreController->m_usersController, &UsersController::adminConfigRevoked, m_coreController->m_serversController,
&ServersController::clearCachedProfile);
connect(m_coreController->m_usersController, &UsersController::adminConfigRevoked, m_coreController->m_installController,
&InstallController::clearCachedProfile);
}
void CoreSignalHandlers::initPassphraseRequestHandler()
@@ -251,7 +241,8 @@ void CoreSignalHandlers::initLanguageHandler()
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(); });
}
}
@@ -271,16 +262,20 @@ void CoreSignalHandlers::initServersModelUpdateHandler()
m_coreController->m_serversUiController, &ServersUiController::updateModel);
connect(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged,
m_coreController->m_serversUiController, &ServersUiController::onDefaultServerChanged);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded,
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited,
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved,
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
connect(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished,
m_coreController->m_serversUiController, &ServersUiController::updateModel);
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded, this,
[this](const QString &serverId) {
if (m_coreController->m_serversRepository->apiV2Config(serverId).has_value()) {
m_coreController->m_apiNewsUiController->fetchNews(false);
}
});
connect(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished, this, [this]() {
m_coreController->m_serversUiController->updateModel();
if (m_coreController->m_serversUiController->hasServersFromGatewayApi()) {
m_coreController->m_apiNewsUiController->fetchNews(false);
}
});
}
void CoreSignalHandlers::initClientManagementModelUpdateHandler()
@@ -315,7 +310,19 @@ void CoreSignalHandlers::initPrepareConfigHandler()
connect(m_coreController->m_connectionUiController, &ConnectionUiController::prepareConfig, this, [this]() {
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) {
@@ -324,7 +331,7 @@ void CoreSignalHandlers::initPrepareConfigHandler()
return;
}
m_coreController->m_installUiController->validateConfig();
m_coreController->m_connectionUiController->openConnection();
});
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()
{
connect(m_coreController->m_settingsUiController, &SettingsUiController::strictKillSwitchEnabledChanged, m_coreController->m_connectionController,
@@ -348,7 +361,10 @@ void CoreSignalHandlers::initAndroidSettingsHandler()
#ifdef Q_OS_ANDROID
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_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); });
#endif
}

View File

@@ -21,7 +21,6 @@ private:
void initImportControllerHandler();
void initApiCountryModelUpdateHandler();
void initSubscriptionRefreshHandler();
void initContainerModelUpdateHandler();
void initAdminConfigRevokedHandler();
void initPassphraseRequestHandler();
void initTranslationsUpdatedHandler();
@@ -34,6 +33,7 @@ private:
void initAllowedDnsModelUpdateHandler();
void initAppSplitTunnelingModelUpdateHandler();
void initPrepareConfigHandler();
void initUnsupportedConnectDrawerHandler();
void initStrictKillSwitchHandler();
void initAndroidSettingsHandler();
void initAndroidConnectionHandler();

View File

@@ -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::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();
QString replyErrorString = reply->errorString();
auto replyError = reply->error();

View File

@@ -5,14 +5,13 @@
#include "core/configurators/configuratorBase.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/qrCodeUtils.h"
#include "core/utils/serialization/serialization.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.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/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;
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
serverConfig.visit([](auto& arg) {
for (auto it = arg.containers.begin(); it != arg.containers.end(); ++it) {
it.value().protocolConfig.clearClientConfig();
}
});
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
result.errorCode = ErrorCode::InternalError;
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();
compressedConfig = qCompress(compressedConfig, 8);
result.config = generateVpnUrl(compressedConfig);
@@ -47,13 +48,22 @@ ExportController::ExportResult ExportController::generateFullAccessConfig(int se
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;
DockerContainer container = static_cast<DockerContainer>(containerIndex);
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
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) {
SshSession sshSession;
@@ -74,35 +84,25 @@ ExportController::ExportResult ExportController::generateConnectionConfig(int se
QString clientId = newProtocolConfig.clientId();
if (!clientId.isEmpty()) {
emit appendClientRequested(serverIndex, clientId, clientName, container);
emit appendClientRequested(serverId, clientId, clientName, container);
}
}
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
serverConfig.visit([container, containerConfig](auto& arg) {
arg.containers.clear();
arg.containers[container] = containerConfig;
arg.defaultContainer = container;
});
const QPair<QString, QString> dns = adminConfig->getDnsPair(m_appSettingsRepository->useAmneziaDns(),
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
if (serverConfig.isSelfHosted()) {
SelfHostedServerConfig* selfHosted = serverConfig.as<SelfHostedServerConfig>();
if (selfHosted) {
selfHosted->userName.reset();
selfHosted->password.reset();
selfHosted->port.reset();
}
}
adminConfig->containers.clear();
adminConfig->containers[container] = containerConfig;
adminConfig->defaultContainer = container;
adminConfig->userName.clear();
adminConfig->password.clear();
adminConfig->port = 0;
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
serverConfig.visit([&dns](auto& arg) {
arg.dns1 = dns.first;
arg.dns2 = dns.second;
});
adminConfig->dns1 = dns.first;
adminConfig->dns2 = dns.second;
QJsonObject serverJson = serverConfig.toJson();
QJsonObject serverJson = adminConfig->toJson();
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
compressedConfig = qCompress(compressedConfig, 8);
result.config = generateVpnUrl(compressedConfig);
@@ -111,7 +111,7 @@ ExportController::ExportResult ExportController::generateConnectionConfig(int se
return result;
}
ExportController::NativeConfigResult ExportController::generateNativeConfig(int serverIndex, DockerContainer container,
ExportController::NativeConfigResult ExportController::generateNativeConfig(const QString &serverId, DockerContainer container,
const ContainerConfig &containerConfig,
const QString &clientName)
{
@@ -123,11 +123,19 @@ ExportController::NativeConfigResult ExportController::generateNativeConfig(int
Proto protocol = ContainerUtils::defaultProtocol(container);
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
result.errorCode = ErrorCode::InternalError;
return result;
}
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;
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) {
QString clientId = newProtocolConfig.clientId();
if (!clientId.isEmpty()) {
emit appendClientRequested(serverIndex, clientId, clientName, container);
emit appendClientRequested(serverId, clientId, clientName, container);
}
}
return result;
}
ExportController::ExportResult ExportController::generateOpenVpnConfig(int serverIndex, const QString &clientName)
ExportController::ExportResult ExportController::generateOpenVpnConfig(const QString &serverId, const QString &clientName)
{
ExportResult result;
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) {
result.errorCode = nativeResult.errorCode;
return result;
@@ -185,13 +198,18 @@ ExportController::ExportResult ExportController::generateOpenVpnConfig(int serve
return result;
}
ExportController::ExportResult ExportController::generateWireGuardConfig(int serverIndex, const QString &clientName)
ExportController::ExportResult ExportController::generateWireGuardConfig(const QString &serverId, const QString &clientName)
{
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) {
result.errorCode = nativeResult.errorCode;
return result;
@@ -206,7 +224,7 @@ ExportController::ExportResult ExportController::generateWireGuardConfig(int ser
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;
@@ -215,9 +233,14 @@ ExportController::ExportResult ExportController::generateAwgConfig(int serverInd
result.errorCode = ErrorCode::InternalError;
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) {
result.errorCode = nativeResult.errorCode;
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;
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) {
result.errorCode = nativeResult.errorCode;
return result;
@@ -302,22 +330,22 @@ ExportController::ExportResult ExportController::generateXrayConfig(int serverIn
return result;
}
void ExportController::updateClientManagementModel(int serverIndex, int containerIndex)
void ExportController::updateClientManagementModel(const QString &serverId, int 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);
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);
emit renameClientRequested(serverIndex, row, clientName, container);
emit renameClientRequested(serverId, row, clientName, container);
}
QString ExportController::generateVpnUrl(const QByteArray &compressedConfig)

View File

@@ -37,23 +37,23 @@ public:
SecureAppSettingsRepository* appSettingsRepository,
QObject *parent = nullptr);
ExportResult generateFullAccessConfig(int serverIndex);
ExportResult generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName);
ExportResult generateOpenVpnConfig(int serverIndex, const QString &clientName);
ExportResult generateWireGuardConfig(int serverIndex, const QString &clientName);
ExportResult generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName);
ExportResult generateXrayConfig(int serverIndex, const QString &clientName);
ExportResult generateFullAccessConfig(const QString &serverId);
ExportResult generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName);
ExportResult generateOpenVpnConfig(const QString &serverId, const QString &clientName);
ExportResult generateWireGuardConfig(const QString &serverId, const QString &clientName);
ExportResult generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName);
ExportResult generateXrayConfig(const QString &serverId, const QString &clientName);
signals:
void appendClientRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container);
void updateClientsRequested(int serverIndex, DockerContainer container);
void revokeClientRequested(int serverIndex, int row, DockerContainer container);
void renameClientRequested(int serverIndex, int row, const QString &clientName, DockerContainer container);
void appendClientRequested(const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container);
void updateClientsRequested(const QString &serverId, DockerContainer container);
void revokeClientRequested(const QString &serverId, int row, DockerContainer container);
void renameClientRequested(const QString &serverId, int row, const QString &clientName, DockerContainer container);
public slots:
void updateClientManagementModel(int serverIndex, int containerIndex);
void revokeConfig(int row, int serverIndex, int containerIndex);
void renameClient(int row, const QString &clientName, int serverIndex, int containerIndex);
void updateClientManagementModel(const QString &serverId, int containerIndex);
void revokeConfig(int row, const QString &serverId, int containerIndex);
void renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex);
private:
struct NativeConfigResult
@@ -62,7 +62,7 @@ private:
QJsonObject jsonNativeConfig;
};
NativeConfigResult generateNativeConfig(int serverIndex, DockerContainer container,
NativeConfigResult generateNativeConfig(const QString &serverId, DockerContainer container,
const ContainerConfig &containerConfig,
const QString &clientName);

View File

@@ -16,7 +16,7 @@
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.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/apiConstants.h"
#include "core/utils/api/apiUtils.h"
@@ -27,7 +27,6 @@
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/qrCodeUtils.h"
#include "core/models/serverConfig.h"
using namespace amnezia;
using namespace ProtocolUtils;
@@ -208,12 +207,18 @@ ImportController::ImportResult ImportController::extractConfigFromData(const QSt
case ConfigTypes::Amnezia: {
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();
apiConfig[apiDefs::key::vpnKey] = data;
result.config[apiDefs::key::apiConfig] = apiConfig;
}
if (serverConfigUtils::isLegacyApiSubscription(serverConfigUtils::configTypeFromJson(result.config))) {
result.errorCode = ErrorCode::LegacyApiV1NotSupportedError;
result.config = {};
return result;
}
processAmneziaConfig(result.config);
if (!result.config.empty()) {
checkForMaliciousStrings(result.config, result.maliciousWarningText);
@@ -381,18 +386,29 @@ void ImportController::importConfig(const QJsonObject &config)
credentials.secretData = config.value(configKey::password).toString();
if (credentials.isValid() || config.contains(configKey::containers)) {
ServerConfig serverConfig = ServerConfig::fromJson(config);
m_serversRepository->addServer(serverConfig);
m_serversRepository->addServer(QString(), config, serverConfigUtils::configTypeFromJson(config));
emit importFinished();
} else if (config.contains(configKey::configVersion)) {
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);
} else {
QJsonObject configWithCrc = config;
configWithCrc.insert(configKey::crc, crc);
ServerConfig serverConfig = ServerConfig::fromJson(configWithCrc);
m_serversRepository->addServer(serverConfig);
m_serversRepository->addServer(QString(), configWithCrc, serverConfigUtils::configTypeFromJson(configWithCrc));
emit importFinished();
}
} else {

View File

@@ -33,7 +33,6 @@
#include "core/protocols/protocolUtils.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/protocols/awgProtocolConfig.h"
#include "ui/models/protocols/wireguardConfigModel.h"
@@ -129,15 +128,27 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
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)
{
if (!isUpdateDockerContainerRequired(container, oldConfig, newConfig)) {
m_serversRepository->setContainerConfig(serverIndex, container, newConfig);
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
adminConfig->updateContainerConfig(container, newConfig);
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
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);
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
@@ -154,42 +165,51 @@ ErrorCode InstallController::updateContainer(int serverIndex, DockerContainer co
}
if (errorCode == ErrorCode::NoError) {
clearCachedProfile(serverIndex, container);
m_serversRepository->setContainerConfig(serverIndex, container, newConfig);
clearCachedProfile(serverId, container);
adminConfig->updateContainerConfig(container, newConfig);
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
}
return errorCode;
}
void InstallController::clearCachedProfile(int serverIndex, DockerContainer container)
void InstallController::clearCachedProfile(const QString &serverId, DockerContainer container)
{
if (ContainerUtils::containerService(container) == ServiceType::Other) {
return;
}
ContainerConfig containerConfigModel = m_serversRepository->containerConfig(serverIndex, container);
m_serversRepository->clearLastConnectionConfig(serverIndex, container);
emit clientRevocationRequested(serverIndex, containerConfigModel, container);
}
ErrorCode InstallController::validateAndPrepareConfig(int serverIndex)
{
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
if (serverConfigModel.isApiConfig()) {
return ErrorCode::NoError;
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return;
}
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) {
return ErrorCode::NoInstalledContainersError;
}
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
ContainerConfig containerConfig = adminConfig->containerConfig(container);
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
SshSession sshSession;
auto isProtocolConfigExists = [](const ContainerConfig &cfg) {
@@ -198,20 +218,21 @@ ErrorCode InstallController::validateAndPrepareConfig(int serverIndex)
if (!isProtocolConfigExists(containerConfig)) {
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) {
return errorCode;
}
m_serversRepository->setContainerConfig(serverIndex, container, containerConfig);
adminConfig->updateContainerConfig(container, containerConfig);
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
}
return ErrorCode::NoError;
}
void InstallController::validateConfig(int serverIndex)
void InstallController::validateConfig(const QString &serverId)
{
QFuture<ErrorCode> future = QtConcurrent::run([this, serverIndex]() {
return validateAndPrepareConfig(serverIndex);
QFuture<ErrorCode> future = QtConcurrent::run([this, serverId]() {
return validateAndPrepareConfig(serverId);
});
auto *watcher = new QFutureWatcher<ErrorCode>(this);
@@ -230,6 +251,21 @@ void InstallController::validateConfig(int serverIndex)
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)
{
if (!ContainerUtils::isSupportedByCurrentPlatform(container)) {
@@ -257,7 +293,7 @@ ErrorCode InstallController::prepareContainerConfig(DockerContainer container, c
return ErrorCode::NoError;
}
void InstallController::adminAppendRequested(int serverIndex, DockerContainer container,
void InstallController::adminAppendRequested(const QString &serverId, DockerContainer container,
const ContainerConfig &containerConfig, const QString &clientName)
{
if (ContainerUtils::containerService(container) == ServiceType::Other
@@ -266,13 +302,13 @@ void InstallController::adminAppendRequested(int serverIndex, DockerContainer co
}
QString clientId = containerConfig.protocolConfig.clientId();
if (!clientId.isEmpty()) {
emit clientAppendRequested(serverIndex, clientId, clientName, container);
emit clientAppendRequested(serverId, clientId, clientName, container);
}
}
ErrorCode InstallController::processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig,
const ServerCredentials &credentials, SshSession &sshSession,
int serverIndex, const QString &clientName)
const QString &serverId, const QString &clientName)
{
if (ContainerUtils::isSupportedByCurrentPlatform(container)) {
ErrorCode errorCode = prepareContainerConfig(container, credentials, containerConfig, sshSession);
@@ -280,7 +316,7 @@ ErrorCode InstallController::processContainerForAdmin(DockerContainer container,
return errorCode;
}
}
adminAppendRequested(serverIndex, container, containerConfig, clientName);
adminAppendRequested(serverId, container, containerConfig, clientName);
return ErrorCode::NoError;
}
@@ -688,9 +724,16 @@ ErrorCode InstallController::setupServerFirewall(const ServerCredentials &creden
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);
QString script = QString("sudo reboot");
@@ -709,27 +752,38 @@ ErrorCode InstallController::rebootServer(int serverIndex)
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);
ErrorCode errorCode = sshSession.runScript(credentials, amnezia::scriptData(SharedScriptType::remove_all_containers));
if (errorCode == ErrorCode::NoError) {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
serverConfigModel.visit([](auto& arg) {
arg.containers.clear();
arg.defaultContainer = DockerContainer::None;
});
m_serversRepository->editServer(serverIndex, serverConfigModel);
adminConfig->containers.clear();
adminConfig->defaultContainer = DockerContainer::None;
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
}
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);
ErrorCode errorCode = sshSession.runScript(
credentials,
@@ -737,11 +791,10 @@ ErrorCode InstallController::removeContainer(int serverIndex, DockerContainer co
amnezia::genBaseVars(credentials, container, QString(), QString())));
if (errorCode == ErrorCode::NoError) {
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = serverConfigModel.containers();
QMap<DockerContainer, ContainerConfig> containers = adminConfig->containers;
containers.remove(container);
DockerContainer defaultContainer = serverConfigModel.defaultContainer();
DockerContainer defaultContainer = adminConfig->defaultContainer;
if (defaultContainer == container) {
if (containers.isEmpty()) {
defaultContainer = DockerContainer::None;
@@ -749,12 +802,10 @@ ErrorCode InstallController::removeContainer(int serverIndex, DockerContainer co
defaultContainer = containers.begin().key();
}
}
serverConfigModel.visit([&containers, defaultContainer](auto& arg) {
arg.containers = containers;
arg.defaultContainer = defaultContainer;
});
m_serversRepository->editServer(serverIndex, serverConfigModel);
adminConfig->containers = containers;
adminConfig->defaultContainer = defaultContainer;
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
}
return errorCode;
@@ -815,9 +866,16 @@ bool InstallController::isUpdateDockerContainerRequired(DockerContainer containe
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);
QMap<DockerContainer, ContainerConfig> installedContainers;
@@ -826,8 +884,7 @@ ErrorCode InstallController::scanServerForInstalledContainers(int serverIndex)
return errorCode;
}
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = serverConfigModel.containers();
QMap<DockerContainer, ContainerConfig> containers = adminConfig->containers;
bool hasNewContainers = false;
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
@@ -835,29 +892,25 @@ ErrorCode InstallController::scanServerForInstalledContainers(int serverIndex)
if (!containers.contains(iterator.key())) {
ContainerConfig containerConfig = iterator.value();
errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession,
serverIndex, clientName);
serverId, clientName);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
containers.insert(iterator.key(), containerConfig);
hasNewContainers = true;
DockerContainer defaultContainer = serverConfigModel.defaultContainer();
DockerContainer defaultContainer = adminConfig->defaultContainer;
if (defaultContainer == DockerContainer::None
&& ContainerUtils::containerService(iterator.key()) != ServiceType::Other
&& ContainerUtils::isSupportedByCurrentPlatform(iterator.key())) {
serverConfigModel.visit([iterator](auto& arg) {
arg.defaultContainer = iterator.key();
});
adminConfig->defaultContainer = iterator.key();
}
}
}
if (hasNewContainers) {
serverConfigModel.visit([&containers](auto& arg) {
arg.containers = containers;
});
m_serversRepository->editServer(serverIndex, serverConfigModel);
adminConfig->containers = containers;
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
}
return ErrorCode::NoError;
@@ -899,7 +952,7 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials,
preparedContainers.insert(container, containerConfig);
}
SelfHostedServerConfig serverConfig;
SelfHostedAdminServerConfig serverConfig;
serverConfig.hostName = credentials.hostName;
serverConfig.userName = credentials.userName;
serverConfig.password = credentials.secretData;
@@ -912,21 +965,29 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials,
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());
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;
}
ErrorCode InstallController::installContainer(int serverIndex, DockerContainer container, int port,
ErrorCode InstallController::installContainer(const QString &serverId, DockerContainer container, int port,
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);
QMap<DockerContainer, ContainerConfig> installedContainers;
@@ -949,15 +1010,17 @@ ErrorCode InstallController::installContainer(int serverIndex, DockerContainer c
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
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) {
ContainerConfig containerConfig = iterator.value();
errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession,
serverIndex, clientName);
serverId, clientName);
if (errorCode != ErrorCode::NoError) {
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 +1056,15 @@ bool InstallController::isServerAlreadyExists(const ServerCredentials &credentia
{
int serversCount = m_serversRepository->serversCount();
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) {
existingServerIndex = i;
return true;

View File

@@ -33,22 +33,22 @@ public:
~InstallController();
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 removeAllContainers(int serverIndex);
ErrorCode removeContainer(int serverIndex, DockerContainer container);
ErrorCode rebootServer(const QString &serverId);
ErrorCode removeAllContainers(const QString &serverId);
ErrorCode removeContainer(const QString &serverId, DockerContainer container);
ContainerConfig generateConfig(DockerContainer container, int port, TransportProto transportProto);
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 installServer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto,
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 isUpdateDockerContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
@@ -62,11 +62,13 @@ public:
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:
void configValidated(bool isValid);
@@ -74,8 +76,8 @@ signals:
void serverIsBusy(const bool isBusy);
void cancelInstallationRequested();
void clientRevocationRequested(int serverIndex, const ContainerConfig &containerConfig, DockerContainer container);
void clientAppendRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container);
void clientRevocationRequested(const QString &serverId, const ContainerConfig &containerConfig, DockerContainer container);
void clientAppendRequested(const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container);
private:
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container, SshSession &sshSession);
@@ -95,9 +97,9 @@ private:
ErrorCode processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig,
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);
static void updateContainerConfigAfterInstallation(DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut);
@@ -114,4 +116,3 @@ private:
};
#endif // INSTALLCONTROLLER_H

View File

@@ -14,7 +14,6 @@
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h"
using namespace amnezia;
@@ -292,11 +291,18 @@ ErrorCode UsersController::getXrayClients(const DockerContainer container, const
return error;
}
ErrorCode UsersController::updateClients(int serverIndex, const DockerContainer container)
ErrorCode UsersController::updateClients(const QString &serverId, const DockerContainer container)
{
ErrorCode error = ErrorCode::NoError;
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");
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;
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) {
return error;
}
int existingIndex = clientIndexById(clientId, m_clientsTable);
if (existingIndex >= 0) {
return renameClient(serverIndex, existingIndex, clientName, container, true);
return renameClient(serverId, existingIndex, clientName, container, true);
}
QJsonObject client;
@@ -426,7 +439,7 @@ ErrorCode UsersController::appendClient(int serverIndex, const QString &clientId
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)
{
if (row < 0 || row >= m_clientsTable.size()) {
@@ -434,7 +447,14 @@ ErrorCode UsersController::renameClient(int serverIndex, const int row, const QS
}
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 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,
const int serverIndex, SshSession* sshSession, QJsonArray &clientsTable)
SshSession* sshSession, QJsonArray &clientsTable)
{
if (row < 0 || row >= clientsTable.size()) {
return ErrorCode::InternalError;
@@ -689,14 +709,21 @@ ErrorCode UsersController::revokeXray(const int row,
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()) {
return ErrorCode::InternalError;
}
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();
ErrorCode errorCode = ErrorCode::NoError;
@@ -704,7 +731,7 @@ ErrorCode UsersController::revokeClient(int serverIndex, const int index, const
switch(container)
{
case DockerContainer::OpenVpn: {
errorCode = revokeOpenVpn(index, container, credentials, serverIndex, &sshSession, m_clientsTable);
errorCode = revokeOpenVpn(index, container, credentials, &sshSession, m_clientsTable);
break;
}
case DockerContainer::WireGuard:
@@ -724,12 +751,15 @@ ErrorCode UsersController::revokeClient(int serverIndex, const int index, const
}
if (errorCode == ErrorCode::NoError) {
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
ContainerConfig containerCfg = m_serversRepository->containerConfig(serverIndex, container);
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ContainerConfig containerCfg = adminConfig->containerConfig(container);
QString containerClientId = containerCfg.protocolConfig.clientId();
if (!clientId.isEmpty() && !containerClientId.isEmpty() && containerClientId.contains(clientId)) {
emit adminConfigRevoked(serverIndex, container);
emit adminConfigRevoked(serverId, container);
}
emit clientRevoked(index);
@@ -739,13 +769,20 @@ ErrorCode UsersController::revokeClient(int serverIndex, const int index, const
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;
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 = updateClients(serverIndex, container);
errorCode = updateClients(serverId, container);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
@@ -778,7 +815,7 @@ ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig &
switch (container)
{
case DockerContainer::OpenVpn: {
errorCode = revokeOpenVpn(row, container, credentials, serverIndex, &sshSession, m_clientsTable);
errorCode = revokeOpenVpn(row, container, credentials, &sshSession, m_clientsTable);
break;
}
case DockerContainer::WireGuard:
@@ -797,7 +834,7 @@ ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig &
}
if (errorCode == ErrorCode::NoError) {
emit adminConfigRevoked(serverIndex, container);
emit adminConfigRevoked(serverId, container);
emit clientRevoked(row);
emit clientsUpdated(m_clientsTable);
}

View File

@@ -37,21 +37,21 @@ signals:
void clientAdded(const QJsonObject &client);
void clientRenamed(int row, const QString &newName);
void clientRevoked(int row);
void adminConfigRevoked(int serverIndex, DockerContainer container);
void adminConfigRevoked(const QString &serverId, DockerContainer container);
public slots:
ErrorCode updateClients(int serverIndex, const DockerContainer container);
ErrorCode appendClient(int serverIndex, 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 revokeClient(int serverIndex, const int index, const DockerContainer container);
ErrorCode revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container);
ErrorCode updateClients(const QString &serverId, const DockerContainer container);
ErrorCode appendClient(const QString &serverId, const QString &clientId, const QString &clientName, const DockerContainer container);
ErrorCode renameClient(const QString &serverId, const int row, const QString &userName, const DockerContainer container, bool addTimeStamp = false);
ErrorCode revokeClient(const QString &serverId, const int index, const DockerContainer container);
ErrorCode revokeClient(const QString &serverId, const ContainerConfig &containerConfig, const DockerContainer container);
private:
bool isClientExists(const QString &clientId, const QJsonArray &clientsTable);
int clientIndexById(const QString &clientId, const 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);
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, QJsonArray &clientsTable);
@@ -73,4 +73,3 @@ private:
};
#endif // USERSCONTROLLER_H

View File

@@ -1,81 +1,268 @@
#include "serversController.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/api/apiEnums.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/serverConfigUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.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/serverDescription.h"
#if defined(Q_OS_IOS) || defined(MACOS_NE)
#include <AmneziaVPN-Swift.h>
#endif
ServersController::ServersController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository,
QObject *parent)
ServersController::ServersController(SecureServersRepository *serversRepository,
SecureAppSettingsRepository *appSettingsRepository, QObject *parent)
: QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository)
{
recomputeGatewayStacks();
ensureDefaultServerValid();
}
void ServersController::addServer(const ServerConfig &server)
void ServersController::ensureDefaultServerValid()
{
m_serversRepository->addServer(server);
}
void ServersController::editServer(int index, const ServerConfig &server)
{
m_serversRepository->editServer(index, server);
}
void ServersController::removeServer(int index)
{
m_serversRepository->removeServer(index);
}
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());
if (!getServersCount()) {
return;
}
const QString defaultId = getDefaultServerId();
if (!defaultId.isEmpty() && indexOfServerId(defaultId) >= 0) {
return;
}
const QString firstId = getServerId(0);
if (!firstId.isEmpty()) {
setDefaultServer(firstId);
}
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
@@ -83,114 +270,131 @@ int ServersController::getDefaultServerIndex() const
return m_serversRepository->defaultServerIndex();
}
QString ServersController::getDefaultServerId() const
{
return m_serversRepository->defaultServerId();
}
int ServersController::getServersCount() const
{
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);
return serverConfig.getDnsPair(isAmneziaDnsEnabled,
m_appSettingsRepository->primaryDns(),
m_appSettingsRepository->secondaryDns());
}
if (serverId.isEmpty()) {
return {};
}
ServersController::GatewayStacksData ServersController::gatewayStacks() const
{
return m_gatewayStacks;
}
void ServersController::recomputeGatewayStacks()
{
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);
using Kind = serverConfigUtils::ConfigType;
switch (m_serversRepository->serverKind(serverId)) {
case Kind::SelfHostedAdmin: {
if (const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId)) {
if (!cfg->displayName.isEmpty()) {
return cfg->displayName;
}
}
break;
}
m_gatewayStacks = std::move(computed);
if (hasNewTags) {
emit gatewayStacksExpanded();
}
}
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;
case Kind::SelfHostedUser: {
if (const auto cfg = m_serversRepository->selfHostedUserConfig(serverId)) {
if (!cfg->displayName.isEmpty()) {
return cfg->displayName;
}
}
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;
}
bool ServersController::hasInstalledContainers(int serverIndex) const
bool ServersController::hasInstalledContainers(const QString &serverId) const
{
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = serverConfig.containers();
const QMap<DockerContainer, ContainerConfig> containers = getServerContainersMap(serverId);
for (auto it = containers.begin(); it != containers.end(); ++it) {
DockerContainer container = it.key();
if (ContainerUtils::containerService(container) == ServiceType::Vpn) {
@@ -203,3 +407,8 @@ bool ServersController::hasInstalledContainers(int serverIndex) const
return false;
}
bool ServersController::isLegacyApiV1Server(const QString &serverId) const
{
return !serverId.isEmpty()
&& serverConfigUtils::isLegacyApiSubscription(m_serversRepository->serverKind(serverId));
}

View File

@@ -1,11 +1,11 @@
#ifndef SERVERSCONTROLLER_H
#define SERVERSCONTROLLER_H
#include <optional>
#include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QSet>
#include <QVector>
#include <QMap>
#include <QPair>
@@ -17,34 +17,18 @@
#include "core/utils/commonStructs.h"
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h"
#include "core/models/serverDescription.h"
class SshSession;
class InstallController;
using namespace amnezia;
/**
* @brief Core business logic controller for server operations
*
* This controller contains pure business logic for managing servers.
*/
class ServersController : public QObject
{
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:
explicit ServersController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository = nullptr,
@@ -52,44 +36,38 @@ public:
~ServersController() = default;
// Server management
void addServer(const ServerConfig &server);
void editServer(int index, const ServerConfig &server);
void removeServer(int index);
void setDefaultServerIndex(int index);
bool renameServer(const QString &serverId, const QString &name);
void removeServer(const QString &serverId);
void setDefaultServer(const QString &serverId);
// Container management
void setDefaultContainer(int serverIndex, DockerContainer container);
void updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config);
// Cache management
void clearCachedProfile(int serverIndex, DockerContainer container);
void setDefaultContainer(const QString &serverId, DockerContainer container);
// Getters
QJsonArray getServersArray() const;
QVector<ServerConfig> getServers() const;
QVector<ServerDescription> buildServerDescriptions(bool isAmneziaDnsEnabled) const;
int getDefaultServerIndex() const;
QString getDefaultServerId() const;
int getServersCount() const;
ServerConfig getServerConfig(int serverIndex) const;
ServerCredentials getServerCredentials(int serverIndex) const;
ContainerConfig getContainerConfig(int serverIndex, DockerContainer container) const;
QPair<QString, QString> getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const;
GatewayStacksData gatewayStacks() const;
QString getServerId(int serverIndex) const;
int indexOfServerId(const QString &serverId) const;
QString notificationDisplayName(const QString &serverId) const;
std::optional<ApiV2ServerConfig> apiV2Config(const QString &serverId) const;
std::optional<SelfHostedAdminServerConfig> selfHostedAdminConfig(const QString &serverId) 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
bool isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const;
bool hasInstalledContainers(int serverIndex) const;
signals:
void gatewayStacksExpanded();
public slots:
void recomputeGatewayStacks();
bool hasInstalledContainers(const QString &serverId) const;
bool isLegacyApiV1Server(const QString &serverId) const;
private:
void ensureDefaultServerValid();
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
GatewayStacksData m_gatewayStacks;
};
#endif // SERVERSCONTROLLER_H

View File

@@ -179,12 +179,9 @@ QString SettingsController::getAppVersion() const
void SettingsController::clearSettings()
{
int serverCount = m_serversRepository->serversCount();
m_appSettingsRepository->clearSettings();
m_serversRepository->setServersArray(QJsonArray());
m_serversRepository->setDefaultServer(0);
m_serversRepository->clearServers();
emit siteSplitTunnelingRouteModeChanged(RouteMode::VpnOnlyForwardSites);
emit siteSplitTunnelingToggled(false);

View File

@@ -21,14 +21,14 @@ namespace
Logger logger("UpdateController");
#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";
#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";
#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_linux_x64.tar");
const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.tar";
const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN-%1-Linux.run");
const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.run";
#endif
}
@@ -346,36 +346,10 @@ int UpdateController::runMacInstaller(const QString &installerPath)
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
int UpdateController::runLinuxInstaller(const QString &installerPath)
{
// Create temporary directory for extraction
QTemporaryDir extractDir;
extractDir.setAutoRemove(false);
if (!extractDir.isValid()) {
logger.error() << "Failed to create temporary directory";
return -1;
}
logger.info() << "Temporary directory created:" << extractDir.path();
QFile::setPermissions(installerPath, QFile::permissions(installerPath) | QFile::ExeUser);
// 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;
bool success =
QProcess::startDetached("/bin/bash", QStringList() << scriptPath << extractDir.path() << installerPath, extractDir.path(), &pid);
bool success = QProcess::startDetached(installerPath, QStringList(), QString(), &pid);
if (success) {
logger.info() << "Installation process started with PID:" << pid;
@@ -387,5 +361,3 @@ int UpdateController::runLinuxInstaller(const QString &installerPath)
return 0;
}
#endif

View File

@@ -6,7 +6,7 @@
#include <QString>
#include <QDateTime>
#include "core/utils/api/apiEnums.h"
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"

View File

@@ -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

View File

@@ -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

View File

@@ -80,6 +80,9 @@ QJsonObject ApiV2ServerConfig::toJson() const
if (!description.isEmpty()) {
obj[configKey::description] = description;
}
if (!displayName.isEmpty()) {
obj[configKey::displayName] = displayName;
}
obj[configKey::configVersion] = configVersion;
@@ -131,6 +134,7 @@ ApiV2ServerConfig ApiV2ServerConfig::fromJson(const QJsonObject& json)
config.name = json.value(configKey::name).toString();
config.nameOverriddenByUser = json.value(configKey::nameOverriddenByUser).toBool(false);
config.description = json.value(configKey::description).toString();
config.displayName = json.value(configKey::displayName).toString();
config.configVersion = json.value(configKey::configVersion).toInt(2);
config.hostName = json.value(configKey::hostName).toString();
@@ -163,6 +167,10 @@ ApiV2ServerConfig ApiV2ServerConfig::fromJson(const QJsonObject& json)
config.authData = AuthData::fromJson(authDataObj);
}
if (config.displayName.isEmpty()) {
config.displayName = config.name.isEmpty() ? config.description : config.name;
}
return config;
}

View File

@@ -10,7 +10,7 @@
#include "core/models/containerConfig.h"
#include "core/models/api/apiConfig.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/apiConstants.h"
@@ -21,6 +21,7 @@ using namespace ContainerEnumNS;
struct ApiV2ServerConfig {
QString description;
QString displayName;
QString hostName;
QMap<DockerContainer, ContainerConfig> containers;
DockerContainer defaultContainer;

View File

@@ -4,7 +4,7 @@
#include <QJsonObject>
#include <QString>
#include "core/utils/api/apiEnums.h"
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"

View File

@@ -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

View File

@@ -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

View File

@@ -35,6 +35,9 @@ QJsonObject NativeServerConfig::toJson() const
if (!description.isEmpty()) {
obj[configKey::description] = this->description;
}
if (!displayName.isEmpty()) {
obj[configKey::displayName] = displayName;
}
if (!hostName.isEmpty()) {
obj[configKey::hostName] = hostName;
}
@@ -67,6 +70,7 @@ NativeServerConfig NativeServerConfig::fromJson(const QJsonObject& json)
NativeServerConfig 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();
@@ -86,6 +90,10 @@ NativeServerConfig NativeServerConfig::fromJson(const QJsonObject& json)
config.dns1 = json.value(configKey::dns1).toString();
config.dns2 = json.value(configKey::dns2).toString();
if (config.displayName.isEmpty()) {
config.displayName = config.description.isEmpty() ? config.hostName : config.description;
}
return config;
}

View File

@@ -16,6 +16,7 @@ using namespace ContainerEnumNS;
struct NativeServerConfig {
QString description;
QString displayName;
QString hostName;
QMap<DockerContainer, ContainerConfig> containers;
DockerContainer defaultContainer;

View File

@@ -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

View File

@@ -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

View File

@@ -1,53 +1,40 @@
#include "selfHostedServerConfig.h"
#include "selfHostedUserServerConfig.h"
#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/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"
namespace amnezia
{
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;
}
ServerCredentials creds;
creds.hostName = hostName;
creds.userName = userName.value();
creds.secretData = password.value();
creds.port = port.value();
return creds;
return std::nullopt;
}
bool SelfHostedServerConfig::hasContainers() const
bool SelfHostedUserServerConfig::hasContainers() const
{
return !containers.isEmpty();
}
ContainerConfig SelfHostedServerConfig::containerConfig(DockerContainer container) const
ContainerConfig SelfHostedUserServerConfig::containerConfig(DockerContainer container) const
{
if (!containers.contains(container)) {
return ContainerConfig{};
@@ -55,17 +42,20 @@ ContainerConfig SelfHostedServerConfig::containerConfig(DockerContainer containe
return containers.value(container);
}
QJsonObject SelfHostedServerConfig::toJson() const
QJsonObject SelfHostedUserServerConfig::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();
@@ -74,67 +64,51 @@ QJsonObject SelfHostedServerConfig::toJson() const
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.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;
}
SelfHostedServerConfig SelfHostedServerConfig::fromJson(const QJsonObject& json)
SelfHostedUserServerConfig SelfHostedUserServerConfig::fromJson(const QJsonObject &json)
{
SelfHostedServerConfig config;
SelfHostedUserServerConfig 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) {
for (const QJsonValue &val : containersArray) {
QJsonObject containerObj = val.toObject();
ContainerConfig containerConfig = ContainerConfig::fromJson(containerObj);
ContainerConfig cc = ContainerConfig::fromJson(containerObj);
QString containerStr = containerObj.value(configKey::container).toString();
DockerContainer container = ContainerUtils::containerFromString(containerStr);
config.containers.insert(container, containerConfig);
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();
if (json.contains(configKey::userName)) {
config.userName = json.value(configKey::userName).toString();
if (config.displayName.isEmpty()) {
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;
}
} // namespace amnezia

View File

@@ -1,5 +1,5 @@
#ifndef SELFHOSTEDSERVERCONFIG_H
#define SELFHOSTEDSERVERCONFIG_H
#ifndef SELFHOSTEDUSERSERVERCONFIG_H
#define SELFHOSTEDUSERSERVERCONFIG_H
#include <QJsonObject>
#include <QMap>
@@ -9,8 +9,6 @@
#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
@@ -18,28 +16,24 @@ namespace amnezia
using namespace ContainerEnumNS;
struct SelfHostedServerConfig {
struct SelfHostedUserServerConfig {
QString description;
QString displayName;
QString hostName;
QMap<DockerContainer, ContainerConfig> containers;
DockerContainer defaultContainer;
QString dns1;
QString dns2;
std::optional<QString> userName;
std::optional<QString> password;
std::optional<int> port;
bool hasCredentials() const;
bool isReadOnly() const;
std::optional<ServerCredentials> credentials() const;
bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const;
QJsonObject toJson() const;
static SelfHostedServerConfig fromJson(const QJsonObject& json);
static SelfHostedUserServerConfig fromJson(const QJsonObject &json);
};
} // namespace amnezia
#endif // SELFHOSTEDSERVERCONFIG_H
#endif // SELFHOSTEDUSERSERVERCONFIG_H

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

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

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

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

View File

@@ -2,15 +2,16 @@
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QUuid>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.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/apiConstants.h"
#include "core/models/serverConfig.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/networkUtilities.h"
using namespace amnezia;

View File

@@ -1,26 +1,44 @@
#include "secureServersRepository.h"
#include <QJsonDocument>
#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/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/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)
{
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();
loadFromStorage();
persistDefaultServerFields();
}
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);
}
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()
{
QJsonArray arr;
for (const ServerConfig &cfg : m_servers) {
arr.append(cfg.toJson());
QJsonArray serversArray;
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()
{
m_servers.clear();
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();
loadFromStorage();
}
void SecureServersRepository::setServersArray(const QJsonArray &servers)
void SecureServersRepository::clearServers()
{
m_servers.clear();
for (const QJsonValue &val : servers) {
m_servers.append(ServerConfig::fromJson(val.toObject()));
}
clearServerStateMaps();
m_defaultServerId.clear();
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();
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;
}
m_servers.replace(index, server);
syncToStorage();
emit serverEdited(index, server);
}
void SecureServersRepository::removeServer(int index)
{
if (index < 0 || index >= m_servers.size()) {
if (!m_serverJsonById.contains(serverId)) {
return;
}
int defaultIndex = m_defaultServerIndex;
m_servers.removeAt(index);
if (defaultIndex == index) {
setDefaultServer(0);
} else if (defaultIndex > index) {
setDefaultServer(defaultIndex - 1);
const QJsonObject oldJson = m_serverJsonById.value(serverId);
const serverConfigUtils::ConfigType oldKind = serverConfigUtils::configTypeFromJson(withoutStorageServerId(oldJson));
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()) {
setDefaultServer(0);
const QString previousDefaultId = m_defaultServerId;
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();
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()) {
return SelfHostedServerConfig{};
const auto it = m_serverJsonById.constFind(serverId);
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
{
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
{
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;
}
if (m_servers.size() > 0 && index >= m_servers.size()) {
if (!m_serverJsonById.contains(serverId)) {
return;
}
if (m_servers.isEmpty() && index != 0) {
if (indexOfServerId(serverId) < 0) {
return;
}
if (m_defaultServerIndex == index) {
if (m_defaultServerId == serverId) {
return;
}
m_defaultServerIndex = index;
setValue("Servers/defaultServerIndex", index);
emit defaultServerChanged(index);
}
void SecureServersRepository::setDefaultContainer(int serverIndex, DockerContainer container)
{
ServerConfig config = server(serverIndex);
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;
m_defaultServerId = serverId;
persistDefaultServerFields();
emit defaultServerChanged(m_defaultServerId);
}

View File

@@ -1,14 +1,20 @@
#ifndef SECURESERVERSREPOSITORY_H
#define SECURESERVERSREPOSITORY_H
#include <QHash>
#include <QJsonObject>
#include <QObject>
#include <QVector>
#include <QJsonArray>
#include <QJsonDocument>
#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/utils/serverConfigUtils.h"
#include "secureQSettings.h"
using namespace amnezia;
@@ -18,47 +24,57 @@ class SecureServersRepository : public QObject
Q_OBJECT
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 indexOfServerId(const QString &serverId) const;
QString serverIdAt(int index) const;
QVector<QString> orderedServerIds() const;
int defaultServerIndex() const;
void setDefaultServer(int index);
QString defaultServerId() const;
void setDefaultServer(const QString &serverId);
void setDefaultContainer(int serverIndex, DockerContainer container);
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 clearServers();
void invalidateCache();
signals:
void serverAdded(ServerConfig config);
void serverEdited(int index, ServerConfig config);
void serverRemoved(int index);
void defaultServerChanged(int index);
void serverAdded(const QString &serverId);
void serverEdited(const QString &serverId);
void serverRemoved(const QString &serverId, int removedIndex);
void defaultServerChanged(const QString &defaultServerId);
private:
void loadFromStorage();
void updateDefaultServerFromStorage();
void persistDefaultServerFields();
QString normalizedOrGeneratedServerId(const QString &candidateId) const;
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);
SecureQSettings* m_settings;
QVector<ServerConfig> m_servers;
int m_defaultServerIndex = 0;
void clearServerStateMaps();
SecureQSettings *m_settings;
QHash<QString, QJsonObject> m_serverJsonById;
QVector<QString> m_orderedServerIds;
QString m_defaultServerId;
};
#endif // SECURESERVERSREPOSITORY_H

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

View File

@@ -1,6 +1,8 @@
#include "apiUtils.h"
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/configKeys.h"
#include <QLatin1Char>
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>
@@ -75,63 +77,6 @@ bool apiUtils::isSubscriptionExpiringSoon(const QString &subscriptionEndDate, in
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,
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
const QByteArray &responseBody)
@@ -197,14 +142,14 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
{
static const QSet<apiDefs::ConfigType> premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2,
apiDefs::ConfigType::ExternalPremium, apiDefs::ConfigType::ExternalTrial };
return premiumTypes.contains(getConfigType(serverConfigObject));
static const QSet<serverConfigUtils::ConfigType> premiumTypes = { serverConfigUtils::ConfigType::AmneziaPremiumV1, serverConfigUtils::ConfigType::AmneziaPremiumV2,
serverConfigUtils::ConfigType::ExternalPremium };
return premiumTypes.contains(serverConfigUtils::configTypeFromJson(serverConfigObject));
}
QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
{
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) {
if (serverConfigUtils::configTypeFromJson(serverConfigObject) != serverConfigUtils::ConfigType::AmneziaPremiumV1) {
return {};
}
@@ -242,9 +187,8 @@ QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
{
auto configType = apiUtils::getConfigType(serverConfigObject);
if (configType != apiDefs::ConfigType::AmneziaPremiumV2 && configType != apiDefs::ConfigType::ExternalPremium
&& configType != apiDefs::ConfigType::ExternalTrial) {
auto configType = serverConfigUtils::configTypeFromJson(serverConfigObject);
if (configType != serverConfigUtils::ConfigType::AmneziaPremiumV2 && configType != serverConfigUtils::ConfigType::ExternalPremium) {
return {};
}
@@ -289,3 +233,18 @@ QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
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();
}

View File

@@ -4,7 +4,7 @@
#include <QNetworkReply>
#include <QObject>
#include "core/utils/api/apiEnums.h"
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/errorCodes.h"
@@ -13,23 +13,21 @@
namespace apiUtils
{
bool isServerFromApi(const QJsonObject &serverConfigObject);
bool isSubscriptionExpired(const QString &subscriptionEndDate);
bool isSubscriptionExpiringSoon(const QString &subscriptionEndDate, int withinDays = 30);
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,
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
const QByteArray &responseBody);
QString getPremiumV1VpnKey(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

View File

@@ -3,9 +3,9 @@
namespace apiDefs
{
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
}
constexpr int requestTimeoutMsecs = 12 * 1000; // 12 secs
} // namespace apiDefs
#endif // APICONSTANTS_H

View File

@@ -2,7 +2,6 @@
#define APIKEYS_H
#include <QLatin1String>
#include "core/utils/api/apiEnums.h"
namespace apiDefs
{
@@ -82,7 +81,7 @@ namespace apiDefs
constexpr QLatin1String expiresAt("expires_at");
constexpr QLatin1String isConnectEvent("is_connect_event");
constexpr QLatin1String certificate("certificate");
}
}
} // namespace key
} // namespace apiDefs
#endif // APIKEYS_H

View File

@@ -18,6 +18,7 @@ namespace amnezia
constexpr QLatin1String serverIndex("serverIndex");
constexpr QLatin1String description("description");
constexpr QLatin1String displayName("displayName");
constexpr QLatin1String name("name");
constexpr QLatin1String cert("cert");
constexpr QLatin1String accessToken("api_key");
@@ -121,6 +122,8 @@ namespace amnezia
constexpr QLatin1String latestHandshake("latestHandshake");
constexpr QLatin1String dataReceived("dataReceived");
constexpr QLatin1String dataSent("dataSent");
constexpr QLatin1String storageServerId("storageServerId");
}
}

View File

@@ -71,10 +71,11 @@ namespace amnezia
// import and install errors
ImportInvalidConfigError = 900,
ImportBackupFileUseRestoreInstead = 903,
RestoreBackupInvalidError = 904,
ImportOpenConfigError = 901,
NoInstalledContainersError = 902,
ImportBackupFileUseRestoreInstead = 903,
RestoreBackupInvalidError = 904,
LegacyApiV1NotSupportedError = 905,
// Android errors
AndroidError = 1000,

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::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::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::NoInstalledContainersError): errorMessage = QObject::tr("VPN Protocols is not installed.\n Please install VPN container at first"); break;

View File

@@ -76,7 +76,6 @@ QString amnezia::scriptName(ProtocolScriptType type)
QString amnezia::scriptName(ClientScriptType type)
{
switch (type) {
case ClientScriptType::linux_installer: return QLatin1String("linux_installer.sh");
case ClientScriptType::mac_installer: return QLatin1String("mac_installer.sh");
default: return QString();
}

View File

@@ -43,7 +43,6 @@ enum ProtocolScriptType {
enum ClientScriptType {
// Client-side scripts
linux_installer,
mac_installer
};

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

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

View File

@@ -977,7 +977,9 @@ bool IosController::shareText(const QStringList& filesToSend) {
}
#if !MACOS_NE
UIViewController *qtController = getViewController();
if (!qtController) return;
if (!qtController) {
return false;
}
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
#endif

View File

@@ -80,6 +80,15 @@ target_link_libraries(test_server_edge_cases PRIVATE
test_common
)
add_executable(test_gateway_stacks
testGatewayStacks.cpp
)
target_link_libraries(test_gateway_stacks PRIVATE
Qt6::Test
test_common
)
add_executable(test_signal_order
testSignalOrder.cpp
)
@@ -98,15 +107,6 @@ target_link_libraries(test_servers_model_sync PRIVATE
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
testComplexOperations.cpp
)
@@ -212,9 +212,9 @@ add_test(NAME MultipleImportsTest COMMAND test_multiple_imports)
add_test(NAME ServerEditTest COMMAND test_server_edit)
add_test(NAME DefaultServerChangeTest COMMAND test_default_server_change)
add_test(NAME ServerEdgeCasesTest COMMAND test_server_edge_cases)
add_test(NAME GatewayStacksTest COMMAND test_gateway_stacks)
add_test(NAME SignalOrderTest COMMAND test_signal_order)
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 SettingsSignalsTest COMMAND test_settings_signals)
add_test(NAME UiServersModelAndControllerTest COMMAND test_ui_servers_model_and_controller)

View File

@@ -7,7 +7,7 @@
#include <QUuid>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "secureQSettings.h"
#include "vpnConnection.h"
@@ -45,7 +45,7 @@ private slots:
{
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}

View File

@@ -7,7 +7,7 @@
#include <QUuid>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "secureQSettings.h"
#include "vpnConnection.h"
@@ -45,7 +45,7 @@ private slots:
{
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}

View File

@@ -8,7 +8,7 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/utils/constants/configKeys.h"
#include "vpnConnection.h"
#include "secureQSettings.h"
@@ -123,8 +123,8 @@ private slots:
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");
int serverIndex = m_coreController->m_serversRepository->defaultServerIndex();
auto exportResult = m_coreController->m_exportController->generateFullAccessConfig(serverIndex);
const QString serverId = m_coreController->m_serversRepository->defaultServerId();
auto exportResult = m_coreController->m_exportController->generateFullAccessConfig(serverId);
QVERIFY2(exportResult.errorCode == ErrorCode::NoError, "Export should succeed");
QVERIFY2(!exportResult.config.isEmpty(), "Exported config should not be empty");

View File

@@ -5,7 +5,8 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "tests/testServerRepositoryHelpers.h"
#include "vpnConnection.h"
#include "secureQSettings.h"
@@ -43,7 +44,7 @@ private slots:
void init() {
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
@@ -71,35 +72,33 @@ private slots:
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 3, "Should have 3 servers");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Default should be index 2");
ServerConfig server0 = m_coreController->m_serversController->getServerConfig(0);
server0.visit([](auto& arg) {
arg.description = "Edited First Server";
});
m_coreController->m_serversController->editServer(0, server0);
amnezia::test::setServerDescription(m_coreController->m_serversRepository,
m_coreController->m_serversController->getServerId(0),
QStringLiteral("Edited First Server"));
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");
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(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)");
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(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default should be index 0");
ServerConfig server0After = m_coreController->m_serversController->getServerConfig(0);
server0After.visit([](auto& arg) {
arg.description = "Final Edited Server";
});
m_coreController->m_serversController->editServer(0, server0After);
amnezia::test::setServerDescription(m_coreController->m_serversRepository,
m_coreController->m_serversController->getServerId(0),
QStringLiteral("Final Edited Server"));
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(m_coreController->m_serversRepository->serversCount() == 2, "Final servers count should be 2");

View File

@@ -5,7 +5,8 @@
#include <QTest>
#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 "vpnConnection.h"
#include "secureQSettings.h"
@@ -45,7 +46,7 @@ private slots:
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
@@ -66,9 +67,10 @@ private slots:
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.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");
if (m_coreController->m_serversModel) {
@@ -76,9 +78,10 @@ private slots:
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.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");
}
@@ -100,28 +103,28 @@ private slots:
QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged);
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(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)");
ServerConfig remainingServer1 = m_coreController->m_serversRepository->server(0);
ServerConfig remainingServer2 = m_coreController->m_serversRepository->server(1);
QString desc1 = remainingServer1.description();
QString desc2 = remainingServer2.description();
QString desc1 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(0));
QString desc2 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(1));
QVERIFY2(desc1 == "Xray Server", "First remaining server should be Xray");
QVERIFY2(desc2 == "WireGuard Server", "Second remaining server should be WireGuard");
defaultServerChangedSpy.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(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)");
ServerConfig lastServer = m_coreController->m_serversRepository->server(0);
QString lastDesc = lastServer.description();
QString lastDesc = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(0));
QVERIFY2(lastDesc == "WireGuard Server", "Last server should be WireGuard");
}
};

View File

@@ -5,7 +5,9 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "ui/controllers/serversUiController.h"
#include "ui/models/serversModel.h"
#include "vpnConnection.h"
#include "secureQSettings.h"
@@ -42,37 +44,41 @@ private slots:
void init() {
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
void testGatewayStacksRecomputeOnServerOperations() {
QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG");
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);
QSignalSpy gatewayServersChangedSpy(m_coreController->m_serversUiController,
&ServersUiController::hasServersFromGatewayApiChanged);
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");
QVERIFY2(!m_coreController->m_serversUiController->hasServersFromGatewayApi(),
"Self-hosted imports should not be treated as gateway API servers");
ServerConfig serverConfig = m_coreController->m_serversController->getServerConfig(0);
serverConfig.visit([](auto& arg) {
arg.description = "Edited Server";
});
m_coreController->m_serversController->editServer(0, serverConfig);
const QString serverId = m_coreController->m_serversController->getServerId(0);
QVERIFY2(m_coreController->m_serversController->renameServer(serverId, QStringLiteral("Edited Server")),
"Server rename should succeed");
QVERIFY2(serverEditedSpy.count() == 1, "serverEdited signal should be emitted");
m_coreController->m_serversController->removeServer(0);
m_coreController->m_serversController->removeServer(serverId);
QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted");
QVERIFY2(m_coreController->m_serversController->gatewayStacks().isEmpty(), "Gateway stacks should remain empty");
QVERIFY2(!m_coreController->m_serversUiController->hasServersFromGatewayApi(),
"Gateway API server state should remain empty");
QVERIFY2(gatewayServersChangedSpy.count() == 0,
"Self-hosted server operations should not emit gateway API state changes");
}
};

View File

@@ -7,7 +7,7 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "secureQSettings.h"
#include "vpnConnection.h"
@@ -48,11 +48,12 @@ private slots:
void cleanupTestCase()
{
int serverIndex = m_coreController->m_serversRepository->defaultServerIndex();
const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex);
for (int containerIndex = 1; containerIndex < 7; ++containerIndex)
m_coreController->m_installUiController->clearCachedProfile(serverIndex, containerIndex);
m_coreController->m_installUiController->clearCachedProfile(serverId, containerIndex);
m_coreController->m_serversController->removeServer(serverIndex);
m_coreController->m_serversController->removeServer(serverId);
qDebug() << "SERVER REMOVED\n";
@@ -65,13 +66,14 @@ private slots:
{
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
void testMultipleExports()
{
int serverIndex = m_coreController->m_serversRepository->defaultServerIndex();
const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex);
QString clientName = "MultipleExports Test Client";
@@ -93,7 +95,7 @@ private slots:
continue;
}
auto exportResult = m_coreController->m_exportController->generateConnectionConfig(serverIndex, containerIndex, clientName);
auto exportResult = m_coreController->m_exportController->generateConnectionConfig(serverId, containerIndex, clientName);
QVERIFY2(exportResult.errorCode == ErrorCode::NoError,
QStringLiteral("\n%1: Export should succeed").arg(containerName).toUtf8().constData());
@@ -105,13 +107,14 @@ private slots:
void testMultipleExportsNative()
{
int serverIndex = m_coreController->m_serversRepository->defaultServerIndex();
const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex);
QString clientName = "MultipleExports Test Client";
auto exportResultAwg = m_coreController->m_exportController->generateAwgConfig(serverIndex, DockerContainer::Awg2, clientName);
auto exportResultWg = m_coreController->m_exportController->generateWireGuardConfig(serverIndex, clientName);
auto exportResultOvpn = m_coreController->m_exportController->generateOpenVpnConfig(serverIndex, clientName);
auto exportResultXray = m_coreController->m_exportController->generateXrayConfig(serverIndex, clientName);
auto exportResultAwg = m_coreController->m_exportController->generateAwgConfig(serverId, DockerContainer::Awg2, clientName);
auto exportResultWg = m_coreController->m_exportController->generateWireGuardConfig(serverId, clientName);
auto exportResultOvpn = m_coreController->m_exportController->generateOpenVpnConfig(serverId, clientName);
auto exportResultXray = m_coreController->m_exportController->generateXrayConfig(serverId, clientName);
QVERIFY2(exportResultAwg.errorCode == ErrorCode::NoError, "\nAwg (native): Export should succeed");
QVERIFY2(exportResultWg.errorCode == ErrorCode::NoError, "\nWg (native): Export should succeed");

View File

@@ -6,7 +6,8 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "tests/testServerRepositoryHelpers.h"
#include "vpnConnection.h"
#include "secureQSettings.h"
@@ -46,7 +47,7 @@ private slots:
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
@@ -76,8 +77,8 @@ private slots:
}
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "First server should be default");
ServerConfig server1 = m_coreController->m_serversRepository->server(0);
QString desc1 = server1.description();
QString desc1 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(0));
QVERIFY2(desc1 == "AWG Server", "First server description should match");
if (m_coreController->m_serversModel) {
@@ -98,8 +99,8 @@ private slots:
}
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Second server should be default");
ServerConfig server2 = m_coreController->m_serversRepository->server(1);
QString desc2 = server2.description();
QString desc2 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(1));
QVERIFY2(desc2 == "Xray Server", "Second server description should match");
if (m_coreController->m_serversModel) {
@@ -120,8 +121,8 @@ private slots:
}
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Third server should be default");
ServerConfig server3 = m_coreController->m_serversRepository->server(2);
QString desc3 = server3.description();
QString desc3 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(2));
QVERIFY2(desc3 == "WireGuard Server", "Third server description should match");
if (m_coreController->m_serversModel) {
@@ -153,25 +154,25 @@ private slots:
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");
ServerConfig server0 = m_coreController->m_serversRepository->server(0);
ServerConfig server1 = m_coreController->m_serversRepository->server(1);
QString desc0 = server0.description();
QString desc1 = server1.description();
QString desc0 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(0));
QString desc1 = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(1));
QVERIFY2(desc0 == "AWG Server", "First server description should match");
QVERIFY2(desc1 == "Xray Server", "Second server description should match");
defaultServerChangedSpy.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.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->defaultServerIndex() == 0, "After removing first server, default index should be 0");
ServerConfig remainingServer = m_coreController->m_serversRepository->server(0);
QString remainingDesc = remainingServer.description();
QString remainingDesc = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(0));
QVERIFY2(remainingDesc == "Xray Server", "Remaining server should be Xray Server");
if (m_coreController->m_serversModel) {
@@ -183,10 +184,10 @@ private slots:
defaultServerChangedSpy.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.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->defaultServerIndex() == 0, "After removing last server, default index should be 0");

View File

@@ -7,8 +7,8 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/selfhosted/selfHostedServerConfig.h"
#include "core/models/serverDescription.h"
#include "core/models/selfhosted/selfHostedAdminServerConfig.h"
#include "core/models/containerConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/models/protocols/dnsProtocolConfig.h"
@@ -66,21 +66,24 @@ private:
qDebug() << "SSH connection successful. Output:" << sshOutput;
}
void verifyAdminAccess(int serverIndex) {
ServerConfig server = m_coreController->m_serversRepository->server(serverIndex);
const SelfHostedServerConfig* selfHosted = server.as<SelfHostedServerConfig>();
QVERIFY2(selfHosted != nullptr, "Server config should be SelfHostedServerConfig");
void verifyAdminAccess(int serverIndex)
{
const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex);
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)");
QVERIFY2(selfHosted->userName.has_value() && !selfHosted->userName.value().isEmpty(),
QVERIFY2(!selfHosted.userName.isEmpty(),
"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");
QVERIFY2(!selfHosted->isReadOnly(),
QVERIFY2(!selfHosted.isReadOnly(),
"Server should not be read-only (should have admin access)");
if (m_coreController->m_serversModel) {
@@ -149,7 +152,7 @@ private slots:
void init() {
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
@@ -183,10 +186,10 @@ private slots:
int serverIndex = m_coreController->m_serversRepository->serversCount() - 1;
qDebug() << "Server with Awg container added at index:" << serverIndex;
ServerConfig serverAfterAwg = m_coreController->m_serversRepository->server(serverIndex);
QVERIFY2(serverAfterAwg.isSelfHosted(), "Server should be self-hosted");
const SelfHostedServerConfig* selfHostedAfterAwg = serverAfterAwg.as<SelfHostedServerConfig>();
QVERIFY2(selfHostedAfterAwg != nullptr, "Server config should be SelfHostedServerConfig");
const auto adminAfterAwg = m_coreController->m_serversRepository->selfHostedAdminConfig(
m_coreController->m_serversRepository->serverIdAt(serverIndex));
QVERIFY2(adminAfterAwg.has_value(), "Server should be self-hosted (admin)");
const SelfHostedAdminServerConfig *selfHostedAfterAwg = &(*adminAfterAwg);
QVERIFY2(selfHostedAfterAwg->defaultContainer == DockerContainer::Awg, "Default container should be Awg");
QVERIFY2(selfHostedAfterAwg->containers.contains(DockerContainer::Awg), "Server should have Awg container");
@@ -204,8 +207,9 @@ private slots:
TransportProto dnsTransportProto = TransportProto::Udp;
bool wasDnsInstalled = false;
const QString serverIdForOps = m_coreController->m_serversRepository->serverIdAt(serverIndex);
ErrorCode installContainerError = m_coreController->m_installController->installContainer(
serverIndex, DockerContainer::Dns, dnsPort, dnsTransportProto, wasDnsInstalled);
serverIdForOps, DockerContainer::Dns, dnsPort, dnsTransportProto, wasDnsInstalled);
QVERIFY2(installContainerError == ErrorCode::NoError,
QString("installContainer for Dns should succeed. Error: %1")
@@ -213,9 +217,10 @@ private slots:
.toUtf8().constData());
qDebug() << "Dns container installed:" << wasDnsInstalled;
ServerConfig serverAfterDns = m_coreController->m_serversRepository->server(serverIndex);
const SelfHostedServerConfig* selfHostedAfterDns = serverAfterDns.as<SelfHostedServerConfig>();
QVERIFY2(selfHostedAfterDns != nullptr, "Server config should be SelfHostedServerConfig");
const auto adminAfterDns = m_coreController->m_serversRepository->selfHostedAdminConfig(
m_coreController->m_serversRepository->serverIdAt(serverIndex));
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::Dns), "Server should have Dns container");
QVERIFY2(selfHostedAfterDns->containers.size() == 2,
@@ -248,16 +253,18 @@ private slots:
verifySshConnection(credentials);
SelfHostedServerConfig serverConfig;
SelfHostedAdminServerConfig serverConfig;
serverConfig.hostName = credentials.hostName;
serverConfig.userName = credentials.userName;
serverConfig.password = credentials.secretData;
serverConfig.port = credentials.port;
serverConfig.description = m_coreController->m_appSettingsRepository->nextAvailableServerName();
serverConfig.displayName = serverConfig.description.isEmpty() ? serverConfig.hostName : serverConfig.description;
serverConfig.defaultContainer = DockerContainer::None;
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(m_coreController->m_serversRepository->serversCount() > 0, "Server should be added");
@@ -265,23 +272,25 @@ private slots:
int serverIndex = m_coreController->m_serversRepository->serversCount() - 1;
qDebug() << "Empty server added at index:" << serverIndex;
ServerConfig addedServer = m_coreController->m_serversRepository->server(serverIndex);
QVERIFY2(addedServer.isSelfHosted(), "Added server should be self-hosted");
const SelfHostedServerConfig* selfHosted = addedServer.as<SelfHostedServerConfig>();
QVERIFY2(selfHosted != nullptr, "Server config should be SelfHostedServerConfig");
const auto addedAdmin = m_coreController->m_serversRepository->selfHostedAdminConfig(
m_coreController->m_serversRepository->serverIdAt(serverIndex));
QVERIFY2(addedAdmin.has_value(), "Added server should be self-hosted admin");
const SelfHostedAdminServerConfig *selfHosted = &(*addedAdmin);
QVERIFY2(selfHosted->containers.isEmpty(), "Server should have no containers initially");
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,
QString("Server scan should succeed. Error: %1")
.arg(static_cast<int>(scanError))
.toUtf8().constData());
qDebug() << "Server scan completed successfully";
ServerConfig scannedServer = m_coreController->m_serversRepository->server(serverIndex);
const SelfHostedServerConfig* scannedSelfHosted = scannedServer.as<SelfHostedServerConfig>();
QVERIFY2(scannedSelfHosted != nullptr, "Scanned server config should be SelfHostedServerConfig");
const auto scannedAdmin = m_coreController->m_serversRepository->selfHostedAdminConfig(
m_coreController->m_serversRepository->serverIdAt(serverIndex));
QVERIFY2(scannedAdmin.has_value(), "Scanned server config should be SelfHostedAdminServerConfig");
const SelfHostedAdminServerConfig *scannedSelfHosted = &(*scannedAdmin);
QMap<DockerContainer, ContainerConfig> containers = scannedSelfHosted->containers;
int containersCount = containers.size();
@@ -342,24 +351,27 @@ private slots:
int serverIndex = m_coreController->m_serversRepository->serversCount() - 1;
qDebug() << "Server with Awg container added at index:" << serverIndex;
ServerConfig serverBeforeRemoval = m_coreController->m_serversRepository->server(serverIndex);
const SelfHostedServerConfig* selfHostedBeforeRemoval = serverBeforeRemoval.as<SelfHostedServerConfig>();
QVERIFY2(selfHostedBeforeRemoval != nullptr, "Server config should be SelfHostedServerConfig");
const auto adminBeforeRemoval = m_coreController->m_serversRepository->selfHostedAdminConfig(
m_coreController->m_serversRepository->serverIdAt(serverIndex));
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->defaultContainer != DockerContainer::None, "Server should have default container before removal");
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,
QString("removeAllContainers should succeed. Error: %1")
.arg(static_cast<int>(removeError))
.toUtf8().constData());
qDebug() << "All containers removed successfully";
ServerConfig serverAfterRemoval = m_coreController->m_serversRepository->server(serverIndex);
const SelfHostedServerConfig* selfHostedAfterRemoval = serverAfterRemoval.as<SelfHostedServerConfig>();
QVERIFY2(selfHostedAfterRemoval != nullptr, "Server config should be SelfHostedServerConfig");
const auto adminAfterRemoval = m_coreController->m_serversRepository->selfHostedAdminConfig(
m_coreController->m_serversRepository->serverIdAt(serverIndex));
QVERIFY2(adminAfterRemoval.has_value(), "Server config should be SelfHostedAdminServerConfig");
const SelfHostedAdminServerConfig *selfHostedAfterRemoval = &(*adminAfterRemoval);
QVERIFY2(selfHostedAfterRemoval->containers.isEmpty(),
"Server should have no containers after removal");

View File

@@ -7,7 +7,7 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "core/utils/serialization/serialization.h"
#include "core/utils/utilities.h"
#include "secureQSettings.h"
@@ -95,11 +95,12 @@ private slots:
void cleanupTestCase()
{
int serverIndex = m_coreController->m_serversRepository->defaultServerIndex();
const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex);
for (int containerIndex = 1; containerIndex < 7; ++containerIndex)
m_coreController->m_installUiController->clearCachedProfile(serverIndex, containerIndex);
m_coreController->m_installUiController->clearCachedProfile(serverId, containerIndex);
m_coreController->m_serversController->removeServer(serverIndex);
m_coreController->m_serversController->removeServer(serverId);
qDebug() << "SERVER REMOVED\n";
@@ -112,17 +113,18 @@ private slots:
{
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
void testVless()
{
int serverIndex = m_coreController->m_serversRepository->defaultServerIndex();
const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex);
QString clientName = "Test Client (vless (de)serialization)";
ExportController::ExportResult exportResult = m_coreController->m_exportController->generateXrayConfig(serverIndex, clientName);
ExportController::ExportResult exportResult = m_coreController->m_exportController->generateXrayConfig(serverId, clientName);
ImportController::ImportResult importResult;

View File

@@ -1,13 +1,17 @@
#include <QJsonDocument>
#include <QTest>
#include <QJsonObject>
#include <QUuid>
#include <QSignalSpy>
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/repositories/secureServersRepository.h"
#include "core/models/serverDescription.h"
#include "core/models/selfhosted/selfHostedAdminServerConfig.h"
#include "vpnConnection.h"
#include "secureQSettings.h"
#include "core/utils/serverConfigUtils.h"
using namespace amnezia;
@@ -44,7 +48,7 @@ private slots:
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
@@ -60,27 +64,32 @@ private slots:
QSignalSpy serverEditedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited);
QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged);
m_coreController->m_serversController->removeServer(-1);
m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(-1));
QVERIFY2(serverRemovedSpy.count() == 0, "serverRemoved should NOT be emitted for invalid index");
m_coreController->m_serversController->removeServer(10);
m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(10));
QVERIFY2(serverRemovedSpy.count() == 0, "serverRemoved should NOT be emitted for invalid index");
m_coreController->m_serversController->removeServer(100);
m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(100));
QVERIFY2(serverRemovedSpy.count() == 0, "serverRemoved should NOT be emitted for invalid index");
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 1, "Server count should remain 1");
ServerConfig serverConfig = m_coreController->m_serversController->getServerConfig(0);
m_coreController->m_serversController->editServer(-1, serverConfig);
const QString validServerId = m_coreController->m_serversController->getServerId(0);
const serverConfigUtils::ConfigType editKind =
m_coreController->m_serversRepository->serverKind(validServerId);
m_coreController->m_serversRepository->editServer(m_coreController->m_serversController->getServerId(-1),
QJsonObject(), editKind);
QVERIFY2(serverEditedSpy.count() == 0, "serverEdited should NOT be emitted for invalid index");
m_coreController->m_serversController->editServer(10, serverConfig);
m_coreController->m_serversRepository->editServer(m_coreController->m_serversController->getServerId(10),
QJsonObject(), editKind);
QVERIFY2(serverEditedSpy.count() == 0, "serverEdited should NOT be emitted for invalid index");
m_coreController->m_serversController->setDefaultServerIndex(-1);
m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(-1));
QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged should NOT be emitted for invalid index");
m_coreController->m_serversController->setDefaultServerIndex(10);
m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(10));
QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged should NOT be emitted for invalid index");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default server index should remain 0");
}
@@ -92,14 +101,15 @@ private slots:
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 0, "Should start with 0 servers");
ServerConfig emptyConfig = SelfHostedServerConfig{};
m_coreController->m_serversController->removeServer(0);
m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0));
QVERIFY2(serverRemovedSpy.count() == 0, "serverRemoved should NOT be emitted for empty repository");
m_coreController->m_serversController->editServer(0, emptyConfig);
m_coreController->m_serversRepository->editServer(m_coreController->m_serversController->getServerId(0),
SelfHostedAdminServerConfig {}.toJson(),
serverConfigUtils::ConfigType::SelfHostedAdmin);
QVERIFY2(serverEditedSpy.count() == 0, "serverEdited should NOT be emitted for empty repository");
m_coreController->m_serversController->setDefaultServerIndex(0);
m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(0));
QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged should NOT be emitted for empty repository");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default server index should be 0 for empty repository");

View File

@@ -5,7 +5,8 @@
#include <QTest>
#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 "vpnConnection.h"
#include "secureQSettings.h"
@@ -45,7 +46,7 @@ private slots:
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
@@ -58,20 +59,17 @@ private slots:
QVERIFY2(importFinishedSpy.count() == 1, "Import should succeed");
QSignalSpy serverEditedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited);
QSignalSpy gatewayStacksExpandedSpy(m_coreController->m_serversController, &ServersController::gatewayStacksExpanded);
ServerConfig serverConfig = m_coreController->m_serversController->getServerConfig(0);
serverConfig.visit([](auto& arg) {
arg.description = "Edited AWG Server";
});
m_coreController->m_serversController->editServer(0, serverConfig);
amnezia::test::setServerDescription(m_coreController->m_serversRepository,
m_coreController->m_serversController->getServerId(0),
QStringLiteral("Edited AWG Server"));
QVERIFY2(serverEditedSpy.count() == 1, "serverEdited signal should be emitted");
QVERIFY2(serverEditedSpy.at(0).at(0).toInt() == 0, "serverEdited should emit index 0");
QVERIFY2(serverEditedSpy.at(0).at(0).toString() == m_coreController->m_serversRepository->serverIdAt(0),
"serverEdited should emit edited server id");
ServerConfig editedServer = m_coreController->m_serversRepository->server(0);
QString editedDesc = editedServer.description();
const QString editedDesc = amnezia::test::serverDescription(m_coreController->m_serversRepository,
m_coreController->m_serversRepository->serverIdAt(0));
QVERIFY2(editedDesc == "Edited AWG Server", "Server description should be updated");
if (m_coreController->m_serversModel) {
@@ -93,20 +91,16 @@ private slots:
QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged);
ServerConfig defaultServerConfig = m_coreController->m_serversController->getServerConfig(1);
defaultServerConfig.visit([](auto& arg) {
arg.description = "Edited Default Server";
});
m_coreController->m_serversController->editServer(1, defaultServerConfig);
amnezia::test::setServerDescription(m_coreController->m_serversRepository,
m_coreController->m_serversController->getServerId(1),
QStringLiteral("Edited Default Server"));
QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged should NOT be emitted when editing default server");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Default server index should remain 1");
ServerConfig nonDefaultServerConfig = m_coreController->m_serversController->getServerConfig(0);
nonDefaultServerConfig.visit([](auto& arg) {
arg.description = "Edited Non-Default Server";
});
m_coreController->m_serversController->editServer(0, nonDefaultServerConfig);
amnezia::test::setServerDescription(m_coreController->m_serversRepository,
m_coreController->m_serversController->getServerId(0),
QStringLiteral("Edited Non-Default Server"));
QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged should NOT be emitted when editing non-default server");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Default server index should remain 1");

View File

@@ -0,0 +1,93 @@
#ifndef TESTSERVERREPOSITORYHELPERS_H
#define TESTSERVERREPOSITORYHELPERS_H
#include <QString>
#include <QJsonObject>
#include "core/repositories/secureServersRepository.h"
#include "core/utils/serverConfigUtils.h"
namespace amnezia::test
{
inline QString serverDescription(SecureServersRepository *repo, const QString &serverId)
{
switch (repo->serverKind(serverId)) {
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
const auto cfg = repo->selfHostedAdminConfig(serverId);
return cfg.has_value() ? cfg->description : QString();
}
case serverConfigUtils::ConfigType::SelfHostedUser: {
const auto cfg = repo->selfHostedUserConfig(serverId);
return cfg.has_value() ? cfg->description : QString();
}
case serverConfigUtils::ConfigType::Native: {
const auto cfg = repo->nativeConfig(serverId);
return cfg.has_value() ? cfg->description : QString();
}
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
case serverConfigUtils::ConfigType::AmneziaFreeV3:
case serverConfigUtils::ConfigType::ExternalPremium: {
const auto cfg = repo->apiV2Config(serverId);
return cfg.has_value() ? cfg->description : QString();
}
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
case serverConfigUtils::ConfigType::AmneziaFreeV2: {
const auto cfg = repo->legacyApiConfig(serverId);
return cfg.has_value() ? cfg->description : QString();
}
case serverConfigUtils::ConfigType::Invalid:
default:
return {};
}
}
inline void setServerDescription(SecureServersRepository *repo, const QString &serverId, const QString &description)
{
const serverConfigUtils::ConfigType kind = repo->serverKind(serverId);
switch (kind) {
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
auto cfg = repo->selfHostedAdminConfig(serverId);
if (!cfg.has_value()) return;
cfg->description = description;
cfg->displayName = description;
repo->editServer(serverId, cfg->toJson(), kind);
return;
}
case serverConfigUtils::ConfigType::SelfHostedUser: {
auto cfg = repo->selfHostedUserConfig(serverId);
if (!cfg.has_value()) return;
cfg->description = description;
cfg->displayName = description;
repo->editServer(serverId, cfg->toJson(), kind);
return;
}
case serverConfigUtils::ConfigType::Native: {
auto cfg = repo->nativeConfig(serverId);
if (!cfg.has_value()) return;
cfg->description = description;
cfg->displayName = description;
repo->editServer(serverId, cfg->toJson(), kind);
return;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
case serverConfigUtils::ConfigType::AmneziaFreeV3:
case serverConfigUtils::ConfigType::ExternalPremium: {
auto cfg = repo->apiV2Config(serverId);
if (!cfg.has_value()) return;
cfg->description = description;
cfg->displayName = description;
repo->editServer(serverId, cfg->toJson(), kind);
return;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
case serverConfigUtils::ConfigType::AmneziaFreeV2:
case serverConfigUtils::ConfigType::Invalid:
default:
return;
}
}
} // namespace amnezia::test
#endif

View File

@@ -5,7 +5,8 @@
#include <QTest>
#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 "vpnConnection.h"
#include "secureQSettings.h"
@@ -44,7 +45,7 @@ private slots:
void init() {
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
@@ -64,16 +65,14 @@ private slots:
QString modelDesc1 = m_coreController->m_serversModel->data(m_coreController->m_serversModel->index(0, 0), ServersModel::NameRole).toString();
QVERIFY2(modelDesc1 == "AWG Server", "Model should have correct server name");
ServerConfig serverConfig = m_coreController->m_serversController->getServerConfig(0);
serverConfig.visit([](auto& arg) {
arg.description = "Edited AWG Server";
});
m_coreController->m_serversController->editServer(0, serverConfig);
amnezia::test::setServerDescription(m_coreController->m_serversRepository,
m_coreController->m_serversController->getServerId(0),
QStringLiteral("Edited AWG Server"));
QString modelDesc2 = m_coreController->m_serversModel->data(m_coreController->m_serversModel->index(0, 0), ServersModel::NameRole).toString();
QVERIFY2(modelDesc2 == "Edited AWG Server", "Model should be updated after edit");
m_coreController->m_serversController->removeServer(0);
m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0));
QVERIFY2(m_coreController->m_serversModel->rowCount() == 0, "Model should have 0 rows after removal");
}
@@ -104,7 +103,7 @@ private slots:
QVERIFY2(!isDefault1, "Server 1 should not be default");
QVERIFY2(isDefault2, "Server 2 should be default");
m_coreController->m_serversController->setDefaultServerIndex(0);
m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(0));
isDefault0 = m_coreController->m_serversModel->data(m_coreController->m_serversModel->index(0, 0), ServersModel::IsDefaultRole).toBool();
isDefault2 = m_coreController->m_serversModel->data(m_coreController->m_serversModel->index(2, 0), ServersModel::IsDefaultRole).toBool();

View File

@@ -6,7 +6,6 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "ui/controllers/settingsUiController.h"
#include "ui/controllers/languageUiController.h"
#include "ui/models/allowedDnsModel.h"

View File

@@ -5,7 +5,7 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "vpnConnection.h"
#include "secureQSettings.h"
@@ -38,7 +38,7 @@ private slots:
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
@@ -73,11 +73,12 @@ private slots:
QSignalSpy serverRemovedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved);
QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged);
m_coreController->m_serversController->removeServer(1);
m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(1));
QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted");
QVERIFY2(defaultServerChangedSpy.count() == 1, "defaultServerChanged signal should be emitted when removing default server");
QVERIFY2(defaultServerChangedSpy.at(0).at(0).toInt() == 0, "defaultServerChanged should emit new default index 0");
QVERIFY2(defaultServerChangedSpy.at(0).at(0).toString() == m_coreController->m_serversRepository->defaultServerId(),
"defaultServerChanged should emit new default server id");
QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default server index should be 0");
}
};

View File

@@ -7,7 +7,7 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "secureQSettings.h"
#include "vpnConnection.h"
@@ -49,7 +49,7 @@ private slots:
{
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}

View File

@@ -7,7 +7,7 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "secureQSettings.h"
#include "vpnConnection.h"
@@ -49,7 +49,7 @@ private slots:
{
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}

View File

@@ -7,7 +7,7 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "secureQSettings.h"
#include "vpnConnection.h"
@@ -59,7 +59,7 @@ private slots:
{
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}

View File

@@ -7,7 +7,7 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "secureQSettings.h"
#include "vpnConnection.h"
@@ -43,7 +43,7 @@ private slots:
{
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}

View File

@@ -8,7 +8,7 @@
#include <QTest>
#include "core/controllers/coreController.h"
#include "core/models/serverConfig.h"
#include "core/models/serverDescription.h"
#include "core/controllers/selfhosted/importController.h"
#include "ui/models/serversModel.h"
#include "ui/models/containersModel.h"
@@ -23,6 +23,18 @@ using namespace amnezia;
using namespace amnezia;
namespace {
int defaultServerRow(const QVector<ServerDescription> &descriptions, const QString &defaultServerId)
{
for (int i = 0; i < descriptions.size(); ++i) {
if (descriptions.at(i).serverId == defaultServerId) {
return i;
}
}
return -1;
}
} // namespace
class TestUiServersModelAndController : public QObject
{
Q_OBJECT
@@ -119,7 +131,7 @@ private slots:
void init() {
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerConfig>(), -1, false);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
}
}
@@ -166,11 +178,9 @@ private slots:
}
if (m_coreController->m_serversUiController) {
m_coreController->m_serversUiController->setProcessedServerIndex(serverIndex);
ServerConfig serverConfig = m_coreController->m_serversRepository->server(serverIndex);
QString actualServerName = serverConfig.description();
QString containerName = ContainerUtils::containerHumanNames().value(DockerContainer::Awg);
m_coreController->m_serversUiController->setProcessedServerId(
m_coreController->m_serversUiController->getServerId(0));
QString hostName = "test.example.com";
QString collapsedDescription = m_coreController->m_serversUiController->getDefaultServerDescriptionCollapsed();
@@ -261,27 +271,29 @@ private slots:
m_coreController->m_importCoreController->importConfig(configNoDns);
QVERIFY2(importFinishedSpy.count() == 1, "importFinished should be emitted");
m_coreController->m_appSettingsRepository->setUseAmneziaDns(false);
m_coreController->m_serversModel->updateModel(
m_coreController->m_serversRepository->servers(),
m_coreController->m_serversRepository->defaultServerIndex(),
QVector<ServerDescription> descriptionsNoDns = m_coreController->m_serversController->buildServerDescriptions(
m_coreController->m_appSettingsRepository->useAmneziaDns());
const QString defIdNoDns = m_coreController->m_serversRepository->defaultServerId();
m_coreController->m_serversModel->updateModel(descriptionsNoDns, defaultServerRow(descriptionsNoDns, defIdNoDns));
QString descNoDns = m_coreController->m_serversModel->data(
m_coreController->m_serversModel->index(0, 0), ServersModel::ServerDescriptionRole).toString();
QVERIFY2(descNoDns == "test.example.com",
QString("Without Amnezia DNS expected 'test.example.com', got '%1'").arg(descNoDns).toUtf8().constData());
m_coreController->m_serversRepository->setServersArray(QJsonArray());
m_coreController->m_serversRepository->setDefaultServer(0);
m_coreController->m_serversRepository->clearServers();
if (m_coreController->m_serversRepository->serversCount() > 0) {
m_coreController->m_serversRepository->setDefaultServer(m_coreController->m_serversRepository->serverIdAt(0));
}
QJsonObject configWithDns = createServerDescriptionTestConfig(true);
m_coreController->m_importCoreController->importConfig(configWithDns);
QVERIFY2(m_coreController->m_serversRepository->serversCount() == 1, "Server should be imported");
m_coreController->m_appSettingsRepository->setUseAmneziaDns(true);
m_coreController->m_serversModel->updateModel(
m_coreController->m_serversRepository->servers(),
m_coreController->m_serversRepository->defaultServerIndex(),
QVector<ServerDescription> descriptionsWithDns = m_coreController->m_serversController->buildServerDescriptions(
m_coreController->m_appSettingsRepository->useAmneziaDns());
const QString defIdWithDns = m_coreController->m_serversRepository->defaultServerId();
m_coreController->m_serversModel->updateModel(descriptionsWithDns, defaultServerRow(descriptionsWithDns, defIdWithDns));
QString descWithDns = m_coreController->m_serversModel->data(
m_coreController->m_serversModel->index(0, 0), ServersModel::ServerDescriptionRole).toString();

View File

@@ -1,5 +1,6 @@
#include "allowedDnsUiController.h"
#include <QDebug>
#include <QFile>
#include <QStandardPaths>
#include <QJsonDocument>
@@ -98,7 +99,10 @@ void AllowedDnsUiController::exportDns(const QString &fileName)
QJsonDocument jsonDocument(jsonArray);
QByteArray jsonData = jsonDocument.toJson();
SystemController::saveFile(fileName, jsonData);
if (!SystemController::saveFile(fileName, jsonData)) {
qInfo() << "AllowedDnsUiController::exportDns: save or share was cancelled or failed";
return;
}
emit finished(tr("Export completed"));
}

View File

@@ -2,14 +2,13 @@
#include "amneziaApplication.h"
#include "core/configurators/wireguardConfigurator.h"
#include "core/utils/api/apiEnums.h"
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/api/apiUtils.h"
#include "core/utils/qrCodeUtils.h"
#include "ui/controllers/systemController.h"
#include "version.h"
#include "core/models/serverConfig.h"
#include <QClipboard>
#include <QDebug>
#include <QSet>
@@ -67,7 +66,17 @@ SubscriptionUiController::SubscriptionUiController(ServersController* serversCon
ApiDevicesModel* apiDevicesModel,
SettingsController* settingsController,
QObject *parent)
: QObject(parent), m_serversController(serversController), m_apiServicesModel(apiServicesModel), m_servicesCatalogController(servicesCatalogController), m_subscriptionController(subscriptionController), m_apiSubscriptionPlansModel(apiSubscriptionPlansModel), m_apiBenefitsModel(apiBenefitsModel), m_apiAccountInfoModel(apiAccountInfoModel), m_apiCountryModel(apiCountryModel), m_apiDevicesModel(apiDevicesModel), m_settingsController(settingsController)
: QObject(parent),
m_serversController(serversController),
m_apiServicesModel(apiServicesModel),
m_servicesCatalogController(servicesCatalogController),
m_subscriptionController(subscriptionController),
m_apiSubscriptionPlansModel(apiSubscriptionPlansModel),
m_apiBenefitsModel(apiBenefitsModel),
m_apiAccountInfoModel(apiAccountInfoModel),
m_apiCountryModel(apiCountryModel),
m_apiDevicesModel(apiDevicesModel),
m_settingsController(settingsController)
{
connect(m_apiServicesModel, &ApiServicesModel::serviceSelectionChanged, this, [this]() {
ApiServicesModel::ApiServicesData selectedServiceData = m_apiServicesModel->selectedServiceData();
@@ -76,24 +85,24 @@ SubscriptionUiController::SubscriptionUiController(ServersController* serversCon
});
}
bool SubscriptionUiController::exportVpnKey(int serverIndex, const QString &fileName)
bool SubscriptionUiController::exportVpnKey(const QString &serverId, const QString &fileName)
{
if (fileName.isEmpty()) {
emit errorOccurred(ErrorCode::PermissionsError);
return false;
}
prepareVpnKeyExport(serverIndex);
prepareVpnKeyExport(serverId);
if (m_vpnKey.isEmpty()) {
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
return false;
}
SystemController::saveFile(fileName, m_vpnKey);
return true;
return SystemController::saveFile(fileName, m_vpnKey);
}
bool SubscriptionUiController::exportNativeConfig(int serverIndex, const QString &serverCountryCode, const QString &fileName)
bool SubscriptionUiController::exportNativeConfig(const QString &serverId, const QString &serverCountryCode, const QString &fileName)
{
if (fileName.isEmpty()) {
emit errorOccurred(ErrorCode::PermissionsError);
@@ -101,19 +110,21 @@ bool SubscriptionUiController::exportNativeConfig(int serverIndex, const QString
}
QString nativeConfig;
ErrorCode errorCode = m_subscriptionController->exportNativeConfig(serverIndex, serverCountryCode, nativeConfig);
ErrorCode errorCode = m_subscriptionController->exportNativeConfig(serverId, serverCountryCode, nativeConfig);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
SystemController::saveFile(fileName, nativeConfig);
return true;
const bool saved = SystemController::saveFile(fileName, nativeConfig);
getAccountInfo(serverId, true);
return saved;
}
bool SubscriptionUiController::revokeNativeConfig(int serverIndex, const QString &serverCountryCode)
bool SubscriptionUiController::revokeNativeConfig(const QString &serverId, const QString &serverCountryCode)
{
ErrorCode errorCode = m_subscriptionController->revokeNativeConfig(serverIndex, serverCountryCode);
ErrorCode errorCode = m_subscriptionController->revokeNativeConfig(serverId, serverCountryCode);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
@@ -121,10 +132,11 @@ bool SubscriptionUiController::revokeNativeConfig(int serverIndex, const QString
return true;
}
void SubscriptionUiController::prepareVpnKeyExport(int serverIndex)
void SubscriptionUiController::prepareVpnKeyExport(const QString &serverId)
{
QString vpnKey;
ErrorCode errorCode = m_subscriptionController->prepareVpnKeyExport(serverIndex, vpnKey);
ErrorCode errorCode = m_subscriptionController->prepareVpnKeyExport(serverId, vpnKey);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return;
@@ -140,6 +152,7 @@ void SubscriptionUiController::prepareVpnKeyExport(int serverIndex)
emit vpnKeyExportReady();
}
void SubscriptionUiController::copyVpnKeyToClipboard()
{
auto clipboard = amnApp->getClipboard();
@@ -170,14 +183,12 @@ bool SubscriptionUiController::importPremiumFromAppStore(const QString &storePro
productId = QStringLiteral("amnezia_premium_6_month");
}
ServerConfig serverConfig;
int duplicateServerIndex = -1;
ErrorCode errorCode = m_subscriptionController->processAppStorePurchase(
m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(),
productId,
serverConfig,
&duplicateServerIndex);
if (errorCode != ErrorCode::NoError) {
@@ -260,11 +271,8 @@ bool SubscriptionUiController::importFreeFromGateway()
}
SubscriptionController::ProtocolData protocolData = m_subscriptionController->generateProtocolData(serviceProtocol);
ServerConfig serverConfig;
ErrorCode errorCode = m_subscriptionController->importServiceFromGateway(userCountryCode, serviceType,
serviceProtocol, protocolData,
serverConfig);
serviceProtocol, protocolData);
if (errorCode == ErrorCode::NoError) {
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
@@ -278,12 +286,10 @@ bool SubscriptionUiController::importFreeFromGateway()
bool SubscriptionUiController::importTrialFromGateway(const QString &email)
{
emit trialEmailError(QString());
ServerConfig serverConfig;
ErrorCode errorCode = m_subscriptionController->importTrialFromGateway(m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(),
email,
serverConfig);
email);
if (errorCode != ErrorCode::NoError) {
if (errorCode == ErrorCode::ApiTrialAlreadyUsedError) {
emit trialEmailError(
@@ -298,21 +304,17 @@ bool SubscriptionUiController::importTrialFromGateway(const QString &email)
return true;
}
bool SubscriptionUiController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool SubscriptionUiController::updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig)
{
bool isConnectEvent = newCountryCode.isEmpty() && newCountryName.isEmpty() && !reloadServiceConfig;
bool wasSubscriptionExpired = false;
ServerConfig oldServerConfig = m_serversController->getServerConfig(serverIndex);
if (oldServerConfig.isApiV2()) {
const ApiV2ServerConfig *oldApiV2 = oldServerConfig.as<ApiV2ServerConfig>();
if (oldApiV2) {
wasSubscriptionExpired = oldApiV2->apiConfig.subscriptionExpiredByServer
|| oldApiV2->apiConfig.isSubscriptionExpired();
}
if (const auto oldApiV2 = m_serversController->apiV2Config(serverId)) {
wasSubscriptionExpired = oldApiV2->apiConfig.subscriptionExpiredByServer
|| oldApiV2->apiConfig.isSubscriptionExpired();
}
ErrorCode errorCode = m_subscriptionController->updateServiceFromGateway(serverIndex, newCountryCode, isConnectEvent);
ErrorCode errorCode = m_subscriptionController->updateServiceFromGateway(serverId, newCountryCode, isConnectEvent);
if (errorCode == ErrorCode::NoError) {
if (wasSubscriptionExpired) {
@@ -336,27 +338,10 @@ bool SubscriptionUiController::updateServiceFromGateway(const int serverIndex, c
}
}
bool SubscriptionUiController::updateServiceFromTelegram(const int serverIndex)
bool SubscriptionUiController::deactivateDevice(const QString &serverId)
{
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
ErrorCode errorCode = m_subscriptionController->updateServiceFromTelegram(serverIndex);
if (errorCode == ErrorCode::NoError) {
emit updateServerFromApiFinished();
return true;
} else {
emit errorOccurred(errorCode);
return false;
}
}
bool SubscriptionUiController::deactivateDevice(int serverIndex)
{
ErrorCode errorCode = m_subscriptionController->deactivateDevice(serverIndex);
ErrorCode errorCode = m_subscriptionController->deactivateDevice(serverId);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
@@ -365,9 +350,10 @@ bool SubscriptionUiController::deactivateDevice(int serverIndex)
return true;
}
bool SubscriptionUiController::deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode)
bool SubscriptionUiController::deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode)
{
ErrorCode errorCode = m_subscriptionController->deactivateExternalDevice(serverIndex, uuid, serverCountryCode);
ErrorCode errorCode = m_subscriptionController->deactivateExternalDevice(serverId, uuid, serverCountryCode);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
@@ -376,12 +362,19 @@ bool SubscriptionUiController::deactivateExternalDevice(int serverIndex, const Q
return true;
}
void SubscriptionUiController::validateConfig()
{
int serverIndex = m_serversController->getDefaultServerIndex();
bool hasInstalledContainers = m_serversController->hasInstalledContainers(serverIndex);
const QString serverId = m_serversController->getDefaultServerId();
if (!serverId.isEmpty() && m_serversController->isLegacyApiV1Server(serverId)) {
emit unsupportedConnectDrawerRequested();
emit configValidated(false);
return;
}
ErrorCode errorCode = m_subscriptionController->validateAndUpdateConfig(serverIndex, hasInstalledContainers);
bool hasInstalledContainers = m_serversController->hasInstalledContainers(serverId);
ErrorCode errorCode = m_subscriptionController->validateAndUpdateConfig(serverId, hasInstalledContainers);
if (errorCode != ErrorCode::NoError) {
if (errorCode == ErrorCode::ApiSubscriptionExpiredError) {
@@ -395,22 +388,34 @@ void SubscriptionUiController::validateConfig()
emit configValidated(true);
}
void SubscriptionUiController::setCurrentProtocol(int serverIndex, const QString &protocolName)
void SubscriptionUiController::setCurrentProtocol(const QString &serverId, const QString &protocolName)
{
m_subscriptionController->setCurrentProtocol(serverIndex, protocolName);
m_subscriptionController->setCurrentProtocol(serverId, protocolName);
}
bool SubscriptionUiController::isVlessProtocol(int serverIndex)
bool SubscriptionUiController::isVlessProtocol(const QString &serverId)
{
return m_subscriptionController->isVlessProtocol(serverIndex);
return m_subscriptionController->isVlessProtocol(serverId);
}
void SubscriptionUiController::removeApiConfig(int serverIndex)
void SubscriptionUiController::removeApiConfig(const QString &serverId)
{
m_subscriptionController->removeApiConfig(serverIndex);
m_subscriptionController->removeApiConfig(serverId);
emit apiConfigRemoved(tr("Api config removed"));
}
void SubscriptionUiController::removeServer(const QString &serverId)
{
const QString serverName = m_serversController->notificationDisplayName(serverId);
if (!m_subscriptionController->removeServer(serverId)) {
return;
}
emit apiServerRemoved(tr("Server '%1' was removed").arg(serverName));
}
QList<QString> SubscriptionUiController::getQrCodes()
{
return m_qrCodes;
@@ -426,7 +431,7 @@ QString SubscriptionUiController::getVpnKey()
return m_vpnKey;
}
bool SubscriptionUiController::getAccountInfo(int serverIndex, bool reload)
bool SubscriptionUiController::getAccountInfo(const QString &serverId, bool reload)
{
if (reload) {
QEventLoop wait;
@@ -434,15 +439,18 @@ bool SubscriptionUiController::getAccountInfo(int serverIndex, bool reload)
wait.exec(QEventLoop::ExcludeUserInputEvents);
}
QJsonObject accountInfo;
ErrorCode errorCode = m_subscriptionController->getAccountInfo(serverIndex, accountInfo);
ErrorCode errorCode = m_subscriptionController->getAccountInfo(serverId, accountInfo);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex);
QJsonObject serverConfigJson = serverConfig.toJson();
m_apiAccountInfoModel->updateModel(accountInfo, serverConfigJson);
const auto apiV2 = m_serversController->apiV2Config(serverId);
if (!apiV2.has_value()) {
emit errorOccurred(ErrorCode::InternalError);
return false;
}
m_apiAccountInfoModel->updateModel(accountInfo, apiV2->toJson());
if (reload) {
updateApiCountryModel();
@@ -463,9 +471,9 @@ void SubscriptionUiController::updateApiDevicesModel()
m_apiDevicesModel->updateModel(m_apiAccountInfoModel->getIssuedConfigsInfo(), m_settingsController->getInstallationUuid(false));
}
void SubscriptionUiController::getRenewalLink(int serverIndex)
void SubscriptionUiController::getRenewalLink(const QString &serverId)
{
if (serverIndex < 0) {
if (serverId.isEmpty()) {
emit errorOccurred(ErrorCode::InternalError);
return;
}
@@ -483,6 +491,6 @@ void SubscriptionUiController::getRenewalLink(int serverIndex)
}
emit renewalLinkReceived(url);
});
watcher->setFuture(m_subscriptionController->getRenewalLink(serverIndex));
watcher->setFuture(m_subscriptionController->getRenewalLink(serverId));
}

View File

@@ -13,6 +13,7 @@
#include "ui/models/api/apiAccountInfoModel.h"
#include "ui/models/api/apiCountryModel.h"
#include "ui/models/api/apiDevicesModel.h"
class SubscriptionUiController : public QObject
{
Q_OBJECT
@@ -34,10 +35,10 @@ public:
Q_PROPERTY(QString vpnKey READ getVpnKey NOTIFY vpnKeyExportReady)
public slots:
bool exportNativeConfig(int serverIndex, const QString &serverCountryCode, const QString &fileName);
bool revokeNativeConfig(int serverIndex, const QString &serverCountryCode);
bool exportVpnKey(int serverIndex, const QString &fileName);
void prepareVpnKeyExport(int serverIndex);
bool exportNativeConfig(const QString &serverId, const QString &serverCountryCode, const QString &fileName);
bool revokeNativeConfig(const QString &serverId, const QString &serverCountryCode);
bool exportVpnKey(const QString &serverId, const QString &fileName);
void prepareVpnKeyExport(const QString &serverId);
void copyVpnKeyToClipboard();
bool fillAvailableServices();
@@ -45,21 +46,23 @@ public slots:
bool importFreeFromGateway();
bool restoreServiceFromAppStore();
bool importTrialFromGateway(const QString &email);
bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig = false);
bool updateServiceFromTelegram(const int serverIndex);
bool deactivateDevice(int serverIndex);
bool deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode);
bool deactivateDevice(const QString &serverId);
bool deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode);
void validateConfig();
void setCurrentProtocol(int serverIndex, const QString &protocolName);
bool isVlessProtocol(int serverIndex);
void setCurrentProtocol(const QString &serverId, const QString &protocolName);
bool isVlessProtocol(const QString &serverId);
void removeApiConfig(int serverIndex);
void removeApiConfig(const QString &serverId);
void removeServer(const QString &serverId);
bool getAccountInfo(const QString &serverId, bool reload);
void getRenewalLink(const QString &serverId);
bool getAccountInfo(int serverIndex, bool reload);
void getRenewalLink(int serverIndex);
void updateApiCountryModel();
void updateApiDevicesModel();
@@ -77,9 +80,12 @@ signals:
void subscriptionRefreshNeeded();
void apiConfigRemoved(const QString &message);
void apiServerRemoved(const QString &message);
void vpnKeyExportReady();
void unsupportedConnectDrawerRequested();
private:
QList<QString> getQrCodes();
int getQrCodesCount();

View File

@@ -25,9 +25,12 @@ ConnectionUiController::ConnectionUiController(ConnectionController* connectionC
void ConnectionUiController::openConnection()
{
int serverIndex = m_serversController->getDefaultServerIndex();
const QString serverId = m_serversController->getDefaultServerId();
if (serverId.isEmpty()) {
return;
}
ErrorCode errorCode = m_connectionController->openConnection(serverIndex);
ErrorCode errorCode = m_connectionController->openConnection(serverId);
if (errorCode != ErrorCode::NoError) {
emit connectionErrorOccurred(errorCode);
@@ -100,16 +103,6 @@ void ConnectionUiController::onConnectionStateChanged(Vpn::ConnectionState state
emit connectionStateChanged();
}
void ConnectionUiController::onCurrentContainerUpdated()
{
if (m_isConnected || m_isConnectionInProgress) {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, reconnection..."));
openConnection();
} else {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully"));
}
}
void ConnectionUiController::onTranslationsUpdated()
{
onConnectionStateChanged(getCurrentConnectionState());

View File

@@ -38,8 +38,6 @@ public slots:
ErrorCode getLastConnectionError();
void onConnectionStateChanged(Vpn::ConnectionState state);
void onCurrentContainerUpdated();
void onTranslationsUpdated();
signals:

View File

@@ -1,5 +1,7 @@
#include "ipSplitTunnelingUiController.h"
#include <QDebug>
#include "systemController.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
@@ -55,7 +57,10 @@ void IpSplitTunnelingUiController::importSites(const QString &fileName, bool rep
void IpSplitTunnelingUiController::exportSites(const QString &fileName)
{
QByteArray jsonData = m_ipSplitTunnelingController->exportSitesToJson();
SystemController::saveFile(fileName, QString::fromUtf8(jsonData));
if (!SystemController::saveFile(fileName, jsonData)) {
qInfo() << "IpSplitTunnelingUiController::exportSites: save or share was cancelled or failed";
return;
}
emit finished(tr("Export completed"));
}

View File

@@ -19,8 +19,7 @@
#include "ui/utils/macosUtil.h"
#endif
PageController::PageController(ServersController* serversController,
SettingsController* settingsController,
PageController::PageController(ServersController* serversController, SettingsController* settingsController,
QObject *parent)
: QObject(parent), m_serversController(serversController), m_settingsController(settingsController)
{
@@ -57,14 +56,7 @@ PageController::PageController(ServersController* serversController,
bool PageController::isStartPageVisible()
{
if (m_serversController->getServersCount()) {
if (m_serversController->getDefaultServerIndex() < 0) {
m_serversController->setDefaultServerIndex(0);
}
return false;
} else {
return true;
}
return m_serversController->getServersCount() == 0;
}
QString PageController::getPagePath(PageLoader::PageEnum page)

View File

@@ -94,8 +94,7 @@ class PageController : public QObject
{
Q_OBJECT
public:
explicit PageController(ServersController* serversController,
SettingsController* settingsController,
explicit PageController(ServersController* serversController, SettingsController* settingsController,
QObject *parent = nullptr);
Q_PROPERTY(int safeAreaTopMargin READ getSafeAreaTopMargin NOTIFY safeAreaTopMarginChanged)
@@ -164,6 +163,8 @@ signals:
void showPassphraseRequestDrawer();
void passphraseRequestDrawerClosed(QString passphrase);
void unsupportedConnectDrawerRequested();
void escapePressed();
void closeTopDrawer();

View File

@@ -1,5 +1,7 @@
#include "exportUiController.h"
#include <QDebug>
#include "../systemController.h"
ExportUiController::ExportUiController(ExportController* exportController, QObject *parent)
@@ -8,46 +10,46 @@ ExportUiController::ExportUiController(ExportController* exportController, QObje
{
}
void ExportUiController::generateFullAccessConfig(int serverIndex)
void ExportUiController::generateFullAccessConfig(const QString &serverId)
{
clearPreviousConfig();
auto result = m_exportController->generateFullAccessConfig(serverIndex);
auto result = m_exportController->generateFullAccessConfig(serverId);
applyExportResult(result);
}
void ExportUiController::generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName)
void ExportUiController::generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName)
{
clearPreviousConfig();
auto result = m_exportController->generateConnectionConfig(serverIndex, containerIndex, clientName);
auto result = m_exportController->generateConnectionConfig(serverId, containerIndex, clientName);
applyExportResult(result);
}
void ExportUiController::generateOpenVpnConfig(int serverIndex, const QString &clientName)
void ExportUiController::generateOpenVpnConfig(const QString &serverId, const QString &clientName)
{
clearPreviousConfig();
auto result = m_exportController->generateOpenVpnConfig(serverIndex, clientName);
auto result = m_exportController->generateOpenVpnConfig(serverId, clientName);
applyExportResult(result);
}
void ExportUiController::generateWireGuardConfig(int serverIndex, const QString &clientName)
void ExportUiController::generateWireGuardConfig(const QString &serverId, const QString &clientName)
{
clearPreviousConfig();
auto result = m_exportController->generateWireGuardConfig(serverIndex, clientName);
auto result = m_exportController->generateWireGuardConfig(serverId, clientName);
applyExportResult(result);
}
void ExportUiController::generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName)
void ExportUiController::generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName)
{
clearPreviousConfig();
auto result = m_exportController->generateAwgConfig(serverIndex, containerIndex, clientName);
auto result = m_exportController->generateAwgConfig(serverId, containerIndex, clientName);
applyExportResult(result);
}
void ExportUiController::generateXrayConfig(int serverIndex, const QString &clientName)
void ExportUiController::generateXrayConfig(const QString &serverId, const QString &clientName)
{
clearPreviousConfig();
auto result = m_exportController->generateXrayConfig(serverIndex, clientName);
auto result = m_exportController->generateXrayConfig(serverId, clientName);
applyExportResult(result);
}
@@ -68,23 +70,25 @@ QList<QString> ExportUiController::getQrCodes()
void ExportUiController::exportConfig(const QString &fileName)
{
SystemController::saveFile(fileName, m_config);
if (!SystemController::saveFile(fileName, m_config)) {
qInfo() << "ExportUiController::exportConfig: save or share was cancelled or failed";
}
}
void ExportUiController::updateClientManagementModel(int serverIndex, int containerIndex)
void ExportUiController::updateClientManagementModel(const QString &serverId, int containerIndex)
{
m_exportController->updateClientManagementModel(serverIndex, containerIndex);
m_exportController->updateClientManagementModel(serverId, containerIndex);
}
void ExportUiController::revokeConfig(int row, int serverIndex, int containerIndex)
void ExportUiController::revokeConfig(int row, const QString &serverId, int containerIndex)
{
m_exportController->revokeConfig(row, serverIndex, containerIndex);
m_exportController->revokeConfig(row, serverId, containerIndex);
emit revokeConfigFinished();
}
void ExportUiController::renameClient(int row, const QString &clientName, int serverIndex, int containerIndex)
void ExportUiController::renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex)
{
m_exportController->renameClient(row, clientName, serverIndex, containerIndex);
m_exportController->renameClient(row, clientName, serverId, containerIndex);
}
int ExportUiController::getQrCodesCount()

View File

@@ -4,6 +4,7 @@
#include <QObject>
#include "core/controllers/selfhosted/exportController.h"
#include "core/utils/errorCodes.h"
class ExportUiController : public QObject
{
@@ -17,12 +18,13 @@ public:
Q_PROPERTY(QString nativeConfigString READ getNativeConfigString NOTIFY exportConfigChanged)
public slots:
void generateFullAccessConfig(int serverIndex);
void generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName);
void generateOpenVpnConfig(int serverIndex, const QString &clientName);
void generateWireGuardConfig(int serverIndex, const QString &clientName);
void generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName);
void generateXrayConfig(int serverIndex, const QString &clientName);
void generateFullAccessConfig(const QString &serverId);
void generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName);
void generateOpenVpnConfig(const QString &serverId, const QString &clientName);
void generateWireGuardConfig(const QString &serverId, const QString &clientName);
void generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName);
void generateXrayConfig(const QString &serverId, const QString &clientName);
QString getConfig();
QString getNativeConfigString();
@@ -30,9 +32,11 @@ public slots:
void exportConfig(const QString &fileName);
void updateClientManagementModel(int serverIndex, int containerIndex);
void revokeConfig(int row, int serverIndex, int containerIndex);
void renameClient(int row, const QString &clientName, int serverIndex, int containerIndex);
void updateClientManagementModel(const QString &serverId, int containerIndex);
void revokeConfig(int row, const QString &serverId, int containerIndex);
void renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex);
signals:
void generateConfig(int type);

View File

@@ -11,7 +11,6 @@
#include "core/controllers/selfhosted/installController.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/networkUtilities.h"
#include "logger.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
@@ -27,33 +26,12 @@
#include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/services/torConfigModel.h"
#include "core/utils/utilities.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/models/protocols/wireGuardProtocolConfig.h"
#include "core/models/protocols/openVpnProtocolConfig.h"
#include "core/models/protocols/xrayProtocolConfig.h"
namespace
{
Logger logger("InstallUiController");
namespace configKey
{
constexpr char serviceInfo[] = "service_info";
constexpr char serviceType[] = "service_type";
constexpr char serviceProtocol[] = "service_protocol";
constexpr char userCountryCode[] = "user_country_code";
constexpr char serverCountryCode[] = "server_country_code";
constexpr char serverCountryName[] = "server_country_name";
constexpr char availableCountries[] = "available_countries";
constexpr char apiConfig[] = "api_config";
constexpr char authData[] = "auth_data";
}
}
InstallUiController::InstallUiController(InstallController *installController,
ServersController *serversController,
SettingsController *settingsController,
@@ -101,19 +79,18 @@ InstallUiController::~InstallUiController()
{
}
void InstallUiController::install(DockerContainer container, int port, TransportProto transportProto, int serverIndex)
void InstallUiController::install(DockerContainer container, int port, TransportProto transportProto, const QString &serverId)
{
const bool isNewServer = serverIndex < 0;
const bool isNewServer = serverId.isEmpty();
ServerCredentials serverCredentials;
if (isNewServer) {
serverCredentials = m_processedServerCredentials;
} else {
serverCredentials = m_serversController->getServerCredentials(serverIndex);
serverCredentials = m_serversController->getServerCredentials(serverId);
m_processedServerCredentials = ServerCredentials();
}
QMap<DockerContainer, QJsonObject> preparedContainers;
QString finishMessage;
ErrorCode errorCode;
@@ -131,9 +108,13 @@ void InstallUiController::install(DockerContainer container, int port, Transport
return;
}
int serverIndex = m_serversController->getServersCount() - 1;
ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = serverConfig.containers();
const QString newServerId = m_serversController->getServerId(m_serversController->getServersCount() - 1);
const auto admin = m_serversController->selfHostedAdminConfig(newServerId);
if (!admin.has_value()) {
emit installationErrorOccurred(ErrorCode::InternalError);
return;
}
QMap<DockerContainer, ContainerConfig> containers = admin->containers;
int containersCount = containers.size();
if (wasContainerInstalled) {
@@ -148,20 +129,28 @@ void InstallUiController::install(DockerContainer container, int port, Transport
emit installServerFinished(finishMessage);
} else {
ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = serverConfig.containers();
const auto adminBefore = m_serversController->selfHostedAdminConfig(serverId);
if (!adminBefore.has_value()) {
emit installationErrorOccurred(ErrorCode::InternalError);
return;
}
QMap<DockerContainer, ContainerConfig> containers = adminBefore->containers;
int containersCount = containers.size();
bool wasContainerInstalled = false;
errorCode = m_installController->installContainer(serverIndex, container, port, transportProto,
errorCode = m_installController->installContainer(serverId, container, port, transportProto,
wasContainerInstalled);
if (errorCode) {
emit installationErrorOccurred(errorCode);
return;
}
ServerConfig newServerConfig = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> newContainers = newServerConfig.containers();
const auto adminAfter = m_serversController->selfHostedAdminConfig(serverId);
if (!adminAfter.has_value()) {
emit installationErrorOccurred(ErrorCode::InternalError);
return;
}
QMap<DockerContainer, ContainerConfig> newContainers = adminAfter->containers;
int newContainersCount = newContainers.size();
bool hasNewContainers = (newContainersCount - containersCount) > (wasContainerInstalled ? 1 : 0);
@@ -181,17 +170,25 @@ void InstallUiController::install(DockerContainer container, int port, Transport
}
}
void InstallUiController::scanServerForInstalledContainers(int serverIndex)
void InstallUiController::scanServerForInstalledContainers(const QString &serverId)
{
ServerConfig serverBefore = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containersBefore = serverBefore.containers();
const auto serverBefore = m_serversController->selfHostedAdminConfig(serverId);
if (!serverBefore.has_value()) {
emit installationErrorOccurred(ErrorCode::InternalError);
return;
}
QMap<DockerContainer, ContainerConfig> containersBefore = serverBefore->containers;
int containersCountBefore = containersBefore.size();
ErrorCode errorCode = m_installController->scanServerForInstalledContainers(serverIndex);
ErrorCode errorCode = m_installController->scanServerForInstalledContainers(serverId);
if (errorCode == ErrorCode::NoError) {
ServerConfig serverAfter = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containersAfter = serverAfter.containers();
const auto serverAfter = m_serversController->selfHostedAdminConfig(serverId);
if (!serverAfter.has_value()) {
emit installationErrorOccurred(ErrorCode::InternalError);
return;
}
QMap<DockerContainer, ContainerConfig> containersAfter = serverAfter->containers;
int containersCountAfter = containersAfter.size();
bool isInstalledContainerAdded = containersCountAfter > containersCountBefore;
@@ -202,7 +199,7 @@ void InstallUiController::scanServerForInstalledContainers(int serverIndex)
emit installationErrorOccurred(errorCode);
}
void InstallUiController::updateContainer(int serverIndex, int containerIndex, int protocolIndex)
void InstallUiController::updateContainer(const QString &serverId, int containerIndex, int protocolIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
@@ -250,32 +247,26 @@ void InstallUiController::updateContainer(int serverIndex, int containerIndex, i
default:
return;
}
ContainerConfig oldContainerConfig = m_serversController->getContainerConfig(serverIndex, container);
ContainerConfig oldContainerConfig = m_serversController->getContainerConfig(serverId, container);
ErrorCode errorCode = m_installController->updateContainer(serverIndex, container, oldContainerConfig, containerConfig);
ErrorCode errorCode = m_installController->updateContainer(serverId, container, oldContainerConfig, containerConfig);
if (errorCode == ErrorCode::NoError) {
ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverIndex, container);
ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverId, container);
m_protocolModel->updateModel(updatedConfig);
auto defaultContainer = m_serversController->getServerConfig(serverIndex).defaultContainer();
if ((serverIndex == m_serversController->getDefaultServerIndex()) && (container == defaultContainer)) {
emit currentContainerUpdated();
} else {
emit updateContainerFinished(tr("Settings updated successfully"));
}
emit updateContainerFinished(tr("Settings updated successfully"));
return;
}
emit installationErrorOccurred(errorCode);
}
void InstallUiController::rebootServer(int serverIndex)
void InstallUiController::rebootServer(const QString &serverId)
{
QString serverName = m_serversController->getServerConfig(serverIndex).displayName();
const QString serverName = m_serversController->notificationDisplayName(serverId);
const auto errorCode = m_installController->rebootServer(serverIndex);
const auto errorCode = m_installController->rebootServer(serverId);
if (errorCode == ErrorCode::NoError) {
emit rebootServerFinished(tr("Server '%1' was rebooted").arg(serverName));
} else {
@@ -283,19 +274,22 @@ void InstallUiController::rebootServer(int serverIndex)
}
}
void InstallUiController::removeServer(int serverIndex)
void InstallUiController::removeServer(const QString &serverId)
{
QString serverName = m_serversController->getServerConfig(serverIndex).displayName();
if (serverId.isEmpty()) {
return;
}
const QString serverName = m_serversController->notificationDisplayName(serverId);
m_serversController->removeServer(serverIndex);
m_serversController->removeServer(serverId);
emit removeServerFinished(tr("Server '%1' was removed").arg(serverName));
}
void InstallUiController::removeAllContainers(int serverIndex)
void InstallUiController::removeAllContainers(const QString &serverId)
{
QString serverName = m_serversController->getServerConfig(serverIndex).displayName();
const QString serverName = m_serversController->notificationDisplayName(serverId);
ErrorCode errorCode = m_installController->removeAllContainers(serverIndex);
ErrorCode errorCode = m_installController->removeAllContainers(serverId);
if (errorCode == ErrorCode::NoError) {
emit removeAllContainersFinished(tr("All containers from server '%1' have been removed").arg(serverName));
return;
@@ -303,14 +297,14 @@ void InstallUiController::removeAllContainers(int serverIndex)
emit installationErrorOccurred(errorCode);
}
void InstallUiController::removeContainer(int serverIndex, int containerIndex)
void InstallUiController::removeContainer(const QString &serverId, int containerIndex)
{
QString serverName = m_serversController->getServerConfig(serverIndex).displayName();
const QString serverName = m_serversController->notificationDisplayName(serverId);
DockerContainer container = static_cast<DockerContainer>(containerIndex);
QString containerName = ContainerUtils::containerHumanNames().value(container);
ErrorCode errorCode = m_installController->removeContainer(serverIndex, container);
ErrorCode errorCode = m_installController->removeContainer(serverId, container);
if (errorCode == ErrorCode::NoError) {
emit removeContainerFinished(tr("%1 has been removed from the server '%2'").arg(containerName, serverName));
@@ -319,17 +313,17 @@ void InstallUiController::removeContainer(int serverIndex, int containerIndex)
emit installationErrorOccurred(errorCode);
}
void InstallUiController::clearCachedProfile(int serverIndex, int containerIndex)
void InstallUiController::clearCachedProfile(const QString &serverId, int containerIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
if (ContainerUtils::containerService(container) == ServiceType::Other) {
return;
}
m_installController->clearCachedProfile(serverIndex, container);
m_installController->clearCachedProfile(serverId, container);
emit cachedProfileCleared(tr("%1 cached profile cleared").arg(ContainerUtils::containerHumanNames().value(container)));
ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverIndex, container);
ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverId, container);
m_protocolModel->updateModel(updatedConfig);
}
@@ -354,9 +348,9 @@ void InstallUiController::setProcessedServerCredentials(const QString &hostName,
m_processedServerCredentials.secretData = secretData;
}
void InstallUiController::mountSftpDrive(int serverIndex, const QString &port, const QString &password, const QString &username)
void InstallUiController::mountSftpDrive(const QString &serverId, const QString &port, const QString &password, const QString &username)
{
ServerCredentials serverCredentials = m_serversController->getServerCredentials(serverIndex);
ServerCredentials serverCredentials = m_serversController->getServerCredentials(serverId);
ErrorCode errorCode = m_installController->mountSftpDrive(serverCredentials, port, password, username);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
@@ -399,40 +393,35 @@ void InstallUiController::setEncryptedPassphrase(QString passphrase)
void InstallUiController::addEmptyServer()
{
SelfHostedServerConfig serverConfig;
serverConfig.hostName = m_processedServerCredentials.hostName;
serverConfig.userName = m_processedServerCredentials.userName;
serverConfig.password = m_processedServerCredentials.secretData;
serverConfig.port = m_processedServerCredentials.port;
serverConfig.description = m_settingsController->nextAvailableServerName();
serverConfig.defaultContainer = DockerContainer::None;
m_serversController->addServer(ServerConfig(serverConfig));
m_installController->addEmptyServer(m_processedServerCredentials);
emit installServerFinished(tr("Server added successfully"));
}
void InstallUiController::validateConfig()
{
int serverIndex = m_serversController->getDefaultServerIndex();
m_installController->validateConfig(serverIndex);
const QString serverId = m_serversController->getDefaultServerId();
if (serverId.isEmpty()) {
return;
}
m_installController->validateConfig(serverId);
}
void InstallUiController::updateProtocols(int serverIndex, int containerIndex)
void InstallUiController::updateProtocols(const QString &serverId, int containerIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
ContainerConfig containerConfig = m_serversController->getContainerConfig(serverIndex, container);
ContainerConfig containerConfig = m_serversController->getContainerConfig(serverId, container);
containerConfig.container = container;
m_protocolModel->updateModel(containerConfig);
}
void InstallUiController::openServerSettings(int serverIndex, int containerIndex, int protocolIndex)
void InstallUiController::openServerSettings(const QString &serverId, int containerIndex, int protocolIndex)
{
updateProtocolConfigModel(serverIndex, containerIndex, protocolIndex);
updateProtocolConfigModel(serverId, containerIndex, protocolIndex);
}
void InstallUiController::openClientSettings(int serverIndex, int containerIndex, int protocolIndex)
void InstallUiController::openClientSettings(const QString &serverId, int containerIndex, int protocolIndex)
{
updateProtocolConfigModel(serverIndex, containerIndex, protocolIndex);
updateProtocolConfigModel(serverId, containerIndex, protocolIndex);
}
int InstallUiController::defaultPort(int protocolIndex)
@@ -465,10 +454,10 @@ bool InstallUiController::defaultTransportProtoChangeable(int protocolIndex)
return ProtocolUtils::defaultTransportProtoChangeable(proto);
}
void InstallUiController::updateProtocolConfigModel(int serverIndex, int containerIndex, int protocolIndex)
void InstallUiController::updateProtocolConfigModel(const QString &serverId, int containerIndex, int protocolIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
ContainerConfig containerConfig = m_serversController->getContainerConfig(serverIndex, container);
ContainerConfig containerConfig = m_serversController->getContainerConfig(serverId, container);
containerConfig.container = container;
Proto protocolType = static_cast<Proto>(protocolIndex);
@@ -490,4 +479,3 @@ void InstallUiController::updateProtocolConfigModel(int serverIndex, int contain
default: break;
}
}

View File

@@ -52,24 +52,24 @@ public:
~InstallUiController();
public slots:
void install(DockerContainer container, int port, TransportProto transportProto, int serverIndex);
void install(DockerContainer container, int port, TransportProto transportProto, const QString &serverId);
void setProcessedServerCredentials(const QString &hostName, const QString &userName, const QString &secretData);
void clearProcessedServerCredentials();
void scanServerForInstalledContainers(int serverIndex);
void scanServerForInstalledContainers(const QString &serverId);
void updateContainer(int serverIndex, int containerIndex, int protocolIndex);
void updateContainer(const QString &serverId, int containerIndex, int protocolIndex);
void removeServer(int serverIndex);
void rebootServer(int serverIndex);
void removeAllContainers(int serverIndex);
void removeContainer(int serverIndex, int containerIndex);
void removeServer(const QString &serverId);
void rebootServer(const QString &serverId);
void removeAllContainers(const QString &serverId);
void removeContainer(const QString &serverId, int containerIndex);
void clearCachedProfile(int serverIndex, int containerIndex);
void clearCachedProfile(const QString &serverId, int containerIndex);
QRegularExpression ipAddressRegExp();
void mountSftpDrive(int serverIndex, const QString &port, const QString &password, const QString &username);
void mountSftpDrive(const QString &serverId, const QString &port, const QString &password, const QString &username);
bool checkSshConnection();
@@ -78,12 +78,12 @@ public slots:
void addEmptyServer();
void validateConfig();
Q_INVOKABLE void updateProtocols(int serverIndex, int containerIndex);
void openServerSettings(int serverIndex, int containerIndex, int protocolIndex);
void openClientSettings(int serverIndex, int containerIndex, int protocolIndex);
Q_INVOKABLE void updateProtocols(const QString &serverId, int containerIndex);
void openServerSettings(const QString &serverId, int containerIndex, int protocolIndex);
void openClientSettings(const QString &serverId, int containerIndex, int protocolIndex);
int defaultPort(int protocolIndex);
int getPortForInstall(int protocolIndex);
int defaultTransportProto(int protocolIndex);
@@ -114,8 +114,6 @@ signals:
void serverIsBusy(const bool isBusy);
void cancelInstallation();
void currentContainerUpdated();
void cachedProfileCleared(const QString &message);
void apiConfigRemoved(const QString &message);
@@ -145,7 +143,7 @@ private:
QString m_privateKeyPassphrase;
void updateProtocolConfigModel(int serverIndex, int containerIndex, int protocolIndex);
void updateProtocolConfigModel(const QString &serverId, int containerIndex, int protocolIndex);
};
#endif // INSTALLUICONTROLLER_H

View File

@@ -1,37 +1,38 @@
#include "serversUiController.h"
#include "core/utils/api/apiEnums.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/api/apiUtils.h"
#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/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include <QJsonDocument>
#include <QJsonArray>
#include "core/models/serverConfig.h"
#include "core/models/protocolConfig.h"
#include "core/models/containerConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
using namespace amnezia;
namespace
namespace {
int rowForServerId(const QVector<ServerDescription> &list, const QString &serverId)
{
namespace configKey
{
constexpr char apiConfig[] = "api_config";
constexpr char serverCountryCode[] = "server_country_code";
constexpr char serverCountryName[] = "server_country_name";
constexpr char userCountryCode[] = "user_country_code";
constexpr char serviceType[] = "service_type";
if (serverId.isEmpty()) {
return -1;
}
for (int i = 0; i < list.size(); ++i) {
if (list.at(i).serverId == serverId) {
return i;
}
}
return -1;
}
bool descriptionsHaveGatewayServers(const QVector<ServerDescription> &list)
{
for (const auto &d : list) {
if (d.isServerFromGatewayApi) {
return true;
}
}
return false;
}
} // namespace
ServersUiController::ServersUiController(ServersController* serversController,
SettingsController* settingsController,
ServersModel* serversModel,
@@ -47,48 +48,70 @@ ServersUiController::ServersUiController(ServersController* serversController,
{
}
void ServersUiController::removeServer(int index)
void ServersUiController::removeServer(const QString &serverId)
{
m_serversController->removeServer(index);
updateModel();
}
void ServersUiController::editServerName(int index, const QString &name)
{
ServerConfig serverConfig = m_serversController->getServerConfig(index);
if (serverConfig.isApiV1()) {
ApiV1ServerConfig* apiV1 = serverConfig.as<ApiV1ServerConfig>();
if (apiV1) {
apiV1->name = name;
}
} else if (serverConfig.isApiV2()) {
ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
if (apiV2) {
apiV2->name = name;
apiV2->nameOverriddenByUser = true;
}
} else {
serverConfig.visit([&name](auto& arg) {
arg.description = name;
});
if (serverId.isEmpty()) {
return;
}
m_serversController->editServer(index, serverConfig);
m_serversController->removeServer(serverId);
updateModel();
}
void ServersUiController::setDefaultServerIndex(int index)
void ServersUiController::removeServerAtIndex(int index)
{
m_serversController->setDefaultServerIndex(index);
updateModel();
emit defaultServerIndexChanged(index);
const QString serverId = getServerId(index);
if (!serverId.isEmpty()) {
removeServer(serverId);
}
}
void ServersUiController::setDefaultContainer(int serverIndex, int containerIndex)
void ServersUiController::setDefaultServerAtIndex(int index)
{
const QString serverId = getServerId(index);
if (!serverId.isEmpty()) {
setDefaultServer(serverId);
}
}
void ServersUiController::setDefaultContainerAtIndex(int index, int containerIndex)
{
const QString serverId = getServerId(index);
if (!serverId.isEmpty()) {
setDefaultContainer(serverId, containerIndex);
}
}
void ServersUiController::editServerName(const QString &serverId, const QString &name)
{
if (serverId.isEmpty()) {
return;
}
if (!m_serversController->renameServer(serverId, name)) {
emit errorOccurred(tr("Legacy API v1 configs are no longer supported. Remove this server to continue."));
emit finished(tr("Use the remove action to delete this legacy config."));
return;
}
updateModel();
}
void ServersUiController::setDefaultServer(const QString &serverId)
{
if (serverId.isEmpty()) {
return;
}
m_serversController->setDefaultServer(serverId);
updateModel();
emit defaultServerIdChanged(serverId);
}
void ServersUiController::setDefaultContainer(const QString &serverId, int containerIndex)
{
if (serverId.isEmpty()) {
return;
}
auto container = static_cast<DockerContainer>(containerIndex);
m_serversController->setDefaultContainer(serverIndex, container);
m_serversController->setDefaultContainer(serverId, container);
updateModel();
}
@@ -98,129 +121,127 @@ void ServersUiController::toggleAmneziaDns(bool enabled)
updateModel();
}
void ServersUiController::onDefaultServerChanged(int index)
void ServersUiController::onDefaultServerChanged(const QString &/*defaultServerId*/)
{
setProcessedServerIndex(index);
updateModel();
setProcessedServerId(m_serversController->getDefaultServerId());
updateDefaultServerContainersModel();
emit defaultServerIndexChanged(index);
emit defaultServerIdChanged(m_serversController->getDefaultServerId());
}
void ServersUiController::updateModel()
{
int defaultIndex = m_serversController->getDefaultServerIndex();
bool wasEmpty = !hasServersFromGatewayApi();
int serversCount = m_serversController->getServersCount();
QVector<ServerDescription> descriptions =
m_serversController->buildServerDescriptions(m_settingsController->isAmneziaDnsEnabled());
if (m_processedServerIndex >= serversCount) {
setProcessedServerIndex(defaultIndex);
} else if (m_processedServerIndex >= 0) {
setProcessedServerIndex(m_processedServerIndex);
const QString defaultServerId = m_serversController->getDefaultServerId();
const bool hadServersFromGatewayBefore = descriptionsHaveGatewayServers(m_orderedServerDescriptions);
const bool hasServersFromGatewayNow = descriptionsHaveGatewayServers(descriptions);
const int listCount = descriptions.size();
const int defaultRowInDescriptions = rowForServerId(descriptions, defaultServerId);
m_orderedServerDescriptions = descriptions;
if (listCount == 0) {
setProcessedServerId(QString());
} else if (m_processedServerIndex >= listCount) {
setProcessedServerId(defaultServerId);
} else if (!m_processedServerId.isEmpty()) {
const int row = rowForServerId(m_orderedServerDescriptions, m_processedServerId);
if (row < 0) {
setProcessedServerId(defaultServerId);
} else {
setProcessedServerId(m_processedServerId);
}
} else if (defaultRowInDescriptions >= 0) {
setProcessedServerId(defaultServerId);
}
m_serversModel->updateModel(m_serversController->getServers(), defaultIndex, m_settingsController->isAmneziaDnsEnabled());
m_serversModel->updateModel(m_orderedServerDescriptions, defaultRowInDescriptions);
updateContainersModel();
updateDefaultServerContainersModel();
bool isEmpty = !hasServersFromGatewayApi();
if (wasEmpty != isEmpty) {
if (hadServersFromGatewayBefore != hasServersFromGatewayNow) {
emit hasServersFromGatewayApiChanged();
}
emit defaultServerIndexChanged(defaultIndex);
emit defaultServerIdChanged(defaultServerId);
emit defaultServerIndexChanged(defaultServerIndex());
}
int ServersUiController::getDefaultServerIndex() const
QString ServersUiController::getDefaultServerId() const
{
return m_serversController->getDefaultServerIndex();
return m_serversController->getDefaultServerId();
}
QString ServersUiController::getDefaultServerName() const
{
int defaultIndex = getDefaultServerIndex();
return m_serversController->getServerConfig(defaultIndex).displayName();
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.serverName;
}
}
return QString();
}
QString ServersUiController::getDefaultServerDefaultContainerName() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
return ContainerUtils::containerHumanNames().value(server.defaultContainer());
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return ContainerUtils::containerHumanNames().value(description.defaultContainer);
}
}
return QString();
}
QString ServersUiController::getDefaultServerDescriptionCollapsed() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
QString description = getDefaultServerDescription(server, defaultIndex);
if (server.isApiConfig()) {
return description;
}
DockerContainer container = server.defaultContainer();
QString containerName = ContainerUtils::containerHumanNames().value(container);
QString protocolVersion;
QString hostName = server.hostName();
if (ContainerUtils::isAwgContainer(container)) {
ContainerConfig containerConfig = server.containerConfig(container);
if (auto* awgProtocolConfig = containerConfig.getAwgProtocolConfig()) {
QString version = awgProtocolConfig->serverConfig.protocolVersion;
if (version == protocols::awg::awgV2) {
protocolVersion = QObject::tr(" (version 2)");
} else if (version == protocols::awg::awgV1_5) {
protocolVersion = QObject::tr(" (version 1.5)");
}
if (container == DockerContainer::Awg && !awgProtocolConfig->serverConfig.isThirdPartyConfig) {
containerName = "AmneziaWG Legacy";
}
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.collapsedServerDescription;
}
}
return description + containerName + protocolVersion + " | " + hostName;
return QString();
}
QString ServersUiController::getDefaultServerImagePathCollapsed() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (!apiV2) return QString();
const QString countryCode = apiV2->apiConfig.serverCountryCode;
if (countryCode.isEmpty()) {
return "";
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
if (!description.isApiV2 || description.apiServerCountryCode.isEmpty()) {
return "";
}
const QString imageCode = apiUtils::countryCodeBaseForFlag(description.apiServerCountryCode.toUpper());
if (imageCode.isEmpty()) {
return QString();
}
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(imageCode);
}
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper());
}
return "";
}
QString ServersUiController::getDefaultServerDescriptionExpanded() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
QString description = getDefaultServerDescription(server, defaultIndex);
if (server.isApiConfig()) {
return description;
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.expandedServerDescription;
}
}
return description + server.hostName();
return QString();
}
bool ServersUiController::isDefaultServerDefaultContainerHasSplitTunneling() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
DockerContainer defaultContainer = server.defaultContainer();
ContainerConfig containerConfig = server.containerConfig(defaultContainer);
const QString defaultServerId = m_serversController->getDefaultServerId();
const DockerContainer defaultContainer = m_serversController->getDefaultContainer(defaultServerId);
const ContainerConfig containerConfig = m_serversController->getContainerConfig(defaultServerId, defaultContainer);
if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) {
auto hasSplitTunnelingFromAllowedIps = [](const QStringList& allowedIps, const QString& nativeConfig) -> bool {
@@ -265,16 +286,13 @@ bool ServersUiController::isDefaultServerDefaultContainerHasSplitTunneling() con
bool ServersUiController::isDefaultServerFromApi() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
const int configVersion = server.configVersion();
return configVersion == apiDefs::ConfigSource::Telegram
|| configVersion == apiDefs::ConfigSource::AmneziaGateway;
}
int ServersUiController::getProcessedServerIndex() const
{
return m_processedServerIndex;
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.isApiV2;
}
}
return false;
}
int ServersUiController::getProcessedContainerIndex() const
@@ -291,186 +309,196 @@ void ServersUiController::setProcessedContainerIndex(int index)
}
}
void ServersUiController::setProcessedServerIndex(int index)
QString ServersUiController::getProcessedServerId() const
{
if (index >= m_serversController->getServersCount()) {
return m_processedServerId;
}
void ServersUiController::setProcessedServerId(const QString &serverId)
{
const int index = serverId.isEmpty() ? -1 : serverIndexForId(serverId);
if (!serverId.isEmpty() && index < 0) {
return;
}
if (m_processedServerIndex != index) {
if (m_processedServerIndex != index || m_processedServerId != serverId) {
m_processedServerIndex = index;
m_processedServerId = serverId;
m_serversModel->setProcessedServerIndex(index);
if (index >= 0) {
updateContainersModel();
ServerConfig server = m_serversController->getServerConfig(index);
setProcessedContainerIndex(static_cast<int>(server.defaultContainer()));
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (apiV2 && !apiV2->apiConfig.availableCountries.isEmpty()) {
emit updateApiCountryModel();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == serverId) {
setProcessedContainerIndex(static_cast<int>(description.defaultContainer));
break;
}
emit updateApiServicesModel();
}
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId != serverId) {
continue;
}
if (description.isApiV2) {
if (description.isCountrySelectionAvailable && !description.apiAvailableCountries.isEmpty()) {
emit updateApiCountryModel();
}
emit updateApiServicesModel();
}
break;
}
}
emit processedServerIdChanged(m_processedServerId);
emit processedServerIndexChanged(m_processedServerIndex);
}
}
int ServersUiController::getProcessedServerIndex() const
{
return m_processedServerIndex;
}
void ServersUiController::setProcessedServerIndex(int index)
{
if (index < 0) {
setProcessedServerId(QString());
return;
}
const QString id = getServerId(index);
if (!id.isEmpty()) {
setProcessedServerId(id);
}
}
int ServersUiController::defaultServerIndex() const
{
return rowForServerId(m_orderedServerDescriptions, getDefaultServerId());
}
bool ServersUiController::processedServerIsPremium() const
{
ServerConfig server = m_serversController->getServerConfig(m_processedServerIndex);
if (server.isApiV1()) {
const ApiV1ServerConfig* apiV1 = server.as<ApiV1ServerConfig>();
return apiV1 ? apiV1->isPremium() : false;
} else if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
return apiV2 ? (apiV2->isPremium() || apiV2->isExternalPremium()) : false;
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == m_processedServerId) {
return description.isPremium;
}
}
return false;
}
const ServerCredentials ServersUiController::getProcessedServerCredentials() const
{
return m_serversController->getServerCredentials(m_processedServerIndex);
return m_serversController->getServerCredentials(m_processedServerId);
}
bool ServersUiController::isDefaultServerCurrentlyProcessed() const
{
return m_serversController->getDefaultServerIndex() == m_processedServerIndex;
return m_serversController->getDefaultServerId() == m_processedServerId;
}
bool ServersUiController::isProcessedServerHasWriteAccess() const
{
ServerCredentials credentials = m_serversController->getServerCredentials(m_processedServerIndex);
ServerCredentials credentials = m_serversController->getServerCredentials(m_processedServerId);
return (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty());
}
QString ServersUiController::getDefaultServerDescription(const ServerConfig& server, int index) const
QString ServersUiController::getDefaultServerDescription(const QString &serverId) const
{
QString description;
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (!apiV2) return QString();
if (!apiV2->apiConfig.serverCountryCode.isEmpty()) {
return apiV2->apiConfig.serverCountryName;
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == serverId) {
return description.baseDescription;
}
return apiV2->description;
} else if (server.isApiV1()) {
const ApiV1ServerConfig* apiV1 = server.as<ApiV1ServerConfig>();
return apiV1 ? apiV1->description : QString();
} else {
ServerCredentials credentials = m_serversController->getServerCredentials(index);
if (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty()) {
bool isAmneziaDnsEnabled = m_settingsController->isAmneziaDnsEnabled();
if (isAmneziaDnsEnabled && isAmneziaDnsContainerInstalled(index)) {
description += "Amnezia DNS | ";
}
} else {
if (server.dns1() == protocols::dns::amneziaDnsIp) {
description += "Amnezia DNS | ";
}
}
return description;
}
}
bool ServersUiController::isAmneziaDnsContainerInstalled(int serverIndex) const
{
const ServerConfig server = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = server.containers();
return containers.contains(DockerContainer::Dns);
return QString();
}
bool ServersUiController::hasServersFromGatewayApi() const
{
QVector<ServerConfig> servers = m_serversController->getServers();
for (const ServerConfig &server : servers) {
if (server.isApiV2()) {
return true;
}
}
return false;
return listHasServersFromGatewayApi();
}
bool ServersUiController::isAdVisible() const
{
int defaultIndex = getDefaultServerIndex();
if (defaultIndex < 0) {
const QString defaultServerId = m_serversController->getDefaultServerId();
if (defaultServerId.isEmpty()) {
return false;
}
ServerConfig server = m_serversController->getServerConfig(defaultIndex);
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (!apiV2) return false;
return apiV2->apiConfig.serviceInfo.isAdVisible;
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.isAdVisible;
}
}
return false;
}
QString ServersUiController::adHeader() const
{
int defaultIndex = getDefaultServerIndex();
if (defaultIndex < 0) {
const QString defaultServerId = m_serversController->getDefaultServerId();
if (defaultServerId.isEmpty()) {
return QString();
}
ServerConfig server = m_serversController->getServerConfig(defaultIndex);
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (!apiV2) return QString();
return apiV2->apiConfig.serviceInfo.adHeader;
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.adHeader;
}
}
return QString();
}
QString ServersUiController::adDescription() const
{
int defaultIndex = getDefaultServerIndex();
if (defaultIndex < 0) {
const QString defaultServerId = m_serversController->getDefaultServerId();
if (defaultServerId.isEmpty()) {
return QString();
}
ServerConfig server = m_serversController->getServerConfig(defaultIndex);
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (!apiV2) return QString();
return apiV2->apiConfig.serviceInfo.adDescription;
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.adDescription;
}
}
return QString();
}
QString ServersUiController::getServerId(int index) const
{
if (index < 0 || index >= m_orderedServerDescriptions.size()) {
return QString();
}
return m_orderedServerDescriptions.at(index).serverId;
}
int ServersUiController::getServerIndexById(const QString &serverId) const
{
return rowForServerId(m_orderedServerDescriptions, serverId);
}
void ServersUiController::updateContainersModel()
{
if (m_processedServerIndex < 0 || m_processedServerIndex >= m_serversController->getServersCount()) {
if (m_processedServerId.isEmpty()) {
return;
}
ServerConfig server = m_serversController->getServerConfig(m_processedServerIndex);
QMap<DockerContainer, ContainerConfig> containers = server.containers();
const QMap<DockerContainer, ContainerConfig> containers =
m_serversController->getServerContainersMap(m_processedServerId);
m_containersModel->updateModel(containers);
}
void ServersUiController::updateDefaultServerContainersModel()
{
int defaultIndex = m_serversController->getDefaultServerIndex();
if (defaultIndex < 0 || defaultIndex >= m_serversController->getServersCount()) {
const QString defaultServerId = m_serversController->getDefaultServerId();
if (defaultServerId.isEmpty()) {
return;
}
ServerConfig server = m_serversController->getServerConfig(defaultIndex);
QMap<DockerContainer, ContainerConfig> containers = server.containers();
const QMap<DockerContainer, ContainerConfig> containers =
m_serversController->getServerContainersMap(defaultServerId);
m_defaultServerContainersModel->updateModel(containers);
}
QStringList ServersUiController::getAllInstalledServicesName(int serverIndex) const
{
QStringList servicesName;
ServerConfig server = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = server.containers();
const QString serverId = getServerId(serverIndex);
const QMap<DockerContainer, ContainerConfig> containers = m_serversController->getServerContainersMap(serverId);
for (auto it = containers.begin(); it != containers.end(); ++it) {
DockerContainer container = it.key();
if (ContainerUtils::containerService(container) == ServiceType::Other) {
@@ -489,3 +517,13 @@ QStringList ServersUiController::getAllInstalledServicesName(int serverIndex) co
return servicesName;
}
int ServersUiController::serverIndexForId(const QString &serverId) const
{
return rowForServerId(m_orderedServerDescriptions, serverId);
}
bool ServersUiController::listHasServersFromGatewayApi() const
{
return descriptionsHaveGatewayServers(m_orderedServerDescriptions);
}

View File

@@ -6,35 +6,39 @@
#include <QSet>
#include <QJsonObject>
#include <QStringList>
#include <QVector>
#include "core/controllers/serversController.h"
#include "core/models/serverDescription.h"
#include "core/controllers/settingsController.h"
#include "ui/models/serversModel.h"
#include "ui/models/containersModel.h"
#include "core/models/serverConfig.h"
class ServersUiController : public QObject
{
Q_OBJECT
Q_PROPERTY(int defaultIndex READ getDefaultServerIndex NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerImagePathCollapsed READ getDefaultServerImagePathCollapsed NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded NOTIFY defaultServerIndexChanged)
Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY defaultServerIndexChanged)
Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerId READ getDefaultServerId NOTIFY defaultServerIdChanged)
Q_PROPERTY(int defaultServerIndex READ defaultServerIndex NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerIdChanged)
Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerIdChanged)
Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerIdChanged)
Q_PROPERTY(QString defaultServerImagePathCollapsed READ getDefaultServerImagePathCollapsed NOTIFY defaultServerIdChanged)
Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded NOTIFY defaultServerIdChanged)
Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY defaultServerIdChanged)
Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIdChanged)
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
Q_PROPERTY(QString processedServerId READ getProcessedServerId WRITE setProcessedServerId NOTIFY processedServerIdChanged)
Q_PROPERTY(int processedServerIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
Q_PROPERTY(int processedContainerIndex READ getProcessedContainerIndex WRITE setProcessedContainerIndex NOTIFY processedContainerIndexChanged)
Q_PROPERTY(bool processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerIndexChanged)
Q_PROPERTY(bool hasServersFromGatewayApi READ hasServersFromGatewayApi NOTIFY hasServersFromGatewayApiChanged)
Q_PROPERTY(bool isAdVisible READ isAdVisible NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString adHeader READ adHeader NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString adDescription READ adDescription NOTIFY defaultServerIndexChanged)
Q_PROPERTY(bool isAdVisible READ isAdVisible NOTIFY defaultServerIdChanged)
Q_PROPERTY(QString adHeader READ adHeader NOTIFY defaultServerIdChanged)
Q_PROPERTY(QString adDescription READ adDescription NOTIFY defaultServerIdChanged)
public:
explicit ServersUiController(ServersController* serversController,
@@ -45,15 +49,22 @@ public:
QObject *parent = nullptr);
public slots:
void removeServer(int index);
void editServerName(int index, const QString &name);
void setDefaultServerIndex(int index);
void setDefaultContainer(int serverIndex, int containerIndex);
void removeServer(const QString &serverId);
void removeServerAtIndex(int index);
void editServerName(const QString &serverId, const QString &name);
void setDefaultServer(const QString &serverId);
void setDefaultServerAtIndex(int index);
void setDefaultContainer(const QString &serverId, int containerIndex);
void setDefaultContainerAtIndex(int index, int containerIndex);
void toggleAmneziaDns(bool enabled);
void onDefaultServerChanged(int index);
void onDefaultServerChanged(const QString &defaultServerId);
// Getters for properties
int getDefaultServerIndex() const;
QString getDefaultServerId() const;
QString getDefaultServerName() const;
QString getDefaultServerDefaultContainerName() const;
QString getDefaultServerDescriptionCollapsed() const;
@@ -62,8 +73,14 @@ public slots:
bool isDefaultServerDefaultContainerHasSplitTunneling() const;
bool isDefaultServerFromApi() const;
QString getProcessedServerId() const;
void setProcessedServerId(const QString &serverId);
int getProcessedServerIndex() const;
void setProcessedServerIndex(int index);
int defaultServerIndex() const;
int getProcessedContainerIndex() const;
void setProcessedContainerIndex(int index);
bool processedServerIsPremium() const;
@@ -78,12 +95,16 @@ public slots:
QString adHeader() const;
QString adDescription() const;
QString getServerId(int index) const;
int getServerIndexById(const QString &serverId) const;
QStringList getAllInstalledServicesName(int serverIndex) const;
signals:
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
void defaultServerIdChanged(const QString &serverId);
void defaultServerIndexChanged(int index);
void processedServerIdChanged(const QString &serverId);
void processedServerIndexChanged(int index);
void processedContainerIndexChanged(int index);
void hasServersFromGatewayApiChanged();
@@ -94,20 +115,23 @@ public:
void updateModel();
private:
QString getDefaultServerDescription(const ServerConfig& server, int index) const;
bool isAmneziaDnsContainerInstalled(int serverIndex) const;
QString getDefaultServerDescription(const QString &serverId) const;
int serverIndexForId(const QString &serverId) const;
bool listHasServersFromGatewayApi() const;
void updateContainersModel();
void updateDefaultServerContainersModel();
void updateApiModelsForProcessedServer();
ServersController* m_serversController;
SettingsController* m_settingsController;
ServersModel* m_serversModel;
ContainersModel* m_containersModel;
ContainersModel* m_defaultServerContainersModel;
QVector<amnezia::ServerDescription> m_orderedServerDescriptions;
int m_processedServerIndex = -1;
QString m_processedServerId;
int m_processedContainerIndex = -1;
};

View File

@@ -107,7 +107,9 @@ void SettingsUiController::exportLogsFile(const QString &fileName)
#ifdef Q_OS_ANDROID
AndroidController::instance()->exportLogsFile(fileName);
#else
SystemController::saveFile(fileName, Logger::getLogFile());
if (!SystemController::saveFile(fileName, Logger::getLogFile())) {
qInfo() << "SettingsUiController::exportLogsFile: save or share was cancelled or failed";
}
#endif
}
@@ -116,7 +118,9 @@ void SettingsUiController::exportServiceLogsFile(const QString &fileName)
#ifdef Q_OS_ANDROID
AndroidController::instance()->exportLogsFile(fileName);
#else
SystemController::saveFile(fileName, Logger::getServiceLogFile());
if (!SystemController::saveFile(fileName, Logger::getServiceLogFile())) {
qInfo() << "SettingsUiController::exportServiceLogsFile: save or share was cancelled or failed";
}
#endif
}
@@ -132,7 +136,9 @@ void SettingsUiController::clearLogs()
void SettingsUiController::backupAppConfig(const QString &fileName)
{
QByteArray data = m_settingsController->backupAppConfig();
SystemController::saveFile(fileName, data);
if (!SystemController::saveFile(fileName, data)) {
qInfo() << "SettingsUiController::backupAppConfig: save or share was cancelled or failed";
}
}
void SettingsUiController::restoreAppConfig(const QString &fileName)

View File

@@ -1,5 +1,6 @@
#include "systemController.h"
#include <QDebug>
#include <QDesktopServices>
#include <QDir>
#include <QEventLoop>
@@ -24,11 +25,20 @@ SystemController::SystemController(QObject *parent)
{
}
void SystemController::saveFile(const QString &fileName, const QString &data)
bool SystemController::saveFile(const QString &fileName, const QString &data)
{
#if defined Q_OS_ANDROID
AndroidController::instance()->saveFile(fileName, data);
return;
return true;
#endif
return saveFile(fileName, data.toUtf8());
}
bool SystemController::saveFile(const QString &fileName, const QByteArray &data)
{
#if defined Q_OS_ANDROID
AndroidController::instance()->saveFile(fileName, QString::fromUtf8(data));
return true;
#endif
#ifdef Q_OS_IOS
@@ -39,17 +49,20 @@ void SystemController::saveFile(const QString &fileName, const QString &data)
#endif
if (!file.open(QIODevice::WriteOnly)) {
return;
qWarning() << "SystemController::saveFile: cannot open" << fileName;
return false;
}
if (file.write(data) != data.size()) {
qWarning() << "SystemController::saveFile: write failed" << fileName;
file.close();
return false;
}
file.write(data.toUtf8());
file.close();
#ifdef Q_OS_IOS
QStringList filesToSend;
filesToSend.append(fileUrl.toString());
// todo check if save successful
IosController::Instance()->shareText(filesToSend);
return;
return IosController::Instance()->shareText(filesToSend);
#else
QFileInfo fi(fileName);
@@ -62,6 +75,7 @@ void SystemController::saveFile(const QString &fileName, const QString &data)
#ifndef MACOS_NE
QDesktopServices::openUrl(url);
#endif
return true;
#endif
}

View File

@@ -1,6 +1,7 @@
#ifndef SYSTEMCONTROLLER_H
#define SYSTEMCONTROLLER_H
#include <QByteArray>
#include <QObject>
class SystemController : public QObject
@@ -9,7 +10,8 @@ class SystemController : public QObject
public:
explicit SystemController(QObject *parent = nullptr);
static void saveFile(const QString &fileName, const QString &data);
static bool saveFile(const QString &fileName, const QString &data);
static bool saveFile(const QString &fileName, const QByteArray &data);
static bool readFile(const QString &fileName, QByteArray &data);
static bool readFile(const QString &fileName, QString &data);

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