diff --git a/.gitmodules b/.gitmodules
index 3ceaa56e..decab9b7 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "client/3rd/OpenVPNAdapter"]
- path = client/3rd/OpenVPNAdapter
- url = https://github.com/amnezia-vpn/OpenVPNAdapter.git
[submodule "client/3rd/qtkeychain"]
path = client/3rd/qtkeychain
url = https://github.com/frankosterfeld/qtkeychain.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 434b195f..7c602249 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
-project(${PROJECT} VERSION 4.8.3.1
+project(${PROJECT} VERSION 4.8.4.2
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
-set(APP_ANDROID_VERSION_CODE 2074)
+set(APP_ANDROID_VERSION_CODE 2079)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
diff --git a/README.md b/README.md
index 368bcfff..992c3ad0 100644
--- a/README.md
+++ b/README.md
@@ -13,13 +13,13 @@
[](https://amnezia.org)
-### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/kldscp/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
+### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
> [!TIP]
-> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/kldscp/amnezia.org).
+> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org ).
-
+
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
diff --git a/README_RU.md b/README_RU.md
index 45b506f2..f9ca7d12 100644
--- a/README_RU.md
+++ b/README_RU.md
@@ -10,12 +10,12 @@
[](https://amnezia.org)
-### [Сайт](https://amnezia.org) | [Зеркало на сайт](https://storage.googleapis.com/kldscp/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
+### [Сайт](https://amnezia.org) | [Зеркало на сайт](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
> [!TIP]
-> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/kldscp/amnezia.org).
+> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
-
+
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt
index ba580dc5..e555c78b 160000
--- a/client/3rd-prebuilt
+++ b/client/3rd-prebuilt
@@ -1 +1 @@
-Subproject commit ba580dc5bd7784f7b1e110ff0365f3286e549a61
+Subproject commit e555c78bcf44070d5c88bcca54480732c9164f18
diff --git a/client/3rd/OpenVPNAdapter b/client/3rd/OpenVPNAdapter
deleted file mode 160000
index 7c821a8d..00000000
--- a/client/3rd/OpenVPNAdapter
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 7c821a8d5c1ad5ad94e0763b4f25a875b5a6fe1b
diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt
index 3ef92385..72adaf25 100644
--- a/client/CMakeLists.txt
+++ b/client/CMakeLists.txt
@@ -96,11 +96,6 @@ configure_file(${CMAKE_CURRENT_LIST_DIR}/translations/translations.qrc.in ${CMAK
qt6_add_resources(QRC ${I18NQRC} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
# -- i18n end
-if(IOS)
- execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/ios/scripts/openvpn.sh args
- WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
-endif()
-
set(IS_CI ${CI})
if(IS_CI)
message("Detected CI env")
@@ -110,8 +105,8 @@ if(IS_CI)
endif()
endif()
-
include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/sources.cmake)
include_directories(
${CMAKE_CURRENT_LIST_DIR}/../ipc
@@ -120,167 +115,22 @@ include_directories(
${CMAKE_CURRENT_BINARY_DIR}
)
-configure_file(${CMAKE_CURRENT_LIST_DIR}/../version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
-
-set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/migrations.h
- ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc.h
- ${CMAKE_CURRENT_LIST_DIR}/amnezia_application.h
- ${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.h
- ${CMAKE_CURRENT_LIST_DIR}/core/defs.h
- ${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.h
- ${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h
- ${CMAKE_CURRENT_LIST_DIR}/core/server_defs.h
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/apiController.h
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.h
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/pages.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
- ${CMAKE_CURRENT_BINARY_DIR}/version.h
- ${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h
- ${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.h
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/serialization.h
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/transfer.h
- ${CMAKE_CURRENT_LIST_DIR}/core/enums/apiEnums.h
- ${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.h
- ${CMAKE_CURRENT_LIST_DIR}/utils/qmlUtils.h
-)
-
-# Mozilla headres
-set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.h
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.h
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.h
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/controllerimpl.h
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.h
-)
-
include_directories(mozilla)
include_directories(mozilla/shared)
include_directories(mozilla/models)
-if(NOT IOS)
- set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h
- )
-endif()
-
-if(NOT ANDROID)
- set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
- )
-endif()
-
-set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/migrations.cpp
- ${CMAKE_CURRENT_LIST_DIR}/amnezia_application.cpp
- ${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/server_defs.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/apiController.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp
- ${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/outbound.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/inbound.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/ss.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/ssd.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vless.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/trojan.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess_new.cpp
- ${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.cpp
- ${CMAKE_CURRENT_LIST_DIR}/utils/qmlUtils.cpp
-)
-
-# Mozilla sources
-set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.cpp
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.cpp
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.cpp
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.cpp
-)
+configure_file(${CMAKE_CURRENT_LIST_DIR}/../version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
endif()
-if(NOT IOS)
- set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp
- )
-endif()
-
-if(NOT ANDROID)
- set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
- )
-endif()
-
-file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.h)
-file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
-
-file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/pages_logic/*.h)
-file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/pages_logic/*.cpp)
-
-file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.h)
-file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.cpp)
-
-file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.h
-)
-file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.cpp
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.cpp
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.cpp
-)
-
-file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h)
-file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp)
-
-set(HEADERS ${HEADERS}
- ${COMMON_FILES_H}
- ${PAGE_LOGIC_H}
- ${CONFIGURATORS_H}
- ${UI_MODELS_H}
- ${UI_CONTROLLERS_H}
-)
-set(SOURCES ${SOURCES}
- ${COMMON_FILES_CPP}
- ${PAGE_LOGIC_CPP}
- ${CONFIGURATORS_CPP}
- ${UI_MODELS_CPP}
- ${UI_CONTROLLERS_CPP}
-)
-
if(WIN32)
configure_file(
${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in
${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
)
- set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.h
- )
-
- set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
- )
-
- set(RESOURCES ${RESOURCES}
- ${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
- )
-
set(LIBS ${LIBS}
user32
rasapi32
@@ -324,30 +174,6 @@ endif()
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
message("Client desktop build")
add_compile_definitions(AMNEZIA_DESKTOP)
-
- set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.h
- ${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/systemtray_notificationhandler.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnprotocol.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/xrayprotocol.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.h
- )
-
- set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.cpp
- ${CMAKE_CURRENT_LIST_DIR}/ui/systemtray_notificationhandler.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/xrayprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.cpp
- )
endif()
if(ANDROID)
diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp
index aeed439b..f32d525a 100644
--- a/client/amnezia_application.cpp
+++ b/client/amnezia_application.cpp
@@ -2,6 +2,8 @@
#include
#include
+#include
+#include
#include
#include
#include
@@ -10,26 +12,16 @@
#include
#include
#include
-#include
-#include
#include "logger.h"
+#include "ui/controllers/pageController.h"
#include "ui/models/installedAppsModel.h"
#include "version.h"
#include "platforms/ios/QRCodeReaderBase.h"
-#if defined(Q_OS_ANDROID)
- #include "core/installedAppsImageProvider.h"
- #include "platforms/android/android_controller.h"
-#endif
#include "protocols/qml_register_protocols.h"
-#if defined(Q_OS_IOS)
- #include "platforms/ios/ios_controller.h"
- #include
-#endif
-
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
{
setQuitOnLastWindowClosed(false);
@@ -84,79 +76,12 @@ void AmneziaApplication::init()
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
- initModels();
- loadTranslator();
- initControllers();
-
-#ifdef Q_OS_ANDROID
- if (!AndroidController::initLogging()) {
- qFatal("Android logging initialization failed");
- }
- AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
- connect(m_settings.get(), &Settings::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
-
- AndroidController::instance()->setScreenshotsEnabled(m_settings->isScreenshotsEnabled());
- connect(m_settings.get(), &Settings::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
-
- connect(m_settings.get(), &Settings::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
-
- connect(m_settings.get(), &Settings::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
-
- connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) {
- m_connectionController->onConnectionStateChanged(state);
- if (m_vpnConnection)
- m_vpnConnection->restoreConnection();
- });
- if (!AndroidController::instance()->initialize()) {
- qFatal("Android controller initialization failed");
- }
-
- connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) {
- emit m_pageController->goToPageHome();
- m_importController->extractConfigFromData(data);
- data.clear();
- emit m_pageController->goToPageViewConfig();
- });
-
- m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
-#endif
-
-#ifdef Q_OS_IOS
- IosController::Instance()->initialize();
- connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
- emit m_pageController->goToPageHome();
- m_importController->extractConfigFromData(data);
- emit m_pageController->goToPageViewConfig();
- });
-
- connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) {
- emit m_pageController->goToPageHome();
- m_pageController->goToPageSettingsBackup();
- emit m_settingsController->importBackupFromOutside(filePath);
- });
-
- QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
-
- connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); });
-#endif
-
-#ifndef Q_OS_ANDROID
- m_notificationHandler.reset(NotificationHandler::create(nullptr));
-
- connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
- &NotificationHandler::setConnectionState);
-
- connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(), &PageController::raiseMainWindow);
- connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
- static_cast(&ConnectionController::openConnection));
- connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
- &ConnectionController::closeConnection);
- connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
-#endif
+ m_coreController.reset(new CoreController(m_vpnConnection, m_settings, m_engine));
m_engine->addImportPath("qrc:/ui/qml/Modules/");
m_engine->load(url);
- m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
+
+ m_coreController->setQmlRoot();
bool enabled = m_settings->isSaveLogs();
#ifndef Q_OS_ANDROID
@@ -168,13 +93,13 @@ void AmneziaApplication::init()
#endif
Logger::setServiceLogsEnabled(enabled);
-#ifdef Q_OS_WIN
+#ifdef Q_OS_WIN //TODO
if (m_parser.isSet("a"))
- m_pageController->showOnStartup();
+ m_coreController->pageController()->showOnStartup();
else
- emit m_pageController->raiseMainWindow();
+ emit m_coreController->pageController()->raiseMainWindow();
#else
- m_pageController->showOnStartup();
+ m_coreController->pageController()->showOnStartup();
#endif
// Android TextArea clipboard workaround
@@ -231,33 +156,6 @@ void AmneziaApplication::loadFonts()
QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf");
}
-void AmneziaApplication::loadTranslator()
-{
- auto locale = m_settings->getAppLanguage();
- m_translator.reset(new QTranslator());
- updateTranslator(locale);
-}
-
-void AmneziaApplication::updateTranslator(const QLocale &locale)
-{
- if (!m_translator->isEmpty()) {
- QCoreApplication::removeTranslator(m_translator.get());
- }
-
- QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
- if (m_translator->load(strFileName)) {
- if (QCoreApplication::installTranslator(m_translator.get())) {
- m_settings->setAppLanguage(locale);
- }
- } else {
- m_settings->setAppLanguage(QLocale::English);
- }
-
- m_engine->retranslate();
-
- emit translationsUpdated();
-}
-
bool AmneziaApplication::parseCommands()
{
m_parser.setApplicationDescription(APPLICATION_NAME);
@@ -282,19 +180,20 @@ bool AmneziaApplication::parseCommands()
}
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
-void AmneziaApplication::startLocalServer() {
+void AmneziaApplication::startLocalServer()
+{
const QString serverName("AmneziaVPNInstance");
QLocalServer::removeServer(serverName);
- QLocalServer* server = new QLocalServer(this);
+ QLocalServer *server = new QLocalServer(this);
server->listen(serverName);
QObject::connect(server, &QLocalServer::newConnection, this, [server, this]() {
if (server) {
- QLocalSocket* clientConnection = server->nextPendingConnection();
+ QLocalSocket *clientConnection = server->nextPendingConnection();
clientConnection->deleteLater();
}
- emit m_pageController->raiseMainWindow();
+ emit m_coreController->pageController()->raiseMainWindow(); //TODO
});
}
#endif
@@ -304,163 +203,12 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
return m_engine;
}
-void AmneziaApplication::initModels()
+QNetworkAccessManager *AmneziaApplication::networkManager()
{
- m_containersModel.reset(new ContainersModel(this));
- m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
-
- m_defaultServerContainersModel.reset(new ContainersModel(this));
- m_engine->rootContext()->setContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel.get());
-
- m_serversModel.reset(new ServersModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
- connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
- connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
- &ContainersModel::updateModel);
- m_serversModel->resetModel();
-
- m_languageModel.reset(new LanguageModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
- connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator);
- connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
-
- m_sitesModel.reset(new SitesModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
-
- m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
-
- m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
-
- m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
- m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
-
- m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
- m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
-
- m_cloakConfigModel.reset(new CloakConfigModel(this));
- m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
-
- m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
- m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
-
- m_awgConfigModel.reset(new AwgConfigModel(this));
- m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
-
- m_xrayConfigModel.reset(new XrayConfigModel(this));
- m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
-
-#ifdef Q_OS_WINDOWS
- m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
- m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
-#endif
-
- m_sftpConfigModel.reset(new SftpConfigModel(this));
- m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
-
- m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
- m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
-
- m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
- connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),
- &ServersModel::clearCachedProfile);
-
- m_apiServicesModel.reset(new ApiServicesModel(this));
- m_engine->rootContext()->setContextProperty("ApiServicesModel", m_apiServicesModel.get());
-
- m_apiCountryModel.reset(new ApiCountryModel(this));
- m_engine->rootContext()->setContextProperty("ApiCountryModel", m_apiCountryModel.get());
- connect(m_serversModel.get(), &ServersModel::updateApiLanguageModel, this, [this]() {
- m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
- m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
- });
- connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this,
- [this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); });
+ return m_nam;
}
-void AmneziaApplication::initControllers()
+QClipboard *AmneziaApplication::getClipboard()
{
- m_connectionController.reset(
- new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings));
- m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
-
- connect(m_connectionController.get(), qOverload(&ConnectionController::connectionErrorOccurred), this,
- [this](const QString &errorMessage) {
- emit m_pageController->showErrorMessage(errorMessage);
- emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
- });
-
- connect(m_connectionController.get(), qOverload(&ConnectionController::connectionErrorOccurred), this,
- [this](ErrorCode errorCode) {
- emit m_pageController->showErrorMessage(errorCode);
- emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
- });
-
- connect(m_connectionController.get(), &ConnectionController::connectButtonClicked, m_connectionController.get(),
- &ConnectionController::toggleConnection, Qt::QueuedConnection);
-
- m_pageController.reset(new PageController(m_serversModel, m_settings));
- m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
-
- m_focusController.reset(new FocusController(m_engine, this));
- m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get());
-
- m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel,
- m_apiServicesModel, m_settings));
- m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
- connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
- &PageController::showPassphraseRequestDrawer);
- connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
- &InstallController::setEncryptedPassphrase);
- connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
- &ConnectionController::onCurrentContainerUpdated);
-
- connect(m_installController.get(), &InstallController::updateServerFromApiFinished, this, [this]() {
- disconnect(m_reloadConfigErrorOccurredConnection);
- emit m_connectionController->configFromApiUpdated();
- });
-
- connect(m_connectionController.get(), &ConnectionController::updateApiConfigFromGateway, this, [this]() {
- m_reloadConfigErrorOccurredConnection = connect(
- m_installController.get(), qOverload(&InstallController::installationErrorOccurred), this,
- [this]() { emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); },
- static_cast(Qt::AutoConnection || Qt::SingleShotConnection));
- m_installController->updateServiceFromApi(m_serversModel->getDefaultServerIndex(), "", "");
- });
-
- connect(m_connectionController.get(), &ConnectionController::updateApiConfigFromTelegram, this, [this]() {
- m_reloadConfigErrorOccurredConnection = connect(
- m_installController.get(), qOverload(&InstallController::installationErrorOccurred), this,
- [this]() { emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); },
- static_cast(Qt::AutoConnection || Qt::SingleShotConnection));
- m_serversModel->removeApiConfig(m_serversModel->getDefaultServerIndex());
- m_installController->updateServiceFromTelegram(m_serversModel->getDefaultServerIndex());
- });
-
- connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
-
- m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
- m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
-
- m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, m_settings));
- m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
-
- m_settingsController.reset(
- new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
- m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
- if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
- QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
- }
- connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
-
- m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
- m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
-
- m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
- m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
-
- m_systemController.reset(new SystemController(m_settings));
- m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
+ return this->clipboard();
}
diff --git a/client/amnezia_application.h b/client/amnezia_application.h
index cfeac0d1..ea5f6f52 100644
--- a/client/amnezia_application.h
+++ b/client/amnezia_application.h
@@ -11,44 +11,12 @@
#else
#include
#endif
+#include
+#include "core/controllers/coreController.h"
#include "settings.h"
#include "vpnconnection.h"
-#include "ui/controllers/connectionController.h"
-#include "ui/controllers/exportController.h"
-#include "ui/controllers/importController.h"
-#include "ui/controllers/installController.h"
-#include "ui/controllers/focusController.h"
-#include "ui/controllers/pageController.h"
-#include "ui/controllers/settingsController.h"
-#include "ui/controllers/sitesController.h"
-#include "ui/controllers/systemController.h"
-#include "ui/controllers/appSplitTunnelingController.h"
-#include "ui/models/containers_model.h"
-#include "ui/models/languageModel.h"
-#include "ui/models/protocols/cloakConfigModel.h"
-#ifndef Q_OS_ANDROID
- #include "ui/notificationhandler.h"
-#endif
-#ifdef Q_OS_WINDOWS
- #include "ui/models/protocols/ikev2ConfigModel.h"
-#endif
-#include "ui/models/protocols/awgConfigModel.h"
-#include "ui/models/protocols/openvpnConfigModel.h"
-#include "ui/models/protocols/shadowsocksConfigModel.h"
-#include "ui/models/protocols/wireguardConfigModel.h"
-#include "ui/models/protocols/xrayConfigModel.h"
-#include "ui/models/protocols_model.h"
-#include "ui/models/servers_model.h"
-#include "ui/models/services/sftpConfigModel.h"
-#include "ui/models/services/socks5ProxyConfigModel.h"
-#include "ui/models/sites_model.h"
-#include "ui/models/clientManagementModel.h"
-#include "ui/models/appSplitTunnelingModel.h"
-#include "ui/models/apiServicesModel.h"
-#include "ui/models/apiCountryModel.h"
-
#define amnApp (static_cast(QCoreApplication::instance()))
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
@@ -67,8 +35,6 @@ public:
void init();
void registerTypes();
void loadFonts();
- void loadTranslator();
- void updateTranslator(const QLocale &locale);
bool parseCommands();
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
@@ -76,68 +42,24 @@ public:
#endif
QQmlApplicationEngine *qmlEngine() const;
- QNetworkAccessManager *manager() { return m_nam; }
-
-signals:
- void translationsUpdated();
+ QNetworkAccessManager *networkManager();
+ QClipboard *getClipboard();
private:
- void initModels();
- void initControllers();
-
QQmlApplicationEngine *m_engine {};
std::shared_ptr m_settings;
+ QScopedPointer m_coreController;
+
QSharedPointer m_containerProps;
QSharedPointer m_protocolProps;
- QSharedPointer m_translator;
QCommandLineParser m_parser;
- QSharedPointer m_containersModel;
- QSharedPointer m_defaultServerContainersModel;
- QSharedPointer m_serversModel;
- QSharedPointer m_languageModel;
- QSharedPointer m_protocolsModel;
- QSharedPointer m_sitesModel;
- QSharedPointer m_appSplitTunnelingModel;
- QSharedPointer m_clientManagementModel;
- QSharedPointer m_apiServicesModel;
- QSharedPointer m_apiCountryModel;
-
- QScopedPointer m_openVpnConfigModel;
- QScopedPointer m_shadowSocksConfigModel;
- QScopedPointer m_cloakConfigModel;
- QScopedPointer m_xrayConfigModel;
- QScopedPointer m_wireGuardConfigModel;
- QScopedPointer m_awgConfigModel;
-#ifdef Q_OS_WINDOWS
- QScopedPointer m_ikev2ConfigModel;
-#endif
-
- QScopedPointer m_sftpConfigModel;
- QScopedPointer m_socks5ConfigModel;
-
QSharedPointer m_vpnConnection;
QThread m_vpnConnectionThread;
-#ifndef Q_OS_ANDROID
- QScopedPointer m_notificationHandler;
-#endif
-
- QScopedPointer m_connectionController;
- QScopedPointer m_focusController;
- QScopedPointer m_pageController;
- QScopedPointer m_installController;
- QScopedPointer m_importController;
- QScopedPointer m_exportController;
- QScopedPointer m_settingsController;
- QScopedPointer m_sitesController;
- QScopedPointer m_systemController;
- QScopedPointer m_appSplitTunnelingController;
QNetworkAccessManager *m_nam;
-
- QMetaObject::Connection m_reloadConfigErrorOccurredConnection;
};
#endif // AMNEZIA_APPLICATION_H
diff --git a/client/cmake/ios.cmake b/client/cmake/ios.cmake
index 5fda3506..58192237 100644
--- a/client/cmake/ios.cmake
+++ b/client/cmake/ios.cmake
@@ -76,11 +76,7 @@ set_target_properties(${PROJECT} PROPERTIES
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
XCODE_EMBED_APP_EXTENSIONS networkextension
- XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
- XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
- XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
- XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN"
- XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN"
+ XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
)
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
@@ -126,9 +122,9 @@ add_subdirectory(ios/networkextension)
add_dependencies(${PROJECT} networkextension)
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
- "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
+ "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework"
)
-set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos)
-target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework")
+set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/)
+target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework")
diff --git a/client/cmake/sources.cmake b/client/cmake/sources.cmake
new file mode 100644
index 00000000..c3af531a
--- /dev/null
+++ b/client/cmake/sources.cmake
@@ -0,0 +1,191 @@
+set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
+
+set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/migrations.h
+ ${CLIENT_ROOT_DIR}/../ipc/ipc.h
+ ${CLIENT_ROOT_DIR}/amnezia_application.h
+ ${CLIENT_ROOT_DIR}/containers/containers_defs.h
+ ${CLIENT_ROOT_DIR}/core/defs.h
+ ${CLIENT_ROOT_DIR}/core/errorstrings.h
+ ${CLIENT_ROOT_DIR}/core/scripts_registry.h
+ ${CLIENT_ROOT_DIR}/core/server_defs.h
+ ${CLIENT_ROOT_DIR}/core/api/apiDefs.h
+ ${CLIENT_ROOT_DIR}/core/qrCodeUtils.h
+ ${CLIENT_ROOT_DIR}/core/controllers/coreController.h
+ ${CLIENT_ROOT_DIR}/core/controllers/gatewayController.h
+ ${CLIENT_ROOT_DIR}/core/controllers/serverController.h
+ ${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.h
+ ${CLIENT_ROOT_DIR}/protocols/protocols_defs.h
+ ${CLIENT_ROOT_DIR}/protocols/qml_register_protocols.h
+ ${CLIENT_ROOT_DIR}/ui/pages.h
+ ${CLIENT_ROOT_DIR}/ui/qautostart.h
+ ${CLIENT_ROOT_DIR}/protocols/vpnprotocol.h
+ ${CMAKE_CURRENT_BINARY_DIR}/version.h
+ ${CLIENT_ROOT_DIR}/core/sshclient.h
+ ${CLIENT_ROOT_DIR}/core/networkUtilities.h
+ ${CLIENT_ROOT_DIR}/core/serialization/serialization.h
+ ${CLIENT_ROOT_DIR}/core/serialization/transfer.h
+ ${CLIENT_ROOT_DIR}/../common/logger/logger.h
+ ${CLIENT_ROOT_DIR}/utils/qmlUtils.h
+ ${CLIENT_ROOT_DIR}/core/api/apiUtils.h
+)
+
+# Mozilla headres
+set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/mozilla/models/server.h
+ ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.h
+ ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.h
+ ${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h
+ ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
+)
+
+if(NOT IOS)
+ set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
+ )
+endif()
+
+if(NOT ANDROID)
+ set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/ui/notificationhandler.h
+ )
+endif()
+
+set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/migrations.cpp
+ ${CLIENT_ROOT_DIR}/amnezia_application.cpp
+ ${CLIENT_ROOT_DIR}/containers/containers_defs.cpp
+ ${CLIENT_ROOT_DIR}/core/errorstrings.cpp
+ ${CLIENT_ROOT_DIR}/core/scripts_registry.cpp
+ ${CLIENT_ROOT_DIR}/core/server_defs.cpp
+ ${CLIENT_ROOT_DIR}/core/qrCodeUtils.cpp
+ ${CLIENT_ROOT_DIR}/core/controllers/coreController.cpp
+ ${CLIENT_ROOT_DIR}/core/controllers/gatewayController.cpp
+ ${CLIENT_ROOT_DIR}/core/controllers/serverController.cpp
+ ${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.cpp
+ ${CLIENT_ROOT_DIR}/protocols/protocols_defs.cpp
+ ${CLIENT_ROOT_DIR}/ui/qautostart.cpp
+ ${CLIENT_ROOT_DIR}/protocols/vpnprotocol.cpp
+ ${CLIENT_ROOT_DIR}/core/sshclient.cpp
+ ${CLIENT_ROOT_DIR}/core/networkUtilities.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/outbound.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/inbound.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/ss.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/ssd.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/vless.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/trojan.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/vmess.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/vmess_new.cpp
+ ${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
+ ${CLIENT_ROOT_DIR}/utils/qmlUtils.cpp
+ ${CLIENT_ROOT_DIR}/core/api/apiUtils.cpp
+)
+
+# Mozilla sources
+set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/mozilla/models/server.cpp
+ ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.cpp
+ ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp
+ ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
+)
+
+if(NOT IOS)
+ set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
+ )
+endif()
+
+if(NOT ANDROID)
+ set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
+ )
+endif()
+
+file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.h)
+file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.cpp)
+
+file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.h)
+file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.cpp)
+
+file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.h)
+file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.cpp)
+
+file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
+ ${CLIENT_ROOT_DIR}/ui/models/*.h
+ ${CLIENT_ROOT_DIR}/ui/models/protocols/*.h
+ ${CLIENT_ROOT_DIR}/ui/models/services/*.h
+ ${CLIENT_ROOT_DIR}/ui/models/api/*.h
+)
+file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
+ ${CLIENT_ROOT_DIR}/ui/models/*.cpp
+ ${CLIENT_ROOT_DIR}/ui/models/protocols/*.cpp
+ ${CLIENT_ROOT_DIR}/ui/models/services/*.cpp
+ ${CLIENT_ROOT_DIR}/ui/models/api/*.cpp
+)
+
+file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS
+ ${CLIENT_ROOT_DIR}/ui/controllers/*.h
+ ${CLIENT_ROOT_DIR}/ui/controllers/api/*.h
+)
+file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS
+ ${CLIENT_ROOT_DIR}/ui/controllers/*.cpp
+ ${CLIENT_ROOT_DIR}/ui/controllers/api/*.cpp
+)
+
+set(HEADERS ${HEADERS}
+ ${COMMON_FILES_H}
+ ${PAGE_LOGIC_H}
+ ${CONFIGURATORS_H}
+ ${UI_MODELS_H}
+ ${UI_CONTROLLERS_H}
+)
+set(SOURCES ${SOURCES}
+ ${COMMON_FILES_CPP}
+ ${PAGE_LOGIC_CPP}
+ ${CONFIGURATORS_CPP}
+ ${UI_MODELS_CPP}
+ ${UI_CONTROLLERS_CPP}
+)
+
+if(WIN32)
+ set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.h
+ )
+
+ set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
+ )
+
+ set(RESOURCES ${RESOURCES}
+ ${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
+ )
+endif()
+
+if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
+ message("Client desktop build")
+ add_compile_definitions(AMNEZIA_DESKTOP)
+
+ set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/core/ipcclient.h
+ ${CLIENT_ROOT_DIR}/core/privileged_process.h
+ ${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
+ ${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h
+ ${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.h
+ ${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.h
+ ${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.h
+ ${CLIENT_ROOT_DIR}/protocols/xrayprotocol.h
+ ${CLIENT_ROOT_DIR}/protocols/awgprotocol.h
+ )
+
+ set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/core/ipcclient.cpp
+ ${CLIENT_ROOT_DIR}/core/privileged_process.cpp
+ ${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
+ ${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp
+ ${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.cpp
+ ${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.cpp
+ ${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.cpp
+ ${CLIENT_ROOT_DIR}/protocols/xrayprotocol.cpp
+ ${CLIENT_ROOT_DIR}/protocols/awgprotocol.cpp
+ )
+endif()
diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp
index ce673a85..52b148c0 100644
--- a/client/containers/containers_defs.cpp
+++ b/client/containers/containers_defs.cpp
@@ -110,22 +110,19 @@ QMap ContainerProps::containerDescriptions()
QObject::tr("OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its "
"own security protocol with SSL/TLS for key exchange.") },
{ DockerContainer::ShadowSocks,
- QObject::tr("Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it "
- "may be recognized by analysis systems in some highly censored regions.") },
+ QObject::tr("Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems.") },
{ DockerContainer::Cloak,
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
- "active-probing detection. Ideal for bypassing blocking in regions with the highest levels "
- "of censorship.") },
+ "active-probing detection. It is very resistant to detection, but offers low speed.") },
{ DockerContainer::WireGuard,
- QObject::tr("WireGuard - New popular VPN protocol with high performance, high speed and low power "
- "consumption. Recommended for regions with low levels of censorship.") },
+ QObject::tr("WireGuard - popular VPN protocol with high performance, high speed and low power "
+ "consumption.") },
{ DockerContainer::Awg,
- QObject::tr("AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, "
- "but very resistant to blockages. "
- "Recommended for regions with high levels of censorship.") },
+ QObject::tr("AmneziaWG is a special protocol from Amnezia based on WireGuard. "
+ "It provides high connection speed and ensures stable operation even in the most challenging network conditions.") },
{ DockerContainer::Xray,
- QObject::tr("XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
- "Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
+ QObject::tr("XRay with REALITY masks VPN traffic as web traffic and protects against active probing. "
+ "It is highly resistant to detection and offers high speed.") },
{ DockerContainer::Ipsec,
QObject::tr("IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after "
"signal loss. It has native support on the latest versions of Android and iOS.") },
@@ -144,20 +141,20 @@ QMap ContainerProps::containerDetailedDescriptions()
return {
{ DockerContainer::OpenVpn,
QObject::tr(
- "OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
- "It employs its unique security protocol, "
- "leveraging the strength of SSL/TLS for encryption and key exchange. "
- "Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, "
- "catering to a wide range of devices and operating systems. "
- "Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, "
- "which continually reinforces its security. "
- "With a strong balance of performance, security, and compatibility, "
- "OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n"
- "* Available in the AmneziaVPN across all platforms\n"
- "* Normal power consumption on mobile devices\n"
- "* Flexible customisation to suit user needs to work with different operating systems and devices\n"
- "* Recognised by DPI analysis systems and therefore susceptible to blocking\n"
- "* Can operate over both TCP and UDP network protocols.") },
+ "OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
+ "It employs its unique security protocol, "
+ "leveraging the strength of SSL/TLS for encryption and key exchange. "
+ "Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, "
+ "catering to a wide range of devices and operating systems. "
+ "Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, "
+ "which continually reinforces its security. "
+ "With a strong balance of performance, security, and compatibility, "
+ "OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n"
+ "* Available in the AmneziaVPN across all platforms\n"
+ "* Normal power consumption on mobile devices\n"
+ "* Flexible customisation to suit user needs to work with different operating systems and devices\n"
+ "* Recognised by DPI systems and therefore susceptible to blocking\n"
+ "* Can operate over both TCP and UDP network protocols.") },
{ DockerContainer::ShadowSocks,
QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. "
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection."
@@ -169,28 +166,26 @@ QMap ContainerProps::containerDetailedDescriptions()
"* Works over TCP network protocol.") },
{ DockerContainer::Cloak,
QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for "
- "protecting against blocking.\n\n"
+ "protecting against detection.\n\n"
"OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client "
"and the server.\n\n"
- "Cloak protects OpenVPN from detection and blocking. \n\n"
+ "Cloak protects OpenVPN from detection. \n\n"
"Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, "
"and also protects the VPN from detection by Active Probing. This makes it very resistant to "
"being detected\n\n"
"Immediately after receiving the first data packet, Cloak authenticates the incoming connection. "
"If authentication fails, the plugin masks the server as a fake website and your VPN becomes "
"invisible to analysis systems.\n\n"
- "If there is a extreme level of Internet censorship in your region, we advise you to use only "
- "OpenVPN over Cloak from the first connection\n\n"
"* Available in the AmneziaVPN across all platforms\n"
"* High power consumption on mobile devices\n"
"* Flexible settings\n"
- "* Not recognised by DPI analysis systems\n"
+ "* Not recognised by detection systems\n"
"* Works over TCP network protocol, 443 port.\n") },
{ DockerContainer::WireGuard,
QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n"
"WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption "
"settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n"
- "WireGuard is very susceptible to blocking due to its distinct packet signatures. "
+ "WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. "
"Unlike some other VPN protocols that employ obfuscation techniques, "
"the consistent signature patterns of WireGuard packets can be more easily identified and "
"thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n"
@@ -213,18 +208,18 @@ QMap ContainerProps::containerDetailedDescriptions()
"* Available in the AmneziaVPN across all platforms\n"
"* Low power consumption\n"
"* Minimum number of settings\n"
- "* Not recognised by DPI analysis systems, resistant to blocking\n"
+ "* Not recognised by traffic analysis systems\n"
"* Works over UDP network protocol.") },
{ DockerContainer::Xray,
- QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
- "is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion.\n"
- "It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, "
- "thus presenting an authentic TLS certificate and data. \n"
- "This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
- "legitimate sites without the need for specific configurations. \n"
- "Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
- "REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. "
- "This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.")
+ QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
+ "is designed to provide the highest level of protection against detection through its innovative approach to security and privacy.\n"
+ "It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, "
+ "thus presenting an authentic TLS certificate and data. \n"
+ "This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
+ "legitimate sites without the need for specific configurations. \n"
+ "Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
+ "REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security. "
+ "This makes REALITY a robust solution for maintaining internet freedom.")
},
{ DockerContainer::Ipsec,
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
@@ -332,9 +327,7 @@ QStringList ContainerProps::fixedPortsForContainer(DockerContainer c)
bool ContainerProps::isEasySetupContainer(DockerContainer container)
{
switch (container) {
- case DockerContainer::WireGuard: return true;
case DockerContainer::Awg: return true;
- // case DockerContainer::Cloak: return true;
default: return false;
}
}
@@ -342,9 +335,7 @@ bool ContainerProps::isEasySetupContainer(DockerContainer container)
QString ContainerProps::easySetupHeader(DockerContainer container)
{
switch (container) {
- case DockerContainer::WireGuard: return tr("Low");
- case DockerContainer::Awg: return tr("High");
- // case DockerContainer::Cloak: return tr("Extreme");
+ case DockerContainer::Awg: return tr("Automatic");
default: return "";
}
}
@@ -352,10 +343,8 @@ QString ContainerProps::easySetupHeader(DockerContainer container)
QString ContainerProps::easySetupDescription(DockerContainer container)
{
switch (container) {
- case DockerContainer::WireGuard: return tr("I just want to increase the level of my privacy.");
- case DockerContainer::Awg: return tr("I want to bypass censorship. This option recommended in most cases.");
- // case DockerContainer::Cloak:
- // return tr("Most VPN protocols are blocked. Recommended if other options are not working.");
+ case DockerContainer::Awg: return tr("AmneziaWG protocol will be installed. "
+ "It provides high connection speed and ensures stable operation even in the most challenging network conditions.");
default: return "";
}
}
@@ -363,9 +352,7 @@ QString ContainerProps::easySetupDescription(DockerContainer container)
int ContainerProps::easySetupOrder(DockerContainer container)
{
switch (container) {
- case DockerContainer::WireGuard: return 3;
- case DockerContainer::Awg: return 2;
- // case DockerContainer::Cloak: return 1;
+ case DockerContainer::Awg: return 1;
default: return 0;
}
}
@@ -384,9 +371,9 @@ bool ContainerProps::isShareable(DockerContainer container)
QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
{
QString protocolConfigString = containerConfig.value(ProtocolProps::protoToString(protocol))
- .toObject()
- .value(config_key::last_config)
- .toString();
+ .toObject()
+ .value(config_key::last_config)
+ .toString();
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
}
diff --git a/client/core/api/apiDefs.h b/client/core/api/apiDefs.h
new file mode 100644
index 00000000..41dd80ba
--- /dev/null
+++ b/client/core/api/apiDefs.h
@@ -0,0 +1,51 @@
+#ifndef APIDEFS_H
+#define APIDEFS_H
+
+#include
+
+namespace apiDefs
+{
+ enum ConfigType {
+ AmneziaFreeV2 = 0,
+ AmneziaFreeV3,
+ AmneziaPremiumV1,
+ AmneziaPremiumV2,
+ SelfHosted
+ };
+
+ enum ConfigSource {
+ Telegram = 1,
+ AmneziaGateway
+ };
+
+ namespace key
+ {
+ constexpr QLatin1String configVersion("config_version");
+
+ constexpr QLatin1String apiConfig("api_config");
+ constexpr QLatin1String stackType("stack_type");
+ constexpr QLatin1String serviceType("service_type");
+
+ constexpr QLatin1String vpnKey("vpn_key");
+
+ constexpr QLatin1String installationUuid("installation_uuid");
+ constexpr QLatin1String workerLastUpdated("worker_last_updated");
+ constexpr QLatin1String lastDownloaded("last_downloaded");
+ constexpr QLatin1String sourceType("source_type");
+
+ constexpr QLatin1String serverCountryCode("server_country_code");
+ constexpr QLatin1String serverCountryName("server_country_name");
+
+ constexpr QLatin1String osVersion("os_version");
+
+ constexpr QLatin1String availableCountries("available_countries");
+ constexpr QLatin1String activeDeviceCount("active_device_count");
+ constexpr QLatin1String maxDeviceCount("max_device_count");
+ constexpr QLatin1String subscriptionEndDate("subscription_end_date");
+ constexpr QLatin1String issuedConfigs("issued_configs");
+ }
+
+ const int requestTimeoutMsecs = 12 * 1000; // 12 secs
+}
+
+#endif // APIDEFS_H
diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp
new file mode 100644
index 00000000..9f518b52
--- /dev/null
+++ b/client/core/api/apiUtils.cpp
@@ -0,0 +1,87 @@
+#include "apiUtils.h"
+
+#include
+#include
+
+bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
+{
+ QDateTime now = QDateTime::currentDateTime();
+ QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs);
+ return endDate < now;
+}
+
+bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
+{
+ auto configVersion = serverConfigObject.value(apiDefs::key::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(apiDefs::key::configVersion).toInt();
+ switch (configVersion) {
+ case apiDefs::ConfigSource::Telegram: {
+ };
+ case apiDefs::ConfigSource::AmneziaGateway: {
+ constexpr QLatin1String stackPremium("prem");
+ constexpr QLatin1String stackFree("free");
+
+ constexpr QLatin1String servicePremium("amnezia-premium");
+ constexpr QLatin1String serviceFree("amnezia-free");
+
+ auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
+ auto stackType = apiConfigObject.value(apiDefs::key::stackType).toString();
+ auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString();
+
+ if (serviceType == servicePremium || stackType == stackPremium) {
+ return apiDefs::ConfigType::AmneziaPremiumV2;
+ } else if (serviceType == serviceFree || stackType == stackFree) {
+ return apiDefs::ConfigType::AmneziaFreeV3;
+ }
+ }
+ default: {
+ return apiDefs::ConfigType::SelfHosted;
+ }
+ };
+}
+
+apiDefs::ConfigSource apiUtils::getConfigSource(const QJsonObject &serverConfigObject)
+{
+ return static_cast(serverConfigObject.value(apiDefs::key::configVersion).toInt());
+}
+
+amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList &sslErrors, QNetworkReply *reply)
+{
+ const int httpStatusCodeConflict = 409;
+ const int httpStatusCodeNotFound = 404;
+
+ if (!sslErrors.empty()) {
+ qDebug().noquote() << sslErrors;
+ return amnezia::ErrorCode::ApiConfigSslError;
+ } else if (reply->error() == QNetworkReply::NoError) {
+ return amnezia::ErrorCode::NoError;
+ } else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
+ || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
+ return amnezia::ErrorCode::ApiConfigTimeoutError;
+ } else {
+ QString err = reply->errorString();
+ int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ qDebug() << QString::fromUtf8(reply->readAll());
+ qDebug() << reply->error();
+ qDebug() << err;
+ qDebug() << httpStatusCode;
+ if (httpStatusCode == httpStatusCodeConflict) {
+ return amnezia::ErrorCode::ApiConfigLimitError;
+ } else if (httpStatusCode == httpStatusCodeNotFound) {
+ return amnezia::ErrorCode::ApiNotFoundError;
+ }
+ return amnezia::ErrorCode::ApiConfigDownloadError;
+ }
+
+ qDebug() << "something went wrong";
+ return amnezia::ErrorCode::InternalError;
+}
diff --git a/client/core/api/apiUtils.h b/client/core/api/apiUtils.h
new file mode 100644
index 00000000..82ac315b
--- /dev/null
+++ b/client/core/api/apiUtils.h
@@ -0,0 +1,22 @@
+#ifndef APIUTILS_H
+#define APIUTILS_H
+
+#include
+#include
+
+#include "apiDefs.h"
+#include "core/defs.h"
+
+namespace apiUtils
+{
+ bool isServerFromApi(const QJsonObject &serverConfigObject);
+
+ bool isSubscriptionExpired(const QString &subscriptionEndDate);
+
+ apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);
+ apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
+
+ amnezia::ErrorCode checkNetworkReplyErrors(const QList &sslErrors, QNetworkReply *reply);
+}
+
+#endif // APIUTILS_H
diff --git a/client/core/controllers/apiController.cpp b/client/core/controllers/apiController.cpp
deleted file mode 100644
index 6562632a..00000000
--- a/client/core/controllers/apiController.cpp
+++ /dev/null
@@ -1,509 +0,0 @@
-#include "apiController.h"
-
-#include
-#include
-
-#include
-#include
-#include
-#include
-
-#include "QBlockCipher.h"
-#include "QRsa.h"
-
-#include "amnezia_application.h"
-#include "configurators/wireguard_configurator.h"
-#include "core/enums/apiEnums.h"
-#include "utilities.h"
-#include "version.h"
-
-namespace
-{
- namespace configKey
- {
- constexpr char cloak[] = "cloak";
- constexpr char awg[] = "awg";
-
- constexpr char apiEdnpoint[] = "api_endpoint";
- constexpr char accessToken[] = "api_key";
- constexpr char certificate[] = "certificate";
- constexpr char publicKey[] = "public_key";
- constexpr char protocol[] = "protocol";
-
- constexpr char uuid[] = "installation_uuid";
- constexpr char osVersion[] = "os_version";
- constexpr char appVersion[] = "app_version";
-
- constexpr char userCountryCode[] = "user_country_code";
- constexpr char serverCountryCode[] = "server_country_code";
- constexpr char serviceType[] = "service_type";
- constexpr char serviceInfo[] = "service_info";
-
- constexpr char aesKey[] = "aes_key";
- constexpr char aesIv[] = "aes_iv";
- constexpr char aesSalt[] = "aes_salt";
-
- constexpr char apiPayload[] = "api_payload";
- constexpr char keyPayload[] = "key_payload";
-
- constexpr char apiConfig[] = "api_config";
- constexpr char authData[] = "auth_data";
- }
-
- const int requestTimeoutMsecs = 12 * 1000; // 12 secs
-
- ErrorCode checkErrors(const QList &sslErrors, QNetworkReply *reply)
- {
- if (!sslErrors.empty()) {
- qDebug().noquote() << sslErrors;
- return ErrorCode::ApiConfigSslError;
- } else if (reply->error() == QNetworkReply::NoError) {
- return ErrorCode::NoError;
- } else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
- || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
- return ErrorCode::ApiConfigTimeoutError;
- } else {
- QString err = reply->errorString();
- qDebug() << QString::fromUtf8(reply->readAll());
- qDebug() << reply->error();
- qDebug() << err;
- qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
- return ErrorCode::ApiConfigDownloadError;
- }
- }
-
- bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
- const QByteArray &iv = "", const QByteArray &salt = "")
- {
- if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
- || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
- qDebug() << "Timeout occurred";
- return true;
- } else if (responseBody.contains("html")) {
- qDebug() << "The response contains an html tag";
- return true;
- } else if (checkEncryption) {
- try {
- QSimpleCrypto::QBlockCipher blockCipher;
- static_cast(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
- } catch (...) {
- qDebug() << "Failed to decrypt the data";
- return true;
- }
- }
- return false;
- }
-}
-
-ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent)
- : QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment)
-{
-}
-
-void ApiController::fillServerConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData,
- const QByteArray &apiResponseBody, QJsonObject &serverConfig)
-{
- QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString();
-
- data.replace("vpn://", "");
- QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
-
- if (ba.isEmpty()) {
- emit errorOccurred(ErrorCode::ApiConfigEmptyError);
- return;
- }
-
- QByteArray ba_uncompressed = qUncompress(ba);
- if (!ba_uncompressed.isEmpty()) {
- ba = ba_uncompressed;
- }
-
- QString configStr = ba;
- if (protocol == configKey::cloak) {
- configStr.replace("", "\n");
- configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
- } else if (protocol == configKey::awg) {
- configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
- auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
- auto containers = newServerConfig.value(config_key::containers).toArray();
- if (containers.isEmpty()) {
- return; // todo process error
- }
- auto container = containers.at(0).toObject();
- QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
- auto containerConfig = container.value(containerName).toObject();
- auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
- containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount);
- containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize);
- containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize);
- containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize);
- containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize);
- containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader);
- containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader);
- containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader);
- containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
- container[containerName] = containerConfig;
- containers.replace(0, container);
- newServerConfig[config_key::containers] = containers;
- configStr = QString(QJsonDocument(newServerConfig).toJson());
- }
-
- QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
- serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
- serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
- serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
- serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
-
- if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
- serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
- serverConfig[config_key::description] = newServerConfig.value(config_key::description);
- serverConfig[config_key::name] = newServerConfig.value(config_key::name);
- }
-
- auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
- serverConfig[config_key::defaultContainer] = defaultContainer;
-
- QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
- map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
- auto apiConfig = QJsonObject::fromVariantMap(map);
-
- if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
- apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject());
- }
-
- serverConfig[configKey::apiConfig] = apiConfig;
-
- return;
-}
-
-QStringList ApiController::getProxyUrls()
-{
- QNetworkRequest request;
- request.setTransferTimeout(requestTimeoutMsecs);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-
- QEventLoop wait;
- QList sslErrors;
- QNetworkReply *reply;
-
- QStringList proxyStorageUrl;
- if (m_isDevEnvironment) {
- proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
- } else {
- proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
- }
-
- QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
-
- for (const auto &proxyStorageUrl : proxyStorageUrl) {
- request.setUrl(proxyStorageUrl);
- reply = amnApp->manager()->get(request);
-
- connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
- connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; });
- wait.exec();
-
- if (reply->error() == QNetworkReply::NetworkError::NoError) {
- break;
- }
- reply->deleteLater();
- }
-
- auto encryptedResponseBody = reply->readAll();
- reply->deleteLater();
-
- EVP_PKEY *privateKey = nullptr;
- QByteArray responseBody;
- try {
- if (!m_isDevEnvironment) {
- QCryptographicHash hash(QCryptographicHash::Sha512);
- hash.addData(key);
- QByteArray hashResult = hash.result().toHex();
-
- QByteArray key = QByteArray::fromHex(hashResult.left(64));
- QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
-
- QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
-
- QSimpleCrypto::QBlockCipher blockCipher;
- responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
- } else {
- responseBody = encryptedResponseBody;
- }
- } catch (...) {
- Utils::logException();
- qCritical() << "error loading private key from environment variables or decrypting payload";
- return {};
- }
-
- auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
-
- QStringList endpoints;
- for (const auto &endpoint : endpointsArray) {
- endpoints.push_back(endpoint.toString());
- }
- return endpoints;
-}
-
-ApiController::ApiPayloadData ApiController::generateApiPayloadData(const QString &protocol)
-{
- ApiController::ApiPayloadData apiPayload;
- if (protocol == configKey::cloak) {
- apiPayload.certRequest = OpenVpnConfigurator::createCertRequest();
- } else if (protocol == configKey::awg) {
- auto connData = WireguardConfigurator::genClientKeys();
- apiPayload.wireGuardClientPubKey = connData.clientPubKey;
- apiPayload.wireGuardClientPrivKey = connData.clientPrivKey;
- }
- return apiPayload;
-}
-
-QJsonObject ApiController::fillApiPayload(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData)
-{
- QJsonObject obj;
- if (protocol == configKey::cloak) {
- obj[configKey::certificate] = apiPayloadData.certRequest.request;
- } else if (protocol == configKey::awg) {
- obj[configKey::publicKey] = apiPayloadData.wireGuardClientPubKey;
- }
-
- obj[configKey::osVersion] = QSysInfo::productType();
- obj[configKey::appVersion] = QString(APP_VERSION);
-
- return obj;
-}
-
-void ApiController::updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig)
-{
-#ifdef Q_OS_IOS
- IosController::Instance()->requestInetAccess();
- QThread::msleep(10);
-#endif
-
- if (serverConfig.value(config_key::configVersion).toInt()) {
- QNetworkRequest request;
- request.setTransferTimeout(requestTimeoutMsecs);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
- QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
- request.setUrl(endpoint);
-
- QString protocol = serverConfig.value(configKey::protocol).toString();
-
- ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
-
- QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
- apiPayload[configKey::uuid] = installationUuid;
-
- QByteArray requestBody = QJsonDocument(apiPayload).toJson();
-
- QNetworkReply *reply = amnApp->manager()->post(request, requestBody);
-
- QObject::connect(reply, &QNetworkReply::finished, [this, reply, protocol, apiPayloadData, serverIndex, serverConfig]() mutable {
- if (reply->error() == QNetworkReply::NoError) {
- auto apiResponseBody = reply->readAll();
- fillServerConfig(protocol, apiPayloadData, apiResponseBody, serverConfig);
- emit finished(serverConfig, serverIndex);
- } else {
- if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
- || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
- emit errorOccurred(ErrorCode::ApiConfigTimeoutError);
- } else {
- QString err = reply->errorString();
- qDebug() << QString::fromUtf8(reply->readAll());
- qDebug() << reply->error();
- qDebug() << err;
- qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
- emit errorOccurred(ErrorCode::ApiConfigDownloadError);
- }
- }
-
- reply->deleteLater();
- });
-
- QObject::connect(reply, &QNetworkReply::errorOccurred,
- [this, reply](QNetworkReply::NetworkError error) { qDebug() << reply->errorString() << error; });
- connect(reply, &QNetworkReply::sslErrors, [this, reply](const QList &errors) {
- qDebug().noquote() << errors;
- emit errorOccurred(ErrorCode::ApiConfigSslError);
- });
- }
-}
-
-ErrorCode ApiController::getServicesList(QByteArray &responseBody)
-{
-#ifdef Q_OS_IOS
- IosController::Instance()->requestInetAccess();
- QThread::msleep(10);
-#endif
-
- QNetworkRequest request;
- request.setTransferTimeout(requestTimeoutMsecs);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-
- request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint));
-
- QNetworkReply *reply;
- reply = amnApp->manager()->get(request);
-
- QEventLoop wait;
- QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
-
- QList sslErrors;
- connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; });
- wait.exec();
-
- responseBody = reply->readAll();
-
- if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
- m_proxyUrls = getProxyUrls();
- std::random_device randomDevice;
- std::mt19937 generator(randomDevice());
- std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
- for (const QString &proxyUrl : m_proxyUrls) {
- qDebug() << "Go to the next endpoint";
- request.setUrl(QString("%1v1/services").arg(proxyUrl));
- reply->deleteLater(); // delete the previous reply
- reply = amnApp->manager()->get(request);
-
- QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
- connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; });
- wait.exec();
-
- responseBody = reply->readAll();
- if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, responseBody, false)) {
- break;
- }
- }
- }
-
- auto errorCode = checkErrors(sslErrors, reply);
- reply->deleteLater();
-
- if (errorCode == ErrorCode::NoError) {
- if (!responseBody.contains("services")) {
- return ErrorCode::ApiServicesMissingError;
- }
- }
-
- return errorCode;
-}
-
-ErrorCode ApiController::getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
- const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData,
- QJsonObject &serverConfig)
-{
-#ifdef Q_OS_IOS
- IosController::Instance()->requestInetAccess();
- QThread::msleep(10);
-#endif
-
- QNetworkRequest request;
- request.setTransferTimeout(requestTimeoutMsecs);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-
- request.setUrl(QString("%1v1/config").arg(m_gatewayEndpoint));
-
- ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
-
- QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
- apiPayload[configKey::userCountryCode] = userCountryCode;
- if (!serverCountryCode.isEmpty()) {
- apiPayload[configKey::serverCountryCode] = serverCountryCode;
- }
- apiPayload[configKey::serviceType] = serviceType;
- apiPayload[configKey::uuid] = installationUuid;
- if (!authData.isEmpty()) {
- apiPayload[configKey::authData] = authData;
- }
-
- QSimpleCrypto::QBlockCipher blockCipher;
- QByteArray key = blockCipher.generatePrivateSalt(32);
- QByteArray iv = blockCipher.generatePrivateSalt(32);
- QByteArray salt = blockCipher.generatePrivateSalt(8);
-
- QJsonObject keyPayload;
- keyPayload[configKey::aesKey] = QString(key.toBase64());
- keyPayload[configKey::aesIv] = QString(iv.toBase64());
- keyPayload[configKey::aesSalt] = QString(salt.toBase64());
-
- QByteArray encryptedKeyPayload;
- QByteArray encryptedApiPayload;
- try {
- QSimpleCrypto::QRsa rsa;
-
- EVP_PKEY *publicKey = nullptr;
- try {
- QByteArray rsaKey = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
- QSimpleCrypto::QRsa rsa;
- publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
- } catch (...) {
- Utils::logException();
- qCritical() << "error loading public key from environment variables";
- return ErrorCode::ApiMissingAgwPublicKey;
- }
-
- encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
- EVP_PKEY_free(publicKey);
-
- encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
- } catch (...) { // todo change error handling in QSimpleCrypto?
- Utils::logException();
- qCritical() << "error when encrypting the request body";
- return ErrorCode::ApiConfigDecryptionError;
- }
-
- QJsonObject requestBody;
- requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
- requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
-
- QNetworkReply *reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson());
-
- QEventLoop wait;
- connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
-
- QList sslErrors;
- connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; });
- wait.exec();
-
- auto encryptedResponseBody = reply->readAll();
-
- if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
- m_proxyUrls = getProxyUrls();
- std::random_device randomDevice;
- std::mt19937 generator(randomDevice());
- std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
- for (const QString &proxyUrl : m_proxyUrls) {
- qDebug() << "Go to the next endpoint";
- request.setUrl(QString("%1v1/config").arg(proxyUrl));
- reply->deleteLater(); // delete the previous reply
- reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson());
-
- QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
- connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; });
- wait.exec();
-
- encryptedResponseBody = reply->readAll();
- if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
- break;
- }
- }
- }
-
- auto errorCode = checkErrors(sslErrors, reply);
- reply->deleteLater();
- if (errorCode) {
- return errorCode;
- }
-
- try {
- auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
- fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
- } catch (...) { // todo change error handling in QSimpleCrypto?
- Utils::logException();
- qCritical() << "error when decrypting the request body";
- return ErrorCode::ApiConfigDecryptionError;
- }
-
- return errorCode;
-}
diff --git a/client/core/controllers/apiController.h b/client/core/controllers/apiController.h
deleted file mode 100644
index bcb25f96..00000000
--- a/client/core/controllers/apiController.h
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifndef APICONTROLLER_H
-#define APICONTROLLER_H
-
-#include
-
-#include "configurators/openvpn_configurator.h"
-
-#ifdef Q_OS_IOS
- #include "platforms/ios/ios_controller.h"
-#endif
-
-class ApiController : public QObject
-{
- Q_OBJECT
-
-public:
- explicit ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent = nullptr);
-
-public slots:
- void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig);
-
- ErrorCode getServicesList(QByteArray &responseBody);
- ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
- const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData, QJsonObject &serverConfig);
-
-signals:
- void errorOccurred(ErrorCode errorCode);
- void finished(const QJsonObject &config, const int serverIndex);
-
-private:
- struct ApiPayloadData
- {
- OpenVpnConfigurator::ConnectionData certRequest;
-
- QString wireGuardClientPrivKey;
- QString wireGuardClientPubKey;
- };
-
- ApiPayloadData generateApiPayloadData(const QString &protocol);
- QJsonObject fillApiPayload(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData);
- void fillServerConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, const QByteArray &apiResponseBody,
- QJsonObject &serverConfig);
- QStringList getProxyUrls();
-
- QString m_gatewayEndpoint;
- QStringList m_proxyUrls;
- bool m_isDevEnvironment = false;
-};
-
-#endif // APICONTROLLER_H
diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp
new file mode 100644
index 00000000..82232c99
--- /dev/null
+++ b/client/core/controllers/coreController.cpp
@@ -0,0 +1,345 @@
+#include "coreController.h"
+
+#include
+
+#if defined(Q_OS_ANDROID)
+ #include "core/installedAppsImageProvider.h"
+ #include "platforms/android/android_controller.h"
+#endif
+
+#if defined(Q_OS_IOS)
+ #include "platforms/ios/ios_controller.h"
+ #include
+#endif
+
+CoreController::CoreController(const QSharedPointer &vpnConnection, const std::shared_ptr &settings,
+ QQmlApplicationEngine *engine, QObject *parent)
+ : QObject(parent), m_vpnConnection(vpnConnection), m_settings(settings), m_engine(engine)
+{
+ initModels();
+ initControllers();
+ initSignalHandlers();
+
+ initAndroidController();
+ initAppleController();
+
+ initNotificationHandler();
+
+ auto locale = m_settings->getAppLanguage();
+ m_translator.reset(new QTranslator());
+ updateTranslator(locale);
+}
+
+void CoreController::initModels()
+{
+ m_containersModel.reset(new ContainersModel(this));
+ m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
+
+ m_defaultServerContainersModel.reset(new ContainersModel(this));
+ m_engine->rootContext()->setContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel.get());
+
+ m_serversModel.reset(new ServersModel(m_settings, this));
+ m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
+
+ m_languageModel.reset(new LanguageModel(m_settings, this));
+ m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
+
+ m_sitesModel.reset(new SitesModel(m_settings, this));
+ m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
+
+ m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
+ m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
+
+ m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
+ m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
+
+ m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
+ m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
+
+ m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
+ m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
+
+ m_cloakConfigModel.reset(new CloakConfigModel(this));
+ m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
+
+ m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
+ m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
+
+ m_awgConfigModel.reset(new AwgConfigModel(this));
+ m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
+
+ m_xrayConfigModel.reset(new XrayConfigModel(this));
+ m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
+
+#ifdef Q_OS_WINDOWS
+ m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
+ m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
+#endif
+
+ m_sftpConfigModel.reset(new SftpConfigModel(this));
+ m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
+
+ m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
+ m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
+
+ m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
+ m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
+
+ m_apiServicesModel.reset(new ApiServicesModel(this));
+ m_engine->rootContext()->setContextProperty("ApiServicesModel", m_apiServicesModel.get());
+
+ m_apiCountryModel.reset(new ApiCountryModel(this));
+ m_engine->rootContext()->setContextProperty("ApiCountryModel", m_apiCountryModel.get());
+
+ m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this));
+ m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get());
+
+ m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
+ m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
+}
+
+void CoreController::initControllers()
+{
+ m_connectionController.reset(
+ new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings));
+ m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
+
+ m_pageController.reset(new PageController(m_serversModel, m_settings));
+ m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
+
+ m_focusController.reset(new FocusController(m_engine, this));
+ m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get());
+
+ m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_settings));
+ m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
+
+ connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
+ &ConnectionController::onCurrentContainerUpdated); // TODO remove this
+
+ m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
+ m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
+
+ m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, m_settings));
+ m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
+
+ m_settingsController.reset(
+ new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
+ m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
+
+ m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
+ m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
+
+ m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
+ m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
+
+ m_systemController.reset(new SystemController(m_settings));
+ m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
+
+ m_apiSettingsController.reset(
+ new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_settings));
+ m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get());
+
+ m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
+ m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
+}
+
+void CoreController::initAndroidController()
+{
+#ifdef Q_OS_ANDROID
+ if (!AndroidController::initLogging()) {
+ qFatal("Android logging initialization failed");
+ }
+ AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
+ connect(m_settings.get(), &Settings::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
+
+ AndroidController::instance()->setScreenshotsEnabled(m_settings->isScreenshotsEnabled());
+ connect(m_settings.get(), &Settings::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
+
+ connect(m_settings.get(), &Settings::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
+
+ connect(m_settings.get(), &Settings::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
+
+ connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) {
+ m_connectionController->onConnectionStateChanged(state);
+ if (m_vpnConnection)
+ m_vpnConnection->restoreConnection();
+ });
+ if (!AndroidController::instance()->initialize()) {
+ qFatal("Android controller initialization failed");
+ }
+
+ connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) {
+ emit m_pageController->goToPageHome();
+ m_importController->extractConfigFromData(data);
+ data.clear();
+ emit m_pageController->goToPageViewConfig();
+ });
+
+ m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
+#endif
+}
+
+void CoreController::initAppleController()
+{
+#ifdef Q_OS_IOS
+ IosController::Instance()->initialize();
+ connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
+ emit m_pageController->goToPageHome();
+ m_importController->extractConfigFromData(data);
+ emit m_pageController->goToPageViewConfig();
+ });
+
+ connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) {
+ emit m_pageController->goToPageHome();
+ m_pageController->goToPageSettingsBackup();
+ emit m_settingsController->importBackupFromOutside(filePath);
+ });
+
+ QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
+
+ connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); });
+#endif
+}
+
+void CoreController::initSignalHandlers()
+{
+ initErrorMessagesHandler();
+
+ initApiCountryModelUpdateHandler();
+ initContainerModelUpdateHandler();
+ initAdminConfigRevokedHandler();
+ initPassphraseRequestHandler();
+ initTranslationsUpdatedHandler();
+ initAutoConnectHandler();
+ initAmneziaDnsToggledHandler();
+ initPrepareConfigHandler();
+}
+
+void CoreController::initNotificationHandler()
+{
+#ifndef Q_OS_ANDROID
+ m_notificationHandler.reset(NotificationHandler::create(nullptr));
+
+ connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
+ &NotificationHandler::setConnectionState);
+
+ connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(), &PageController::raiseMainWindow);
+ connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
+ static_cast(&ConnectionController::openConnection));
+ connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
+ &ConnectionController::closeConnection);
+ connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
+#endif
+}
+
+void CoreController::updateTranslator(const QLocale &locale)
+{
+ if (!m_translator->isEmpty()) {
+ QCoreApplication::removeTranslator(m_translator.get());
+ }
+
+ QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
+ if (m_translator->load(strFileName)) {
+ if (QCoreApplication::installTranslator(m_translator.get())) {
+ m_settings->setAppLanguage(locale);
+ }
+ } else {
+ m_settings->setAppLanguage(QLocale::English);
+ }
+
+ m_engine->retranslate();
+
+ emit translationsUpdated();
+}
+
+void CoreController::initErrorMessagesHandler()
+{
+ connect(m_connectionController.get(), &ConnectionController::connectionErrorOccurred, this, [this](ErrorCode errorCode) {
+ emit m_pageController->showErrorMessage(errorCode);
+ emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
+ });
+
+ connect(m_apiConfigsController.get(), &ApiConfigsController::errorOccurred, m_pageController.get(),
+ qOverload(&PageController::showErrorMessage));
+}
+
+void CoreController::setQmlRoot()
+{
+ m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
+}
+
+void CoreController::initApiCountryModelUpdateHandler()
+{
+ // TODO
+ connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
+ m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
+ m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
+ });
+ connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this,
+ [this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); });
+}
+
+void CoreController::initContainerModelUpdateHandler()
+{
+ connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
+ connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
+ &ContainersModel::updateModel);
+ m_serversModel->resetModel();
+}
+
+void CoreController::initAdminConfigRevokedHandler()
+{
+ connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),
+ &ServersModel::clearCachedProfile);
+}
+
+void CoreController::initPassphraseRequestHandler()
+{
+ connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
+ &PageController::showPassphraseRequestDrawer);
+ connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
+ &InstallController::setEncryptedPassphrase);
+}
+
+void CoreController::initTranslationsUpdatedHandler()
+{
+ connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &CoreController::updateTranslator);
+ connect(this, &CoreController::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
+ connect(this, &CoreController::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
+}
+
+void CoreController::initAutoConnectHandler()
+{
+ if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
+ QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
+ }
+}
+
+void CoreController::initAmneziaDnsToggledHandler()
+{
+ connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
+}
+
+void CoreController::initPrepareConfigHandler()
+{
+ connect(m_connectionController.get(), &ConnectionController::prepareConfig, this, [this]() {
+ emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
+
+ if (!m_apiConfigsController->isConfigValid()) {
+ emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
+ return;
+ }
+
+ if (!m_installController->isConfigValid()) {
+ emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
+ return;
+ }
+
+ m_connectionController->openConnection();
+ });
+}
+
+QSharedPointer CoreController::pageController() const
+{
+ return m_pageController;
+}
diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h
new file mode 100644
index 00000000..700504af
--- /dev/null
+++ b/client/core/controllers/coreController.h
@@ -0,0 +1,136 @@
+#ifndef CORECONTROLLER_H
+#define CORECONTROLLER_H
+
+#include
+#include
+#include
+
+#include "ui/controllers/api/apiConfigsController.h"
+#include "ui/controllers/api/apiSettingsController.h"
+#include "ui/controllers/appSplitTunnelingController.h"
+#include "ui/controllers/connectionController.h"
+#include "ui/controllers/exportController.h"
+#include "ui/controllers/focusController.h"
+#include "ui/controllers/importController.h"
+#include "ui/controllers/installController.h"
+#include "ui/controllers/pageController.h"
+#include "ui/controllers/settingsController.h"
+#include "ui/controllers/sitesController.h"
+#include "ui/controllers/systemController.h"
+
+#include "ui/models/containers_model.h"
+#include "ui/models/languageModel.h"
+#include "ui/models/protocols/cloakConfigModel.h"
+#ifdef Q_OS_WINDOWS
+ #include "ui/models/protocols/ikev2ConfigModel.h"
+#endif
+#include "ui/models/api/apiAccountInfoModel.h"
+#include "ui/models/api/apiCountryModel.h"
+#include "ui/models/api/apiDevicesModel.h"
+#include "ui/models/api/apiServicesModel.h"
+#include "ui/models/appSplitTunnelingModel.h"
+#include "ui/models/clientManagementModel.h"
+#include "ui/models/protocols/awgConfigModel.h"
+#include "ui/models/protocols/openvpnConfigModel.h"
+#include "ui/models/protocols/shadowsocksConfigModel.h"
+#include "ui/models/protocols/wireguardConfigModel.h"
+#include "ui/models/protocols/xrayConfigModel.h"
+#include "ui/models/protocols_model.h"
+#include "ui/models/servers_model.h"
+#include "ui/models/services/sftpConfigModel.h"
+#include "ui/models/services/socks5ProxyConfigModel.h"
+#include "ui/models/sites_model.h"
+
+#ifndef Q_OS_ANDROID
+ #include "ui/notificationhandler.h"
+#endif
+
+class CoreController : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit CoreController(const QSharedPointer &vpnConnection, const std::shared_ptr &settings,
+ QQmlApplicationEngine *engine, QObject *parent = nullptr);
+
+ QSharedPointer pageController() const;
+ void setQmlRoot();
+
+signals:
+ void translationsUpdated();
+
+private:
+ void initModels();
+ void initControllers();
+ void initAndroidController();
+ void initAppleController();
+ void initSignalHandlers();
+
+ void initNotificationHandler();
+
+ void updateTranslator(const QLocale &locale);
+
+ void initErrorMessagesHandler();
+
+ void initApiCountryModelUpdateHandler();
+ void initContainerModelUpdateHandler();
+ void initAdminConfigRevokedHandler();
+ void initPassphraseRequestHandler();
+ void initTranslationsUpdatedHandler();
+ void initAutoConnectHandler();
+ void initAmneziaDnsToggledHandler();
+ void initPrepareConfigHandler();
+
+ QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
+ std::shared_ptr m_settings;
+ QSharedPointer m_vpnConnection;
+ QSharedPointer m_translator;
+
+#ifndef Q_OS_ANDROID
+ QScopedPointer m_notificationHandler;
+#endif
+
+ QMetaObject::Connection m_reloadConfigErrorOccurredConnection;
+
+ QScopedPointer m_connectionController;
+ QScopedPointer m_focusController;
+ QSharedPointer m_pageController; // TODO
+ QScopedPointer m_installController;
+ QScopedPointer m_importController;
+ QScopedPointer m_exportController;
+ QScopedPointer m_settingsController;
+ QScopedPointer m_sitesController;
+ QScopedPointer m_systemController;
+ QScopedPointer m_appSplitTunnelingController;
+
+ QScopedPointer m_apiSettingsController;
+ QScopedPointer m_apiConfigsController;
+
+ QSharedPointer m_containersModel;
+ QSharedPointer m_defaultServerContainersModel;
+ QSharedPointer m_serversModel;
+ QSharedPointer m_languageModel;
+ QSharedPointer m_protocolsModel;
+ QSharedPointer m_sitesModel;
+ QSharedPointer m_appSplitTunnelingModel;
+ QSharedPointer m_clientManagementModel;
+
+ QSharedPointer m_apiServicesModel;
+ QSharedPointer m_apiCountryModel;
+ QSharedPointer m_apiAccountInfoModel;
+ QSharedPointer m_apiDevicesModel;
+
+ QScopedPointer m_openVpnConfigModel;
+ QScopedPointer m_shadowSocksConfigModel;
+ QScopedPointer m_cloakConfigModel;
+ QScopedPointer m_xrayConfigModel;
+ QScopedPointer m_wireGuardConfigModel;
+ QScopedPointer m_awgConfigModel;
+#ifdef Q_OS_WINDOWS
+ QScopedPointer m_ikev2ConfigModel;
+#endif
+ QScopedPointer m_sftpConfigModel;
+ QScopedPointer m_socks5ConfigModel;
+};
+
+#endif // CORECONTROLLER_H
diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp
new file mode 100644
index 00000000..15776328
--- /dev/null
+++ b/client/core/controllers/gatewayController.cpp
@@ -0,0 +1,303 @@
+#include "gatewayController.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "QBlockCipher.h"
+#include "QRsa.h"
+
+#include "amnezia_application.h"
+#include "core/api/apiUtils.h"
+#include "utilities.h"
+
+namespace
+{
+ namespace configKey
+ {
+ constexpr char aesKey[] = "aes_key";
+ constexpr char aesIv[] = "aes_iv";
+ constexpr char aesSalt[] = "aes_salt";
+
+ constexpr char apiPayload[] = "api_payload";
+ constexpr char keyPayload[] = "key_payload";
+ }
+}
+
+GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent)
+ : QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment), m_requestTimeoutMsecs(requestTimeoutMsecs)
+{
+}
+
+ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBody)
+{
+#ifdef Q_OS_IOS
+ IosController::Instance()->requestInetAccess();
+ QThread::msleep(10);
+#endif
+
+ QNetworkRequest request;
+ request.setTransferTimeout(m_requestTimeoutMsecs);
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+
+ request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
+
+ QNetworkReply *reply;
+ reply = amnApp->networkManager()->get(request);
+
+ QEventLoop wait;
+ QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
+
+ QList sslErrors;
+ connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; });
+ wait.exec();
+
+ responseBody = reply->readAll();
+
+ if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
+ auto requestFunction = [&request, &responseBody](const QString &url) {
+ request.setUrl(url);
+ return amnApp->networkManager()->get(request);
+ };
+
+ auto replyProcessingFunction = [&responseBody, &reply, &sslErrors, this](QNetworkReply *nestedReply,
+ const QList &nestedSslErrors) {
+ responseBody = nestedReply->readAll();
+ if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, responseBody, false)) {
+ sslErrors = nestedSslErrors;
+ reply = nestedReply;
+ return true;
+ }
+ return false;
+ };
+
+ bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
+ }
+
+ auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
+ reply->deleteLater();
+
+ return errorCode;
+}
+
+ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
+{
+#ifdef Q_OS_IOS
+ IosController::Instance()->requestInetAccess();
+ QThread::msleep(10);
+#endif
+
+ QNetworkRequest request;
+ request.setTransferTimeout(m_requestTimeoutMsecs);
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+
+ request.setUrl(endpoint.arg(m_gatewayEndpoint));
+
+ QSimpleCrypto::QBlockCipher blockCipher;
+ QByteArray key = blockCipher.generatePrivateSalt(32);
+ QByteArray iv = blockCipher.generatePrivateSalt(32);
+ QByteArray salt = blockCipher.generatePrivateSalt(8);
+
+ QJsonObject keyPayload;
+ keyPayload[configKey::aesKey] = QString(key.toBase64());
+ keyPayload[configKey::aesIv] = QString(iv.toBase64());
+ keyPayload[configKey::aesSalt] = QString(salt.toBase64());
+
+ QByteArray encryptedKeyPayload;
+ QByteArray encryptedApiPayload;
+ try {
+ QSimpleCrypto::QRsa rsa;
+
+ EVP_PKEY *publicKey = nullptr;
+ try {
+ QByteArray rsaKey = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
+ QSimpleCrypto::QRsa rsa;
+ publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
+ } catch (...) {
+ Utils::logException();
+ qCritical() << "error loading public key from environment variables";
+ return ErrorCode::ApiMissingAgwPublicKey;
+ }
+
+ encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
+ EVP_PKEY_free(publicKey);
+
+ encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
+ } catch (...) { // todo change error handling in QSimpleCrypto?
+ Utils::logException();
+ qCritical() << "error when encrypting the request body";
+ return ErrorCode::ApiConfigDecryptionError;
+ }
+
+ QJsonObject requestBody;
+ requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
+ requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
+
+ QNetworkReply *reply = amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
+
+ QEventLoop wait;
+ connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
+
+ QList sslErrors;
+ connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; });
+ wait.exec();
+
+ QByteArray encryptedResponseBody = reply->readAll();
+
+ if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
+ auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
+ request.setUrl(url);
+ return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
+ };
+
+ auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
+ this](QNetworkReply *nestedReply, const QList &nestedSslErrors) {
+ encryptedResponseBody = nestedReply->readAll();
+ if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
+ sslErrors = nestedSslErrors;
+ reply = nestedReply;
+ return true;
+ }
+ return false;
+ };
+
+ bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
+ }
+
+ auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
+ reply->deleteLater();
+ if (errorCode) {
+ return errorCode;
+ }
+
+ try {
+ responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
+ return ErrorCode::NoError;
+ } catch (...) { // todo change error handling in QSimpleCrypto?
+ Utils::logException();
+ qCritical() << "error when decrypting the request body";
+ return ErrorCode::ApiConfigDecryptionError;
+ }
+}
+
+QStringList GatewayController::getProxyUrls()
+{
+ QNetworkRequest request;
+ request.setTransferTimeout(m_requestTimeoutMsecs);
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+
+ QEventLoop wait;
+ QList sslErrors;
+ QNetworkReply *reply;
+
+ QStringList proxyStorageUrl;
+ if (m_isDevEnvironment) {
+ proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
+ } else {
+ proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
+ }
+
+ QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
+
+ for (const auto &proxyStorageUrl : proxyStorageUrl) {
+ request.setUrl(proxyStorageUrl);
+ reply = amnApp->networkManager()->get(request);
+
+ connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
+ connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; });
+ wait.exec();
+
+ if (reply->error() == QNetworkReply::NetworkError::NoError) {
+ break;
+ }
+ reply->deleteLater();
+ }
+
+ auto encryptedResponseBody = reply->readAll();
+ reply->deleteLater();
+
+ EVP_PKEY *privateKey = nullptr;
+ QByteArray responseBody;
+ try {
+ if (!m_isDevEnvironment) {
+ QCryptographicHash hash(QCryptographicHash::Sha512);
+ hash.addData(key);
+ QByteArray hashResult = hash.result().toHex();
+
+ QByteArray key = QByteArray::fromHex(hashResult.left(64));
+ QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
+
+ QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
+
+ QSimpleCrypto::QBlockCipher blockCipher;
+ responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
+ } else {
+ responseBody = encryptedResponseBody;
+ }
+ } catch (...) {
+ Utils::logException();
+ qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
+ return {};
+ }
+
+ auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
+
+ QStringList endpoints;
+ for (const auto &endpoint : endpointsArray) {
+ endpoints.push_back(endpoint.toString());
+ }
+ return endpoints;
+}
+
+bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
+ const QByteArray &iv, const QByteArray &salt)
+{
+ if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
+ qDebug() << "Timeout occurred";
+ return true;
+ } else if (responseBody.contains("html")) {
+ qDebug() << "The response contains an html tag";
+ return true;
+ } else if (checkEncryption) {
+ try {
+ QSimpleCrypto::QBlockCipher blockCipher;
+ static_cast(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
+ } catch (...) {
+ qDebug() << "Failed to decrypt the data";
+ return true;
+ }
+ }
+ return false;
+}
+
+void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *reply,
+ std::function requestFunction,
+ std::function &sslErrors)> replyProcessingFunction)
+{
+ QStringList proxyUrls = getProxyUrls();
+ std::random_device randomDevice;
+ std::mt19937 generator(randomDevice());
+ std::shuffle(proxyUrls.begin(), proxyUrls.end(), generator);
+
+ QEventLoop wait;
+ QList sslErrors;
+ QByteArray responseBody;
+
+ for (const QString &proxyUrl : proxyUrls) {
+ qDebug() << "Go to the next endpoint";
+ reply->deleteLater(); // delete the previous reply
+ reply = requestFunction(endpoint.arg(proxyUrl));
+
+ QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
+ connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; });
+ wait.exec();
+
+ if (!replyProcessingFunction(reply, sslErrors)) {
+ break;
+ }
+ }
+}
diff --git a/client/core/controllers/gatewayController.h b/client/core/controllers/gatewayController.h
new file mode 100644
index 00000000..45d989f0
--- /dev/null
+++ b/client/core/controllers/gatewayController.h
@@ -0,0 +1,35 @@
+#ifndef GATEWAYCONTROLLER_H
+#define GATEWAYCONTROLLER_H
+
+#include
+#include
+
+#include "core/defs.h"
+
+#ifdef Q_OS_IOS
+ #include "platforms/ios/ios_controller.h"
+#endif
+
+class GatewayController : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent = nullptr);
+
+ amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody);
+ amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
+
+private:
+ QStringList getProxyUrls();
+ bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
+ const QByteArray &iv = "", const QByteArray &salt = "");
+ void bypassProxy(const QString &endpoint, QNetworkReply *reply, std::function requestFunction,
+ std::function &sslErrors)> replyProcessingFunction);
+
+ int m_requestTimeoutMsecs;
+ QString m_gatewayEndpoint;
+ bool m_isDevEnvironment = false;
+};
+
+#endif // GATEWAYCONTROLLER_H
diff --git a/client/core/controllers/vpnConfigurationController.cpp b/client/core/controllers/vpnConfigurationController.cpp
index 52f42c42..61287972 100644
--- a/client/core/controllers/vpnConfigurationController.cpp
+++ b/client/core/controllers/vpnConfigurationController.cpp
@@ -77,8 +77,7 @@ ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isA
}
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair &dns, const QJsonObject &serverConfig,
- const QJsonObject &containerConfig, const DockerContainer container,
- ErrorCode &errorCode)
+ const QJsonObject &containerConfig, const DockerContainer container)
{
QJsonObject vpnConfiguration {};
@@ -103,7 +102,8 @@ QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair &settings, QSharedPointer serverController, QObject *parent = nullptr);
+ explicit VpnConfigurationsController(const std::shared_ptr &settings, QSharedPointer serverController,
+ QObject *parent = nullptr);
public slots:
ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container,
@@ -21,7 +22,7 @@ public slots:
const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol,
QString &protocolConfigString);
QJsonObject createVpnConfiguration(const QPair &dns, const QJsonObject &serverConfig,
- const QJsonObject &containerConfig, const DockerContainer container, ErrorCode &errorCode);
+ const QJsonObject &containerConfig, const DockerContainer container);
static void updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig, const QString &stdOut);
signals:
diff --git a/client/core/defs.h b/client/core/defs.h
index 4a174e95..6c4b93b3 100644
--- a/client/core/defs.h
+++ b/client/core/defs.h
@@ -6,9 +6,6 @@
namespace amnezia
{
-
- constexpr const qint16 qrMagicCode = 1984;
-
struct ServerCredentials
{
QString hostName;
@@ -47,6 +44,7 @@ namespace amnezia
InternalError = 101,
NotImplementedError = 102,
AmneziaServiceNotRunning = 103,
+ NotSupportedOnThisPlatform = 104,
// Server errors
ServerCheckFailed = 200,
@@ -100,6 +98,7 @@ namespace amnezia
// import and install errors
ImportInvalidConfigError = 900,
ImportOpenConfigError = 901,
+ NoInstalledContainersError = 902,
// Android errors
AndroidError = 1000,
@@ -113,6 +112,8 @@ namespace amnezia
ApiMissingAgwPublicKey = 1105,
ApiConfigDecryptionError = 1106,
ApiServicesMissingError = 1107,
+ ApiConfigLimitError = 1108,
+ ApiNotFoundError = 1109,
// QFile errors
OpenError = 1200,
diff --git a/client/core/enums/apiEnums.h b/client/core/enums/apiEnums.h
deleted file mode 100644
index 1f050007..00000000
--- a/client/core/enums/apiEnums.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef APIENUMS_H
-#define APIENUMS_H
-
-enum ApiConfigSources {
- Telegram = 1,
- AmneziaGateway
-};
-
-#endif // APIENUMS_H
diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp
index ac3ff971..e9f47cff 100644
--- a/client/core/errorstrings.cpp
+++ b/client/core/errorstrings.cpp
@@ -12,6 +12,7 @@ QString errorString(ErrorCode code) {
case(ErrorCode::UnknownError): errorMessage = QObject::tr("Unknown error"); break;
case(ErrorCode::NotImplementedError): errorMessage = QObject::tr("Function not implemented"); break;
case(ErrorCode::AmneziaServiceNotRunning): errorMessage = QObject::tr("Background service is not running"); break;
+ case(ErrorCode::NotSupportedOnThisPlatform): errorMessage = QObject::tr("The selected protocol is not supported on the current platform"); break;
// Server errors
case(ErrorCode::ServerCheckFailed): errorMessage = QObject::tr("Server check failed"); break;
@@ -54,6 +55,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::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;
// Android errors
case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break;
@@ -67,6 +69,8 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ApiMissingAgwPublicKey): errorMessage = QObject::tr("Missing AGW public key"); break;
case (ErrorCode::ApiConfigDecryptionError): errorMessage = QObject::tr("Failed to decrypt response payload"); break;
case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break;
+ case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break;
+ case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
// QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
diff --git a/client/core/ipcclient.cpp b/client/core/ipcclient.cpp
index b44da1bf..69edcd15 100644
--- a/client/core/ipcclient.cpp
+++ b/client/core/ipcclient.cpp
@@ -5,12 +5,12 @@ IpcClient *IpcClient::m_instance = nullptr;
IpcClient::IpcClient(QObject *parent) : QObject(parent)
{
-
}
IpcClient::~IpcClient()
{
- if (m_localSocket) m_localSocket->close();
+ if (m_localSocket)
+ m_localSocket->close();
}
bool IpcClient::isSocketConnected() const
@@ -25,13 +25,15 @@ IpcClient *IpcClient::Instance()
QSharedPointer IpcClient::Interface()
{
- if (!Instance()) return nullptr;
+ if (!Instance())
+ return nullptr;
return Instance()->m_ipcClient;
}
QSharedPointer IpcClient::InterfaceTun2Socks()
{
- if (!Instance()) return nullptr;
+ if (!Instance())
+ return nullptr;
return Instance()->m_Tun2SocksClient;
}
@@ -42,15 +44,28 @@ bool IpcClient::init(IpcClient *instance)
Instance()->m_localSocket = new QLocalSocket(Instance());
connect(Instance()->m_localSocket.data(), &QLocalSocket::connected, &Instance()->m_ClientNode, []() {
Instance()->m_ClientNode.addClientSideConnection(Instance()->m_localSocket.data());
+ auto cliNode = Instance()->m_ClientNode.acquire();
+ cliNode->waitForSource(5000);
+ Instance()->m_ipcClient.reset(cliNode);
+
+ if (!Instance()->m_ipcClient) {
+ qWarning() << "IpcClient is not ready!";
+ }
- Instance()->m_ipcClient.reset(Instance()->m_ClientNode.acquire());
Instance()->m_ipcClient->waitForSource(1000);
if (!Instance()->m_ipcClient->isReplicaValid()) {
qWarning() << "IpcClient replica is not connected!";
}
- Instance()->m_Tun2SocksClient.reset(Instance()->m_ClientNode.acquire());
+ auto t2sNode = Instance()->m_ClientNode.acquire();
+ t2sNode->waitForSource(5000);
+ Instance()->m_Tun2SocksClient.reset(t2sNode);
+
+ if (!Instance()->m_Tun2SocksClient) {
+ qWarning() << "IpcClient::m_Tun2SocksClient is not ready!";
+ }
+
Instance()->m_Tun2SocksClient->waitForSource(1000);
if (!Instance()->m_Tun2SocksClient->isReplicaValid()) {
@@ -58,9 +73,8 @@ bool IpcClient::init(IpcClient *instance)
}
});
- connect(Instance()->m_localSocket, &QLocalSocket::disconnected, [instance](){
- instance->m_isSocketConnected = false;
- });
+ connect(Instance()->m_localSocket, &QLocalSocket::disconnected,
+ [instance]() { instance->m_isSocketConnected = false; });
Instance()->m_localSocket->connectToServer(amnezia::getIpcServiceUrl());
Instance()->m_localSocket->waitForConnected();
@@ -77,7 +91,7 @@ bool IpcClient::init(IpcClient *instance)
QSharedPointer IpcClient::CreatePrivilegedProcess()
{
- if (! Instance()->m_ipcClient || ! Instance()->m_ipcClient->isReplicaValid()) {
+ if (!Instance()->m_ipcClient || !Instance()->m_ipcClient->isReplicaValid()) {
qWarning() << "IpcClient::createPrivilegedProcess : IpcClient IpcClient replica is not valid";
return nullptr;
}
@@ -100,18 +114,15 @@ QSharedPointer IpcClient::CreatePrivilegedProcess()
pd->ipcProcess.reset(priv);
if (!pd->ipcProcess) {
qWarning() << "Acquire PrivilegedProcess failed";
- }
- else {
+ } else {
pd->ipcProcess->waitForSource(1000);
if (!pd->ipcProcess->isReplicaValid()) {
qWarning() << "PrivilegedProcess replica is not connected!";
}
- QObject::connect(pd->ipcProcess.data(), &PrivilegedProcess::destroyed, pd->ipcProcess.data(), [pd](){
- pd->replicaNode->deleteLater();
- });
+ QObject::connect(pd->ipcProcess.data(), &PrivilegedProcess::destroyed, pd->ipcProcess.data(),
+ [pd]() { pd->replicaNode->deleteLater(); });
}
-
});
pd->localSocket->connectToServer(amnezia::getIpcProcessUrl(pid));
pd->localSocket->waitForConnected();
@@ -119,5 +130,3 @@ QSharedPointer IpcClient::CreatePrivilegedProcess()
auto processReplica = QSharedPointer(pd->ipcProcess);
return processReplica;
}
-
-
diff --git a/client/core/networkUtilities.h b/client/core/networkUtilities.h
index 3057b852..3b64b547 100644
--- a/client/core/networkUtilities.h
+++ b/client/core/networkUtilities.h
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
class NetworkUtilities : public QObject
@@ -30,7 +31,6 @@ public:
static QString ipAddressFromIpWithSubnet(const QString ip);
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
-
};
#endif // NETWORKUTILITIES_H
diff --git a/client/core/qrCodeUtils.cpp b/client/core/qrCodeUtils.cpp
new file mode 100644
index 00000000..a18af172
--- /dev/null
+++ b/client/core/qrCodeUtils.cpp
@@ -0,0 +1,35 @@
+#include "qrCodeUtils.h"
+
+#include
+#include
+
+QList qrCodeUtils::generateQrCodeImageSeries(const QByteArray &data)
+{
+ double k = 850;
+
+ quint8 chunksCount = std::ceil(data.size() / k);
+ QList chunks;
+ for (int i = 0; i < data.size(); i = i + k) {
+ QByteArray chunk;
+ QDataStream s(&chunk, QIODevice::WriteOnly);
+ s << qrCodeUtils::qrMagicCode << chunksCount << (quint8)std::round(i / k) << data.mid(i, k);
+
+ QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
+
+ qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW);
+ QString svg = QString::fromStdString(toSvgString(qr, 1));
+ chunks.append(svgToBase64(svg));
+ }
+
+ return chunks;
+}
+
+QString qrCodeUtils::svgToBase64(const QString &image)
+{
+ return "data:image/svg;base64," + QString::fromLatin1(image.toUtf8().toBase64().data());
+}
+
+qrcodegen::QrCode qrCodeUtils::generateQrCode(const QByteArray &data)
+{
+ return qrcodegen::QrCode::encodeText(data, qrcodegen::QrCode::Ecc::LOW);
+}
diff --git a/client/core/qrCodeUtils.h b/client/core/qrCodeUtils.h
new file mode 100644
index 00000000..cda0723b
--- /dev/null
+++ b/client/core/qrCodeUtils.h
@@ -0,0 +1,17 @@
+#ifndef QRCODEUTILS_H
+#define QRCODEUTILS_H
+
+#include
+
+#include "qrcodegen.hpp"
+
+namespace qrCodeUtils
+{
+ constexpr const qint16 qrMagicCode = 1984;
+
+ QList generateQrCodeImageSeries(const QByteArray &data);
+ qrcodegen::QrCode generateQrCode(const QByteArray &data);
+ QString svgToBase64(const QString &image);
+};
+
+#endif // QRCODEUTILS_H
diff --git a/client/images/controls/monitor.svg b/client/images/controls/monitor.svg
new file mode 100644
index 00000000..1cdf57c2
--- /dev/null
+++ b/client/images/controls/monitor.svg
@@ -0,0 +1,5 @@
+
diff --git a/client/ios/networkextension/CMakeLists.txt b/client/ios/networkextension/CMakeLists.txt
index c448ed08..dde03b3b 100644
--- a/client/ios/networkextension/CMakeLists.txt
+++ b/client/ios/networkextension/CMakeLists.txt
@@ -27,12 +27,7 @@ set_target_properties(networkextension PROPERTIES
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
- XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
- XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
-
- XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
- XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN.network-extension"
- XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN.network-extension"
+ XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
)
set_target_properties(networkextension PROPERTIES
diff --git a/client/ios/scripts/openvpn.sh b/client/ios/scripts/openvpn.sh
deleted file mode 100755
index 544b8078..00000000
--- a/client/ios/scripts/openvpn.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-XCODEBUILD="/usr/bin/xcodebuild"
-WORKINGDIR=`pwd`
-PATCH="/usr/bin/patch"
-
- cat $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/Project.xcconfig > $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
- cat << EOF >> $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
- PROJECT_TEMP_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/OpenVPNAdapter.build
- CONFIGURATION_BUILD_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-iphoneos
- BUILT_PRODUCTS_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-iphoneos
-EOF
-
-
- cd 3rd/OpenVPNAdapter
- if $XCODEBUILD -scheme OpenVPNAdapter -configuration Release -xcconfig Configuration/amnezia.xcconfig -sdk iphoneos -destination 'generic/platform=iOS' -project OpenVPNAdapter.xcodeproj ; then
- echo "OpenVPNAdapter built successfully"
- else
- echo "OpenVPNAdapter build failed"
- fi
- cd ../../
diff --git a/client/platforms/ios/HevSocksTunnel.swift b/client/platforms/ios/HevSocksTunnel.swift
index a86a0758..87d995e8 100644
--- a/client/platforms/ios/HevSocksTunnel.swift
+++ b/client/platforms/ios/HevSocksTunnel.swift
@@ -1,4 +1,5 @@
import HevSocks5Tunnel
+import NetworkExtension
public enum Socks5Tunnel {
diff --git a/client/protocols/ikev2_vpn_protocol_windows.cpp b/client/protocols/ikev2_vpn_protocol_windows.cpp
index e2e4ca90..b4110f03 100644
--- a/client/protocols/ikev2_vpn_protocol_windows.cpp
+++ b/client/protocols/ikev2_vpn_protocol_windows.cpp
@@ -238,7 +238,7 @@ ErrorCode Ikev2Protocol::start()
"-CipherTransformConstants GCMAES128 "
"-EncryptionMethod AES256 "
"-IntegrityCheckMethod SHA256 "
- "-PfsGroup None "
+ "-PfsGroup PFS2048 "
"-DHGroup Group14 "
"-PassThru -Force\"")
.arg(tunnelName());
diff --git a/client/protocols/xrayprotocol.cpp b/client/protocols/xrayprotocol.cpp
index 7c69ccde..faad8e94 100755
--- a/client/protocols/xrayprotocol.cpp
+++ b/client/protocols/xrayprotocol.cpp
@@ -1,16 +1,14 @@
#include "xrayprotocol.h"
-#include "utilities.h"
-#include "core/networkUtilities.h"
-
#include
#include
#include
#include
+#include "core/networkUtilities.h"
+#include "utilities.h"
-XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent):
- VpnProtocol(configuration, parent)
+XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
{
readXrayConfiguration(configuration);
m_routeGateway = NetworkUtilities::getGatewayAndIface();
@@ -45,10 +43,7 @@ ErrorCode XrayProtocol::start()
QStringList args = QStringList() << "-c" << m_xrayCfgFile.fileName() << "-format=json";
- qDebug().noquote() << "XrayProtocol::start()"
- << xrayExecPath() << args.join(" ");
-
-
+ qDebug().noquote() << "XrayProtocol::start()" << xrayExecPath() << args.join(" ");
m_xrayProcess.setProcessChannelMode(QProcess::MergedChannels);
m_xrayProcess.setProgram(xrayExecPath());
@@ -66,14 +61,15 @@ ErrorCode XrayProtocol::start()
#endif
});
- connect(&m_xrayProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
- qDebug().noquote() << "XrayProtocol finished, exitCode, exitStatus" << exitCode << exitStatus;
- setConnectionState(Vpn::ConnectionState::Disconnected);
- if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) {
- emit protocolError(amnezia::ErrorCode::XrayExecutableCrashed);
- emit setConnectionState(Vpn::ConnectionState::Error);
- }
- });
+ connect(&m_xrayProcess, QOverload::of(&QProcess::finished), this,
+ [this](int exitCode, QProcess::ExitStatus exitStatus) {
+ qDebug().noquote() << "XrayProtocol finished, exitCode, exitStatus" << exitCode << exitStatus;
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) {
+ emit protocolError(amnezia::ErrorCode::XrayExecutableCrashed);
+ emit setConnectionState(Vpn::ConnectionState::Error);
+ }
+ });
m_xrayProcess.start();
m_xrayProcess.waitForStarted();
@@ -82,11 +78,10 @@ ErrorCode XrayProtocol::start()
setConnectionState(Vpn::ConnectionState::Connecting);
QThread::msleep(1000);
return startTun2Sock();
- }
- else return ErrorCode::XrayExecutableMissing;
+ } else
+ return ErrorCode::XrayExecutableMissing;
}
-
ErrorCode XrayProtocol::startTun2Sock()
{
m_t2sProcess->start();
@@ -98,71 +93,68 @@ ErrorCode XrayProtocol::startTun2Sock()
connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::stateChanged, this,
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
- connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::setConnectionState, this,
- [&](int vpnState) {
- qDebug() << "PrivilegedProcess setConnectionState " << vpnState;
- if (vpnState == Vpn::ConnectionState::Connected)
- {
- setConnectionState(Vpn::ConnectionState::Connecting);
- QList dnsAddr;
- dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns1).toString()));
- dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString()));
+ connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::setConnectionState, this, [&](int vpnState) {
+ qDebug() << "PrivilegedProcess setConnectionState " << vpnState;
+ if (vpnState == Vpn::ConnectionState::Connected) {
+ setConnectionState(Vpn::ConnectionState::Connecting);
+ QList dnsAddr;
+ dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns1).toString()));
+ dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString()));
#ifdef Q_OS_WIN
- QThread::msleep(8000);
+ QThread::msleep(8000);
#endif
#ifdef Q_OS_MACOS
- QThread::msleep(5000);
- IpcClient::Interface()->createTun("utun22", amnezia::protocols::xray::defaultLocalAddr);
- IpcClient::Interface()->updateResolvers("utun22", dnsAddr);
+ QThread::msleep(5000);
+ IpcClient::Interface()->createTun("utun22", amnezia::protocols::xray::defaultLocalAddr);
+ IpcClient::Interface()->updateResolvers("utun22", dnsAddr);
#endif
#ifdef Q_OS_LINUX
- QThread::msleep(1000);
- IpcClient::Interface()->createTun("tun2", amnezia::protocols::xray::defaultLocalAddr);
- IpcClient::Interface()->updateResolvers("tun2", dnsAddr);
+ QThread::msleep(1000);
+ IpcClient::Interface()->createTun("tun2", amnezia::protocols::xray::defaultLocalAddr);
+ IpcClient::Interface()->updateResolvers("tun2", dnsAddr);
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
- // killSwitch toggle
- if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
- m_configData.insert("vpnServer", m_remoteAddress);
- IpcClient::Interface()->enableKillSwitch(m_configData, 0);
- }
+ // killSwitch toggle
+ if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
+ m_configData.insert("vpnServer", m_remoteAddress);
+ IpcClient::Interface()->enableKillSwitch(m_configData, 0);
+ }
#endif
- if (m_routeMode == 0) {
- IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1");
- IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "128.0.0.0/1");
- IpcClient::Interface()->routeAddList(m_routeGateway, QStringList() << m_remoteAddress);
- }
- IpcClient::Interface()->StopRoutingIpv6();
+ if (m_routeMode == Settings::RouteMode::VpnAllSites) {
+ IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1");
+ IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "128.0.0.0/1");
+ IpcClient::Interface()->routeAddList(m_routeGateway, QStringList() << m_remoteAddress);
+ }
+ IpcClient::Interface()->StopRoutingIpv6();
#ifdef Q_OS_WIN
- IpcClient::Interface()->updateResolvers("tun2", dnsAddr);
- QList netInterfaces = QNetworkInterface::allInterfaces();
- for (int i = 0; i < netInterfaces.size(); i++) {
- for (int j = 0; j < netInterfaces.at(i).addressEntries().size(); j++)
- {
- // killSwitch toggle
- if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
- if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
- IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
- }
- m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
- m_configData.insert("vpnGateway", m_vpnGateway);
- m_configData.insert("vpnServer", m_remoteAddress);
- IpcClient::Interface()->enablePeerTraffic(m_configData);
- }
+ IpcClient::Interface()->updateResolvers("tun2", dnsAddr);
+ QList netInterfaces = QNetworkInterface::allInterfaces();
+ for (int i = 0; i < netInterfaces.size(); i++) {
+ for (int j = 0; j < netInterfaces.at(i).addressEntries().size(); j++) {
+ // killSwitch toggle
+ if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
+ if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
+ IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
}
+ m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
+ m_configData.insert("vpnGateway", m_vpnGateway);
+ m_configData.insert("vpnServer", m_remoteAddress);
+ IpcClient::Interface()->enablePeerTraffic(m_configData);
}
-#endif
- setConnectionState(Vpn::ConnectionState::Connected);
}
+ }
+#endif
+ setConnectionState(Vpn::ConnectionState::Connected);
+ }
#if !defined(Q_OS_MACOS)
- if (vpnState == Vpn::ConnectionState::Disconnected) {
- setConnectionState(Vpn::ConnectionState::Disconnected);
- IpcClient::Interface()->deleteTun("tun2");
- IpcClient::Interface()->StartRoutingIpv6();
- IpcClient::Interface()->clearSavedRoutes();
- }
+ if (vpnState == Vpn::ConnectionState::Disconnected) {
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ IpcClient::Interface()->deleteTun("tun2");
+ IpcClient::Interface()->StartRoutingIpv6();
+ IpcClient::Interface()->clearSavedRoutes();
+ }
#endif
- });
+ });
return ErrorCode::NoError;
}
@@ -204,7 +196,7 @@ void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
m_localPort = QString(amnezia::protocols::xray::defaultLocalProxyPort).toInt();
m_remoteHost = configuration.value(amnezia::config_key::hostName).toString();
m_remoteAddress = NetworkUtilities::getIPAddress(m_remoteHost);
- m_routeMode = configuration.value(amnezia::config_key::splitTunnelType).toInt();
+ m_routeMode = static_cast(configuration.value(amnezia::config_key::splitTunnelType).toInt());
m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString();
m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString();
}
diff --git a/client/protocols/xrayprotocol.h b/client/protocols/xrayprotocol.h
index ee632333..c79ef608 100644
--- a/client/protocols/xrayprotocol.h
+++ b/client/protocols/xrayprotocol.h
@@ -1,14 +1,16 @@
#ifndef XRAYPROTOCOL_H
#define XRAYPROTOCOL_H
-#include "openvpnprotocol.h"
#include "QProcess"
+
#include "containers/containers_defs.h"
+#include "openvpnprotocol.h"
+#include "settings.h"
class XrayProtocol : public VpnProtocol
{
public:
- XrayProtocol(const QJsonObject& configuration, QObject* parent = nullptr);
+ XrayProtocol(const QJsonObject &configuration, QObject *parent = nullptr);
virtual ~XrayProtocol() override;
ErrorCode start() override;
@@ -24,11 +26,12 @@ protected:
private:
static QString xrayExecPath();
static QString tun2SocksExecPath();
+
private:
int m_localPort;
QString m_remoteHost;
QString m_remoteAddress;
- int m_routeMode;
+ Settings::RouteMode m_routeMode;
QJsonObject m_configData;
QString m_primaryDNS;
QString m_secondaryDNS;
@@ -37,7 +40,6 @@ private:
QSharedPointer m_t2sProcess;
#endif
QTemporaryFile m_xrayCfgFile;
-
};
#endif // XRAYPROTOCOL_H
diff --git a/client/resources.qrc b/client/resources.qrc
index ff03a6e7..16071da0 100644
--- a/client/resources.qrc
+++ b/client/resources.qrc
@@ -192,7 +192,7 @@
ui/qml/Pages2/PageServiceTorWebsiteSettings.qml
ui/qml/Pages2/PageSettings.qml
ui/qml/Pages2/PageSettingsAbout.qml
- ui/qml/Pages2/PageSettingsApiLanguageList.qml
+ ui/qml/Pages2/PageSettingsApiAvailableCountries.qml
ui/qml/Pages2/PageSettingsApiServerInfo.qml
ui/qml/Pages2/PageSettingsApplication.qml
ui/qml/Pages2/PageSettingsAppSplitTunneling.qml
@@ -224,6 +224,13 @@
ui/qml/Pages2/PageShare.qml
ui/qml/Pages2/PageShareFullAccess.qml
ui/qml/Pages2/PageStart.qml
+ ui/qml/Components/RenameServerDrawer.qml
+ ui/qml/Controls2/ListViewType.qml
+ ui/qml/Pages2/PageSettingsApiSupport.qml
+ ui/qml/Pages2/PageSettingsApiInstructions.qml
+ ui/qml/Pages2/PageSettingsApiNativeConfigs.qml
+ ui/qml/Pages2/PageSettingsApiDevices.qml
+ images/controls/monitor.svg
images/flagKit/ZW.svg
diff --git a/client/secure_qsettings.cpp b/client/secure_qsettings.cpp
index 88c0242b..4fd199db 100644
--- a/client/secure_qsettings.cpp
+++ b/client/secure_qsettings.cpp
@@ -15,6 +15,12 @@
using namespace QKeychain;
+namespace {
+ constexpr const char *settingsKeyTag = "settingsKeyTag";
+ constexpr const char *settingsIvTag = "settingsIvTag";
+ constexpr const char *keyChainName = "AmneziaVPN-Keychain";
+}
+
SecureQSettings::SecureQSettings(const QString &organization, const QString &application, QObject *parent)
: QObject { parent }, m_settings(organization, application, parent), encryptedKeys({ "Servers/serversList" })
{
@@ -49,7 +55,7 @@ QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue
// check if value is not encrypted, v. < 2.0.x
retVal = m_settings.value(key);
if (retVal.isValid()) {
- if (retVal.userType() == QVariant::ByteArray && retVal.toByteArray().mid(0, magicString.size()) == magicString) {
+ if (retVal.userType() == QMetaType::QByteArray && retVal.toByteArray().mid(0, magicString.size()) == magicString) {
if (getEncKey().isEmpty() || getEncIv().isEmpty()) {
qCritical() << "SecureQSettings::setValue Decryption requested, but key is empty";
diff --git a/client/secure_qsettings.h b/client/secure_qsettings.h
index 43890578..3f04096e 100644
--- a/client/secure_qsettings.h
+++ b/client/secure_qsettings.h
@@ -8,10 +8,6 @@
#include "keychain.h"
-constexpr const char *settingsKeyTag = "settingsKeyTag";
-constexpr const char *settingsIvTag = "settingsIvTag";
-constexpr const char *keyChainName = "AmneziaVPN-Keychain";
-
class SecureQSettings : public QObject
{
Q_OBJECT
@@ -44,7 +40,7 @@ public:
private:
QSettings m_settings;
- mutable QMap m_cache;
+ mutable QHash m_cache;
QStringList encryptedKeys; // encode only key listed here
// only this fields need for backup
diff --git a/client/server_scripts/install_docker.sh b/client/server_scripts/install_docker.sh
index 6fed78c0..619b08d6 100644
--- a/client/server_scripts/install_docker.sh
+++ b/client/server_scripts/install_docker.sh
@@ -1,7 +1,7 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
-elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="> /dev/null 2>&1"; docker_pkg="docker"; dist="archlinux";\
+elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\
else echo "Packet manager not found"; exit 1; fi;\
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
@@ -12,6 +12,9 @@ if ! command -v docker > /dev/null 2>&1; then \
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
sleep 5; sudo systemctl enable --now docker; sleep 5;\
fi;\
+if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = "Y" ]; then \
+ if ! command -v apparmor_parser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst apparmor; fi;\
+fi;\
if [ "$(systemctl is-active docker)" != "active" ]; then \
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
sleep 5; sudo systemctl start docker; sleep 5;\
diff --git a/client/server_scripts/ipsec/configure_container.sh b/client/server_scripts/ipsec/configure_container.sh
index 76c4dfaf..1f0a45cb 100644
--- a/client/server_scripts/ipsec/configure_container.sh
+++ b/client/server_scripts/ipsec/configure_container.sh
@@ -33,14 +33,14 @@ conn shared
right=%any
encapsulation=yes
authby=secret
- pfs=no
+ pfs=yes
rekey=no
keyingtries=5
dpddelay=30
dpdtimeout=120
dpdaction=clear
ikev2=never
- ike=aes256-sha2,aes128-sha2,aes256-sha1,aes128-sha1,aes256-sha2;modp1024,aes128-sha1;modp1024
+ ike=aes256-sha2,aes128-sha2,aes256-sha1,aes128-sha1,aes256-sha2;modp2048,aes128-sha1;modp2048
phase2alg=aes_gcm-null,aes128-sha1,aes256-sha1,aes256-sha2_512,aes128-sha2,aes256-sha2
ikelifetime=24h
salifetime=24h
@@ -244,9 +244,9 @@ conn ikev2-cp
auto=add
ikev2=insist
rekey=no
- pfs=no
- ike=aes256-sha2,aes128-sha2,aes256-sha1,aes128-sha1
- phase2alg=aes_gcm-null,aes128-sha1,aes256-sha1,aes128-sha2,aes256-sha2
+ pfs=yes
+ ike=aes256-sha2,aes128-sha2,aes256-sha1,aes128-sha1,aes256-sha2;modp2048,aes128-sha1;modp2048
+ phase2alg=aes_gcm-null,aes128-sha1,aes256-sha1,aes256-sha2_512,aes128-sha2,aes256-sha2
ikelifetime=24h
salifetime=24h
encapsulation=yes
diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp
new file mode 100644
index 00000000..d3c8747d
--- /dev/null
+++ b/client/ui/controllers/api/apiConfigsController.cpp
@@ -0,0 +1,537 @@
+#include "apiConfigsController.h"
+
+#include
+#include
+
+#include "amnezia_application.h"
+#include "configurators/wireguard_configurator.h"
+#include "core/api/apiDefs.h"
+#include "core/api/apiUtils.h"
+#include "core/controllers/gatewayController.h"
+#include "core/qrCodeUtils.h"
+#include "ui/controllers/systemController.h"
+#include "version.h"
+
+namespace
+{
+ namespace configKey
+ {
+ constexpr char cloak[] = "cloak";
+ constexpr char awg[] = "awg";
+
+ constexpr char apiEdnpoint[] = "api_endpoint";
+ constexpr char accessToken[] = "api_key";
+ constexpr char certificate[] = "certificate";
+ constexpr char publicKey[] = "public_key";
+ constexpr char protocol[] = "protocol";
+
+ constexpr char uuid[] = "installation_uuid";
+ constexpr char osVersion[] = "os_version";
+ constexpr char appVersion[] = "app_version";
+
+ constexpr char userCountryCode[] = "user_country_code";
+ constexpr char serverCountryCode[] = "server_country_code";
+ constexpr char serviceType[] = "service_type";
+ constexpr char serviceInfo[] = "service_info";
+ constexpr char serviceProtocol[] = "service_protocol";
+
+ constexpr char aesKey[] = "aes_key";
+ constexpr char aesIv[] = "aes_iv";
+ constexpr char aesSalt[] = "aes_salt";
+
+ constexpr char apiPayload[] = "api_payload";
+ constexpr char keyPayload[] = "key_payload";
+
+ constexpr char apiConfig[] = "api_config";
+ constexpr char authData[] = "auth_data";
+
+ constexpr char config[] = "config";
+ }
+}
+
+ApiConfigsController::ApiConfigsController(const QSharedPointer &serversModel,
+ const QSharedPointer &apiServicesModel,
+ const std::shared_ptr &settings, QObject *parent)
+ : QObject(parent), m_serversModel(serversModel), m_apiServicesModel(apiServicesModel), m_settings(settings)
+{
+}
+
+bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, const QString &fileName)
+{
+ if (fileName.isEmpty()) {
+ emit errorOccurred(ErrorCode::PermissionsError);
+ return false;
+ }
+
+ GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
+
+ auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
+ auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
+
+ QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
+ ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
+
+ QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
+ apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
+ apiPayload[configKey::serverCountryCode] = serverCountryCode;
+ apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
+ apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
+
+ QByteArray responseBody;
+ ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody);
+ if (errorCode != ErrorCode::NoError) {
+ emit errorOccurred(errorCode);
+ return false;
+ }
+
+ QJsonObject jsonConfig = QJsonDocument::fromJson(responseBody).object();
+ QString nativeConfig = jsonConfig.value(configKey::config).toString();
+ nativeConfig.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
+
+ SystemController::saveFile(fileName, nativeConfig);
+ return true;
+}
+
+bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
+{
+ GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
+
+ auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
+ auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
+
+ QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
+ ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
+
+ QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
+ apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
+ apiPayload[configKey::serverCountryCode] = serverCountryCode;
+ apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
+ apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
+
+ QByteArray responseBody;
+ ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody);
+ if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
+ emit errorOccurred(errorCode);
+ return false;
+ }
+ return true;
+}
+
+void ApiConfigsController::prepareVpnKeyExport()
+{
+ auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
+ auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
+
+ auto vpnKey = apiConfigObject.value(apiDefs::key::vpnKey).toString();
+ m_vpnKey = vpnKey;
+
+ vpnKey.replace("vpn://", "");
+
+ m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(vpnKey.toUtf8());
+
+ emit vpnKeyExportReady();
+}
+
+void ApiConfigsController::copyVpnKeyToClipboard()
+{
+ auto clipboard = amnApp->getClipboard();
+ clipboard->setText(m_vpnKey);
+}
+
+bool ApiConfigsController::fillAvailableServices()
+{
+ GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
+
+ QJsonObject apiPayload;
+ apiPayload[configKey::osVersion] = QSysInfo::productType();
+
+ QByteArray responseBody;
+ ErrorCode errorCode = gatewayController.post(QString("%1v1/services"), apiPayload, responseBody);
+ if (errorCode == ErrorCode::NoError) {
+ if (!responseBody.contains("services")) {
+ errorCode = ErrorCode::ApiServicesMissingError;
+ }
+ }
+
+ if (errorCode != ErrorCode::NoError) {
+ emit errorOccurred(errorCode);
+ return false;
+ }
+
+ QJsonObject data = QJsonDocument::fromJson(responseBody).object();
+ m_apiServicesModel->updateModel(data);
+ return true;
+}
+
+bool ApiConfigsController::importServiceFromGateway()
+{
+ if (m_serversModel->isServerFromApiAlreadyExists(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(),
+ m_apiServicesModel->getSelectedServiceProtocol())) {
+ emit errorOccurred(ErrorCode::ApiConfigAlreadyAdded);
+ return false;
+ }
+
+ GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
+
+ auto installationUuid = m_settings->getInstallationUuid(true);
+ auto userCountryCode = m_apiServicesModel->getCountryCode();
+ auto serviceType = m_apiServicesModel->getSelectedServiceType();
+ auto serviceProtocol = m_apiServicesModel->getSelectedServiceProtocol();
+
+ ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
+
+ QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
+ apiPayload[configKey::userCountryCode] = userCountryCode;
+ apiPayload[configKey::serviceType] = serviceType;
+ apiPayload[configKey::uuid] = installationUuid;
+
+ QByteArray responseBody;
+ ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
+
+ QJsonObject serverConfig;
+ if (errorCode == ErrorCode::NoError) {
+ fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig);
+
+ QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject();
+ apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode());
+ apiConfig.insert(configKey::serviceType, m_apiServicesModel->getSelectedServiceType());
+ apiConfig.insert(configKey::serviceProtocol, m_apiServicesModel->getSelectedServiceProtocol());
+
+ serverConfig.insert(configKey::apiConfig, apiConfig);
+
+ m_serversModel->addServer(serverConfig);
+ emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
+ return true;
+ } else {
+ emit errorOccurred(errorCode);
+ return false;
+ }
+}
+
+bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
+ bool reloadServiceConfig)
+{
+ GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
+
+ auto serverConfig = m_serversModel->getServerConfig(serverIndex);
+ auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
+ auto authData = serverConfig.value(configKey::authData).toObject();
+
+ auto installationUuid = m_settings->getInstallationUuid(true);
+ auto userCountryCode = apiConfig.value(configKey::userCountryCode).toString();
+ auto serviceType = apiConfig.value(configKey::serviceType).toString();
+ auto serviceProtocol = apiConfig.value(configKey::serviceProtocol).toString();
+
+ ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
+
+ QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
+ apiPayload[configKey::userCountryCode] = userCountryCode;
+ apiPayload[configKey::serviceType] = serviceType;
+ apiPayload[configKey::uuid] = installationUuid;
+
+ if (!newCountryCode.isEmpty()) {
+ apiPayload[configKey::serverCountryCode] = newCountryCode;
+ }
+ if (!authData.isEmpty()) {
+ apiPayload[configKey::authData] = authData;
+ }
+
+ QByteArray responseBody;
+ ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
+
+ QJsonObject newServerConfig;
+ if (errorCode == ErrorCode::NoError) {
+ fillServerConfig(serviceProtocol, apiPayloadData, responseBody, newServerConfig);
+
+ QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject();
+ newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode));
+ newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType));
+ newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));
+ newApiConfig.insert(apiDefs::key::vpnKey, apiConfig.value(apiDefs::key::vpnKey));
+
+ newServerConfig.insert(configKey::apiConfig, newApiConfig);
+ newServerConfig.insert(configKey::authData, authData);
+ // newServerConfig.insert(
+
+ m_serversModel->editServer(newServerConfig, serverIndex);
+ if (reloadServiceConfig) {
+ emit reloadServerFromApiFinished(tr("API config reloaded"));
+ } else if (newCountryName.isEmpty()) {
+ emit updateServerFromApiFinished();
+ } else {
+ emit changeApiCountryFinished(tr("Successfully changed the country of connection to %1").arg(newCountryName));
+ }
+ return true;
+ } else {
+ emit errorOccurred(errorCode);
+ return false;
+ }
+}
+
+bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
+{
+ auto serverConfig = m_serversModel->getServerConfig(serverIndex);
+ auto installationUuid = m_settings->getInstallationUuid(true);
+
+#ifdef Q_OS_IOS
+ IosController::Instance()->requestInetAccess();
+ QThread::msleep(10);
+#endif
+
+ if (serverConfig.value(config_key::configVersion).toInt()) {
+ QNetworkRequest request;
+ request.setTransferTimeout(apiDefs::requestTimeoutMsecs);
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+ request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
+ QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
+ request.setUrl(endpoint);
+
+ QString protocol = serverConfig.value(configKey::protocol).toString();
+
+ ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
+
+ QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
+ apiPayload[configKey::uuid] = installationUuid;
+
+ QByteArray requestBody = QJsonDocument(apiPayload).toJson();
+
+ QNetworkReply *reply = amnApp->networkManager()->post(request, requestBody);
+
+ QEventLoop wait;
+ connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
+
+ QList sslErrors;
+ connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; });
+ wait.exec();
+
+ auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
+ if (errorCode != ErrorCode::NoError) {
+ reply->deleteLater();
+ emit errorOccurred(errorCode);
+ return false;
+ }
+
+ auto apiResponseBody = reply->readAll();
+ reply->deleteLater();
+ fillServerConfig(protocol, apiPayloadData, apiResponseBody, serverConfig);
+ m_serversModel->editServer(serverConfig, serverIndex);
+ emit updateServerFromApiFinished();
+ }
+ return true;
+}
+
+bool ApiConfigsController::deactivateDevice()
+{
+ GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
+
+ auto serverIndex = m_serversModel->getProcessedServerIndex();
+ auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
+ auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
+
+ if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) {
+ return true;
+ }
+
+ QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
+ ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
+
+ QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
+ apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
+ apiPayload[configKey::serverCountryCode] = apiConfigObject.value(configKey::serverCountryCode);
+ apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
+ apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
+ apiPayload[configKey::uuid] = m_settings->getInstallationUuid(true);
+
+ QByteArray responseBody;
+ ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
+ if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
+ emit errorOccurred(errorCode);
+ return false;
+ }
+
+ serverConfigObject.remove(config_key::containers);
+ m_serversModel->editServer(serverConfigObject, serverIndex);
+
+ return true;
+}
+
+bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode)
+{
+ GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
+
+ auto serverIndex = m_serversModel->getProcessedServerIndex();
+ auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
+ auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
+
+ if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) {
+ return true;
+ }
+
+ QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
+ ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
+
+ QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
+ apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
+ apiPayload[configKey::serverCountryCode] = serverCountryCode;
+ apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
+ apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
+ apiPayload[configKey::uuid] = uuid;
+
+ QByteArray responseBody;
+ ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
+ if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
+ emit errorOccurred(errorCode);
+ return false;
+ }
+
+ if (uuid == m_settings->getInstallationUuid(true)) {
+ serverConfigObject.remove(config_key::containers);
+ m_serversModel->editServer(serverConfigObject, serverIndex);
+ }
+
+ return true;
+}
+
+bool ApiConfigsController::isConfigValid()
+{
+ int serverIndex = m_serversModel->getDefaultServerIndex();
+ QJsonObject serverConfigObject = m_serversModel->getServerConfig(serverIndex);
+ auto configSource = apiUtils::getConfigSource(serverConfigObject);
+
+ if (configSource == apiDefs::ConfigSource::Telegram
+ && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
+ m_serversModel->removeApiConfig(serverIndex);
+ return updateServiceFromTelegram(serverIndex);
+ } else if (configSource == apiDefs::ConfigSource::AmneziaGateway
+ && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
+ return updateServiceFromGateway(serverIndex, "", "");
+ } else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) {
+ qDebug() << "attempt to update api config by expires_at event";
+ if (configSource == apiDefs::ConfigSource::Telegram) {
+ return updateServiceFromGateway(serverIndex, "", "");
+ } else {
+ m_serversModel->removeApiConfig(serverIndex);
+ return updateServiceFromTelegram(serverIndex);
+ }
+ }
+ return true;
+}
+
+ApiConfigsController::ApiPayloadData ApiConfigsController::generateApiPayloadData(const QString &protocol)
+{
+ ApiConfigsController::ApiPayloadData apiPayload;
+ if (protocol == configKey::cloak) {
+ apiPayload.certRequest = OpenVpnConfigurator::createCertRequest();
+ } else if (protocol == configKey::awg) {
+ auto connData = WireguardConfigurator::genClientKeys();
+ apiPayload.wireGuardClientPubKey = connData.clientPubKey;
+ apiPayload.wireGuardClientPrivKey = connData.clientPrivKey;
+ }
+ return apiPayload;
+}
+
+QJsonObject ApiConfigsController::fillApiPayload(const QString &protocol, const ApiPayloadData &apiPayloadData)
+{
+ QJsonObject obj;
+ if (protocol == configKey::cloak) {
+ obj[configKey::certificate] = apiPayloadData.certRequest.request;
+ } else if (protocol == configKey::awg) {
+ obj[configKey::publicKey] = apiPayloadData.wireGuardClientPubKey;
+ }
+
+ obj[configKey::osVersion] = QSysInfo::productType();
+ obj[configKey::appVersion] = QString(APP_VERSION);
+
+ return obj;
+}
+
+void ApiConfigsController::fillServerConfig(const QString &protocol, const ApiPayloadData &apiPayloadData,
+ const QByteArray &apiResponseBody, QJsonObject &serverConfig)
+{
+ QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString();
+
+ data.replace("vpn://", "");
+ QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
+
+ if (ba.isEmpty()) {
+ emit errorOccurred(ErrorCode::ApiConfigEmptyError);
+ return;
+ }
+
+ QByteArray ba_uncompressed = qUncompress(ba);
+ if (!ba_uncompressed.isEmpty()) {
+ ba = ba_uncompressed;
+ }
+
+ QString configStr = ba;
+ if (protocol == configKey::cloak) {
+ configStr.replace("", "\n");
+ configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
+ } else if (protocol == configKey::awg) {
+ configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
+ auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
+ auto containers = newServerConfig.value(config_key::containers).toArray();
+ if (containers.isEmpty()) {
+ return; // todo process error
+ }
+ auto container = containers.at(0).toObject();
+ QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
+ auto containerConfig = container.value(containerName).toObject();
+ auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
+ containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount);
+ containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize);
+ containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize);
+ containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize);
+ containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize);
+ containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader);
+ containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader);
+ containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader);
+ containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
+ container[containerName] = containerConfig;
+ containers.replace(0, container);
+ newServerConfig[config_key::containers] = containers;
+ configStr = QString(QJsonDocument(newServerConfig).toJson());
+ }
+
+ QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
+ serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
+ serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
+ serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
+ serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
+
+ if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
+ serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
+ serverConfig[config_key::description] = newServerConfig.value(config_key::description);
+ serverConfig[config_key::name] = newServerConfig.value(config_key::name);
+ }
+
+ auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
+ serverConfig[config_key::defaultContainer] = defaultContainer;
+
+ QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
+ map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
+ auto apiConfig = QJsonObject::fromVariantMap(map);
+
+ if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
+ apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject());
+ }
+
+ serverConfig[configKey::apiConfig] = apiConfig;
+
+ return;
+}
+
+QList ApiConfigsController::getQrCodes()
+{
+ return m_qrCodes;
+}
+
+int ApiConfigsController::getQrCodesCount()
+{
+ return m_qrCodes.size();
+}
+
+QString ApiConfigsController::getVpnKey()
+{
+ return m_vpnKey;
+}
diff --git a/client/ui/controllers/api/apiConfigsController.h b/client/ui/controllers/api/apiConfigsController.h
new file mode 100644
index 00000000..2fe981e4
--- /dev/null
+++ b/client/ui/controllers/api/apiConfigsController.h
@@ -0,0 +1,74 @@
+#ifndef APICONFIGSCONTROLLER_H
+#define APICONFIGSCONTROLLER_H
+
+#include
+
+#include "configurators/openvpn_configurator.h"
+#include "ui/models/api/apiServicesModel.h"
+#include "ui/models/servers_model.h"
+
+class ApiConfigsController : public QObject
+{
+ Q_OBJECT
+public:
+ ApiConfigsController(const QSharedPointer &serversModel, const QSharedPointer &apiServicesModel,
+ const std::shared_ptr &settings, QObject *parent = nullptr);
+
+ Q_PROPERTY(QList qrCodes READ getQrCodes NOTIFY vpnKeyExportReady)
+ Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY vpnKeyExportReady)
+ Q_PROPERTY(QString vpnKey READ getVpnKey NOTIFY vpnKeyExportReady)
+
+public slots:
+ bool exportNativeConfig(const QString &serverCountryCode, const QString &fileName);
+ bool revokeNativeConfig(const QString &serverCountryCode);
+ // bool exportVpnKey(const QString &fileName);
+ void prepareVpnKeyExport();
+ void copyVpnKeyToClipboard();
+
+ bool fillAvailableServices();
+ bool importServiceFromGateway();
+ bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
+ bool reloadServiceConfig = false);
+ bool updateServiceFromTelegram(const int serverIndex);
+ bool deactivateDevice();
+ bool deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode);
+
+ bool isConfigValid();
+
+signals:
+ void errorOccurred(ErrorCode errorCode);
+
+ void installServerFromApiFinished(const QString &message);
+ void changeApiCountryFinished(const QString &message);
+ void reloadServerFromApiFinished(const QString &message);
+ void updateServerFromApiFinished();
+
+ void vpnKeyExportReady();
+
+private:
+ struct ApiPayloadData
+ {
+ OpenVpnConfigurator::ConnectionData certRequest;
+
+ QString wireGuardClientPrivKey;
+ QString wireGuardClientPubKey;
+ };
+
+ ApiPayloadData generateApiPayloadData(const QString &protocol);
+ QJsonObject fillApiPayload(const QString &protocol, const ApiPayloadData &apiPayloadData);
+ void fillServerConfig(const QString &protocol, const ApiPayloadData &apiPayloadData, const QByteArray &apiResponseBody,
+ QJsonObject &serverConfig);
+
+ QList getQrCodes();
+ int getQrCodesCount();
+ QString getVpnKey();
+
+ QList m_qrCodes;
+ QString m_vpnKey;
+
+ QSharedPointer m_serversModel;
+ QSharedPointer m_apiServicesModel;
+ std::shared_ptr m_settings;
+};
+
+#endif // APICONFIGSCONTROLLER_H
diff --git a/client/ui/controllers/api/apiSettingsController.cpp b/client/ui/controllers/api/apiSettingsController.cpp
new file mode 100644
index 00000000..737bfd1a
--- /dev/null
+++ b/client/ui/controllers/api/apiSettingsController.cpp
@@ -0,0 +1,93 @@
+#include "apiSettingsController.h"
+
+#include
+#include
+
+#include "core/api/apiUtils.h"
+#include "core/controllers/gatewayController.h"
+
+namespace
+{
+ namespace configKey
+ {
+ constexpr char userCountryCode[] = "user_country_code";
+ constexpr char serverCountryCode[] = "server_country_code";
+ constexpr char serviceType[] = "service_type";
+ constexpr char serviceInfo[] = "service_info";
+
+ constexpr char apiConfig[] = "api_config";
+ constexpr char authData[] = "auth_data";
+ }
+
+ const int requestTimeoutMsecs = 12 * 1000; // 12 secs
+}
+
+ApiSettingsController::ApiSettingsController(const QSharedPointer &serversModel,
+ const QSharedPointer &apiAccountInfoModel,
+ const QSharedPointer &apiCountryModel,
+ const QSharedPointer &apiDevicesModel,
+ const std::shared_ptr &settings, QObject *parent)
+ : QObject(parent),
+ m_serversModel(serversModel),
+ m_apiAccountInfoModel(apiAccountInfoModel),
+ m_apiCountryModel(apiCountryModel),
+ m_apiDevicesModel(apiDevicesModel),
+ m_settings(settings)
+{
+}
+
+ApiSettingsController::~ApiSettingsController()
+{
+}
+
+bool ApiSettingsController::getAccountInfo(bool reload)
+{
+ if (reload) {
+ QEventLoop wait;
+ QTimer::singleShot(1000, &wait, &QEventLoop::quit);
+ wait.exec();
+ }
+
+ GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs);
+
+ auto processedIndex = m_serversModel->getProcessedServerIndex();
+ auto serverConfig = m_serversModel->getServerConfig(processedIndex);
+ auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
+ auto authData = serverConfig.value(configKey::authData).toObject();
+
+ QJsonObject apiPayload;
+ apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString();
+ apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
+ apiPayload[configKey::authData] = authData;
+
+ QByteArray responseBody;
+
+ if (apiUtils::getConfigType(serverConfig) == apiDefs::ConfigType::AmneziaPremiumV2) {
+ ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody);
+ if (errorCode != ErrorCode::NoError) {
+ emit errorOccurred(errorCode);
+ return false;
+ }
+ }
+
+ QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object();
+ m_apiAccountInfoModel->updateModel(accountInfo, serverConfig);
+
+ if (reload) {
+ updateApiCountryModel();
+ updateApiDevicesModel();
+ }
+
+ return true;
+}
+
+void ApiSettingsController::updateApiCountryModel()
+{
+ m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
+ m_apiCountryModel->updateIssuedConfigsInfo(m_apiAccountInfoModel->getIssuedConfigsInfo());
+}
+
+void ApiSettingsController::updateApiDevicesModel()
+{
+ m_apiDevicesModel->updateModel(m_apiAccountInfoModel->getIssuedConfigsInfo());
+}
diff --git a/client/ui/controllers/api/apiSettingsController.h b/client/ui/controllers/api/apiSettingsController.h
new file mode 100644
index 00000000..afe9a570
--- /dev/null
+++ b/client/ui/controllers/api/apiSettingsController.h
@@ -0,0 +1,37 @@
+#ifndef APISETTINGSCONTROLLER_H
+#define APISETTINGSCONTROLLER_H
+
+#include
+
+#include "ui/models/api/apiAccountInfoModel.h"
+#include "ui/models/api/apiCountryModel.h"
+#include "ui/models/api/apiDevicesModel.h"
+#include "ui/models/servers_model.h"
+
+class ApiSettingsController : public QObject
+{
+ Q_OBJECT
+public:
+ ApiSettingsController(const QSharedPointer &serversModel, const QSharedPointer &apiAccountInfoModel,
+ const QSharedPointer &apiCountryModel, const QSharedPointer &apiDevicesModel,
+ const std::shared_ptr &settings, QObject *parent = nullptr);
+ ~ApiSettingsController();
+
+public slots:
+ bool getAccountInfo(bool reload);
+ void updateApiCountryModel();
+ void updateApiDevicesModel();
+
+signals:
+ void errorOccurred(ErrorCode errorCode);
+
+private:
+ QSharedPointer m_serversModel;
+ QSharedPointer m_apiAccountInfoModel;
+ QSharedPointer m_apiCountryModel;
+ QSharedPointer m_apiDevicesModel;
+
+ std::shared_ptr m_settings;
+};
+
+#endif // APISETTINGSCONTROLLER_H
diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp
index f9491d4e..9fc60493 100644
--- a/client/ui/controllers/connectionController.cpp
+++ b/client/ui/controllers/connectionController.cpp
@@ -5,10 +5,8 @@
#else
#include
#endif
-#include
#include "core/controllers/vpnConfigurationController.h"
-#include "core/enums/apiEnums.h"
#include "version.h"
ConnectionController::ConnectionController(const QSharedPointer &serversModel,
@@ -27,7 +25,7 @@ ConnectionController::ConnectionController(const QSharedPointer &s
connect(this, &ConnectionController::connectToVpn, m_vpnConnection.get(), &VpnConnection::connectToVpn, Qt::QueuedConnection);
connect(this, &ConnectionController::disconnectFromVpn, m_vpnConnection.get(), &VpnConnection::disconnectFromVpn, Qt::QueuedConnection);
- connect(this, &ConnectionController::configFromApiUpdated, this, &ConnectionController::continueConnection);
+ connect(this, &ConnectionController::connectButtonClicked, this, &ConnectionController::toggleConnection, Qt::QueuedConnection);
m_state = Vpn::ConnectionState::Disconnected;
}
@@ -35,8 +33,7 @@ ConnectionController::ConnectionController(const QSharedPointer &s
void ConnectionController::openConnection()
{
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
- if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
- {
+ if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) {
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
return;
}
@@ -44,26 +41,24 @@ void ConnectionController::openConnection()
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
- auto configVersion = serverConfig.value(config_key::configVersion).toInt();
- emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
+ DockerContainer container = qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
- if (configVersion == ApiConfigSources::Telegram
- && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
- emit updateApiConfigFromTelegram();
- } else if (configVersion == ApiConfigSources::AmneziaGateway
- && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
- emit updateApiConfigFromGateway();
- } else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) {
- qDebug() << "attempt to update api config by expires_at event";
- if (configVersion == ApiConfigSources::Telegram) {
- emit updateApiConfigFromTelegram();
- } else {
- emit updateApiConfigFromGateway();
- }
- } else {
- continueConnection();
+ if (!m_containersModel->isSupportedByCurrentPlatform(container)) {
+ emit connectionErrorOccurred(ErrorCode::NotSupportedOnThisPlatform);
+ return;
}
+
+ QSharedPointer serverController(new ServerController(m_settings));
+ VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
+
+ QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
+ ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
+
+ auto dns = m_serversModel->getDnsPair(serverIndex);
+
+ auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container);
+ emit connectToVpn(serverIndex, credentials, container, vpnConfiguration);
}
void ConnectionController::closeConnection()
@@ -167,7 +162,7 @@ void ConnectionController::toggleConnection()
} else if (isConnected()) {
closeConnection();
} else {
- openConnection();
+ emit prepareConfig();
}
}
@@ -180,98 +175,3 @@ bool ConnectionController::isConnected() const
{
return m_isConnected;
}
-
-bool ConnectionController::isProtocolConfigExists(const QJsonObject &containerConfig, const DockerContainer container)
-{
- for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
- QString protocolConfig =
- containerConfig.value(ProtocolProps::protoToString(protocol)).toObject().value(config_key::last_config).toString();
-
- if (protocolConfig.isEmpty()) {
- return false;
- }
- }
- return true;
-}
-
-void ConnectionController::continueConnection()
-{
- int serverIndex = m_serversModel->getDefaultServerIndex();
- QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
- auto configVersion = serverConfig.value(config_key::configVersion).toInt();
-
- if (!m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
- emit noInstalledContainers();
- emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
- return;
- }
-
- DockerContainer container = qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
-
- if (!m_containersModel->isSupportedByCurrentPlatform(container)) {
- emit connectionErrorOccurred(tr("The selected protocol is not supported on the current platform"));
- return;
- }
-
- if (container == DockerContainer::None) {
- emit connectionErrorOccurred(tr("VPN Protocols is not installed.\n Please install VPN container at first"));
- return;
- }
-
- QSharedPointer serverController(new ServerController(m_settings));
- VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
-
- QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
- ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
- ErrorCode errorCode = updateProtocolConfig(container, credentials, containerConfig, serverController);
- if (errorCode != ErrorCode::NoError) {
- emit connectionErrorOccurred(errorCode);
- return;
- }
-
- auto dns = m_serversModel->getDnsPair(serverIndex);
-
- auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container, errorCode);
- if (errorCode != ErrorCode::NoError) {
- emit connectionErrorOccurred(tr("unable to create configuration"));
- return;
- }
-
- emit connectToVpn(serverIndex, credentials, container, vpnConfiguration);
-}
-
-ErrorCode ConnectionController::updateProtocolConfig(const DockerContainer container, const ServerCredentials &credentials,
- QJsonObject &containerConfig, QSharedPointer serverController)
-{
- QFutureWatcher watcher;
-
- if (serverController.isNull()) {
- serverController.reset(new ServerController(m_settings));
- }
-
- QFuture