From e9250afd2b908229749063e495c171b6d422a833 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sat, 15 Feb 2025 11:50:42 +0700 Subject: [PATCH] refactoring: simplified the validity check of the config before connection - improved project structure --- client/CMakeLists.txt | 5 +- client/amnezia_application.cpp | 290 +-------------- client/amnezia_application.h | 97 +---- client/cmake/sources.cmake | 7 +- client/core/api/apiDefs.h | 2 +- client/core/api/apiUtils.cpp | 5 + client/core/api/apiUtils.h | 1 + client/core/controllers/apiController.cpp | 262 -------------- client/core/controllers/apiController.h | 50 --- client/core/controllers/coreController.cpp | 332 ++++++++++++++++++ client/core/controllers/coreController.h | 133 +++++++ .../vpnConfigurationController.cpp | 6 +- .../controllers/vpnConfigurationController.h | 5 +- client/core/defs.h | 2 + client/core/errorstrings.cpp | 2 + .../controllers/api/apiConfigsController.cpp | 294 +++++++++++++++- .../ui/controllers/api/apiConfigsController.h | 22 +- .../ui/controllers/connectionController.cpp | 136 +------ client/ui/controllers/connectionController.h | 12 +- client/ui/controllers/installController.cpp | 148 +++----- client/ui/controllers/installController.h | 15 +- .../ui/models/{ => api}/apiServicesModel.cpp | 0 client/ui/models/{ => api}/apiServicesModel.h | 0 client/ui/models/servers_model.cpp | 2 +- client/ui/models/servers_model.h | 2 +- .../PageSettingsApiAvailableCountries.qml | 2 +- .../qml/Pages2/PageSettingsApiServerInfo.qml | 2 +- .../Pages2/PageSetupWizardApiServiceInfo.qml | 2 +- .../Pages2/PageSetupWizardConfigSource.qml | 2 +- client/ui/qml/Pages2/PageShare.qml | 2 +- client/ui/qml/Pages2/PageStart.qml | 70 ++-- 31 files changed, 941 insertions(+), 969 deletions(-) delete mode 100644 client/core/controllers/apiController.cpp delete mode 100644 client/core/controllers/apiController.h create mode 100644 client/core/controllers/coreController.cpp create mode 100644 client/core/controllers/coreController.h rename client/ui/models/{ => api}/apiServicesModel.cpp (100%) rename client/ui/models/{ => api}/apiServicesModel.h (100%) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 24edfb07..294b9646 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -57,10 +57,7 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) endif() qt_standard_project_setup() -qt_add_executable(${PROJECT} MANUAL_FINALIZATION - core/api/apiDefs.h - core/qrCodeUtils.h core/qrCodeUtils.cpp -) +qt_add_executable(${PROJECT} MANUAL_FINALIZATION) if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 7e72defe..8dea8c0a 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -14,22 +14,14 @@ #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); @@ -295,7 +193,7 @@ void AmneziaApplication::startLocalServer() QLocalSocket *clientConnection = server->nextPendingConnection(); clientConnection->deleteLater(); } - emit m_pageController->raiseMainWindow(); + emit m_coreController->pageController()->raiseMainWindow(); //TODO }); } #endif @@ -304,173 +202,3 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const { return m_engine; } - -void AmneziaApplication::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()); - 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()); }); - - m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this)); - m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get()); -} - -void AmneziaApplication::initControllers() -{ - 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()); - - m_apiSettingsController.reset(new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_settings)); - m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get()); - - m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_settings)); - m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get()); -} diff --git a/client/amnezia_application.h b/client/amnezia_application.h index 3b9d06dd..b967f160 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -12,46 +12,10 @@ #include #endif +#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/controllers/api/apiConfigsController.h" -#include "ui/controllers/api/apiSettingsController.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" -#include "ui/models/api/apiAccountInfoModel.h" - #define amnApp (static_cast(QCoreApplication::instance())) #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) @@ -70,8 +34,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) @@ -79,71 +41,26 @@ public: #endif QQmlApplicationEngine *qmlEngine() const; - QNetworkAccessManager *manager() { return m_nam; } - -signals: - void translationsUpdated(); + QNetworkAccessManager *manager() + { + return m_nam; + } 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; - QSharedPointer m_apiAccountInfoModel; - - 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; - - QScopedPointer m_apiSettingsController; - QScopedPointer m_apiConfigsController; QNetworkAccessManager *m_nam; - - QMetaObject::Connection m_reloadConfigErrorOccurredConnection; }; #endif // AMNEZIA_APPLICATION_H diff --git a/client/cmake/sources.cmake b/client/cmake/sources.cmake index 1d7c4080..c3af531a 100644 --- a/client/cmake/sources.cmake +++ b/client/cmake/sources.cmake @@ -9,7 +9,9 @@ set(HEADERS ${HEADERS} ${CLIENT_ROOT_DIR}/core/errorstrings.h ${CLIENT_ROOT_DIR}/core/scripts_registry.h ${CLIENT_ROOT_DIR}/core/server_defs.h - ${CLIENT_ROOT_DIR}/core/controllers/apiController.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 @@ -56,7 +58,8 @@ set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/core/errorstrings.cpp ${CLIENT_ROOT_DIR}/core/scripts_registry.cpp ${CLIENT_ROOT_DIR}/core/server_defs.cpp - ${CLIENT_ROOT_DIR}/core/controllers/apiController.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 diff --git a/client/core/api/apiDefs.h b/client/core/api/apiDefs.h index 2df4c833..b01832ce 100644 --- a/client/core/api/apiDefs.h +++ b/client/core/api/apiDefs.h @@ -6,7 +6,7 @@ namespace apiDefs { enum ConfigType { - AmneziaFreeV2 = 1, + AmneziaFreeV2 = 0, AmneziaFreeV3, AmneziaPremiumV1, AmneziaPremiumV2, diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp index d90ac1dc..7c58e0e1 100644 --- a/client/core/api/apiUtils.cpp +++ b/client/core/api/apiUtils.cpp @@ -44,3 +44,8 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec } }; } + +apiDefs::ConfigSource apiUtils::getConfigSource(const QJsonObject &serverConfigObject) +{ + return static_cast(serverConfigObject.value(apiDefs::key::configVersion).toInt()); +} diff --git a/client/core/api/apiUtils.h b/client/core/api/apiUtils.h index 2c9496bd..bb122736 100644 --- a/client/core/api/apiUtils.h +++ b/client/core/api/apiUtils.h @@ -12,6 +12,7 @@ namespace apiUtils bool isSubscriptionExpired(const QString &subscriptionEndDate); apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject); + apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject); } #endif // APIUTILS_H diff --git a/client/core/controllers/apiController.cpp b/client/core/controllers/apiController.cpp deleted file mode 100644 index fab91408..00000000 --- a/client/core/controllers/apiController.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include "apiController.h" - -#include -#include -#include -#include - -#include "amnezia_application.h" -#include "configurators/wireguard_configurator.h" -#include "core/api/apiDefs.h" -#include "gatewayController.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 -} - -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() == 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; -} - -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) -{ - GatewayController gatewayController(m_gatewayEndpoint, m_isDevEnvironment, requestTimeoutMsecs); - - QJsonObject apiPayload; - apiPayload[configKey::osVersion] = QSysInfo::productType(); - - ErrorCode errorCode = gatewayController.post(QString("%1v1/services"), apiPayload, responseBody); - 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) -{ - GatewayController gatewayController(m_gatewayEndpoint, m_isDevEnvironment, requestTimeoutMsecs); - - 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; - } - - QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody); - - if (errorCode == ErrorCode::NoError) { - fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig); - } - - return errorCode; -} - - diff --git a/client/core/controllers/apiController.h b/client/core/controllers/apiController.h deleted file mode 100644 index 367c89e4..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..551eb61f --- /dev/null +++ b/client/core/controllers/coreController.cpp @@ -0,0 +1,332 @@ +#include "coreController.h" + +#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(); + + 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()); +} + +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_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() +{ + initApiCountryModelUpdateHandler(); + initContainerModelUpdateHandler(); + initAdminConfigRevokedHandler(); + initConnectionErrorOccurredHandler(); + 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::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::initConnectionErrorOccurredHandler() +{ + connect(m_connectionController.get(), &ConnectionController::connectionErrorOccurred, this, [this](ErrorCode errorCode) { + emit m_pageController->showErrorMessage(errorCode); + emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); + }); +} + +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..c67c9ccc --- /dev/null +++ b/client/core/controllers/coreController.h @@ -0,0 +1,133 @@ +#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/apiServicesModel.h" +#include "ui/models/apiCountryModel.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 initApiCountryModelUpdateHandler(); + void initContainerModelUpdateHandler(); + void initAdminConfigRevokedHandler(); + void initConnectionErrorOccurredHandler(); + 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; + + 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/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 4ea0e613..c7fde0ee 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -44,6 +44,7 @@ namespace amnezia InternalError = 101, NotImplementedError = 102, AmneziaServiceNotRunning = 103, + NotSupportedOnThisPlatform = 104, // Server errors ServerCheckFailed = 200, @@ -94,6 +95,7 @@ namespace amnezia // import and install errors ImportInvalidConfigError = 900, ImportOpenConfigError = 901, + NoInstalledContainersError = 902, // Android errors AndroidError = 1000, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 70f433c6..976915c2 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; @@ -51,6 +52,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; diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 0858ecff..ee133253 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -1,8 +1,13 @@ #include "apiConfigsController.h" +#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/networkUtilities.h" #include "core/qrCodeUtils.h" #include "ui/controllers/systemController.h" #include "version.h" @@ -18,6 +23,7 @@ namespace 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"; @@ -43,9 +49,10 @@ namespace } } -ApiConfigsController::ApiConfigsController(const QSharedPointer &serversModel, const std::shared_ptr &settings, - QObject *parent) - : QObject(parent), m_serversModel(serversModel), m_settings(settings) +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) { } @@ -98,6 +105,211 @@ void ApiConfigsController::prepareVpnKeyExport() emit vpnKeyExportReady(); } +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)); + + newServerConfig.insert(configKey::apiConfig, newApiConfig); + newServerConfig.insert(configKey::authData, authData); + + 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->manager()->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 = NetworkUtilities::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::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; @@ -126,6 +338,82 @@ QJsonObject ApiConfigsController::fillApiPayload(const QString &protocol, const 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; diff --git a/client/ui/controllers/api/apiConfigsController.h b/client/ui/controllers/api/apiConfigsController.h index f9bf977f..a8202afc 100644 --- a/client/ui/controllers/api/apiConfigsController.h +++ b/client/ui/controllers/api/apiConfigsController.h @@ -4,14 +4,15 @@ #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 std::shared_ptr &settings, - QObject *parent = nullptr); + 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) @@ -21,8 +22,22 @@ public slots: // bool exportVpnKey(const QString &fileName); void prepareVpnKeyExport(); + bool fillAvailableServices(); + bool importServiceFromGateway(); + bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, + bool reloadServiceConfig = false); + bool updateServiceFromTelegram(const int serverIndex); + + 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: @@ -36,6 +51,8 @@ private: 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(); @@ -43,6 +60,7 @@ private: QList m_qrCodes; QSharedPointer m_serversModel; + QSharedPointer m_apiServicesModel; std::shared_ptr m_settings; }; diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index 9cd386c5..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/api/apiDefs.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 == apiDefs::ConfigSource::Telegram - && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { - emit updateApiConfigFromTelegram(); - } else if (configVersion == apiDefs::ConfigSource::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 == apiDefs::ConfigSource::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 future = QtConcurrent::run([this, container, &credentials, &containerConfig, &serverController]() { - ErrorCode errorCode = ErrorCode::NoError; - if (!isProtocolConfigExists(containerConfig, container)) { - VpnConfigurationsController vpnConfigurationController(m_settings, serverController); - errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container, containerConfig); - if (errorCode != ErrorCode::NoError) { - return errorCode; - } - m_serversModel->updateContainerConfig(container, containerConfig); - - errorCode = m_clientManagementModel->appendClient(container, credentials, containerConfig, - QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController); - if (errorCode != ErrorCode::NoError) { - return errorCode; - } - } - return errorCode; - }); - - QEventLoop wait; - connect(&watcher, &QFutureWatcher::finished, &wait, &QEventLoop::quit); - watcher.setFuture(future); - wait.exec(); - - return watcher.result(); -} diff --git a/client/ui/controllers/connectionController.h b/client/ui/controllers/connectionController.h index 25d4d74a..cabeb601 100644 --- a/client/ui/controllers/connectionController.h +++ b/client/ui/controllers/connectionController.h @@ -40,30 +40,20 @@ public slots: void onTranslationsUpdated(); - ErrorCode updateProtocolConfig(const DockerContainer container, const ServerCredentials &credentials, QJsonObject &containerConfig, - QSharedPointer serverController = nullptr); - signals: void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration); void disconnectFromVpn(); void connectionStateChanged(); - void connectionErrorOccurred(const QString &errorMessage); void connectionErrorOccurred(ErrorCode errorCode); void reconnectWithUpdatedContainer(const QString &message); - void noInstalledContainers(); - void connectButtonClicked(); void preparingConfig(); - - void updateApiConfigFromGateway(); - void updateApiConfigFromTelegram(); - void configFromApiUpdated(); + void prepareConfig(); private: Vpn::ConnectionState getCurrentConnectionState(); - bool isProtocolConfigExists(const QJsonObject &containerConfig, const DockerContainer container); void continueConnection(); diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 26e433e0..7a6d8d40 100755 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -6,8 +6,8 @@ #include #include #include +#include -#include "core/controllers/apiController.h" #include "core/controllers/serverController.h" #include "core/controllers/vpnConfigurationController.h" #include "core/networkUtilities.h" @@ -15,6 +15,7 @@ #include "ui/models/protocols/awgConfigModel.h" #include "ui/models/protocols/wireguardConfigModel.h" #include "utilities.h" +#include "core/api/apiUtils.h" namespace { @@ -39,14 +40,12 @@ namespace InstallController::InstallController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, const QSharedPointer &protocolsModel, const QSharedPointer &clientManagementModel, - const QSharedPointer &apiServicesModel, const std::shared_ptr &settings, - QObject *parent) + const std::shared_ptr &settings, QObject *parent) : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), m_protocolModel(protocolsModel), m_clientManagementModel(clientManagementModel), - m_apiServicesModel(apiServicesModel), m_settings(settings) { } @@ -773,110 +772,79 @@ void InstallController::addEmptyServer() emit installServerFinished(tr("Server added successfully")); } -bool InstallController::fillAvailableServices() +bool InstallController::isConfigValid() { - ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv()); + int serverIndex = m_serversModel->getDefaultServerIndex(); + QJsonObject serverConfigObject = m_serversModel->getServerConfig(serverIndex); - QByteArray responseBody; - ErrorCode errorCode = apiController.getServicesList(responseBody); - if (errorCode != ErrorCode::NoError) { - emit installationErrorOccurred(errorCode); + if (apiUtils::isServerFromApi(serverConfigObject)) { + return true; + } + + if (!m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { + emit noInstalledContainers(); return false; } - QJsonObject data = QJsonDocument::fromJson(responseBody).object(); - m_apiServicesModel->updateModel(data); - return true; -} + DockerContainer container = qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole)); -bool InstallController::installServiceFromApi() -{ - if (m_serversModel->isServerFromApiAlreadyExists(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(), - m_apiServicesModel->getSelectedServiceProtocol())) { - emit installationErrorOccurred(ErrorCode::ApiConfigAlreadyAdded); + if (container == DockerContainer::None) { + emit installationErrorOccurred(ErrorCode::NoInstalledContainersError); return false; } - ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv()); - QJsonObject serverConfig; + QSharedPointer serverController(new ServerController(m_settings)); + VpnConfigurationsController vpnConfigurationController(m_settings, serverController); - ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(), - m_apiServicesModel->getSelectedServiceType(), - m_apiServicesModel->getSelectedServiceProtocol(), "", QJsonObject(), serverConfig); - if (errorCode != ErrorCode::NoError) { - emit installationErrorOccurred(errorCode); - return false; - } + QJsonObject containerConfig = m_containersModel->getContainerConfig(container); + ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); - auto serviceInfo = m_apiServicesModel->getSelectedServiceInfo(); - QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject(); - apiConfig.insert(configKey::serviceInfo, serviceInfo); - apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode()); - apiConfig.insert(configKey::serviceType, m_apiServicesModel->getSelectedServiceType()); - apiConfig.insert(configKey::serviceProtocol, m_apiServicesModel->getSelectedServiceProtocol()); + QFutureWatcher watcher; - serverConfig.insert(configKey::apiConfig, apiConfig); + QFuture future = QtConcurrent::run([this, container, &credentials, &containerConfig, &serverController]() { + ErrorCode errorCode = ErrorCode::NoError; - m_serversModel->addServer(serverConfig); - emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName())); - return true; -} + auto 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(); -bool InstallController::updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, - bool reloadServiceConfig) -{ - ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv()); + if (protocolConfig.isEmpty()) { + return false; + } + } + return true; + }; - auto serverConfig = m_serversModel->getServerConfig(serverIndex); - auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); - auto authData = serverConfig.value(configKey::authData).toObject(); + if (!isProtocolConfigExists(containerConfig, container)) { + VpnConfigurationsController vpnConfigurationController(m_settings, serverController); + errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container, containerConfig); + if (errorCode != ErrorCode::NoError) { + return errorCode; + } + m_serversModel->updateContainerConfig(container, containerConfig); - QJsonObject newServerConfig; - - ErrorCode errorCode = apiController.getConfigForService( - m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(), - apiConfig.value(configKey::serviceType).toString(), apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode, - authData, newServerConfig); - if (errorCode != ErrorCode::NoError) { - emit installationErrorOccurred(errorCode); - return false; - } - - 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)); - - newServerConfig.insert(configKey::apiConfig, newApiConfig); - newServerConfig.insert(configKey::authData, authData); - 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; -} - -void InstallController::updateServiceFromTelegram(const int serverIndex) -{ - ApiController *apiController = new ApiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv()); - - auto serverConfig = m_serversModel->getServerConfig(serverIndex); - - apiController->updateServerConfigFromApi(m_settings->getInstallationUuid(true), serverIndex, serverConfig); - connect(apiController, &ApiController::finished, this, [this, apiController](const QJsonObject &config, const int serverIndex) { - m_serversModel->editServer(config, serverIndex); - emit updateServerFromApiFinished(); - apiController->deleteLater(); + errorCode = m_clientManagementModel->appendClient(container, credentials, containerConfig, + QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController); + if (errorCode != ErrorCode::NoError) { + return errorCode; + } + } + return errorCode; }); - connect(apiController, &ApiController::errorOccurred, this, [this, apiController](ErrorCode errorCode) { + + QEventLoop wait; + connect(&watcher, &QFutureWatcher::finished, &wait, &QEventLoop::quit); + watcher.setFuture(future); + wait.exec(); + + ErrorCode errorCode = watcher.result(); + + if (errorCode != ErrorCode::NoError) { emit installationErrorOccurred(errorCode); - apiController->deleteLater(); - }); + return false; + } + return true; } bool InstallController::isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig, diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index d7ab3553..8e42b5b2 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -10,7 +10,6 @@ #include "ui/models/containers_model.h" #include "ui/models/protocols_model.h" #include "ui/models/servers_model.h" -#include "ui/models/apiServicesModel.h" class InstallController : public QObject { @@ -19,7 +18,6 @@ public: explicit InstallController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, const QSharedPointer &protocolsModel, const QSharedPointer &clientManagementModel, - const QSharedPointer &apiServicesModel, const std::shared_ptr &settings, QObject *parent = nullptr); ~InstallController(); @@ -52,21 +50,13 @@ public slots: void addEmptyServer(); - bool fillAvailableServices(); - bool installServiceFromApi(); - bool updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, bool reloadServiceConfig = false); - - void updateServiceFromTelegram(const int serverIndex); + bool isConfigValid(); signals: void installContainerFinished(const QString &finishMessage, bool isServiceInstall); void installServerFinished(const QString &finishMessage); - void installServerFromApiFinished(const QString &message); void updateContainerFinished(const QString &message); - void updateServerFromApiFinished(); - void changeApiCountryFinished(const QString &message); - void reloadServerFromApiFinished(const QString &message); void scanServerFinished(bool isInstalledContainerFound); @@ -91,6 +81,8 @@ signals: void cachedProfileCleared(const QString &message); void apiConfigRemoved(const QString &message); + void noInstalledContainers(); + private: void installServer(const DockerContainer container, const QMap &installedContainers, const ServerCredentials &serverCredentials, const QSharedPointer &serverController, @@ -108,7 +100,6 @@ private: QSharedPointer m_containersModel; QSharedPointer m_protocolModel; QSharedPointer m_clientManagementModel; - QSharedPointer m_apiServicesModel; std::shared_ptr m_settings; diff --git a/client/ui/models/apiServicesModel.cpp b/client/ui/models/api/apiServicesModel.cpp similarity index 100% rename from client/ui/models/apiServicesModel.cpp rename to client/ui/models/api/apiServicesModel.cpp diff --git a/client/ui/models/apiServicesModel.h b/client/ui/models/api/apiServicesModel.h similarity index 100% rename from client/ui/models/apiServicesModel.h rename to client/ui/models/api/apiServicesModel.h diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index f77eb6fc..7cde28b4 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -261,7 +261,7 @@ void ServersModel::setProcessedServerIndex(const int index) updateContainersModel(); if (data(index, IsServerFromGatewayApiRole).toBool()) { if (data(index, IsCountrySelectionAvailableRole).toBool()) { - emit updateApiLanguageModel(); + emit updateApiCountryModel(); } emit updateApiServicesModel(); } diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index 78bc22cc..4b790c7a 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -140,7 +140,7 @@ signals: void defaultServerContainersUpdated(const QJsonArray &containers); void defaultServerDefaultContainerChanged(const int containerIndex); - void updateApiLanguageModel(); + void updateApiCountryModel(); void updateApiServicesModel(); private: diff --git a/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml b/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml index 41735b14..353547ef 100644 --- a/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml +++ b/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml @@ -136,7 +136,7 @@ PageType { PageController.showBusyIndicator(true) var prevIndex = ApiCountryModel.currentIndex ApiCountryModel.currentIndex = index - if (!InstallController.updateServiceFromApi(ServersModel.defaultIndex, countryCode, countryName)) { + if (!ApiConfigsController.updateServiceFromGateway(ServersModel.defaultIndex, countryCode, countryName)) { ApiCountryModel.currentIndex = prevIndex } } diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 303ba36c..e377140f 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -253,7 +253,7 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot reload API config during active connection")) } else { PageController.showBusyIndicator(true) - InstallController.updateServiceFromApi(ServersModel.processedIndex, "", "", true) + ApiConfigsController.updateServiceFromGateway(ServersModel.processedIndex, "", "", true) PageController.showBusyIndicator(false) } } diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml b/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml index 8c02195e..134e73b6 100644 --- a/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml +++ b/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml @@ -138,7 +138,7 @@ PageType { PageController.closePage() } else { PageController.showBusyIndicator(true) - InstallController.installServiceFromApi() + ApiConfigsController.importServiceFromGateway() PageController.showBusyIndicator(false) } } diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index f145127f..38a1da52 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -254,7 +254,7 @@ PageType { property bool isVisible: true property var handler: function() { PageController.showBusyIndicator(true) - var result = InstallController.fillAvailableServices() + var result = ApiConfigsController.fillAvailableServices() PageController.showBusyIndicator(false) if (result) { PageController.goToPage(PageEnum.PageSetupWizardApiServicesList) diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 27e851de..76133da8 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -46,7 +46,7 @@ PageType { shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text shareConnectionDrawer.openTriggered() - shareConnectionDrawer.contentVisible = false + shareConnectionDrawer.contentVisible = true PageController.showBusyIndicator(true) switch (type) { diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 6081bbc8..bf13552c 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -132,29 +132,6 @@ PageType { PageController.showNotificationMessage(message) } - function onInstallServerFromApiFinished(message) { - PageController.showBusyIndicator(false) - if (!ConnectionController.isConnected) { - ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1); - ServersModel.processedIndex = ServersModel.defaultIndex - } - - PageController.goToPageHome() - PageController.showNotificationMessage(message) - } - - function onChangeApiCountryFinished(message) { - PageController.showBusyIndicator(false) - - PageController.goToPageHome() - PageController.showNotificationMessage(message) - } - - function onReloadServerFromApiFinished(message) { - PageController.goToPageHome() - PageController.showNotificationMessage(message) - } - function onRemoveProcessedServerFinished(finishedMessage) { if (!ServersModel.getServersCount()) { PageController.goToPageHome() @@ -164,6 +141,14 @@ PageType { } PageController.showNotificationMessage(finishedMessage) } + + function onNoInstalledContainers() { + PageController.setTriggeredByConnectButton(true) + + ServersModel.processedIndex = ServersModel.getDefaultServerIndex() + InstallController.setShouldCreateServer(false) + PageController.goToPage(PageEnum.PageSetupWizardEasy) + } } Connections { @@ -175,14 +160,6 @@ PageType { PageController.showNotificationMessage(message) PageController.closePage() } - - function onNoInstalledContainers() { - PageController.setTriggeredByConnectButton(true) - - ServersModel.processedIndex = ServersModel.getDefaultServerIndex() - InstallController.setShouldCreateServer(false) - PageController.goToPage(PageEnum.PageSetupWizardEasy) - } } Connections { @@ -232,6 +209,37 @@ PageType { } } + Connections { + target: ApiConfigsController + + function onErrorOccurred(error) { + PageController.showErrorMessage(error) + } + + function onInstallServerFromApiFinished(message) { + PageController.showBusyIndicator(false) + if (!ConnectionController.isConnected) { + ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1); + ServersModel.processedIndex = ServersModel.defaultIndex + } + + PageController.goToPageHome() + PageController.showNotificationMessage(message) + } + + function onChangeApiCountryFinished(message) { + PageController.showBusyIndicator(false) + + PageController.goToPageHome() + PageController.showNotificationMessage(message) + } + + function onReloadServerFromApiFinished(message) { + PageController.goToPageHome() + PageController.showNotificationMessage(message) + } + } + StackViewType { id: tabBarStackView objectName: "tabBarStackView"