From 4d0efc7ea55b2ea57936375310c2bb5eae936536 Mon Sep 17 00:00:00 2001 From: vkamn Date: Wed, 10 Sep 2025 15:01:52 +0800 Subject: [PATCH 01/18] fix: remove duplicate m_vpnConnection delete from AmneziaApplication destructor (#1848) --- client/amnezia_application.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index f17b76738..f64759a50 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -53,18 +53,8 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_C AmneziaApplication::~AmneziaApplication() { - if (m_vpnConnection) { - QMetaObject::invokeMethod(m_vpnConnection.get(), "disconnectFromVpn", Qt::QueuedConnection); - QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteLater", Qt::QueuedConnection); - } - m_vpnConnectionThread.quit(); - if (!m_vpnConnectionThread.wait(5000)) { - m_vpnConnectionThread.terminate(); - m_vpnConnectionThread.wait(); - } - if (m_engine) { QObject::disconnect(m_engine, 0, 0, 0); delete m_engine; From c8970521073e467237256e1a32f3c6aadd1e44c8 Mon Sep 17 00:00:00 2001 From: albexk Date: Wed, 10 Sep 2025 14:28:36 +0300 Subject: [PATCH 02/18] chore: bump version (#1850) --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18149d461..ffbf34d2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -set(AMNEZIAVPN_VERSION 4.8.10.0) +set(AMNEZIAVPN_VERSION 4.8.10.1) project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION} DESCRIPTION "AmneziaVPN" @@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d") set(RELEASE_DATE "${CURRENT_DATE}") set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) -set(APP_ANDROID_VERSION_CODE 2093) +set(APP_ANDROID_VERSION_CODE 2094) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") From a81e32ff95f03d867d7ce9225d2699dd32dfa675 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Mon, 15 Sep 2025 06:53:51 +0400 Subject: [PATCH 03/18] fix: clean service/client logs in uninstall scripts (#1846) - Windows (x64/x86): - Remove delegation to `AmneziaVPN.exe -c` - Delete `%ProgramData%\AmneziaVPN\log\AmneziaVPN-service.log` - Delete current user logs at `%AppData%\AmneziaVPN.ORG\AmneziaVPN\log` - Remove empty parent dirs (app/org, log) - Linux: - Delete only `/var/log/AmneziaVPN/AmneziaVPN-service.log` (preserve `post-uninstall.log`) - Delete current user logs at `$HOME/.local/share/AmneziaVPN.ORG/AmneziaVPN/log` --- deploy/data/linux/post_uninstall.sh | 19 +++++++++++++++++++ deploy/data/windows/x32/post_uninstall.cmd | 22 +++++++++++++++++++++- deploy/data/windows/x64/post_uninstall.cmd | 22 +++++++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/deploy/data/linux/post_uninstall.sh b/deploy/data/linux/post_uninstall.sh index 98090d20a..59248b012 100755 --- a/deploy/data/linux/post_uninstall.sh +++ b/deploy/data/linux/post_uninstall.sh @@ -1,6 +1,7 @@ #!/bin/bash APP_NAME=AmneziaVPN +ORG_NAME=AmneziaVPN.ORG LOG_FOLDER=/var/log/$APP_NAME LOG_FILE="$LOG_FOLDER/post-uninstall.log" APP_PATH=/opt/$APP_NAME @@ -64,6 +65,24 @@ if test -f /usr/share/pixmaps/$APP_NAME.png; then fi +### Remove the service log file (keep post-uninstall.log) +if test -f "$LOG_FOLDER/AmneziaVPN-service.log"; then + sudo rm -f "$LOG_FOLDER/AmneziaVPN-service.log" >> $LOG_FILE 2>&1 +fi + +### Remove user logs for current user only +TARGET_HOME="$HOME" +if [ -n "$SUDO_USER" ] && [ "$SUDO_USER" != "root" ]; then + TARGET_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6) +fi +if test -d "$TARGET_HOME/.local/share/$ORG_NAME/$APP_NAME/log"; then + rm -rf "$TARGET_HOME/.local/share/$ORG_NAME/$APP_NAME/log" >> $LOG_FILE 2>&1 +fi + +# Try to remove empty app and organization directories under user share +if rmdir "$TARGET_HOME/.local/share/$ORG_NAME/$APP_NAME" 2>/dev/null; then :; fi +if rmdir "$TARGET_HOME/.local/share/$ORG_NAME" 2>/dev/null; then :; fi + if command -v steamos-readonly &> /dev/null; then sudo steamos-readonly enable >> $LOG_FILE echo "steamos-readonly enabled" >> $LOG_FILE diff --git a/deploy/data/windows/x32/post_uninstall.cmd b/deploy/data/windows/x32/post_uninstall.cmd index 83f8a8b78..e5f400679 100644 --- a/deploy/data/windows/x32/post_uninstall.cmd +++ b/deploy/data/windows/x32/post_uninstall.cmd @@ -1,7 +1,14 @@ set AmneziaPath=%~dp0 echo %AmneziaPath% -"%AmneziaPath%\AmneziaVPN.exe" -c +rem Define directories for logs +set "ORG_DIR=%AppData%\AmneziaVPN.ORG" +set "USER_APP_DIR=%ORG_DIR%\AmneziaVPN" +set "USER_LOG_DIR=%USER_APP_DIR%\log" +set "SYS_APP_DIR=%ProgramData%\AmneziaVPN" +set "SYS_LOG_DIR=%SYS_APP_DIR%\log" +set "SYS_LOG_FILE=%SYS_LOG_DIR%\AmneziaVPN-service.log" + timeout /t 1 sc stop AmneziaVPN-service sc delete AmneziaVPN-service @@ -9,4 +16,17 @@ sc stop AmneziaWGTunnel$AmneziaVPN sc delete AmneziaWGTunnel$AmneziaVPN taskkill /IM "AmneziaVPN-service.exe" /F taskkill /IM "AmneziaVPN.exe" /F + +rem Delete the service log file under ProgramData +if exist "%SYS_LOG_FILE%" del /F /Q "%SYS_LOG_FILE%" +if exist "%SYS_LOG_DIR%" rmdir /S /Q "%SYS_LOG_DIR%" +rem Try to remove application dir if empty +rd "%SYS_APP_DIR%" 2>nul + +rem Delete client logs under current user's AppData\Roaming (Organization\Application) +if exist "%USER_LOG_DIR%" rmdir /S /Q "%USER_LOG_DIR%" +rem Try to remove app and org directories if empty +rd "%USER_APP_DIR%" 2>nul +rd "%ORG_DIR%" 2>nul + exit /b 0 diff --git a/deploy/data/windows/x64/post_uninstall.cmd b/deploy/data/windows/x64/post_uninstall.cmd index 83f8a8b78..e5f400679 100644 --- a/deploy/data/windows/x64/post_uninstall.cmd +++ b/deploy/data/windows/x64/post_uninstall.cmd @@ -1,7 +1,14 @@ set AmneziaPath=%~dp0 echo %AmneziaPath% -"%AmneziaPath%\AmneziaVPN.exe" -c +rem Define directories for logs +set "ORG_DIR=%AppData%\AmneziaVPN.ORG" +set "USER_APP_DIR=%ORG_DIR%\AmneziaVPN" +set "USER_LOG_DIR=%USER_APP_DIR%\log" +set "SYS_APP_DIR=%ProgramData%\AmneziaVPN" +set "SYS_LOG_DIR=%SYS_APP_DIR%\log" +set "SYS_LOG_FILE=%SYS_LOG_DIR%\AmneziaVPN-service.log" + timeout /t 1 sc stop AmneziaVPN-service sc delete AmneziaVPN-service @@ -9,4 +16,17 @@ sc stop AmneziaWGTunnel$AmneziaVPN sc delete AmneziaWGTunnel$AmneziaVPN taskkill /IM "AmneziaVPN-service.exe" /F taskkill /IM "AmneziaVPN.exe" /F + +rem Delete the service log file under ProgramData +if exist "%SYS_LOG_FILE%" del /F /Q "%SYS_LOG_FILE%" +if exist "%SYS_LOG_DIR%" rmdir /S /Q "%SYS_LOG_DIR%" +rem Try to remove application dir if empty +rd "%SYS_APP_DIR%" 2>nul + +rem Delete client logs under current user's AppData\Roaming (Organization\Application) +if exist "%USER_LOG_DIR%" rmdir /S /Q "%USER_LOG_DIR%" +rem Try to remove app and org directories if empty +rd "%USER_APP_DIR%" 2>nul +rd "%ORG_DIR%" 2>nul + exit /b 0 From fcb7b8fa8dd0fad382fad195d52859ab164d3063 Mon Sep 17 00:00:00 2001 From: Mitternacht822 Date: Mon, 15 Sep 2025 06:54:34 +0400 Subject: [PATCH 04/18] fix: save/restore AmneziaDNS state (#1833) --- client/ui/controllers/settingsController.cpp | 6 ++++++ client/ui/models/servers_model.cpp | 1 + 2 files changed, 7 insertions(+) diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 605b0c416..8cec25797 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -151,6 +151,7 @@ void SettingsController::backupAppConfig(const QString &fileName) config["Conf/autoStart"] = Autostart::isAutostart(); config["Conf/killSwitchEnabled"] = isKillSwitchEnabled(); config["Conf/strictKillSwitchEnabled"] = isStrictKillSwitchEnabled(); + config["Conf/useAmneziaDns"] = isAmneziaDnsEnabled(); SystemController::saveFile(fileName, QJsonDocument(config).toJson()); } @@ -214,6 +215,11 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data) m_settings->setStrictKillSwitchEnabled(false); #endif + bool amneziaDnsEnabled = newConfigData.contains("Conf/useAmneziaDns") + ? newConfigData.value("Conf/useAmneziaDns").toBool() + : m_settings->useAmneziaDns(); + emit amneziaDnsToggled(amneziaDnsEnabled); + emit restoreBackupFinished(); } else { emit changeSettingsErrorOccurred(tr("Backup file is corrupted")); diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index b53e70f7b..ebe6c857a 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -173,6 +173,7 @@ void ServersModel::resetModel() m_servers = m_settings->serversArray(); m_defaultServerIndex = m_settings->defaultServerIndex(); m_processedServerIndex = m_defaultServerIndex; + m_isAmneziaDnsEnabled = m_settings->useAmneziaDns(); endResetModel(); emit defaultServerIndexChanged(m_defaultServerIndex); } From 5fc68cca832b2d62f976537ff559ff41e302398f Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 15 Sep 2025 05:55:18 +0300 Subject: [PATCH 05/18] fix: split tunneling restoration from backup (#1835) --- client/ui/controllers/settingsController.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 8cec25797..c82916246 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -187,7 +187,8 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data) #if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID) int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt(); - bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toString().toLower() == "true"; + bool appSplittunnelingEnabled = + newConfigData.value("Conf/appsSplitTunnelingEnabled").toVariant().toString().toLower() == "true"; m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode); #if defined(Q_OS_WINDOWS) @@ -199,12 +200,13 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data) m_appSplitTunnelingModel->clearAppsList(); } } - + m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled); #endif int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt(); - bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toString().toLower() == "true"; + bool siteSplittunnelingEnabled = + newConfigData.value("Conf/sitesSplitTunnelingEnabled").toVariant().toString().toLower() == "true"; m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode); m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled); From 73f13404bb17ad97c689010f6044a1014fb701dd Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Mon, 29 Sep 2025 05:40:58 +0300 Subject: [PATCH 06/18] feat: add support for multiple scenes and handle URL contexts in iOS 13+ (#1889) --- client/cmake/ios.cmake | 1 + client/ios/app/Info.plist.in | 28 ++++++- .../ios/AmneziaSceneDelegateHooks.mm | 82 +++++++++++++++++++ client/platforms/ios/ios_controller.mm | 40 ++++++++- 4 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 client/platforms/ios/AmneziaSceneDelegateHooks.mm diff --git a/client/cmake/ios.cmake b/client/cmake/ios.cmake index a498a5b19..ab820c712 100644 --- a/client/cmake/ios.cmake +++ b/client/cmake/ios.cmake @@ -46,6 +46,7 @@ set(SOURCES ${SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/AmneziaSceneDelegateHooks.mm ) diff --git a/client/ios/app/Info.plist.in b/client/ios/app/Info.plist.in index 45b08cc9e..6165daf32 100644 --- a/client/ios/app/Info.plist.in +++ b/client/ios/app/Info.plist.in @@ -32,17 +32,41 @@ UILaunchStoryboardName AmneziaVPNLaunchScreen + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + QIOSWindowSceneDelegate + + + + UIRequiredDeviceCapabilities UIRequiresFullScreen - + UISupportedInterfaceOrientations UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad - + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIUserInterfaceStyle Light com.wireguard.ios.app_group_id diff --git a/client/platforms/ios/AmneziaSceneDelegateHooks.mm b/client/platforms/ios/AmneziaSceneDelegateHooks.mm new file mode 100644 index 000000000..60cbbe0fa --- /dev/null +++ b/client/platforms/ios/AmneziaSceneDelegateHooks.mm @@ -0,0 +1,82 @@ +#import +#import +#include + +#include +#include +#include + +#include "ios_controller.h" + +using SceneOpenURLContexts = void (*)(id, SEL, UIScene *, NSSet *); + +static SceneOpenURLContexts g_originalSceneOpenURLContexts = nullptr; + +static void amnezia_handleURL(NSURL *url) +{ + if (!url || !url.isFileURL) { + return; + } + + QString filePath(url.path.UTF8String); + if (filePath.isEmpty()) { + return; + } + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if (filePath.contains("backup")) { + IosController::Instance()->importBackupFromOutside(filePath); + return; + } + + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly)) { + return; + } + + const QByteArray data = file.readAll(); + IosController::Instance()->importConfigFromOutside(QString::fromUtf8(data)); + }); +} + +static void amnezia_scene_openURLContexts(id self, SEL _cmd, UIScene *scene, NSSet *contexts) +{ + if (g_originalSceneOpenURLContexts) { + g_originalSceneOpenURLContexts(self, _cmd, scene, contexts); + } + + if (!contexts || contexts.count == 0) { + return; + } + + if (@available(iOS 13.0, *)) { + for (UIOpenURLContext *context in contexts) { + amnezia_handleURL(context.URL); + } + } +} + +@interface AmneziaSceneDelegateHooks : NSObject +@end + +@implementation AmneziaSceneDelegateHooks + ++ (void)load +{ + Class cls = objc_getClass("QIOSWindowSceneDelegate"); + if (!cls) { + return; + } + + SEL selector = @selector(scene:openURLContexts:); + Method method = class_getInstanceMethod(cls, selector); + if (method) { + g_originalSceneOpenURLContexts = reinterpret_cast(method_getImplementation(method)); + method_setImplementation(method, reinterpret_cast(amnezia_scene_openURLContexts)); + } else { + const char *types = "v@:@@"; + class_addMethod(cls, selector, reinterpret_cast(amnezia_scene_openURLContexts), types); + } +} + +@end diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index b57f8d1d6..64da50ea0 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -29,12 +29,46 @@ const char* MessageKey::SplitTunnelSites = "SplitTunnelSites"; #if !MACOS_NE static UIViewController* getViewController() { - NSArray *windows = [[UIApplication sharedApplication]windows]; - for (UIWindow *window in windows) { - if (window.isKeyWindow) { + UIApplication *application = [UIApplication sharedApplication]; + + if (@available(iOS 13.0, *)) { + for (UIScene *scene in application.connectedScenes) { + if (scene.activationState != UISceneActivationStateForegroundActive) { + continue; + } + + if (![scene isKindOfClass:[UIWindowScene class]]) { + continue; + } + + UIWindowScene *windowScene = (UIWindowScene *)scene; + + for (UIWindow *window in windowScene.windows) { + if (window.isKeyWindow && window.rootViewController) { + return window.rootViewController; + } + } + + for (UIWindow *window in windowScene.windows) { + if (!window.isHidden && window.rootViewController) { + return window.rootViewController; + } + } + } + } + + for (UIWindow *window in application.windows) { + if (window.isKeyWindow && window.rootViewController) { return window.rootViewController; } } + + for (UIWindow *window in application.windows) { + if (window.rootViewController) { + return window.rootViewController; + } + } + return nil; } #endif From 524fefc5cbce7e56cbda131ec0efeb9085098a79 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 29 Sep 2025 05:45:14 +0300 Subject: [PATCH 07/18] feat: warning on app split tunneling for windows (#1880) --- .../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml index 60b59f45c..c7b3eaf5b 100644 --- a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml @@ -145,6 +145,18 @@ PageType { } } } + + WarningType { + Layout.fillWidth: true + Layout.topMargin: 8 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + textString: qsTr("Only \"Apps from the list should not have access via VPN\" mode is available on Windows") + iconPath: "qrc:/images/controls/alert-circle.svg" + + enabled: (Qt.platform.os === "windows") && root.pageEnabled + } } ListViewType { From b4938c2cc96473a38ce0d792d705aa0cf8469d08 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 29 Sep 2025 05:47:54 +0300 Subject: [PATCH 08/18] fix: default lang matching between app and OS (#1855) * fix: default lang matching between app and OS * remake * fix: set default lang value --- client/core/controllers/coreController.cpp | 3 +-- client/settings.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp index 78da21a1f..cdc3b3709 100644 --- a/client/core/controllers/coreController.cpp +++ b/client/core/controllers/coreController.cpp @@ -26,9 +26,8 @@ CoreController::CoreController(const QSharedPointer &vpnConnectio initNotificationHandler(); - auto locale = m_settings->getAppLanguage(); m_translator.reset(new QTranslator()); - updateTranslator(locale); + updateTranslator(m_settings->getAppLanguage()); } void CoreController::initModels() diff --git a/client/settings.h b/client/settings.h index 7c244cd44..0a73e13f3 100644 --- a/client/settings.h +++ b/client/settings.h @@ -174,7 +174,7 @@ public: QLocale getAppLanguage() { - QString localeStr = m_settings.value("Conf/appLanguage").toString(); + QString localeStr = m_settings.value("Conf/appLanguage", QLocale::system().name()).toString(); return QLocale(localeStr); }; void setAppLanguage(QLocale locale) From 657eeb40c77b193cdb0b3557bad15bcc4f7b63aa Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 29 Sep 2025 05:48:36 +0300 Subject: [PATCH 09/18] fix: mirror error code link (#1863) * fix: mirror error code link * remake --- client/ui/controllers/pageController.cpp | 2 +- client/ui/qml/Controls2/PopupType.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ui/controllers/pageController.cpp b/client/ui/controllers/pageController.cpp index 38c753466..3f3319c6d 100644 --- a/client/ui/controllers/pageController.cpp +++ b/client/ui/controllers/pageController.cpp @@ -169,7 +169,7 @@ void PageController::onShowErrorMessage(ErrorCode errorCode) { const auto fullErrorMessage = errorString(errorCode); const auto errorMessage = fullErrorMessage.mid(fullErrorMessage.indexOf(". ") + 1); // remove ErrorCode %1. - const auto errorUrl = QStringLiteral("https://docs.amnezia.org/troubleshooting/error-codes/#error-%1-%2").arg(static_cast(errorCode)).arg(utils::enumToString(errorCode).toLower()); + const auto errorUrl = QStringLiteral("troubleshooting/error-codes/#error-%1-%2").arg(static_cast(errorCode)).arg(utils::enumToString(errorCode).toLower()); const auto fullMessage = QStringLiteral("ErrorCode: %2. %3").arg(errorUrl).arg(static_cast(errorCode)).arg(errorMessage); emit showErrorMessage(fullMessage); diff --git a/client/ui/qml/Controls2/PopupType.qml b/client/ui/qml/Controls2/PopupType.qml index dfb6f273e..d8b79bca6 100644 --- a/client/ui/qml/Controls2/PopupType.qml +++ b/client/ui/qml/Controls2/PopupType.qml @@ -72,7 +72,7 @@ Popup { Layout.fillWidth: true onLinkActivated: function(link) { - Qt.openUrlExternally(link) + Qt.openUrlExternally(LanguageModel.getCurrentDocsUrl(link)) } text: root.text From 34309261a8c9b0dcf56de3da5792f2c47446faa8 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 29 Sep 2025 05:49:19 +0300 Subject: [PATCH 10/18] fix: scrollbar always visible (#1877) * fix: scrollbar always visible * fix: scrollbar always visible on app split tunneling page --- client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml | 2 ++ client/ui/qml/Pages2/PageSettingsSplitTunneling.qml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml index c7b3eaf5b..91f6c6e59 100644 --- a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml @@ -162,6 +162,8 @@ PageType { ListViewType { id: listView + ScrollBar.vertical: ScrollBarType { policy: ScrollBar.AlwaysOn } + anchors.top: header.bottom anchors.bottom: addAppButton.top anchors.left: parent.left diff --git a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml index 7d0ba5998..3c4377c80 100644 --- a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml @@ -164,6 +164,8 @@ PageType { ListViewType { id: listView + ScrollBar.vertical: ScrollBarType { policy: ScrollBar.AlwaysOn } + anchors.top: header.bottom anchors.topMargin: 16 anchors.bottom: addSiteButton.top From cef4c262e983ffd7d5fcf9c631b2959ebedb9807 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 29 Sep 2025 05:50:18 +0300 Subject: [PATCH 11/18] fix: keyboard fix for api 'connection key' buttons (#1872) --- client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml b/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml index 2ad818ea5..91c9ce8d6 100644 --- a/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml +++ b/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml @@ -56,7 +56,7 @@ PageType { text: qsTr("Copy key") leftImageSource: "qrc:/images/controls/copy.svg" - onClicked: { + clickedFunc: function() { ApiConfigsController.copyVpnKeyToClipboard() PageController.showNotificationMessage(qsTr("Copied")) } @@ -77,7 +77,7 @@ PageType { text: qsTr("Save key as a file") leftImageSource: "qrc:/images/controls/share-2.svg" - onClicked: { + clickedFunc: function() { var fileName = GC.isMobile() ? "amnezia_vpn_key.vpn" : SystemController.getFileName( @@ -110,7 +110,7 @@ PageType { text: qsTr("Show key text") leftImageSource: "qrc:/images/controls/eye.svg" - onClicked: { + clickedFunc: function() { PageController.showBusyIndicator(true) ApiConfigsController.prepareVpnKeyExport() PageController.showBusyIndicator(false) From 2d5dc54e0f6f16609dda9ab1342633b1d9a11fdb Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 29 Sep 2025 05:50:57 +0300 Subject: [PATCH 12/18] fix: keyboard navigation for text fields (#1879) --- .../ui/qml/Controls2/TextFieldWithHeaderType.qml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index d76978674..fdb7a3aef 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -37,6 +37,22 @@ Item { implicitWidth: content.implicitWidth implicitHeight: content.implicitHeight + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + ColumnLayout { id: content anchors.fill: parent From 7a96c212f3ebb7bd05ba7b77a65c8fda888a660a Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 29 Sep 2025 05:51:52 +0300 Subject: [PATCH 13/18] fix: rename user in search (#1847) --- client/ui/qml/Pages2/PageShare.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 968dfb3ef..67540c6ae 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -603,12 +603,14 @@ PageType { visible: accessTypeSelector.currentIndex === 1 property bool isFocusable: true + property bool freezeFilter: false model: SortFilterProxyModel { id: proxyClientManagementModel sourceModel: ClientManagementModel filters: RegExpFilter { roleName: "clientName" + enabled: !clientsListView.freezeFilter pattern: ".*" + searchTextField.textField.text + ".*" caseSensitivity: Qt.CaseInsensitive } @@ -791,12 +793,14 @@ PageType { } if (clientNameEditor.textField.text !== clientName) { + clientsListView.freezeFilter = true PageController.showBusyIndicator(true) ExportController.renameClient(proxyClientManagementModel.mapToSource(index), clientNameEditor.textField.text, ContainersModel.getProcessedContainerIndex(), ServersModel.getProcessedServerCredentials()) PageController.showBusyIndicator(false) + Qt.callLater(function(){ clientsListView.freezeFilter = false }) clientNameEditDrawer.closeTriggered() } } From f3539b263260619d7f599121fe0e2ae3ab69d19a Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 29 Sep 2025 05:55:53 +0300 Subject: [PATCH 14/18] fix: proper wl name on connection key page (#1867) * fix: proper wl name on connection key page * some changings * little change * added missing import * fix: proper wl default filename --- .../Pages2/PageSettingsApiSubscriptionKey.qml | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml b/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml index 91c9ce8d6..f54641370 100644 --- a/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml +++ b/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml @@ -6,6 +6,8 @@ import Qt.labs.platform 1.1 import QtCore +import SortFilterProxyModel 0.2 + import PageEnum 1.0 import Style 1.0 @@ -17,6 +19,33 @@ import "../Components" PageType { id: root + property var processedServer + + Connections { + target: ServersModel + + function onProcessedServerChanged() { + root.processedServer = proxyServersModel.get(0) + } + } + + SortFilterProxyModel { + id: proxyServersModel + objectName: "proxyServersModel" + + sourceModel: ServersModel + filters: [ + ValueFilter { + roleName: "isCurrentlyProcessed" + value: true + } + ] + + Component.onCompleted: { + root.processedServer = proxyServersModel.get(0) + } + } + Component.onCompleted: { PageController.showBusyIndicator(true) ApiConfigsController.prepareVpnKeyExport() @@ -40,7 +69,7 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 Layout.topMargin: 16 - text: qsTr("Amnezia Premium\nsubscription key") + text: qsTr(root.processedServer.name + "\nsubscription key") font.pixelSize: 32 font.bold: true color: AmneziaStyle.color.paleGray @@ -79,11 +108,11 @@ PageType { clickedFunc: function() { var fileName = GC.isMobile() - ? "amnezia_vpn_key.vpn" + ? root.processedServer.name.toLowerCase().replace(/\s+/g, "_") + "_key.vpn" : SystemController.getFileName( qsTr("Save AmneziaVPN config"), qsTr("Config files (*.vpn)"), - StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_vpn_key", + StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + root.processedServer.name.toLowerCase().replace(/\s+/g, "_") + "_key", true, ".vpn" ) @@ -177,7 +206,7 @@ PageType { Header2Type { Layout.fillWidth: true - headerText: qsTr("Amnezia Premium Subscription key") + headerText: qsTr(root.processedServer.name + " Subscription key") } TextArea { From 8bce4328244650273ab5e5939f7a68f902161276 Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Mon, 29 Sep 2025 05:56:41 +0300 Subject: [PATCH 15/18] fix: enable paste from clipboard on ios in addition to android (#1868) --- client/ui/qml/Controls2/ContextMenuType.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ui/qml/Controls2/ContextMenuType.qml b/client/ui/qml/Controls2/ContextMenuType.qml index fd341e6d5..3842af2a9 100644 --- a/client/ui/qml/Controls2/ContextMenuType.qml +++ b/client/ui/qml/Controls2/ContextMenuType.qml @@ -20,8 +20,8 @@ Menu { MenuItem { text: qsTr("&Paste") shortcut: StandardKey.Paste - // Fix calling paste from clipboard when launching app on android - enabled: Qt.platform.os === "android" ? true : textObj.canPaste + // Fix calling paste from clipboard when launching app on android/ios + enabled: (Qt.platform.os === "android" || Qt.platform.os === "ios") ? true : textObj.canPaste onTriggered: textObj.paste() } From 89f30d8c31cbb0bc27fa31a371ff8eb5c3bd0ac5 Mon Sep 17 00:00:00 2001 From: vkamn Date: Mon, 29 Sep 2025 10:58:44 +0800 Subject: [PATCH 16/18] fix: fixed native wg obfuscation (#1890) --- client/mozilla/localsocketcontroller.cpp | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 9abab81cc..585b93d3b 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -264,13 +264,13 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { && !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined() && !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined() && !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined() - && !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined() - && !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined() + // && !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined() + // && !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined() && !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined() && !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined() && !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined() && !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined() - && !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined() +/* && !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined() && !wgConfig.value(amnezia::config_key::specialJunk2).isUndefined() && !wgConfig.value(amnezia::config_key::specialJunk3).isUndefined() && !wgConfig.value(amnezia::config_key::specialJunk4).isUndefined() @@ -278,27 +278,27 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { && !wgConfig.value(amnezia::config_key::controlledJunk1).isUndefined() && !wgConfig.value(amnezia::config_key::controlledJunk2).isUndefined() && !wgConfig.value(amnezia::config_key::controlledJunk3).isUndefined() - && !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()) { + && !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()*/) { json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount)); json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize)); json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize)); json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize)); json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize)); - json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize)); - json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize)); + // json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize)); + // json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize)); json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader)); json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader)); json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader)); json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader)); - json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1)); - json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2)); - json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3)); - json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4)); - json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5)); - json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1)); - json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2)); - json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3)); - json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout)); + // json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1)); + // json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2)); + // json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3)); + // json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4)); + // json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5)); + // json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1)); + // json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2)); + // json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3)); + // json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout)); } write(json); From db535f7e7d5f8cc68bd5672e508667d0576ad59e Mon Sep 17 00:00:00 2001 From: vkamn Date: Mon, 29 Sep 2025 11:05:30 +0800 Subject: [PATCH 17/18] chore: increase default values (#1891) --- client/ui/controllers/importController.cpp | 2 +- client/ui/controllers/installController.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 33d95e8cc..473ca27a8 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -274,7 +274,7 @@ void ImportController::processNativeWireGuardConfig() auto serverProtocolConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::WireGuard)).toObject(); auto clientProtocolConfig = QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object(); - QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5)); + QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7)); QString junkPacketMinSize = QString::number(10); QString junkPacketMaxSize = QString::number(50); clientProtocolConfig[config_key::junkPacketCount] = junkPacketCount; diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index ec22fc121..7d2699e14 100755 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -73,7 +73,7 @@ void InstallController::install(DockerContainer container, int port, TransportPr containerConfig.insert(config_key::transport_proto, ProtocolProps::transportProtoToString(transportProto, protocol)); if (container == DockerContainer::Awg) { - QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5)); + QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7)); QString junkPacketMinSize = QString::number(10); QString junkPacketMaxSize = QString::number(50); From c66d8ecca00a437f972326fd5f007fada1bea0b4 Mon Sep 17 00:00:00 2001 From: vkamn Date: Mon, 29 Sep 2025 11:07:27 +0800 Subject: [PATCH 18/18] chore: bump version (#1892) --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ffbf34d2a..fa641b691 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -set(AMNEZIAVPN_VERSION 4.8.10.1) +set(AMNEZIAVPN_VERSION 4.8.11.0) project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION} DESCRIPTION "AmneziaVPN" @@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d") set(RELEASE_DATE "${CURRENT_DATE}") set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) -set(APP_ANDROID_VERSION_CODE 2094) +set(APP_ANDROID_VERSION_CODE 2095) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux")