mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
* added changelog drawer * Created a scaffold for Linux installation * implement Linux updating * Add debug logs about installer in service * Add client side of installation logic for Windows and MacOS * Add service side of installation logic for Windows * ru readme * Update README_RU.md * Add files via upload * chore: added clang-format config files (#1293) * Update README_RU.md * Update README.md * feature: added subscription expiration date for premium v2 (#1261) * feature: added subscription expiration date for premium v2 * feature: added a check for the presence of the “services” field in the response body of the getServicesList() function * feature: added prohibition to change location when connection is active * bugfix: renamed public_key->end_date to public_key->expires_at according to the changes on the backend * feature/xray user management (#972) * feature: implement client management functionality for Xray --------- Co-authored-by: aiamnezia <ai@amnezia.org> Co-authored-by: vladimir.kuznetsov <nethiuswork@gmail.com> * Fix formatting * Add some logs * Add logs from installattion shell on Windows * Fix installation for Windows and MacOS * Optimized code * Move installer running to client side for Ubuntu * Move installer launch logic to client side for Windows * Clean service code * Add linux_install script to resources * Add logs for UpdateController * Add draft for MacOS installation * Disable updates checking for Android and iOS * chore: fixed macos update script * chore: remove duplicate lines * chore: post merge fixes * chore: add missing ifdef * decrease version for testing * chore: added changelog text processing depend on OS * add .vscode to .gitignore * Change updater downloading method to retrieving link from the gateway * add Release date file creation to s3 deploy script * Add release date downloading from endpoint * update check refactoring * feat: switch macOS auto-update from DMG to ZIP+PKG installer - Update macOS artifact URL from .dmg to .zip - Rewrite mac_installer.sh to extract ZIP and install PKG via osascript - Increase download timeout to 30s for larger ZIP files * fix: fix Android build * feat: Change get request for updater link to post * refactor: preparing NewsModel for update notifications - Changed `updateModel` to `setNewsList` for better semantic meaning. - Delegate model container updating to private method updateModel - Updated the logic for marking news as read to use item IDs instead of a boolean flag. * feat: Move update notification in news list - Updated `UpdateController` to handle empty release dates in header text. - Added `getVersion` method to `UpdateController` for version retrieval. - Enhanced `NewsModel` to support update notifications with new methods for marking updates as skipped and setting update notifications. - Updated QML pages to display update information and provide actions for updates and skipping them. - Introduced `isUpdate` property in `NewsItem` to differentiate between regular news and updates. * feat: Implement rate limit workaround for gateway requests - Added a delay before contacting the gateway in both `UpdateController` and `ApiNewsController` to prevent rate limit issues caused by simultaneous requests. * refactor: Convert synchronous network requests to asynchronous in UpdateController - Updated `UpdateController` to use asynchronous network requests for fetching gateway URL, version info, changelog, and release date. - Introduced `doGetAsync` method to handle asynchronous GET requests with error handling. - Removed synchronous methods to improve responsiveness and prevent blocking the UI during network operations. - Added a mechanism to prevent multiple concurrent update checks. * chore: Decrease AmneziaVPN version to 4.8.10.0 in CMakeLists.txt for testing * refactor: Improve update check handling to avoid rate limit issues - Updated `CoreController` to initiate update checks after news fetching is complete. - Removed synchronous waiting in `ApiNewsController` to streamline the fetching process. * fix: fixed typo in IsReadRole * fix: fix updater filenames * chore: move updateController to core * refactor: update to mvvm * chore: tiny fix --------- Co-authored-by: aiamnezia <ai@amnezia.org> Co-authored-by: aiamnezia <ai@amnezia.com> Co-authored-by: Pokamest Nikak <pokamest@gmail.com> Co-authored-by: KsZnak <ksu@amnezia.org> Co-authored-by: Cyril Anisimov <cyan84@gmail.com> Co-authored-by: vkamn <vk@amnezia.org>
343 lines
13 KiB
C++
343 lines
13 KiB
C++
#include "coreController.h"
|
|
|
|
#include <QDirIterator>
|
|
#include <QTranslator>
|
|
#include <QTimer>
|
|
|
|
#include "core/utils/selfhosted/sshSession.h"
|
|
#include "core/controllers/selfhosted/installController.h"
|
|
#include "core/controllers/selfhosted/importController.h"
|
|
#include "core/controllers/coreSignalHandlers.h"
|
|
#include "core/models/serverConfig.h"
|
|
#include "logger.h"
|
|
#include "secureQSettings.h"
|
|
|
|
#if defined(Q_OS_ANDROID)
|
|
#include "core/utils/installedAppsImageProvider.h"
|
|
#include "platforms/android/android_controller.h"
|
|
#endif
|
|
|
|
#if defined(Q_OS_IOS)
|
|
#include "platforms/ios/ios_controller.h"
|
|
#include <AmneziaVPN-Swift.h>
|
|
#endif
|
|
|
|
CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnection, SecureQSettings* settings,
|
|
QQmlApplicationEngine *engine, QObject *parent)
|
|
: QObject(parent), m_vpnConnection(vpnConnection), m_settings(settings), m_engine(engine)
|
|
{
|
|
initRepositories();
|
|
initCoreControllers();
|
|
initModels();
|
|
initControllers();
|
|
initSignalHandlers();
|
|
|
|
initAndroidController();
|
|
initAppleController();
|
|
initLogging();
|
|
|
|
m_translator = new QTranslator(this);
|
|
if (m_appSettingsRepository) {
|
|
updateTranslator(m_appSettingsRepository->getAppLanguage());
|
|
}
|
|
}
|
|
|
|
void CoreController::setQmlContextProperty(const QString &name, QObject *value)
|
|
{
|
|
if (m_engine) {
|
|
m_engine->rootContext()->setContextProperty(name, value);
|
|
}
|
|
}
|
|
|
|
void CoreController::initModels()
|
|
{
|
|
m_containersModel = new ContainersModel(this);
|
|
setQmlContextProperty("ContainersModel", m_containersModel);
|
|
|
|
m_defaultServerContainersModel = new ContainersModel(this);
|
|
setQmlContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel);
|
|
|
|
m_serversModel = new ServersModel(this);
|
|
setQmlContextProperty("ServersModel", m_serversModel);
|
|
|
|
m_languageModel = new LanguageModel(this);
|
|
setQmlContextProperty("LanguageModel", m_languageModel);
|
|
|
|
m_ipSplitTunnelingModel = new IpSplitTunnelingModel(this);
|
|
setQmlContextProperty("IpSplitTunnelingModel", m_ipSplitTunnelingModel);
|
|
|
|
m_allowedDnsModel = new AllowedDnsModel(this);
|
|
setQmlContextProperty("AllowedDnsModel", m_allowedDnsModel);
|
|
|
|
m_appSplitTunnelingModel = new AppSplitTunnelingModel(this);
|
|
setQmlContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel);
|
|
|
|
m_protocolsModel = new ProtocolsModel(this);
|
|
setQmlContextProperty("ProtocolsModel", m_protocolsModel);
|
|
|
|
m_openVpnConfigModel = new OpenVpnConfigModel(this);
|
|
setQmlContextProperty("OpenVpnConfigModel", m_openVpnConfigModel);
|
|
|
|
m_wireGuardConfigModel = new WireGuardConfigModel(this);
|
|
setQmlContextProperty("WireGuardConfigModel", m_wireGuardConfigModel);
|
|
|
|
m_awgConfigModel = new AwgConfigModel(this);
|
|
setQmlContextProperty("AwgConfigModel", m_awgConfigModel);
|
|
|
|
m_xrayConfigModel = new XrayConfigModel(this);
|
|
setQmlContextProperty("XrayConfigModel", m_xrayConfigModel);
|
|
|
|
m_torConfigModel = new TorConfigModel(this);
|
|
setQmlContextProperty("TorConfigModel", m_torConfigModel);
|
|
|
|
#ifdef Q_OS_WINDOWS
|
|
m_ikev2ConfigModel = new Ikev2ConfigModel(this);
|
|
setQmlContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel);
|
|
#endif
|
|
|
|
m_sftpConfigModel = new SftpConfigModel(this);
|
|
setQmlContextProperty("SftpConfigModel", m_sftpConfigModel);
|
|
|
|
m_socks5ConfigModel = new Socks5ProxyConfigModel(this);
|
|
setQmlContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel);
|
|
|
|
m_clientManagementModel = new ClientManagementModel(this);
|
|
setQmlContextProperty("ClientManagementModel", m_clientManagementModel);
|
|
|
|
m_apiServicesModel = new ApiServicesModel(this);
|
|
setQmlContextProperty("ApiServicesModel", m_apiServicesModel);
|
|
|
|
m_apiCountryModel = new ApiCountryModel(this);
|
|
setQmlContextProperty("ApiCountryModel", m_apiCountryModel);
|
|
|
|
m_apiSubscriptionPlansModel = new ApiSubscriptionPlansModel(this);
|
|
setQmlContextProperty("ApiSubscriptionPlansModel", m_apiSubscriptionPlansModel);
|
|
|
|
m_apiBenefitsModel = new ApiBenefitsModel(this);
|
|
setQmlContextProperty("ApiBenefitsModel", m_apiBenefitsModel);
|
|
|
|
m_apiAccountInfoModel = new ApiAccountInfoModel(this);
|
|
setQmlContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel);
|
|
|
|
m_apiDevicesModel = new ApiDevicesModel(this);
|
|
setQmlContextProperty("ApiDevicesModel", m_apiDevicesModel);
|
|
|
|
m_newsModel = new NewsModel(m_appSettingsRepository, this);
|
|
setQmlContextProperty("NewsModel", m_newsModel);
|
|
}
|
|
|
|
void CoreController::initRepositories()
|
|
{
|
|
m_serversRepository = new SecureServersRepository(m_settings, this);
|
|
m_appSettingsRepository = new SecureAppSettingsRepository(m_settings, this);
|
|
|
|
if (m_vpnConnection) {
|
|
m_vpnConnection->setRepositories(m_serversRepository, m_appSettingsRepository);
|
|
}
|
|
}
|
|
|
|
void CoreController::initCoreControllers()
|
|
{
|
|
m_serversController = new ServersController(m_serversRepository, m_appSettingsRepository, this);
|
|
m_appSplitTunnelingController = new AppSplitTunnelingController(m_appSettingsRepository);
|
|
m_usersController = new UsersController(m_serversRepository, this);
|
|
m_ipSplitTunnelingController = new IpSplitTunnelingController(m_appSettingsRepository, this);
|
|
m_allowedDnsController = new AllowedDnsController(m_appSettingsRepository);
|
|
m_servicesCatalogController = new ServicesCatalogController(m_appSettingsRepository);
|
|
m_subscriptionController = new SubscriptionController(m_serversRepository, m_appSettingsRepository);
|
|
m_newsController = new NewsController(m_appSettingsRepository, m_serversController);
|
|
m_updateController = new UpdateController(m_appSettingsRepository, this);
|
|
|
|
m_installController = new InstallController(m_serversRepository, m_appSettingsRepository, this);
|
|
m_exportController = new ExportController(m_serversRepository, m_appSettingsRepository, this);
|
|
m_importCoreController = new ImportController(m_serversRepository, m_appSettingsRepository, this);
|
|
m_connectionController = new ConnectionController(m_serversRepository, m_appSettingsRepository, m_vpnConnection.get(), this);
|
|
m_settingsController = new SettingsController(m_serversRepository, m_appSettingsRepository, this);
|
|
}
|
|
|
|
void CoreController::initControllers()
|
|
{
|
|
m_connectionUiController = new ConnectionUiController(m_connectionController, m_serversController, this);
|
|
setQmlContextProperty("ConnectionController", m_connectionUiController);
|
|
|
|
if (m_engine) {
|
|
m_focusController = new FocusController(m_engine, this);
|
|
setQmlContextProperty("FocusController", m_focusController);
|
|
}
|
|
|
|
m_installUiController = new InstallUiController(m_installController, m_serversController, m_settingsController, m_protocolsModel, m_usersController,
|
|
m_awgConfigModel, m_wireGuardConfigModel, m_openVpnConfigModel, m_xrayConfigModel, m_torConfigModel,
|
|
#ifdef Q_OS_WINDOWS
|
|
m_ikev2ConfigModel,
|
|
#endif
|
|
m_sftpConfigModel, m_socks5ConfigModel, this);
|
|
setQmlContextProperty("InstallController", m_installUiController);
|
|
|
|
m_importController = new ImportUiController(m_importCoreController, this);
|
|
setQmlContextProperty("ImportController", m_importController);
|
|
|
|
m_exportUiController = new ExportUiController(m_exportController, this);
|
|
setQmlContextProperty("ExportController", m_exportUiController);
|
|
|
|
m_languageUiController = new LanguageUiController(m_settingsController, m_languageModel, this);
|
|
setQmlContextProperty("LanguageUiController", m_languageUiController);
|
|
|
|
m_settingsUiController = new SettingsUiController(m_settingsController, m_serversController, m_languageUiController, this);
|
|
setQmlContextProperty("SettingsController", m_settingsUiController);
|
|
|
|
m_pageController = new PageController(m_serversController, m_settingsController, this);
|
|
setQmlContextProperty("PageController", m_pageController);
|
|
|
|
m_serversUiController = new ServersUiController(m_serversController, m_settingsController, m_serversModel, m_containersModel, m_defaultServerContainersModel, this);
|
|
setQmlContextProperty("ServersUiController", m_serversUiController);
|
|
|
|
m_ipSplitTunnelingUiController = new IpSplitTunnelingUiController(m_ipSplitTunnelingController, m_ipSplitTunnelingModel, this);
|
|
setQmlContextProperty("IpSplitTunnelingController", m_ipSplitTunnelingUiController);
|
|
|
|
m_allowedDnsUiController = new AllowedDnsUiController(m_allowedDnsController, m_allowedDnsModel, this);
|
|
setQmlContextProperty("AllowedDnsController", m_allowedDnsUiController);
|
|
|
|
m_appSplitTunnelingUiController = new AppSplitTunnelingUiController(m_appSplitTunnelingController, m_appSplitTunnelingModel, this);
|
|
setQmlContextProperty("AppSplitTunnelingController", m_appSplitTunnelingUiController);
|
|
|
|
m_systemController = new SystemController(this);
|
|
setQmlContextProperty("SystemController", m_systemController);
|
|
|
|
m_servicesCatalogUiController = new ServicesCatalogUiController(m_servicesCatalogController, m_apiServicesModel, this);
|
|
setQmlContextProperty("ServicesCatalogUiController", m_servicesCatalogUiController);
|
|
|
|
m_subscriptionUiController = new SubscriptionUiController(m_serversController, m_apiServicesModel, m_servicesCatalogController, m_subscriptionController,
|
|
m_apiSubscriptionPlansModel, m_apiBenefitsModel, m_apiAccountInfoModel,
|
|
m_apiCountryModel, m_apiDevicesModel, m_settingsController, this);
|
|
setQmlContextProperty("SubscriptionUiController", m_subscriptionUiController);
|
|
|
|
m_apiNewsUiController = new ApiNewsUiController(m_newsModel, m_newsController, this);
|
|
setQmlContextProperty("ApiNewsController", m_apiNewsUiController);
|
|
|
|
m_updateUiController = new UpdateUiController(m_updateController, this);
|
|
setQmlContextProperty("UpdateController", m_updateUiController);
|
|
}
|
|
|
|
void CoreController::initAndroidController()
|
|
{
|
|
#ifdef Q_OS_ANDROID
|
|
if (!AndroidController::initLogging()) {
|
|
qFatal("Android logging initialization failed");
|
|
}
|
|
AndroidController::instance()->setSaveLogs(m_appSettingsRepository->isSaveLogs());
|
|
AndroidController::instance()->setScreenshotsEnabled(m_appSettingsRepository->isScreenshotsEnabled());
|
|
|
|
if (!AndroidController::instance()->initialize()) {
|
|
qFatal("Android controller initialization failed");
|
|
}
|
|
|
|
if (m_engine) {
|
|
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CoreController::initAppleController()
|
|
{
|
|
#ifdef Q_OS_IOS
|
|
IosController::Instance()->initialize();
|
|
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_appSettingsRepository->isScreenshotsEnabled()); });
|
|
#endif
|
|
}
|
|
|
|
void CoreController::initLogging()
|
|
{
|
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
|
bool enabled = m_appSettingsRepository->isSaveLogs();
|
|
if (enabled) {
|
|
if (!Logger::init(false)) {
|
|
qWarning() << "Initialization of debug subsystem failed";
|
|
}
|
|
}
|
|
Logger::setServiceLogsEnabled(enabled);
|
|
#endif
|
|
}
|
|
|
|
void CoreController::initSignalHandlers()
|
|
{
|
|
m_signalHandlers = new CoreSignalHandlers(this, this);
|
|
m_signalHandlers->initAllHandlers();
|
|
|
|
// Trigger initial update after handlers are connected
|
|
m_serversUiController->updateModel();
|
|
}
|
|
|
|
void CoreController::updateTranslator(const QLocale &locale)
|
|
{
|
|
if (!m_translator->isEmpty()) {
|
|
QCoreApplication::removeTranslator(m_translator);
|
|
}
|
|
|
|
QStringList availableTranslations;
|
|
QDirIterator it(":/translations", QStringList("amneziavpn_*.qm"), QDir::Files);
|
|
while (it.hasNext()) {
|
|
availableTranslations << it.next();
|
|
}
|
|
|
|
// This code allow to load translation for the language only, without country code
|
|
const QString lang = locale.name().split("_").first();
|
|
const QString translationFilePrefix = QString(":/translations/amneziavpn_") + lang;
|
|
QString strFileName = QString(":/translations/amneziavpn_%1.qm").arg(locale.name());
|
|
for (const QString &translation : availableTranslations) {
|
|
if (translation.contains(translationFilePrefix)) {
|
|
strFileName = translation;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_translator->load(strFileName)) {
|
|
QCoreApplication::installTranslator(m_translator);
|
|
} else {
|
|
if (m_translator->load(QString(":/translations/amneziavpn_en.qm"))) {
|
|
QCoreApplication::installTranslator(m_translator);
|
|
}
|
|
}
|
|
|
|
if (m_engine) {
|
|
m_engine->retranslate();
|
|
}
|
|
|
|
emit translationsUpdated();
|
|
if (m_languageUiController) {
|
|
emit websiteUrlChanged(m_languageUiController->getCurrentSiteUrl());
|
|
}
|
|
}
|
|
|
|
void CoreController::setQmlRoot()
|
|
{
|
|
if (m_engine && m_systemController) {
|
|
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
|
}
|
|
}
|
|
|
|
PageController* CoreController::pageController() const
|
|
{
|
|
return m_pageController;
|
|
}
|
|
|
|
void CoreController::openConnectionByIndex(int serverIndex)
|
|
{
|
|
if (m_serversModel) {
|
|
m_serversModel->setProcessedServerIndex(serverIndex);
|
|
}
|
|
if (m_serversController) {
|
|
m_serversController->setDefaultServerIndex(serverIndex);
|
|
}
|
|
m_connectionUiController->toggleConnection();
|
|
}
|
|
|
|
void CoreController::importConfigFromData(const QString &data)
|
|
{
|
|
if (!m_importController)
|
|
return;
|
|
|
|
if (m_importController->extractConfigFromData(data)) {
|
|
m_importController->importConfig();
|
|
}
|
|
}
|