Compare commits

...

45 Commits

Author SHA1 Message Date
Mykola Baibuz 38082f9940 Merge branch 'dev' into feature/awg-network-check 2025-04-08 20:52:48 +03:00
Nethius 9d571a4c71 feature: new mirrors support (#1519) 2025-04-08 12:07:31 +07:00
pokamest f283858490 Merge pull request #1517 from amnezia-vpn/chore/update-go-version
Update go version in actions to 1.24
2025-04-07 21:53:05 +01:00
pokamest 76fe203767 Update go version in actions to 1.24 2025-04-07 18:05:08 +01:00
pokamest b9a47f2f50 Merge pull request #1516 from amnezia-vpn/feature/openvpn-warning
feature: warning when importing openvpn configurations
2025-04-07 17:59:37 +01:00
vladimir.kuznetsov 27cb17c640 chore: clear warning text before extract 2025-04-07 23:35:24 +08:00
vladimir.kuznetsov ef8fb89eb3 feature: warning when importing openvpn configurations 2025-04-07 23:30:11 +08:00
Mykola Baibuz 71691fa01e Add delay for Linux wakeup reconnect 2025-04-06 20:50:37 +03:00
Mykola Baibuz 62d9bcaf7f Add delay for Linux wakeup reconnect 2025-04-06 20:30:52 +03:00
Mykola Baibuz 5ef8254cba MacOS suspend mode handler draft 2025-04-03 22:12:54 +03:00
Mykola Baibuz f767171c06 Windows suspend mode handler 2025-04-03 20:45:27 +03:00
Mykola Baibuz eff460b227 Use ping check for tun interfce 2025-04-02 20:26:59 +03:00
Mykola Baibuz 319043818a Add DBus network checker for Linux 2025-04-02 11:33:15 +03:00
Nethius f1b045f8a8 fixed selecting the default button on PageSetupWizardEasy (#1502) 2025-03-30 12:53:26 +07:00
Mykola Baibuz e730521576 Restart IpcClient after OS suspend 2025-03-29 14:43:19 +02:00
Mykola Baibuz 517930dd22 handle for interafe problems on windows 2025-03-27 22:53:06 +02:00
Mykola Baibuz 26994c21b1 add delay for ping checker stop 2025-03-27 21:46:10 +02:00
Mykola Baibuz 681eb5aa86 fix android build 2025-03-27 21:28:14 +02:00
Mykola Baibuz 4b86425992 Use networkchecker for all protocols 2025-03-27 21:07:03 +02:00
Mykola Baibuz 9b41ed66bb Cleanup unused code 2025-03-26 21:11:40 +02:00
Mykola Baibuz 8bb4fa3f35 Use service for PingSender 2025-03-26 20:16:17 +02:00
Anton Sosnin 050066132b Fix iOS initial translation loading (#1477) 2025-03-24 14:35:22 +07:00
Nethius 2a6e6a1e24 chore: bump version (#1485) 2025-03-21 14:12:56 +07:00
Nethius 92689d084c feature/old api proxy (#1484)
* feature: proxy old api requests through gateway

* chore: bump version
2025-03-21 10:25:44 +07:00
lunardunno 00f314039d Patch for user checking. (#1481)
* Direct use of the $HOME variable.

* Sudo check witch variable $HOME.

Direct use of the $HOME variable.

* Changing for Error 208

Changing description and title for error 208

* Revert "Changing for Error 208"

This reverts commit f45624c023.

* Changing for Error 207

Changing description and title for Error 207
2025-03-20 10:24:37 +07:00
lunardunno fcb75e837d chore: correcting version (#1480)
* Сorrecting version

Correction: return to the correct version

* Correction for SH
2025-03-19 21:51:49 +07:00
Yaroslav 9fbea76b74 There's a common issue of building iOS apps on Qt 6.8 because of new introduced ffmpeg dependency in multimedia Qt package (#1414)
ref: https://community.esri.com/t5/qt-maps-sdk-questions/build-failure-on-ios-with-qt-6-8/m-p/1548701#M5339
2025-03-14 20:40:27 +07:00
lunardunno b3ff120bcf Checking server user permissions to use sudo (#1442)
* Username if whoami returns an error

Сommand to use home directory name if whoami returns error or is missing for prepare_host.sh.

* Update check_user_in_sudo.sh

Сommand to use home directory name if whoami returns error or is missing for check_user_in_sudo.sh.
Checking server user permissions to use sudo using a package manager or using uname.
Сhecking and redefining the system language.
Checking requirements for sudo users or root in script.

* Cases have been changed and added.

Changed description of the “Server User Not In Sudo” case.
Corrected the name and description of the "ServerPacketManagerError" case. Packet to Package.
Adding a "SudoPackageIsNotPreinstalled" case.
Adding a "ServerUserNotAllowedInSudoers" case.
Adding a "ServerUserPasswordRequired" case.

* Serves errors have been changed and added.

Corrected the name of the "ServerPacketManagerError" error to "ServerPackageManagerError".
Adding a "SudoPackageIsNotPreinstalled" error.
Adding a "ServerUserNotAllowedInSudoers" error.
Adding a "ServerUserPasswordRequired" error.

* Return ServerPacketManagerError

Return to the name "ServerPacketManagerError".

* Added errors handling 

Added new errors' handling to serverController.cpp.
Permission checks are also performed for the root user.

* Update translations

Updating translations for two existing server errors.

* Myanmar translation update

* Update for my_MM.ts

* checking for not allowed

Checking for "not allowed" in stdOut

* Removed "not allowed"

Removed check for "not allowed" in stdOut

* Removed nested launch

Removed nested launch via sudo

* Returned nested launch

Returned nested launch via sudo

* All checks with sudo

Both checks with sudo always run.

* Moved removing timestamp sudo

Removing the sudo timestamp is done every time.

* Checking the user directory

Checking the accessibility of the user's home directory

* Polishing

Изменение порядка обработки ошибок.

* changing detection order 

change the order of detection of inconsistencies:
1. sudo not preinstalled. (if user != root)
2. user not in sudo or wheel group. (if user != root)
3. user's directory is not accessible. (for all)
4. user not allowed in sudoers. (for all)
5. user password required. (for all)

* Packet to Package

* chore: bump version (#1463)

* fix for sh (#1462)

Fix for servers where sh is used as default shell.

* Username if whoami returns an error

Сommand to use home directory name if whoami returns error or is missing for prepare_host.sh.

* Update check_user_in_sudo.sh

Сommand to use home directory name if whoami returns error or is missing for check_user_in_sudo.sh.
Checking server user permissions to use sudo using a package manager or using uname.
Сhecking and redefining the system language.
Checking requirements for sudo users or root in script.

* Cases have been changed and added.

Changed description of the “Server User Not In Sudo” case.
Corrected the name and description of the "ServerPacketManagerError" case. Packet to Package.
Adding a "SudoPackageIsNotPreinstalled" case.
Adding a "ServerUserNotAllowedInSudoers" case.
Adding a "ServerUserPasswordRequired" case.

* Serves errors have been changed and added.

Corrected the name of the "ServerPacketManagerError" error to "ServerPackageManagerError".
Adding a "SudoPackageIsNotPreinstalled" error.
Adding a "ServerUserNotAllowedInSudoers" error.
Adding a "ServerUserPasswordRequired" error.

* Return ServerPacketManagerError

Return to the name "ServerPacketManagerError".

* Update translations

Updating translations for two existing server errors.

* Added errors handling 

Added new errors' handling to serverController.cpp.
Permission checks are also performed for the root user.

* Myanmar translation update

* Update for my_MM.ts

* checking for not allowed

Checking for "not allowed" in stdOut

* Removed "not allowed"

Removed check for "not allowed" in stdOut

* Removed nested launch

Removed nested launch via sudo

* Returned nested launch

Returned nested launch via sudo

* All checks with sudo

Both checks with sudo always run.

* Moved removing timestamp sudo

Removing the sudo timestamp is done every time.

* Checking the user directory

Checking the accessibility of the user's home directory

* Polishing

Изменение порядка обработки ошибок.

* changing detection order 

change the order of detection of inconsistencies:
1. sudo not preinstalled. (if user != root)
2. user not in sudo or wheel group. (if user != root)
3. user's directory is not accessible. (for all)
4. user not allowed in sudoers. (for all)
5. user password required. (for all)

* Undoing unintended changes

Undoing unintended changes.

* Undoing unintended change

Undoing unintended change.

* not allowed to use sudo

The user is not allowed to use sudo on this server.

* Capital letters in the error

Capital letters in the error description.

---------

Co-authored-by: albexk <albexk@proton.me>
2025-03-14 20:39:58 +07:00
Mykola Baibuz e792117be1 Add network status check for AWG/WG protocol 2025-03-12 23:32:00 +02:00
paldeflex 9dea98f020 chore: README typo fixes (#1467) 2025-03-10 23:22:09 +07:00
Mykola Baibuz c4701d4e7a Update XRay for Desktops (#1459)
version 25.3.6
2025-03-10 15:11:26 +07:00
Nethius 48903ca3a1 chore: fixed proxyStorageUrl typo (#1466) 2025-03-09 13:36:21 +07:00
Nethius 0c9fd4aef4 feature: added multiply proxy storage support (#1465) 2025-03-09 13:07:08 +07:00
lunardunno b2af2e46ac fix for sh (#1462)
Fix for servers where sh is used as default shell.
2025-03-09 12:34:00 +07:00
albexk efc76a0683 chore: bump version (#1463) 2025-03-09 10:30:43 +07:00
Nethius c4a553c166 chore: error body processing (#1458) 2025-03-07 10:39:12 +07:00
Cyril Anisimov 69a00b0252 feature: remove the limit of ip addresses = 254 (#1438) 2025-03-06 21:43:47 +07:00
KsZnak 4257c08b43 Update amneziavpn_ru_RU.ts (#1457) 2025-03-06 21:38:42 +07:00
Mykola Baibuz c9e5b92f79 Remove unneeded flushDns (#1443) 2025-03-05 13:21:39 +07:00
Mykola Baibuz 99818c2ad8 Fixes for native OpenVPN config import (#1444)
* Remote address in OpenVPN config can be host name

* Protocol parameter in OpenVPN config is not mandatory
2025-03-05 13:20:46 +07:00
shiroow 99e3afabad chore: update eng text (#1456)
chore: update eng text
2025-03-05 10:11:31 +07:00
Yaroslav d3339a7f3a fix: iOS/iPadOS crashes on a start of the app because of there's no keyFrame set (#1448)
So setting one if it's not set.
2025-03-04 18:13:04 +07:00
Nethius 678bfffe49 chore: minor ui fixes (#1446)
* chore: minor ui fixes

* chore: update ru translation file

* bugfix: fixed config update by ttl for gateway configs

* bugfix: fixed proxy bypassing

* chore: minor ui fixes

* chore: update ru translation file

* chore: bump version
2025-03-04 13:33:35 +07:00
Nethius 728b48044c Merge pull request #1440 from amnezia-vpn/feature/subscription-settings-page
feature/subscription settings page
2025-02-28 22:17:43 +07:00
Nethius 7ccbfa48bc bugfix: fixed mobile controllers initialization (#1436)
* bugfix: fixed mobile controllers initialization

* chore: bump version
2025-02-25 22:29:58 +07:00
88 changed files with 2310 additions and 758 deletions
+1 -1
View File
@@ -190,7 +190,7 @@ jobs:
- name: 'Install go' - name: 'Install go'
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: '1.22.1' go-version: '1.24'
cache: false cache: false
- name: 'Setup gomobile' - name: 'Setup gomobile'
+2 -2
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.8.4.0 project(${PROJECT} VERSION 4.8.5.0
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/" HOMEPAGE_URL "https://amnezia.org/"
) )
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}") set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 2077) set(APP_ANDROID_VERSION_CODE 2082)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")
+10 -10
View File
@@ -6,11 +6,11 @@
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский ### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
[AmneziaVPN](https://amnezia.org) — это open sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере. [AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org) [![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Сайт](https://amnezia.org) | [Зеркало на сайт](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting) ### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
> [!TIP] > [!TIP]
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org). > Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
@@ -30,7 +30,7 @@
- Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2. - Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2.
- Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay. - Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них. - Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них.
- Поддерживает платформы: Windows, MacOS, Linux, Android, iOS. - Поддерживает платформы: Windows, macOS, Linux, Android, iOS.
- Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved). - Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
## Ссылки ## Ссылки
@@ -38,10 +38,10 @@
- [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org) - [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация - [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit - [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддржки в Telegram (Английский) - [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддержки в Telegram (Английский)
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддржки в Telegram (Фарси) - [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддержки в Telegram (Фарси)
- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддржки в Telegram (Мьянма) - [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддержки в Telegram (Мьянма)
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддржки в Telegram (Русский) - [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддержки в Telegram (Русский)
- [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium | [Зеркало](https://storage.googleapis.com/kldscp/vpnpay.io/ru/amnezia-premium\) - [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium | [Зеркало](https://storage.googleapis.com/kldscp/vpnpay.io/ru/amnezia-premium\)
## Технологии ## Технологии
@@ -80,8 +80,8 @@ git submodule update --init --recursive
Проверьте папку deploy для скриптов сборки. Проверьте папку deploy для скриптов сборки.
### Как собрать iOS-приложение из исходного кода на MacOS ### Как собрать iOS-приложение из исходного кода на MacOS
1. Убедитесь, что у вас установлен XCode версии 14 или выше. 1. Убедитесь, что у вас установлен Xcode версии 14 или выше.
2. Для генерации проекта XCode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули: 2. Для генерации проекта Xcode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
- MacOS - MacOS
- iOS - iOS
- Модуль совместимости с Qt 5 - Модуль совместимости с Qt 5
@@ -117,7 +117,7 @@ $QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
export PATH=$(PATH):/path/to/GOPATH/bin export PATH=$(PATH):/path/to/GOPATH/bin
``` ```
6. Откройте проект в XCode. Теперь вы можете тестировать, архивировать или публиковать приложение. 6. Откройте проект в Xcode. Теперь вы можете тестировать, архивировать или публиковать приложение.
Если сборка завершится с ошибкой: Если сборка завершится с ошибкой:
``` ```
-8
View File
@@ -31,10 +31,6 @@ add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}") add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}") add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
if(IOS)
set(PACKAGES ${PACKAGES} Multimedia)
endif()
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
set(PACKAGES ${PACKAGES} Widgets) set(PACKAGES ${PACKAGES} Widgets)
endif() endif()
@@ -48,10 +44,6 @@ set(LIBS ${LIBS}
Qt6::Core5Compat Qt6::Concurrent Qt6::Core5Compat Qt6::Concurrent
) )
if(IOS)
set(LIBS ${LIBS} Qt6::Multimedia)
endif()
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
set(LIBS ${LIBS} Qt6::Widgets) set(LIBS ${LIBS} Qt6::Widgets)
endif() endif()
+2 -2
View File
@@ -36,7 +36,6 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.h ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.h
${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.h ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.h
${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h ${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
) )
if(NOT IOS) if(NOT IOS)
@@ -86,7 +85,6 @@ set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/mozilla/models/server.cpp ${CLIENT_ROOT_DIR}/mozilla/models/server.cpp
${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.cpp ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.cpp
${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
) )
if(NOT IOS) if(NOT IOS)
@@ -175,11 +173,13 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.h ${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.h
${CLIENT_ROOT_DIR}/protocols/xrayprotocol.h ${CLIENT_ROOT_DIR}/protocols/xrayprotocol.h
${CLIENT_ROOT_DIR}/protocols/awgprotocol.h ${CLIENT_ROOT_DIR}/protocols/awgprotocol.h
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
) )
set(SOURCES ${SOURCES} set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/core/ipcclient.cpp ${CLIENT_ROOT_DIR}/core/ipcclient.cpp
${CLIENT_ROOT_DIR}/core/privileged_process.cpp ${CLIENT_ROOT_DIR}/core/privileged_process.cpp
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp ${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp ${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.cpp ${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.cpp
+65 -56
View File
@@ -3,6 +3,7 @@
#include <QDebug> #include <QDebug>
#include <QJsonDocument> #include <QJsonDocument>
#include <QProcess> #include <QProcess>
#include <QRegularExpression>
#include <QString> #include <QString>
#include <QTemporaryDir> #include <QTemporaryDir>
#include <QTemporaryFile> #include <QTemporaryFile>
@@ -19,13 +20,17 @@
#include "settings.h" #include "settings.h"
#include "utilities.h" #include "utilities.h"
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
bool isAwg, QObject *parent) const QSharedPointer<ServerController> &serverController, bool isAwg,
QObject *parent)
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg) : ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
{ {
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath; m_serverConfigPath =
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath; m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath; m_serverPublicKeyPath =
m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
m_serverPskKeyPath =
m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template; m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard; m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
@@ -63,9 +68,31 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
return connData; return connData;
} }
QList<QHostAddress> WireguardConfigurator::getIpsFromConf(const QString &input)
{
QRegularExpression regex("AllowedIPs = (\\d+\\.\\d+\\.\\d+\\.\\d+)");
QRegularExpressionMatchIterator matchIterator = regex.globalMatch(input);
QList<QHostAddress> ips;
while (matchIterator.hasNext()) {
QRegularExpressionMatch match = matchIterator.next();
const QString address_string { match.captured(1) };
const QHostAddress address { address_string };
if (address.isNull()) {
qWarning() << "Couldn't recognize the ip address: " << address_string;
} else {
ips << address;
}
}
return ips;
}
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials, WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
DockerContainer container, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode) const QJsonObject &containerConfig,
ErrorCode &errorCode)
{ {
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys(); WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
connData.host = credentials.hostName; connData.host = credentials.hostName;
@@ -76,65 +103,45 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return connData; return connData;
} }
// Get list of already created clients (only IP addresses) QString getIpsScript = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
QString nextIpNumber;
{
QString script = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
QString stdOut; QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) { auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n"; stdOut += data + "\n";
return ErrorCode::NoError; return ErrorCode::NoError;
}; };
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut); errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return connData; return connData;
} }
auto ips = getIpsFromConf(stdOut);
stdOut.replace("AllowedIPs = ", ""); QHostAddress nextIp = [&] {
stdOut.replace("/32", ""); QHostAddress result;
QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts); QHostAddress lastIp;
if (ips.empty()) {
// remove extra IPs from each line for case when user manually edited the wg0.conf lastIp.setAddress(containerConfig.value(m_protocolName)
// and added there more IPs for route his itnernal networks, like: .toObject()
// ... .value(config_key::subnet_address)
// AllowedIPs = 10.8.1.6/32, 192.168.1.0/24, 192.168.2.0/24, ... .toString(protocols::wireguard::defaultSubnetAddress));
// ...
// without this code - next IP would be 1 if last item in 'ips' has format above
QStringList vpnIps;
for (const auto &ip : ips) {
vpnIps.append(ip.split(",", Qt::SkipEmptyParts).first().trimmed());
}
ips = vpnIps;
// Calc next IP address
if (ips.isEmpty()) {
nextIpNumber = "2";
} else { } else {
int next = ips.last().split(".").last().toInt() + 1; lastIp = ips.last();
if (next > 254) {
errorCode = ErrorCode::AddressPoolError;
return connData;
}
nextIpNumber = QString::number(next);
} }
quint8 lastOctet = static_cast<quint8>(lastIp.toIPv4Address());
switch (lastOctet) {
case 254: result.setAddress(lastIp.toIPv4Address() + 3); break;
case 255: result.setAddress(lastIp.toIPv4Address() + 2); break;
default: result.setAddress(lastIp.toIPv4Address() + 1); break;
} }
QString subnetIp = containerConfig.value(m_protocolName).toObject().value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress); return result;
{ }();
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
if (l.isEmpty()) {
errorCode = ErrorCode::AddressPoolError;
return connData;
}
l.removeLast();
l.append(nextIpNumber);
connData.clientIP = l.join("."); connData.clientIP = nextIp.toString();
}
// Get keys // Get keys
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode); connData.serverPubKey =
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
connData.serverPubKey.replace("\n", ""); connData.serverPubKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return connData; return connData;
@@ -161,10 +168,12 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return connData; return connData;
} }
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'").arg(m_serverConfigPath); QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'")
.arg(m_serverConfigPath);
errorCode = m_serverController->runScript( errorCode = m_serverController->runScript(
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container))); credentials,
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
return connData; return connData;
} }
@@ -173,8 +182,8 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
const QJsonObject &containerConfig, ErrorCode &errorCode) const QJsonObject &containerConfig, ErrorCode &errorCode)
{ {
QString scriptData = amnezia::scriptData(m_configTemplate, container); QString scriptData = amnezia::scriptData(m_configTemplate, container);
QString config = QString config = m_serverController->replaceVars(
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig)); scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode); ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
@@ -208,16 +217,16 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
return QJsonDocument(jConfig).toJson(); return QJsonDocument(jConfig).toJson();
} }
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
QString &protocolConfigString) const bool isApiConfig, QString &protocolConfigString)
{ {
processConfigWithDnsSettings(dns, protocolConfigString); processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString; return protocolConfigString;
} }
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
QString &protocolConfigString) const bool isApiConfig, QString &protocolConfigString)
{ {
processConfigWithDnsSettings(dns, protocolConfigString); processConfigWithDnsSettings(dns, protocolConfigString);
+10 -6
View File
@@ -1,6 +1,7 @@
#ifndef WIREGUARD_CONFIGURATOR_H #ifndef WIREGUARD_CONFIGURATOR_H
#define WIREGUARD_CONFIGURATOR_H #define WIREGUARD_CONFIGURATOR_H
#include <QHostAddress>
#include <QObject> #include <QObject>
#include <QProcessEnvironment> #include <QProcessEnvironment>
@@ -12,8 +13,8 @@ class WireguardConfigurator : public ConfiguratorBase
{ {
Q_OBJECT Q_OBJECT
public: public:
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg, WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
QObject *parent = nullptr); bool isAwg, QObject *parent = nullptr);
struct ConnectionData struct ConnectionData
{ {
@@ -26,15 +27,18 @@ public:
QString port; QString port;
}; };
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, QString createConfig(const ServerCredentials &credentials, DockerContainer container,
ErrorCode &errorCode); const QJsonObject &containerConfig, ErrorCode &errorCode);
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString); QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString); QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
static ConnectionData genClientKeys(); static ConnectionData genClientKeys();
private: private:
QList<QHostAddress> getIpsFromConf(const QString &input);
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container, ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode); const QJsonObject &containerConfig, ErrorCode &errorCode);
+26 -2
View File
@@ -1,5 +1,6 @@
#include "coreController.h" #include "coreController.h"
#include <QDirIterator>
#include <QTranslator> #include <QTranslator>
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
@@ -20,6 +21,9 @@ CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnectio
initControllers(); initControllers();
initSignalHandlers(); initSignalHandlers();
initAndroidController();
initAppleController();
initNotificationHandler(); initNotificationHandler();
auto locale = m_settings->getAppLanguage(); auto locale = m_settings->getAppLanguage();
@@ -90,6 +94,9 @@ void CoreController::initModels()
m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this)); m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this));
m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get()); m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get());
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
} }
void CoreController::initControllers() void CoreController::initControllers()
@@ -129,7 +136,8 @@ void CoreController::initControllers()
m_systemController.reset(new SystemController(m_settings)); m_systemController.reset(new SystemController(m_settings));
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get()); m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
m_apiSettingsController.reset(new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_settings)); m_apiSettingsController.reset(
new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_settings));
m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get()); m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get());
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings)); m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
@@ -231,7 +239,23 @@ void CoreController::updateTranslator(const QLocale &locale)
QCoreApplication::removeTranslator(m_translator.get()); QCoreApplication::removeTranslator(m_translator.get());
} }
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm"; 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)) { if (m_translator->load(strFileName)) {
if (QCoreApplication::installTranslator(m_translator.get())) { if (QCoreApplication::installTranslator(m_translator.get())) {
m_settings->setAppLanguage(locale); m_settings->setAppLanguage(locale);
+3 -1
View File
@@ -25,8 +25,9 @@
#include "ui/models/protocols/ikev2ConfigModel.h" #include "ui/models/protocols/ikev2ConfigModel.h"
#endif #endif
#include "ui/models/api/apiAccountInfoModel.h" #include "ui/models/api/apiAccountInfoModel.h"
#include "ui/models/api/apiServicesModel.h"
#include "ui/models/api/apiCountryModel.h" #include "ui/models/api/apiCountryModel.h"
#include "ui/models/api/apiDevicesModel.h"
#include "ui/models/api/apiServicesModel.h"
#include "ui/models/appSplitTunnelingModel.h" #include "ui/models/appSplitTunnelingModel.h"
#include "ui/models/clientManagementModel.h" #include "ui/models/clientManagementModel.h"
#include "ui/models/protocols/awgConfigModel.h" #include "ui/models/protocols/awgConfigModel.h"
@@ -117,6 +118,7 @@ private:
QSharedPointer<ApiServicesModel> m_apiServicesModel; QSharedPointer<ApiServicesModel> m_apiServicesModel;
QSharedPointer<ApiCountryModel> m_apiCountryModel; QSharedPointer<ApiCountryModel> m_apiCountryModel;
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel; QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel; QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel; QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
+29 -16
View File
@@ -26,6 +26,10 @@ namespace
constexpr char apiPayload[] = "api_payload"; constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload"; constexpr char keyPayload[] = "key_payload";
} }
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
constexpr QLatin1String errorResponsePattern3("Account not found.");
} }
GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent) GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent)
@@ -148,7 +152,7 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
QByteArray encryptedResponseBody = reply->readAll(); QByteArray encryptedResponseBody = reply->readAll();
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, false)) { if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) { auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
request.setUrl(url); request.setUrl(url);
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson()); return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
@@ -157,12 +161,12 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt, auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) { this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
encryptedResponseBody = nestedReply->readAll(); encryptedResponseBody = nestedReply->readAll();
if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
sslErrors = nestedSslErrors;
reply = nestedReply; reply = nestedReply;
return true; if (!sslErrors.isEmpty() || shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
} sslErrors = nestedSslErrors;
return false; return false;
}
return true;
}; };
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction); bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
@@ -194,16 +198,16 @@ QStringList GatewayController::getProxyUrls()
QList<QSslError> sslErrors; QList<QSslError> sslErrors;
QNetworkReply *reply; QNetworkReply *reply;
QStringList proxyStorageUrl; QStringList proxyStorageUrls;
if (m_isDevEnvironment) { if (m_isDevEnvironment) {
proxyStorageUrl = QStringList { DEV_S3_ENDPOINT }; proxyStorageUrls = QString(DEV_S3_ENDPOINT).split(", ");
} else { } else {
proxyStorageUrl = QStringList { PROD_S3_ENDPOINT }; proxyStorageUrls = QString(PROD_S3_ENDPOINT).split(", ");
} }
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY; QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
for (const auto &proxyStorageUrl : proxyStorageUrl) { for (const auto &proxyStorageUrl : proxyStorageUrls) {
request.setUrl(proxyStorageUrl); request.setUrl(proxyStorageUrl);
reply = amnApp->networkManager()->get(request); reply = amnApp->networkManager()->get(request);
@@ -212,11 +216,6 @@ QStringList GatewayController::getProxyUrls()
wait.exec(); wait.exec();
if (reply->error() == QNetworkReply::NetworkError::NoError) { if (reply->error() == QNetworkReply::NetworkError::NoError) {
break;
}
reply->deleteLater();
}
auto encryptedResponseBody = reply->readAll(); auto encryptedResponseBody = reply->readAll();
reply->deleteLater(); reply->deleteLater();
@@ -241,7 +240,7 @@ QStringList GatewayController::getProxyUrls()
} catch (...) { } catch (...) {
Utils::logException(); Utils::logException();
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody; qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
return {}; continue;
} }
auto endpointsArray = QJsonDocument::fromJson(responseBody).array(); auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
@@ -251,6 +250,11 @@ QStringList GatewayController::getProxyUrls()
endpoints.push_back(endpoint.toString()); endpoints.push_back(endpoint.toString());
} }
return endpoints; return endpoints;
} else {
reply->deleteLater();
}
}
return {};
} }
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key, bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
@@ -262,6 +266,15 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray
} else if (responseBody.contains("html")) { } else if (responseBody.contains("html")) {
qDebug() << "The response contains an html tag"; qDebug() << "The response contains an html tag";
return true; return true;
} else if (reply->error() == QNetworkReply::NetworkError::ContentNotFoundError) {
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|| responseBody.contains(errorResponsePattern3)) {
return false;
} else {
return true;
}
} else if (reply->error() != QNetworkReply::NetworkError::NoError) {
return true;
} else if (checkEncryption) { } else if (checkEncryption) {
try { try {
QSimpleCrypto::QBlockCipher blockCipher; QSimpleCrypto::QBlockCipher blockCipher;
@@ -296,7 +309,7 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; }); connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(); wait.exec();
if (!replyProcessingFunction(reply, sslErrors)) { if (replyProcessingFunction(reply, sslErrors)) {
break; break;
} }
} }
+10 -6
View File
@@ -709,7 +709,7 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto); QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
// TODO reimplement with netstat // TODO reimplement with netstat
QString script = QString("which lsof &>/dev/null || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port); QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
for (auto &port : fixedPorts) { for (auto &port : fixedPorts) {
script = script.append("|:%1").arg(port); script = script.append("|:%1").arg(port);
} }
@@ -757,10 +757,6 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container) ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
{ {
if (credentials.userName == "root") {
return ErrorCode::NoError;
}
QString stdOut; QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) { auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n"; stdOut += data + "\n";
@@ -774,8 +770,16 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo); const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr); ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
if (!stdOut.contains("sudo")) if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
return ErrorCode::ServerUserNotInSudo; return ErrorCode::ServerUserNotInSudo;
if (stdOut.contains("can't cd to") || stdOut.contains("Permission denied") || stdOut.contains("No such file or directory"))
return ErrorCode::ServerUserDirectoryNotAccessible;
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
return ErrorCode::ServerUserNotAllowedInSudoers;
if (stdOut.contains("password is required"))
return ErrorCode::ServerUserPasswordRequired;
return error; return error;
} }
+4
View File
@@ -54,6 +54,10 @@ namespace amnezia
ServerCancelInstallation = 204, ServerCancelInstallation = 204,
ServerUserNotInSudo = 205, ServerUserNotInSudo = 205,
ServerPacketManagerError = 206, ServerPacketManagerError = 206,
ServerSudoPackageIsNotPreinstalled = 207,
ServerUserDirectoryNotAccessible = 208,
ServerUserNotAllowedInSudoers = 209,
ServerUserPasswordRequired = 210,
// Ssh connection errors // Ssh connection errors
SshRequestDeniedError = 300, SshRequestDeniedError = 300,
+6 -2
View File
@@ -20,8 +20,12 @@ QString errorString(ErrorCode code) {
case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break; case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break;
case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break; case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break; case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break; case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user is not a member of the sudo group"); break;
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break; case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Package manager error"); break;
case(ErrorCode::ServerSudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed on the server"); break;
case(ErrorCode::ServerUserDirectoryNotAccessible): errorMessage = QObject::tr("The server user's home directory is not accessible"); break;
case(ErrorCode::ServerUserNotAllowedInSudoers): errorMessage = QObject::tr("Action not allowed in sudoers"); break;
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
// Libssh errors // Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break; case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
+6
View File
@@ -18,6 +18,12 @@ bool IpcClient::isSocketConnected() const
return m_isSocketConnected; return m_isSocketConnected;
} }
void IpcClient::close()
{
if (m_localSocket)
m_localSocket->close();
}
IpcClient *IpcClient::Instance() IpcClient *IpcClient::Instance()
{ {
return m_instance; return m_instance;
+1
View File
@@ -23,6 +23,7 @@ public:
static QSharedPointer<PrivilegedProcess> CreatePrivilegedProcess(); static QSharedPointer<PrivilegedProcess> CreatePrivilegedProcess();
bool isSocketConnected() const; bool isSocketConnected() const;
void close();
signals: signals:
+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 3H4C2.89543 3 2 3.89543 2 5V15C2 16.1046 2.89543 17 4 17H20C21.1046 17 22 16.1046 22 15V5C22 3.89543 21.1046 3 20 3Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 21H16" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 17V21" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 522 B

+12 -6
View File
@@ -14,10 +14,8 @@
#include <QJsonValue> #include <QJsonValue>
#include <QStandardPaths> #include <QStandardPaths>
#include "ipaddress.h"
#include "leakdetector.h" #include "leakdetector.h"
#include "logger.h" #include "logger.h"
#include "models/server.h"
#include "daemon/daemonerrors.h" #include "daemon/daemonerrors.h"
#include "protocols/protocols_defs.h" #include "protocols/protocols_defs.h"
@@ -115,7 +113,6 @@ void LocalSocketController::daemonConnected() {
} }
void LocalSocketController::activate(const QJsonObject &rawConfig) { void LocalSocketController::activate(const QJsonObject &rawConfig) {
QString protocolName = rawConfig.value("protocol").toString(); QString protocolName = rawConfig.value("protocol").toString();
int splitTunnelType = rawConfig.value("splitTunnelType").toInt(); int splitTunnelType = rawConfig.value("splitTunnelType").toInt();
@@ -131,13 +128,17 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
// json.insert("hopindex", QJsonValue((double)hop.m_hopindex)); // json.insert("hopindex", QJsonValue((double)hop.m_hopindex));
json.insert("privateKey", wgConfig.value(amnezia::config_key::client_priv_key)); json.insert("privateKey", wgConfig.value(amnezia::config_key::client_priv_key));
json.insert("deviceIpv4Address", wgConfig.value(amnezia::config_key::client_ip)); json.insert("deviceIpv4Address", wgConfig.value(amnezia::config_key::client_ip));
m_deviceIpv4 = wgConfig.value(amnezia::config_key::client_ip).toString();
// set up IPv6 unique-local-address, ULA, with "fd00::/8" prefix, not globally routable. // set up IPv6 unique-local-address, ULA, with "fd00::/8" prefix, not globally routable.
// this will be default IPv6 gateway, OS recognizes that IPv6 link is local and switches to IPv4. // this will be default IPv6 gateway, OS recognizes that IPv6 link
// is local and switches to IPv4.
// Otherwise some OSes (Linux) try IPv6 forever and hang. // Otherwise some OSes (Linux) try IPv6 forever and hang.
// https://en.wikipedia.org/wiki/Unique_local_address (RFC 4193) // https://en.wikipedia.org/wiki/Unique_local_address (RFC 4193)
// https://man7.org/linux/man-pages/man5/gai.conf.5.html // https://man7.org/linux/man-pages/man5/gai.conf.5.html
json.insert("deviceIpv6Address", "fd58:baa6:dead::1"); // simply "dead::1" is globally-routable, don't use it
// simply "dead::1" is globally-routable, don't use it
json.insert("deviceIpv6Address", "fd58:baa6:dead::1");
json.insert("serverPublicKey", wgConfig.value(amnezia::config_key::server_pub_key)); json.insert("serverPublicKey", wgConfig.value(amnezia::config_key::server_pub_key));
json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key)); json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key));
@@ -212,7 +213,6 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses); json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
QJsonArray jsExcludedAddresses; QJsonArray jsExcludedAddresses;
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName)); jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
if (splitTunnelType == 2) { if (splitTunnelType == 2) {
@@ -406,6 +406,7 @@ void LocalSocketController::parseCommand(const QByteArray& command) {
} }
if (type == "status") { if (type == "status") {
QJsonValue serverIpv4Gateway = obj.value("serverIpv4Gateway"); QJsonValue serverIpv4Gateway = obj.value("serverIpv4Gateway");
if (!serverIpv4Gateway.isString()) { if (!serverIpv4Gateway.isString()) {
logger.error() << "Unexpected serverIpv4Gateway value"; logger.error() << "Unexpected serverIpv4Gateway value";
@@ -450,6 +451,11 @@ void LocalSocketController::parseCommand(const QByteArray& command) {
logger.debug() << "Handshake completed with:" logger.debug() << "Handshake completed with:"
<< pubkey.toString(); << pubkey.toString();
checkStatus();
emit statusUpdated("", m_deviceIpv4, 0, 0);
emit connected(pubkey.toString()); emit connected(pubkey.toString());
return; return;
} }
+2
View File
@@ -12,6 +12,7 @@
#include "controllerimpl.h" #include "controllerimpl.h"
class QJsonObject; class QJsonObject;
class LocalSocketController final : public ControllerImpl { class LocalSocketController final : public ControllerImpl {
@@ -58,6 +59,7 @@ class LocalSocketController final : public ControllerImpl {
QByteArray m_buffer; QByteArray m_buffer;
QString m_deviceIpv4;
std::function<void(const QString&)> m_logCallback = nullptr; std::function<void(const QString&)> m_logCallback = nullptr;
QTimer m_initializingTimer; QTimer m_initializingTimer;
+11 -4
View File
@@ -11,7 +11,6 @@
#include "logger.h" #include "logger.h"
//#include "mozillavpn.h" //#include "mozillavpn.h"
#include "networkwatcherimpl.h" #include "networkwatcherimpl.h"
#include "platforms/dummy/dummynetworkwatcher.h"
//#include "settingsholder.h" //#include "settingsholder.h"
#ifdef MZ_WINDOWS #ifdef MZ_WINDOWS
@@ -51,7 +50,7 @@ NetworkWatcher::NetworkWatcher() { MZ_COUNT_CTOR(NetworkWatcher); }
NetworkWatcher::~NetworkWatcher() { MZ_COUNT_DTOR(NetworkWatcher); } NetworkWatcher::~NetworkWatcher() { MZ_COUNT_DTOR(NetworkWatcher); }
void NetworkWatcher::initialize() { void NetworkWatcher::initialize() {
logger.debug() << "Initialize"; logger.debug() << "Initialize NetworkWatcher";
#if defined(MZ_WINDOWS) #if defined(MZ_WINDOWS)
m_impl = new WindowsNetworkWatcher(this); m_impl = new WindowsNetworkWatcher(this);
@@ -69,14 +68,17 @@ void NetworkWatcher::initialize() {
m_impl = new DummyNetworkWatcher(this); m_impl = new DummyNetworkWatcher(this);
#endif #endif
connect(m_impl, &NetworkWatcherImpl::unsecuredNetwork, this, connect(m_impl, &NetworkWatcherImpl::unsecuredNetwork, this,
&NetworkWatcher::unsecuredNetwork); &NetworkWatcher::unsecuredNetwork);
connect(m_impl, &NetworkWatcherImpl::networkChanged, this, connect(m_impl, &NetworkWatcherImpl::networkChanged, this,
&NetworkWatcher::networkChange); &NetworkWatcher::networkChange);
connect(m_impl, &NetworkWatcherImpl::sleepMode, this,
&NetworkWatcher::onSleepMode);
m_impl->initialize(); m_impl->initialize();
// TODO: IMPL FOR AMNEZIA // TODO: IMPL FOR AMNEZIA
#if 0 #if 0
SettingsHolder* settingsHolder = SettingsHolder::instance(); SettingsHolder* settingsHolder = SettingsHolder::instance();
@@ -117,11 +119,16 @@ void NetworkWatcher::settingsChanged() {
#endif #endif
} }
void NetworkWatcher::onSleepMode()
{
logger.debug() << "Resumed from sleep mode";
emit sleepMode();
}
void NetworkWatcher::unsecuredNetwork(const QString& networkName, void NetworkWatcher::unsecuredNetwork(const QString& networkName,
const QString& networkId) { const QString& networkId) {
logger.debug() << "Unsecured network:" << logger.sensitive(networkName) logger.debug() << "Unsecured network:" << logger.sensitive(networkName)
<< "id:" << logger.sensitive(networkId); << "id:" << logger.sensitive(networkId);
#ifndef UNIT_TEST #ifndef UNIT_TEST
if (!m_reportUnsecuredNetwork) { if (!m_reportUnsecuredNetwork) {
logger.debug() << "Disabled. Ignoring unsecured network"; logger.debug() << "Disabled. Ignoring unsecured network";
+3
View File
@@ -29,10 +29,13 @@ public:
// false to restore. // false to restore.
void simulateDisconnection(bool simulatedDisconnection); void simulateDisconnection(bool simulatedDisconnection);
void onSleepMode();
QNetworkInformation::Reachability getReachability(); QNetworkInformation::Reachability getReachability();
signals: signals:
void networkChange(); void networkChange();
void sleepMode();
private: private:
void settingsChanged(); void settingsChanged();
+2
View File
@@ -41,6 +41,8 @@ signals:
// TODO: Only windows-networkwatcher has this, the other plattforms should // TODO: Only windows-networkwatcher has this, the other plattforms should
// too. // too.
void networkChanged(QString newBSSID); void networkChanged(QString newBSSID);
void sleepMode();
private: private:
bool m_active = false; bool m_active = false;
+5 -2
View File
@@ -41,6 +41,7 @@ void PingHelper::start(const QString& serverIpv4Gateway,
m_gateway = QHostAddress(serverIpv4Gateway); m_gateway = QHostAddress(serverIpv4Gateway);
m_source = QHostAddress(deviceIpv4Address.section('/', 0, 0)); m_source = QHostAddress(deviceIpv4Address.section('/', 0, 0));
m_pingSender = PingSenderFactory::create(m_source, this); m_pingSender = PingSenderFactory::create(m_source, this);
// Some platforms require root access to send and receive ICMP pings. If // Some platforms require root access to send and receive ICMP pings. If
@@ -53,8 +54,10 @@ void PingHelper::start(const QString& serverIpv4Gateway,
connect(m_pingSender, &PingSender::recvPing, this, &PingHelper::pingReceived, connect(m_pingSender, &PingSender::recvPing, this, &PingHelper::pingReceived,
Qt::QueuedConnection); Qt::QueuedConnection);
connect(m_pingSender, &PingSender::criticalPingError, this, connect(m_pingSender, &PingSender::criticalPingError, this, [this]() {
[]() { logger.info() << "Encountered Unrecoverable ping error"; }); logger.info() << "Encountered Unrecoverable ping error";
emit connectionLose();
});
// Reset the ping statistics // Reset the ping statistics
m_sequence = 0; m_sequence = 0;
+2
View File
@@ -33,6 +33,8 @@ class PingHelper final : public QObject {
signals: signals:
void pingSentAndReceived(qint64 msec); void pingSentAndReceived(qint64 msec);
void connectionLose();
private: private:
void nextPing(); void nextPing();
+7 -8
View File
@@ -5,22 +5,21 @@
#include "pingsenderfactory.h" #include "pingsenderfactory.h"
#if defined(MZ_LINUX) || defined(MZ_ANDROID) #if defined(MZ_LINUX) || defined(MZ_ANDROID)
//# include "platforms/linux/linuxpingsender.h" # include "platforms/linux/linuxpingsender.h"
#elif defined(MZ_MACOS) || defined(MZ_IOS) #elif defined(MZ_MACOS) || defined(MZ_IOS)
# include "platforms/macos/macospingsender.h" # include "platforms/macos/macospingsender.h"
#elif defined(MZ_WINDOWS) #elif defined(MZ_WINDOWS)
# include "platforms/windows/windowspingsender.h" # include "platforms/windows/windowspingsender.h"
#elif defined(MZ_DUMMY) || defined(UNIT_TEST) #elif defined(MZ_WASM) || defined(UNIT_TEST)
# include "platforms/dummy/dummypingsender.h" # include "platforms/dummy/dummypingsender.h"
#else #else
# error "Unsupported platform" # error "Unsupported platform"
#endif #endif
PingSender* PingSenderFactory::create(const QHostAddress& source, PingSender* PingSenderFactory::create(const QHostAddress& source,
QObject* parent) { QObject* parent) {
#if defined(MZ_LINUX) || defined(MZ_ANDROID) #if defined(MZ_LINUX) || defined(MZ_ANDROID)
return nullptr; return new LinuxPingSender(source, parent);
// return new LinuxPingSender(source, parent);
#elif defined(MZ_MACOS) || defined(MZ_IOS) #elif defined(MZ_MACOS) || defined(MZ_IOS)
return new MacOSPingSender(source, parent); return new MacOSPingSender(source, parent);
#elif defined(MZ_WINDOWS) #elif defined(MZ_WINDOWS)
+2 -1
View File
@@ -10,9 +10,10 @@ class QHostAddress;
class QObject; class QObject;
class PingSenderFactory final { class PingSenderFactory final {
public: public:
PingSenderFactory() = delete; PingSenderFactory() = delete;
static PingSender* create(const QHostAddress& source, QObject* parent); static PingSender* create(const QHostAddress& source, QObject* parent);
}; };
#endif // PINGSENDERFACTORY_H #endif // PINGSENDERFACTORY_H
+7 -2
View File
@@ -14,10 +14,15 @@ extension UIApplication {
var keyWindows: [UIWindow] { var keyWindows: [UIWindow] {
connectedScenes connectedScenes
.compactMap { .compactMap {
guard let windowScene = $0 as? UIWindowScene else { return nil }
if #available(iOS 15.0, *) { if #available(iOS 15.0, *) {
($0 as? UIWindowScene)?.keyWindow guard let keywindow = windowScene.keyWindow else {
windowScene.windows.first?.makeKey()
return windowScene.windows.first
}
return keywindow
} else { } else {
($0 as? UIWindowScene)?.windows.first { $0.isKeyWindow } return windowScene.windows.first { $0.isKeyWindow }
} }
} }
} }
@@ -41,6 +41,9 @@ void LinuxNetworkWatcher::initialize() {
connect(m_worker, &LinuxNetworkWatcherWorker::unsecuredNetwork, this, connect(m_worker, &LinuxNetworkWatcherWorker::unsecuredNetwork, this,
&LinuxNetworkWatcher::unsecuredNetwork); &LinuxNetworkWatcher::unsecuredNetwork);
connect(m_worker, &LinuxNetworkWatcherWorker::sleepMode, this,
&NetworkWatcherImpl::sleepMode);
// Let's wait a few seconds to allow the UI to be fully loaded and shown. // Let's wait a few seconds to allow the UI to be fully loaded and shown.
// This is not strictly needed, but it's better for user experience because // This is not strictly needed, but it's better for user experience because
// it makes the UI faster to appear, plus it gives a bit of delay between the // it makes the UI faster to appear, plus it gives a bit of delay between the
@@ -33,7 +33,21 @@
#define NM_802_11_AP_SEC_WEAK_CRYPTO \ #define NM_802_11_AP_SEC_WEAK_CRYPTO \
(NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104) (NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104)
enum NMState {
NM_STATE_UNKNOWN = 0,
NM_STATE_ASLEEP = 10,
NM_STATE_DISCONNECTED = 20,
NM_STATE_DISCONNECTING = 30,
NM_STATE_CONNECTING = 40,
NM_STATE_CONNECTED_LOCAL = 50,
NM_STATE_CONNECTED_SITE = 60,
NM_STATE_CONNECTED_GLOBAL = 70
};
constexpr const char* DBUS_NETWORKMANAGER = "org.freedesktop.NetworkManager"; constexpr const char* DBUS_NETWORKMANAGER = "org.freedesktop.NetworkManager";
constexpr const char* DBUS_NETWORKMANAGER_PATH = "/org/freedesktop/NetworkManager";
namespace { namespace {
Logger logger("LinuxNetworkWatcherWorker"); Logger logger("LinuxNetworkWatcherWorker");
@@ -73,7 +87,7 @@ void LinuxNetworkWatcherWorker::initialize() {
// documentation: // documentation:
// https://developer.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.html // https://developer.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.html
QDBusInterface nm(DBUS_NETWORKMANAGER, "/org/freedesktop/NetworkManager", QDBusInterface nm(DBUS_NETWORKMANAGER, DBUS_NETWORKMANAGER_PATH,
DBUS_NETWORKMANAGER, QDBusConnection::systemBus()); DBUS_NETWORKMANAGER, QDBusConnection::systemBus());
if (!nm.isValid()) { if (!nm.isValid()) {
logger.error() logger.error()
@@ -108,6 +122,12 @@ void LinuxNetworkWatcherWorker::initialize() {
SLOT(propertyChanged(QString, QVariantMap, QStringList))); SLOT(propertyChanged(QString, QVariantMap, QStringList)));
} }
QDBusConnection::systemBus().connect(DBUS_NETWORKMANAGER,
DBUS_NETWORKMANAGER_PATH,
DBUS_NETWORKMANAGER,
"StateChanged",
this, SLOT(NMStateChanged(quint32)));
if (m_devicePaths.isEmpty()) { if (m_devicePaths.isEmpty()) {
logger.warning() << "No wifi devices found"; logger.warning() << "No wifi devices found";
return; return;
@@ -173,5 +193,16 @@ void LinuxNetworkWatcherWorker::checkDevices() {
emit unsecuredNetwork(ssid, bssid); emit unsecuredNetwork(ssid, bssid);
break; break;
} }
} }
} }
void LinuxNetworkWatcherWorker::NMStateChanged(quint32 state)
{
if (state == NM_STATE_ASLEEP) {
emit sleepMode();
}
logger.debug() << "NMStateChanged " << state;
}
@@ -23,6 +23,7 @@ class LinuxNetworkWatcherWorker final : public QObject {
signals: signals:
void unsecuredNetwork(const QString& networkName, const QString& networkId); void unsecuredNetwork(const QString& networkName, const QString& networkId);
void sleepMode();
public slots: public slots:
void initialize(); void initialize();
@@ -30,6 +31,7 @@ class LinuxNetworkWatcherWorker final : public QObject {
private slots: private slots:
void propertyChanged(QString interface, QVariantMap properties, void propertyChanged(QString interface, QVariantMap properties,
QStringList list); QStringList list);
void NMStateChanged(quint32 state);
private: private:
// We collect the list of DBus wifi network device paths during the // We collect the list of DBus wifi network device paths during the
+185
View File
@@ -0,0 +1,185 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "linuxpingsender.h"
#include <arpa/inet.h>
#include <errno.h>
#include <linux/filter.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <sys/socket.h>
#include <unistd.h>
#include <QSocketNotifier>
#include <QtEndian>
#include "leakdetector.h"
#include "logger.h"
#include "qhostaddress.h"
namespace {
Logger logger("LinuxPingSender");
}
int LinuxPingSender::createSocket() {
// Try creating an ICMP socket. This would be the ideal choice, but it can
// fail depending on the kernel config (see: sys.net.ipv4.ping_group_range)
m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
if (m_socket >= 0) {
m_ident = 0;
return m_socket;
}
if ((errno != EPERM) && (errno != EACCES)) {
return -1;
}
// As a fallback, create a raw socket, which requires root permissions
// or CAP_NET_RAW to be granted to the VPN client.
m_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (m_socket < 0) {
return -1;
}
m_ident = getpid() & 0xffff;
// Attach a BPF filter to discard everything but replies to our echo.
struct sock_filter bpf_prog[] = {
BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0), /* Skip IP header. */
BPF_STMT(BPF_LD | BPF_H | BPF_IND, 4), /* Load icmp echo ident */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, m_ident, 1, 0), /* Ours? */
BPF_STMT(BPF_RET | BPF_K, 0), /* Unexpected identifier. Reject. */
BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), /* Load icmp type */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */
BPF_STMT(BPF_RET | BPF_K, 0), /* Unexpected type. Reject. */
BPF_STMT(BPF_RET | BPF_K, ~0U), /* Packet passes the filter. */
};
struct sock_fprog filter = {
.len = sizeof(bpf_prog) / sizeof(struct sock_filter),
.filter = bpf_prog,
};
setsockopt(m_socket, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
return m_socket;
}
LinuxPingSender::LinuxPingSender(const QHostAddress& source, QObject* parent)
: PingSender(parent) {
MZ_COUNT_CTOR(LinuxPingSender);
logger.debug() << "LinuxPingSender(" + logger.sensitive(source.toString()) +
") created";
m_socket = createSocket();
if (m_socket < 0) {
logger.error() << "Socket creation error: " << strerror(errno);
return;
}
quint32 ipv4addr = INADDR_ANY;
if (!source.isNull()) {
ipv4addr = source.toIPv4Address();
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = qToBigEndian<quint32>(ipv4addr);
if (bind(m_socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
close(m_socket);
m_socket = -1;
logger.error() << "bind error:" << strerror(errno);
return;
}
m_notifier = new QSocketNotifier(m_socket, QSocketNotifier::Read, this);
if (m_ident) {
connect(m_notifier, &QSocketNotifier::activated, this,
&LinuxPingSender::rawSocketReady);
} else {
connect(m_notifier, &QSocketNotifier::activated, this,
&LinuxPingSender::icmpSocketReady);
}
}
LinuxPingSender::~LinuxPingSender() {
MZ_COUNT_DTOR(LinuxPingSender);
if (m_socket >= 0) {
close(m_socket);
}
}
void LinuxPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
quint32 ipv4dest = dest.toIPv4Address();
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = qToBigEndian<quint32>(ipv4dest);
struct icmphdr packet;
memset(&packet, 0, sizeof(packet));
packet.type = ICMP_ECHO;
packet.un.echo.id = htons(m_ident);
packet.un.echo.sequence = htons(sequence);
packet.checksum = inetChecksum(&packet, sizeof(packet));
int rc = sendto(m_socket, &packet, sizeof(packet), 0, (struct sockaddr*)&addr,
sizeof(addr));
if (rc < 0) {
logger.error() << "failed to send:" << strerror(errno);
if (errno == ENETUNREACH) {
emit criticalPingError();
}
}
}
void LinuxPingSender::icmpSocketReady() {
socklen_t slen = 0;
unsigned char data[2048];
int rc = recvfrom(m_socket, data, sizeof(data), MSG_DONTWAIT, NULL, &slen);
if (rc <= 0) {
logger.error() << "recvfrom failed:" << strerror(errno);
return;
}
struct icmphdr packet;
if (rc >= (int)sizeof(packet)) {
memcpy(&packet, data, sizeof(packet));
if (packet.type == ICMP_ECHOREPLY) {
emit recvPing(htons(packet.un.echo.sequence));
}
}
}
void LinuxPingSender::rawSocketReady() {
socklen_t slen = 0;
unsigned char data[2048];
int rc = recvfrom(m_socket, data, sizeof(data), MSG_DONTWAIT, NULL, &slen);
if (rc <= 0) {
logger.error() << "recvfrom failed:" << strerror(errno);
return;
}
// Check the IP header
const struct iphdr* ip = (struct iphdr*)data;
int iphdrlen = ip->ihl * 4;
if (rc < iphdrlen || iphdrlen < (int)sizeof(struct iphdr)) {
logger.error() << "malformed IP packet:" << strerror(errno);
return;
}
// Check the ICMP packet
struct icmphdr packet;
if (inetChecksum(data + iphdrlen, rc - iphdrlen) != 0) {
logger.warning() << "invalid checksum";
return;
}
if (rc >= (iphdrlen + (int)sizeof(packet))) {
memcpy(&packet, data + iphdrlen, sizeof(packet));
quint16 id = htons(m_ident);
if ((packet.type == ICMP_ECHOREPLY) && (packet.un.echo.id == id)) {
emit recvPing(htons(packet.un.echo.sequence));
}
}
}
+39
View File
@@ -0,0 +1,39 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef LINUXPINGSENDER_H
#define LINUXPINGSENDER_H
#include <QObject>
#include "../client/mozilla/pingsender.h"
class QSocketNotifier;
class LinuxPingSender final : public PingSender {
Q_OBJECT
Q_DISABLE_COPY_MOVE(LinuxPingSender)
public:
LinuxPingSender(const QHostAddress& source, QObject* parent = nullptr);
~LinuxPingSender();
bool isValid() override { return (m_socket >= 0); };
void sendPing(const QHostAddress& dest, quint16 sequence) override;
private:
int createSocket();
private slots:
void rawSocketReady();
void icmpSocketReady();
private:
QSocketNotifier* m_notifier = nullptr;
int m_socket = -1;
quint16 m_ident = 0;
};
#endif // LINUXPINGSENDER_H
@@ -10,8 +10,28 @@
#include "../ios/iosnetworkwatcher.h" #include "../ios/iosnetworkwatcher.h"
#include "networkwatcherimpl.h" #include "networkwatcherimpl.h"
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/IOMessage.h>
class QString; class QString;
// Inspired by https://ladydebug.com/blog/2020/05/21/programmatically-capture-energy-saver-event-on-mac/
class PowerNotificationsListener
{
public:
void registerForNotifications();
private:
static void sleepWakeupCallBack(void *refParam, io_service_t service, natural_t messageType, void *messageArgument);
private:
IONotificationPortRef notifyPortRef = nullptr; // notification port allocated by IORegisterForSystemPower
io_object_t notifierObj = IO_OBJECT_NULL; // notifier object, used to deregister later
io_connect_t rootPowerDomain = IO_OBJECT_NULL; // a reference to the Root Power Domain IOService
};
class MacOSNetworkWatcher final : public IOSNetworkWatcher { class MacOSNetworkWatcher final : public IOSNetworkWatcher {
public: public:
MacOSNetworkWatcher(QObject* parent); MacOSNetworkWatcher(QObject* parent);
@@ -25,6 +45,7 @@ class MacOSNetworkWatcher final : public IOSNetworkWatcher {
private: private:
void* m_delegate = nullptr; void* m_delegate = nullptr;
PowerNotificationsListener m_powerlistener;
}; };
#endif // MACOSNETWORKWATCHER_H #endif // MACOSNETWORKWATCHER_H
@@ -38,6 +38,93 @@ Logger logger("MacOSNetworkWatcher");
@end @end
void PowerNotificationsListener::registerForNotifications()
{
rootPowerDomain = IORegisterForSystemPower(this, &notifyPortRef, sleepWakeupCallBack, &notifierObj);
if (rootPowerDomain == IO_OBJECT_NULL) {
logger.debug() << "Failed to register for system power notifications!";
return;
}
logger.debug() << "IORegisterForSystemPower OK! Root port:" << rootPowerDomain;
// add the notification port to the application runloop
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes);
}
static void PowerNotificationsListener::sleepWakeupCallBack(void *refParam, io_service_t service, natural_t messageType, void *messageArgument)
{
Q_UNUSED(service)
auto listener = static_cast<PowerNotificationsListener *>(refParam);
switch (messageType) {
case kIOMessageCanSystemSleep:
/* Idle sleep is about to kick in. This message will not be sent for forced sleep.
* Applications have a chance to prevent sleep by calling IOCancelPowerChange.
* Most applications should not prevent idle sleep. Power Management waits up to
* 30 seconds for you to either allow or deny idle sleep. If you dont acknowledge
* this power change by calling either IOAllowPowerChange or IOCancelPowerChange,
* the system will wait 30 seconds then go to sleep.
*/
logger.debug() << "System power message: can system sleep?";
// Uncomment to cancel idle sleep
// IOCancelPowerChange(thiz->rootPowerDomain, reinterpret_cast<long>(messageArgument));
// Allow idle sleep
IOAllowPowerChange(listener->rootPowerDomain, reinterpret_cast<long>(messageArgument));
break;
case kIOMessageSystemWillNotSleep:
/* Announces that the system has retracted a previous attempt to sleep; it
* follows `kIOMessageCanSystemSleep`.
*/
logger.debug() << "System power message: system will NOT sleep.";
break;
case kIOMessageSystemWillSleep:
/* The system WILL go to sleep. If you do not call IOAllowPowerChange or
* IOCancelPowerChange to acknowledge this message, sleep will be delayed by
* 30 seconds.
*
* NOTE: If you call IOCancelPowerChange to deny sleep it returns kIOReturnSuccess,
* however the system WILL still go to sleep.
*/
logger.debug() << "System power message: system WILL sleep.";
IOAllowPowerChange(listener->rootPowerDomain, reinterpret_cast<long>(messageArgument));
break;
case kIOMessageSystemWillPowerOn:
/* Announces that the system is beginning to power the device tree; most devices
* are still unavailable at this point.
*/
/* From the documentation:
*
* - kIOMessageSystemWillPowerOn is delivered at early wakeup time, before most hardware
* has been powered on. Be aware that any attempts to access disk, network, the display,
* etc. may result in errors or blocking your process until those resources become
* available.
*
* So we do NOT log this event.
*/
break;
case kIOMessageSystemHasPoweredOn:
/* Announces that the system and its devices have woken up. */
logger.debug() << "System power message: system has powered on.";
break;
default:
logger.debug() << "System power message: other event: " << messageType;
/* Not a system sleep and wake notification. */
break;
}
}
MacOSNetworkWatcher::MacOSNetworkWatcher(QObject* parent) : IOSNetworkWatcher(parent) { MacOSNetworkWatcher::MacOSNetworkWatcher(QObject* parent) : IOSNetworkWatcher(parent) {
MZ_COUNT_CTOR(MacOSNetworkWatcher); MZ_COUNT_CTOR(MacOSNetworkWatcher);
} }
@@ -67,6 +154,8 @@ void MacOSNetworkWatcher::start() {
return; return;
} }
m_powerlistener.registerForNotifications();
CWWiFiClient* client = CWWiFiClient.sharedWiFiClient; CWWiFiClient* client = CWWiFiClient.sharedWiFiClient;
if (!client) { if (!client) {
logger.error() << "Unable to retrieve the CWWiFiClient shared instance"; logger.error() << "Unable to retrieve the CWWiFiClient shared instance";
@@ -22,7 +22,6 @@
#include "logger.h" #include "logger.h"
#include "platforms/windows/daemon/windowsfirewall.h" #include "platforms/windows/daemon/windowsfirewall.h"
#include "platforms/windows/daemon/windowssplittunnel.h" #include "platforms/windows/daemon/windowssplittunnel.h"
#include "platforms/windows/windowscommons.h"
#include "windowsfirewall.h" #include "windowsfirewall.h"
#include "core/networkUtilities.h" #include "core/networkUtilities.h"
@@ -32,9 +32,28 @@ WindowsNetworkWatcher::~WindowsNetworkWatcher() {
} }
} }
LRESULT WindowsNetworkWatcher::PowerWndProcCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
auto obj = reinterpret_cast<WindowsNetworkWatcher*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (!obj){
logger.debug() << "obj not casted";
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
switch (uMsg) {
case WM_POWERBROADCAST:
if (wParam == PBT_APMRESUMESUSPEND) {
emit obj->sleepMode();
}
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
void WindowsNetworkWatcher::initialize() { void WindowsNetworkWatcher::initialize() {
logger.debug() << "initialize"; logger.debug() << "initialize";
DWORD negotiatedVersion; DWORD negotiatedVersion;
if (WlanOpenHandle(2, nullptr, &negotiatedVersion, &m_wlanHandle) != if (WlanOpenHandle(2, nullptr, &negotiatedVersion, &m_wlanHandle) !=
ERROR_SUCCESS) { ERROR_SUCCESS) {
@@ -51,6 +70,25 @@ void WindowsNetworkWatcher::initialize() {
return; return;
} }
const wchar_t* className = L"PowerMonitorClass";
WNDCLASS wc = { 0 };
wc.lpfnWndProc = &WindowsNetworkWatcher::PowerWndProcCallback;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = className;
wc.cbWndExtra = sizeof(WindowsNetworkWatcher*);
if (!RegisterClass(&wc)) {
logger.debug() << "Failed to register window class in createPowerMonitorWindow.";
return;
}
HWND hwnd = CreateWindowEx(0, className, L"Power Monitor", 0, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), static_cast<LPVOID>(this));
if (!hwnd) {
logger.debug() << "Failed to create window in createPowerMonitorWindow.";
return;
}
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
logger.debug() << "callback registered"; logger.debug() << "callback registered";
} }
@@ -19,6 +19,7 @@ class WindowsNetworkWatcher final : public NetworkWatcherImpl {
private: private:
static void wlanCallback(PWLAN_NOTIFICATION_DATA data, PVOID context); static void wlanCallback(PWLAN_NOTIFICATION_DATA data, PVOID context);
static LRESULT PowerWndProcCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void processWlan(PWLAN_NOTIFICATION_DATA data); void processWlan(PWLAN_NOTIFICATION_DATA data);
@@ -179,6 +179,7 @@ void WindowsPingSender::pingEventReady() {
return; return;
} }
QString errmsg = WindowsUtils::getErrorMessage(); QString errmsg = WindowsUtils::getErrorMessage();
emit criticalPingError();
logger.error() << "No ping reply. Code: " << error logger.error() << "No ping reply. Code: " << error
<< " Message: " << errmsg; << " Message: " << errmsg;
return; return;
+5
View File
@@ -103,6 +103,11 @@ QString VpnProtocol::vpnGateway() const
return m_vpnGateway; return m_vpnGateway;
} }
QString VpnProtocol::vpnLocalAddress() const
{
return m_vpnLocalAddress;
}
VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &configuration) VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &configuration)
{ {
switch (container) { switch (container) {
+1
View File
@@ -63,6 +63,7 @@ public:
QString routeGateway() const; QString routeGateway() const;
QString vpnGateway() const; QString vpnGateway() const;
QString vpnLocalAddress() const;
static VpnProtocol* factory(amnezia::DockerContainer container, const QJsonObject &configuration); static VpnProtocol* factory(amnezia::DockerContainer container, const QJsonObject &configuration);
+7
View File
@@ -17,6 +17,13 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
[this](const QString &pubkey, const QDateTime &connectionTimestamp) { [this](const QString &pubkey, const QDateTime &connectionTimestamp) {
emit connectionStateChanged(Vpn::ConnectionState::Connected); emit connectionStateChanged(Vpn::ConnectionState::Connected);
}); });
connect(m_impl.get(), &ControllerImpl::statusUpdated, this,
[this](const QString& serverIpv4Gateway,
const QString& deviceIpv4Address, uint64_t txBytes,
uint64_t rxBytes) {
m_vpnLocalAddress = deviceIpv4Address;
});
connect(m_impl.get(), &ControllerImpl::disconnected, this, connect(m_impl.get(), &ControllerImpl::disconnected, this,
[this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); }); [this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); });
m_impl->initialize(nullptr, nullptr); m_impl->initialize(nullptr, nullptr);
+2
View File
@@ -229,6 +229,8 @@
<file>ui/qml/Pages2/PageSettingsApiSupport.qml</file> <file>ui/qml/Pages2/PageSettingsApiSupport.qml</file>
<file>ui/qml/Pages2/PageSettingsApiInstructions.qml</file> <file>ui/qml/Pages2/PageSettingsApiInstructions.qml</file>
<file>ui/qml/Pages2/PageSettingsApiNativeConfigs.qml</file> <file>ui/qml/Pages2/PageSettingsApiNativeConfigs.qml</file>
<file>ui/qml/Pages2/PageSettingsApiDevices.qml</file>
<file>images/controls/monitor.svg</file>
</qresource> </qresource>
<qresource prefix="/countriesFlags"> <qresource prefix="/countriesFlags">
<file>images/flagKit/ZW.svg</file> <file>images/flagKit/ZW.svg</file>
+13 -2
View File
@@ -1,2 +1,13 @@
CUR_USER=$(whoami);\ if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\
groups $CUR_USER elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
else pm="uname"; opt="-a";\
fi;\
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\
echo $LANG | grep -qE '^(en_US.UTF-8|C.UTF-8|C)$' || export LC_ALL=C;\
sudo -K;\
cd ~;\
if [ "$CUR_USER" = "root" ] || ( groups "$CUR_USER" | grep -E '\<(sudo|wheel)\>' ); then \
sudo -nu $CUR_USER $pm $opt > /dev/null; sudo -n $pm $opt > /dev/null;\
fi
+1 -1
View File
@@ -1,4 +1,4 @@
CUR_USER=$(whoami);\ CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\
sudo mkdir -p $DOCKERFILE_FOLDER;\ sudo mkdir -p $DOCKERFILE_FOLDER;\
sudo chown $CUR_USER $DOCKERFILE_FOLDER;\ sudo chown $CUR_USER $DOCKERFILE_FOLDER;\
if ! sudo docker network ls | grep -q amnezia-dns-net; then sudo docker network create \ if ! sudo docker network ls | grep -q amnezia-dns-net; then sudo docker network create \
+3 -3
View File
@@ -3334,8 +3334,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source> <source>The user is not a member of the sudo group</source>
<translation>ليس لدي المستخدم الصلحيات لأستخدام sudo</translation> <translation>المستخدم ليس عضوًا في مجموعة sudo</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
@@ -3399,7 +3399,7 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source> <source>Server error: Package manager error</source>
<translation>خطأ في الخادم: خطأ في مدير الحزم</translation> <translation>خطأ في الخادم: خطأ في مدير الحزم</translation>
</message> </message>
<message> <message>
+4 -4
View File
@@ -3468,8 +3468,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source> <source>The user is not a member of the sudo group</source>
<translation>The user does not have permission to use sudo</translation> <translation>کاربر عضو گروه sudo نیست</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
@@ -3590,8 +3590,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source> <source>Server error: Package manager error</source>
<translation>Server error: Packet manager error</translation> <translation>خطای سرور: خطای مدیر بسته</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="34"/> <location filename="../core/errorstrings.cpp" line="34"/>
+4 -4
View File
@@ -3434,13 +3434,13 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source> <source>The user is not a member of the sudo group</source>
<translation> sudo ि </translation> <translation> sudo </translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source> <source>Server error: Package manager error</source>
<translation> ि: ि</translation> <translation> ि: ि</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
+4 -4
View File
@@ -3330,8 +3330,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source> <source>The user is not a member of the sudo group</source>
<translation> sudo ကက</translation> <translation> sudo </translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
@@ -3395,8 +3395,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source> <source>Server error: Package manager error</source>
<translation> မှု: Packet Manager </translation> <translation> - Package manager </translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="34"/> <location filename="../core/errorstrings.cpp" line="34"/>
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -3700,13 +3700,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source> <source>The user is not a member of the sudo group</source>
<translation>The user does not have permission to use sudo</translation> <translation>Користувач не входить до групи sudo</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source> <source>Server error: Package manager error</source>
<translation type="unfinished"></translation> <translation>Помилка сервера: Помилка менеджера пакетів</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
+3 -3
View File
@@ -3433,8 +3433,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source> <source>The user is not a member of the sudo group</source>
<translation>صارف کو sudo استعمال کرنے کی اجازت نہیں ہے</translation> <translation>صارف sudo گروپ کا رکن نہیں ہے</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
@@ -3498,7 +3498,7 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source> <source>Server error: Package manager error</source>
<translation>سرور خطا: پیکیج منیجر خطا</translation> <translation>سرور خطا: پیکیج منیجر خطا</translation>
</message> </message>
<message> <message>
+4 -4
View File
@@ -3675,13 +3675,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source> <source>The user is not a member of the sudo group</source>
<translation>root权限</translation> <translation> sudo </translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source> <source>Server error: Package manager error</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
@@ -1,7 +1,7 @@
#include "apiConfigsController.h" #include "apiConfigsController.h"
#include <QEventLoop>
#include <QClipboard> #include <QClipboard>
#include <QEventLoop>
#include "amnezia_application.h" #include "amnezia_application.h"
#include "configurators/wireguard_configurator.h" #include "configurators/wireguard_configurator.h"
@@ -19,7 +19,7 @@ namespace
constexpr char cloak[] = "cloak"; constexpr char cloak[] = "cloak";
constexpr char awg[] = "awg"; constexpr char awg[] = "awg";
constexpr char apiEdnpoint[] = "api_endpoint"; constexpr char apiEndpoint[] = "api_endpoint";
constexpr char accessToken[] = "api_key"; constexpr char accessToken[] = "api_key";
constexpr char certificate[] = "certificate"; constexpr char certificate[] = "certificate";
constexpr char publicKey[] = "public_key"; constexpr char publicKey[] = "public_key";
@@ -269,54 +269,37 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
{ {
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto installationUuid = m_settings->getInstallationUuid(true);
#ifdef Q_OS_IOS #ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess(); IosController::Instance()->requestInetAccess();
QThread::msleep(10); QThread::msleep(10);
#endif #endif
if (serverConfig.value(config_key::configVersion).toInt()) { GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
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(); auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto installationUuid = m_settings->getInstallationUuid(true);
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); QString serviceProtocol = serverConfig.value(configKey::protocol).toString();
ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData); QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
apiPayload[configKey::uuid] = installationUuid; apiPayload[configKey::uuid] = installationUuid;
apiPayload[configKey::accessToken] = serverConfig.value(configKey::accessToken).toString();
apiPayload[configKey::apiEndpoint] = serverConfig.value(configKey::apiEndpoint).toString();
QByteArray requestBody = QJsonDocument(apiPayload).toJson(); QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody);
QNetworkReply *reply = amnApp->networkManager()->post(request, requestBody); if (errorCode == ErrorCode::NoError) {
fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig);
QEventLoop wait; m_serversModel->editServer(serverConfig, serverIndex);
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); emit updateServerFromApiFinished();
return true;
QList<QSslError> sslErrors; } else {
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
if (errorCode != ErrorCode::NoError) {
reply->deleteLater();
emit errorOccurred(errorCode); emit errorOccurred(errorCode);
return false; return false;
} }
auto apiResponseBody = reply->readAll();
reply->deleteLater();
fillServerConfig(protocol, apiPayloadData, apiResponseBody, serverConfig);
m_serversModel->editServer(serverConfig, serverIndex);
emit updateServerFromApiFinished();
}
return true;
} }
bool ApiConfigsController::deactivateDevice() bool ApiConfigsController::deactivateDevice()
@@ -354,6 +337,43 @@ bool ApiConfigsController::deactivateDevice()
return true; return true;
} }
bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode)
{
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
auto serverIndex = m_serversModel->getProcessedServerIndex();
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) {
return true;
}
QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
apiPayload[configKey::serverCountryCode] = serverCountryCode;
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
apiPayload[configKey::uuid] = uuid;
QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
emit errorOccurred(errorCode);
return false;
}
if (uuid == m_settings->getInstallationUuid(true)) {
serverConfigObject.remove(config_key::containers);
m_serversModel->editServer(serverConfigObject, serverIndex);
}
return true;
}
bool ApiConfigsController::isConfigValid() bool ApiConfigsController::isConfigValid()
{ {
int serverIndex = m_serversModel->getDefaultServerIndex(); int serverIndex = m_serversModel->getDefaultServerIndex();
@@ -369,7 +389,7 @@ bool ApiConfigsController::isConfigValid()
return updateServiceFromGateway(serverIndex, "", ""); return updateServiceFromGateway(serverIndex, "", "");
} else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) { } else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) {
qDebug() << "attempt to update api config by expires_at event"; qDebug() << "attempt to update api config by expires_at event";
if (configSource == apiDefs::ConfigSource::Telegram) { if (configSource == apiDefs::ConfigSource::AmneziaGateway) {
return updateServiceFromGateway(serverIndex, "", ""); return updateServiceFromGateway(serverIndex, "", "");
} else { } else {
m_serversModel->removeApiConfig(serverIndex); m_serversModel->removeApiConfig(serverIndex);
@@ -31,6 +31,7 @@ public slots:
bool reloadServiceConfig = false); bool reloadServiceConfig = false);
bool updateServiceFromTelegram(const int serverIndex); bool updateServiceFromTelegram(const int serverIndex);
bool deactivateDevice(); bool deactivateDevice();
bool deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode);
bool isConfigValid(); bool isConfigValid();
@@ -25,11 +25,13 @@ namespace
ApiSettingsController::ApiSettingsController(const QSharedPointer<ServersModel> &serversModel, ApiSettingsController::ApiSettingsController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel, const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
const QSharedPointer<ApiCountryModel> &apiCountryModel, const QSharedPointer<ApiCountryModel> &apiCountryModel,
const QSharedPointer<ApiDevicesModel> &apiDevicesModel,
const std::shared_ptr<Settings> &settings, QObject *parent) const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent), : QObject(parent),
m_serversModel(serversModel), m_serversModel(serversModel),
m_apiAccountInfoModel(apiAccountInfoModel), m_apiAccountInfoModel(apiAccountInfoModel),
m_apiCountryModel(apiCountryModel), m_apiCountryModel(apiCountryModel),
m_apiDevicesModel(apiDevicesModel),
m_settings(settings) m_settings(settings)
{ {
} }
@@ -73,6 +75,7 @@ bool ApiSettingsController::getAccountInfo(bool reload)
if (reload) { if (reload) {
updateApiCountryModel(); updateApiCountryModel();
updateApiDevicesModel();
} }
return true; return true;
@@ -83,3 +86,8 @@ void ApiSettingsController::updateApiCountryModel()
m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), ""); m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
m_apiCountryModel->updateIssuedConfigsInfo(m_apiAccountInfoModel->getIssuedConfigsInfo()); m_apiCountryModel->updateIssuedConfigsInfo(m_apiAccountInfoModel->getIssuedConfigsInfo());
} }
void ApiSettingsController::updateApiDevicesModel()
{
m_apiDevicesModel->updateModel(m_apiAccountInfoModel->getIssuedConfigsInfo());
}
@@ -5,6 +5,7 @@
#include "ui/models/api/apiAccountInfoModel.h" #include "ui/models/api/apiAccountInfoModel.h"
#include "ui/models/api/apiCountryModel.h" #include "ui/models/api/apiCountryModel.h"
#include "ui/models/api/apiDevicesModel.h"
#include "ui/models/servers_model.h" #include "ui/models/servers_model.h"
class ApiSettingsController : public QObject class ApiSettingsController : public QObject
@@ -12,13 +13,14 @@ class ApiSettingsController : public QObject
Q_OBJECT Q_OBJECT
public: public:
ApiSettingsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel, ApiSettingsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
const QSharedPointer<ApiCountryModel> &apiCountryModel, const std::shared_ptr<Settings> &settings, const QSharedPointer<ApiCountryModel> &apiCountryModel, const QSharedPointer<ApiDevicesModel> &apiDevicesModel,
QObject *parent = nullptr); const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
~ApiSettingsController(); ~ApiSettingsController();
public slots: public slots:
bool getAccountInfo(bool reload); bool getAccountInfo(bool reload);
void updateApiCountryModel(); void updateApiCountryModel();
void updateApiDevicesModel();
signals: signals:
void errorOccurred(ErrorCode errorCode); void errorOccurred(ErrorCode errorCode);
@@ -27,6 +29,7 @@ private:
QSharedPointer<ServersModel> m_serversModel; QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel; QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
QSharedPointer<ApiCountryModel> m_apiCountryModel; QSharedPointer<ApiCountryModel> m_apiCountryModel;
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
std::shared_ptr<Settings> m_settings; std::shared_ptr<Settings> m_settings;
}; };
+11 -8
View File
@@ -27,8 +27,6 @@ namespace
ConfigTypes checkConfigFormat(const QString &config) ConfigTypes checkConfigFormat(const QString &config)
{ {
const QString openVpnConfigPatternCli = "client"; const QString openVpnConfigPatternCli = "client";
const QString openVpnConfigPatternProto1 = "proto tcp";
const QString openVpnConfigPatternProto2 = "proto udp";
const QString openVpnConfigPatternDriver1 = "dev tun"; const QString openVpnConfigPatternDriver1 = "dev tun";
const QString openVpnConfigPatternDriver2 = "dev tap"; const QString openVpnConfigPatternDriver2 = "dev tap";
@@ -53,14 +51,13 @@ namespace
|| (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName) || (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName)
&& config.contains(amneziaConfigPatternPassword))) { && config.contains(amneziaConfigPatternPassword))) {
return ConfigTypes::Amnezia; return ConfigTypes::Amnezia;
} else if (config.contains(openVpnConfigPatternCli)
&& (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2))
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
return ConfigTypes::OpenVpn;
} else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) { } else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) {
return ConfigTypes::WireGuard; return ConfigTypes::WireGuard;
} else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) { } else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) {
return ConfigTypes::Xray; return ConfigTypes::Xray;
} else if (config.contains(openVpnConfigPatternCli)
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
return ConfigTypes::OpenVpn;
} }
return ConfigTypes::Invalid; return ConfigTypes::Invalid;
} }
@@ -97,6 +94,8 @@ bool ImportController::extractConfigFromFile(const QString &fileName)
bool ImportController::extractConfigFromData(QString data) bool ImportController::extractConfigFromData(QString data)
{ {
m_maliciousWarningText.clear();
QString config = data; QString config = data;
QString prefix; QString prefix;
QString errormsg; QString errormsg;
@@ -345,7 +344,7 @@ QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
arr.push_back(containers); arr.push_back(containers);
QString hostName; QString hostName;
const static QRegularExpression hostNameRegExp("remote (.*) [0-9]*"); const static QRegularExpression hostNameRegExp("remote\\s+([^\\s]+)");
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data); QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
if (hostNameMatch.hasMatch()) { if (hostNameMatch.hasMatch()) {
hostName = hostNameMatch.captured(1); hostName = hostNameMatch.captured(1);
@@ -661,6 +660,7 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
if ((containerName == ContainerProps::containerToString(DockerContainer::OpenVpn)) if ((containerName == ContainerProps::containerToString(DockerContainer::OpenVpn))
|| (containerName == ContainerProps::containerToString(DockerContainer::Cloak)) || (containerName == ContainerProps::containerToString(DockerContainer::Cloak))
|| (containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks))) { || (containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks))) {
QString protocolConfig = QString protocolConfig =
containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString(); containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString();
QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::config].toString(); QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::config].toString();
@@ -682,8 +682,11 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
} }
} }
m_maliciousWarningText = tr("This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious "
"scripts, so only add it if you fully trust the provider of this config. ");
if (maliciousStrings.size() >= dangerousTagsMaxCount) { if (maliciousStrings.size() >= dangerousTagsMaxCount) {
m_maliciousWarningText = tr("In the imported configuration, potentially dangerous lines were found:"); m_maliciousWarningText.push_back(tr("<br>In the imported configuration, potentially dangerous lines were found:"));
for (const auto &string : maliciousStrings) { for (const auto &string : maliciousStrings) {
m_maliciousWarningText.push_back(QString("<br><i>%1</i>").arg(string)); m_maliciousWarningText.push_back(QString("<br><i>%1</i>").arg(string));
} }
+1
View File
@@ -36,6 +36,7 @@ namespace PageLoader
PageSettingsApiSupport, PageSettingsApiSupport,
PageSettingsApiInstructions, PageSettingsApiInstructions,
PageSettingsApiNativeConfigs, PageSettingsApiNativeConfigs,
PageSettingsApiDevices,
PageServiceSftpSettings, PageServiceSftpSettings,
PageServiceTorWebsiteSettings, PageServiceTorWebsiteSettings,
@@ -44,7 +44,6 @@ void SitesController::addSite(QString hostname)
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << hostname)); Q_ARG(QStringList, QStringList() << hostname));
} }
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
}; };
const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) { const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) {
@@ -75,7 +74,6 @@ void SitesController::removeSite(int index)
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection, QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << hostname)); Q_ARG(QStringList, QStringList() << hostname));
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
emit finished(tr("Site removed: %1").arg(hostname)); emit finished(tr("Site removed: %1").arg(hostname));
} }
@@ -124,7 +122,6 @@ void SitesController::importSites(const QString &fileName, bool replaceExisting)
m_sitesModel->addSites(sites, replaceExisting); m_sitesModel->addSites(sites, replaceExisting);
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, Q_ARG(QStringList, ips)); QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, Q_ARG(QStringList, ips));
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
emit finished(tr("Import completed")); emit finished(tr("Import completed"));
} }
+16 -2
View File
@@ -48,8 +48,8 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
} }
case ServiceDescriptionRole: { case ServiceDescriptionRole: {
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) { if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) {
return tr("Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to 200 " return tr("Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. "
"Mb/s"); "Speeds up to 200 Mbps");
} else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { } else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
return tr("Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and " return tr("Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and "
"more. YouTube is not included in the free plan."); "more. YouTube is not included in the free plan.");
@@ -58,6 +58,19 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
case IsComponentVisibleRole: { case IsComponentVisibleRole: {
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2; return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2;
} }
case HasExpiredWorkerRole: {
for (int i = 0; i < m_issuedConfigsInfo.size(); i++) {
QJsonObject issuedConfigObject = m_issuedConfigsInfo.at(i).toObject();
auto lastDownloaded = QDateTime::fromString(issuedConfigObject.value(apiDefs::key::lastDownloaded).toString());
auto workerLastUpdated = QDateTime::fromString(issuedConfigObject.value(apiDefs::key::workerLastUpdated).toString());
if (lastDownloaded < workerLastUpdated) {
return true;
}
}
return false;
}
} }
return QVariant(); return QVariant();
@@ -124,6 +137,7 @@ QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
roles[ConnectedDevicesRole] = "connectedDevices"; roles[ConnectedDevicesRole] = "connectedDevices";
roles[ServiceDescriptionRole] = "serviceDescription"; roles[ServiceDescriptionRole] = "serviceDescription";
roles[IsComponentVisibleRole] = "isComponentVisible"; roles[IsComponentVisibleRole] = "isComponentVisible";
roles[HasExpiredWorkerRole] = "hasExpiredWorker";
return roles; return roles;
} }
+2 -1
View File
@@ -17,7 +17,8 @@ public:
ConnectedDevicesRole, ConnectedDevicesRole,
ServiceDescriptionRole, ServiceDescriptionRole,
EndDateRole, EndDateRole,
IsComponentVisibleRole IsComponentVisibleRole,
HasExpiredWorkerRole
}; };
explicit ApiAccountInfoModel(QObject *parent = nullptr); explicit ApiAccountInfoModel(QObject *parent = nullptr);
+4
View File
@@ -44,6 +44,9 @@ QVariant ApiCountryModel::data(const QModelIndex &index, int role) const
case IsIssuedRole: { case IsIssuedRole: {
return isIssued; return isIssued;
} }
case IsWorkerExpiredRole: {
return issuedConfigInfo.lastDownloaded < issuedConfigInfo.workerLastUpdated;
}
} }
return QVariant(); return QVariant();
@@ -114,5 +117,6 @@ QHash<int, QByteArray> ApiCountryModel::roleNames() const
roles[CountryCodeRole] = "countryCode"; roles[CountryCodeRole] = "countryCode";
roles[CountryImageCodeRole] = "countryImageCode"; roles[CountryImageCodeRole] = "countryImageCode";
roles[IsIssuedRole] = "isIssued"; roles[IsIssuedRole] = "isIssued";
roles[IsWorkerExpiredRole] = "isWorkerExpired";
return roles; return roles;
} }
+2 -1
View File
@@ -14,7 +14,8 @@ public:
CountryNameRole = Qt::UserRole + 1, CountryNameRole = Qt::UserRole + 1,
CountryCodeRole, CountryCodeRole,
CountryImageCodeRole, CountryImageCodeRole,
IsIssuedRole IsIssuedRole,
IsWorkerExpiredRole
}; };
explicit ApiCountryModel(QObject *parent = nullptr); explicit ApiCountryModel(QObject *parent = nullptr);
+90
View File
@@ -0,0 +1,90 @@
#include "apiDevicesModel.h"
#include <QJsonObject>
#include "core/api/apiDefs.h"
#include "logger.h"
namespace
{
Logger logger("ApiDevicesModel");
constexpr QLatin1String gatewayAccount("gateway_account");
}
ApiDevicesModel::ApiDevicesModel(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent)
{
}
int ApiDevicesModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_issuedConfigs.size();
}
QVariant ApiDevicesModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
return QVariant();
IssuedConfigInfo issuedConfigInfo = m_issuedConfigs.at(index.row());
switch (role) {
case OsVersionRole: {
return issuedConfigInfo.osVersion;
}
case SupportTagRole: {
return issuedConfigInfo.installationUuid;
}
case CountryCodeRole: {
return issuedConfigInfo.countryCode;
}
case LastUpdateRole: {
return QDateTime::fromString(issuedConfigInfo.lastDownloaded, Qt::ISODate).toLocalTime().toString("d MMM yyyy");
}
case IsCurrentDeviceRole: {
return issuedConfigInfo.installationUuid == m_settings->getInstallationUuid(false);
}
}
return QVariant();
}
void ApiDevicesModel::updateModel(const QJsonArray &issuedConfigs)
{
beginResetModel();
m_issuedConfigs.clear();
for (int i = 0; i < issuedConfigs.size(); i++) {
IssuedConfigInfo issuedConfigInfo;
QJsonObject issuedConfigObject = issuedConfigs.at(i).toObject();
if (issuedConfigObject.value(apiDefs::key::sourceType).toString() != gatewayAccount) {
continue;
}
issuedConfigInfo.installationUuid = issuedConfigObject.value(apiDefs::key::installationUuid).toString();
issuedConfigInfo.workerLastUpdated = issuedConfigObject.value(apiDefs::key::workerLastUpdated).toString();
issuedConfigInfo.lastDownloaded = issuedConfigObject.value(apiDefs::key::lastDownloaded).toString();
issuedConfigInfo.sourceType = issuedConfigObject.value(apiDefs::key::sourceType).toString();
issuedConfigInfo.osVersion = issuedConfigObject.value(apiDefs::key::osVersion).toString();
issuedConfigInfo.countryName = issuedConfigObject.value(apiDefs::key::serverCountryName).toString();
issuedConfigInfo.countryCode = issuedConfigObject.value(apiDefs::key::serverCountryCode).toString();
m_issuedConfigs.push_back(issuedConfigInfo);
}
endResetModel();
}
QHash<int, QByteArray> ApiDevicesModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[OsVersionRole] = "osVersion";
roles[SupportTagRole] = "supportTag";
roles[CountryCodeRole] = "countryCode";
roles[LastUpdateRole] = "lastUpdate";
roles[IsCurrentDeviceRole] = "isCurrentDevice";
return roles;
}
+52
View File
@@ -0,0 +1,52 @@
#ifndef APIDEVICESMODEL_H
#define APIDEVICESMODEL_H
#include <QAbstractListModel>
#include <QJsonArray>
#include <QVector>
#include "settings.h"
class ApiDevicesModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
OsVersionRole = Qt::UserRole + 1,
SupportTagRole,
CountryCodeRole,
LastUpdateRole,
IsCurrentDeviceRole
};
explicit ApiDevicesModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
public slots:
void updateModel(const QJsonArray &issuedConfigs);
protected:
QHash<int, QByteArray> roleNames() const override;
private:
struct IssuedConfigInfo
{
QString installationUuid;
QString workerLastUpdated;
QString lastDownloaded;
QString sourceType;
QString osVersion;
QString countryName;
QString countryCode;
};
QVector<IssuedConfigInfo> m_issuedConfigs;
std::shared_ptr<Settings> m_settings;
};
#endif // APIDEVICESMODEL_H
+4 -4
View File
@@ -65,8 +65,8 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
case CardDescriptionRole: { case CardDescriptionRole: {
auto speed = apiServiceData.serviceInfo.speed; auto speed = apiServiceData.serviceInfo.speed;
if (serviceType == serviceType::amneziaPremium) { if (serviceType == serviceType::amneziaPremium) {
return tr("Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. " return tr("Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. "
"Works for any sites with no restrictions. Speed up to %1 MBit/s. Unlimited traffic.") "Access all websites and online resources. Speeds up to %1 Mbps.")
.arg(speed); .arg(speed);
} else if (serviceType == serviceType::amneziaFree) { } else if (serviceType == serviceType::amneziaFree) {
QString description = tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan."); QString description = tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan.");
@@ -79,8 +79,8 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
} }
case ServiceDescriptionRole: { case ServiceDescriptionRole: {
if (serviceType == serviceType::amneziaPremium) { if (serviceType == serviceType::amneziaPremium) {
return tr("Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. " return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. "
"Works for any sites with no restrictions."); "Access all websites and online resources.");
} else { } else {
return tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan."); return tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan.");
} }
+7 -9
View File
@@ -1,12 +1,10 @@
#include "languageModel.h" #include "languageModel.h"
LanguageModel::LanguageModel(std::shared_ptr<Settings> settings, QObject *parent) LanguageModel::LanguageModel(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent)
: m_settings(settings), QAbstractListModel(parent)
{ {
QMetaEnum metaEnum = QMetaEnum::fromType<LanguageSettings::AvailableLanguageEnum>(); QMetaEnum metaEnum = QMetaEnum::fromType<LanguageSettings::AvailableLanguageEnum>();
for (int i = 0; i < metaEnum.keyCount(); i++) { for (int i = 0; i < metaEnum.keyCount(); i++) {
m_availableLanguages.push_back( m_availableLanguages.push_back(LanguageModelData { getLocalLanguageName(static_cast<LanguageSettings::AvailableLanguageEnum>(i)),
LanguageModelData {getLocalLanguageName(static_cast<LanguageSettings::AvailableLanguageEnum>(i)),
static_cast<LanguageSettings::AvailableLanguageEnum>(i) }); static_cast<LanguageSettings::AvailableLanguageEnum>(i) });
} }
} }
@@ -50,8 +48,7 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan
case LanguageSettings::AvailableLanguageEnum::Burmese: strLanguage = "မြန်မာဘာသာ"; break; case LanguageSettings::AvailableLanguageEnum::Burmese: strLanguage = "မြန်မာဘာသာ"; break;
case LanguageSettings::AvailableLanguageEnum::Urdu: strLanguage = "اُرْدُوْ"; break; case LanguageSettings::AvailableLanguageEnum::Urdu: strLanguage = "اُرْدُوْ"; break;
case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break; case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break;
default: default: break;
break;
} }
return strLanguage; return strLanguage;
@@ -104,11 +101,12 @@ QString LanguageModel::getCurrentLanguageName()
return m_availableLanguages[getCurrentLanguageIndex()].name; return m_availableLanguages[getCurrentLanguageIndex()].name;
} }
QString LanguageModel::getCurrentSiteUrl() QString LanguageModel::getCurrentSiteUrl(const QString &path)
{ {
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex()); auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
switch (language) { switch (language) {
case LanguageSettings::AvailableLanguageEnum::Russian: return "https://storage.googleapis.com/amnezia/amnezia.org"; case LanguageSettings::AvailableLanguageEnum::Russian:
default: return "https://amnezia.org"; return "https://storage.googleapis.com/amnezia/amnezia.org" + (path.isEmpty() ? "" : (QString("?m-path=/%1").arg(path)));
default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
} }
} }
+1 -1
View File
@@ -59,7 +59,7 @@ public slots:
int getCurrentLanguageIndex(); int getCurrentLanguageIndex();
int getLineHeightAppend(); int getLineHeightAppend();
QString getCurrentLanguageName(); QString getCurrentLanguageName();
QString getCurrentSiteUrl(); QString getCurrentSiteUrl(const QString &path = "");
signals: signals:
void updateTranslations(const QLocale &locale); void updateTranslations(const QLocale &locale);
+2 -2
View File
@@ -29,7 +29,7 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: function() { onClicked: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/premium") Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("premium"))
} }
} }
@@ -54,7 +54,7 @@ Rectangle {
Layout.rightMargin: 10 Layout.rightMargin: 10
Layout.leftMargin: 10 Layout.leftMargin: 10
text: qsTr("Amnezia Premium - for access to any website") text: qsTr("Amnezia Premium - for access to all websites and online resources")
color: AmneziaStyle.color.pearlGray color: AmneziaStyle.color.pearlGray
lineHeight: 18 lineHeight: 18
@@ -27,5 +27,6 @@ QtObject {
readonly property color mistyGray: Qt.rgba(215/255, 216/255, 219/255, 0.8) readonly property color mistyGray: Qt.rgba(215/255, 216/255, 219/255, 0.8)
readonly property color cloudyGray: Qt.rgba(215/255, 216/255, 219/255, 0.65) readonly property color cloudyGray: Qt.rgba(215/255, 216/255, 219/255, 0.65)
readonly property color pearlGray: '#EAEAEC' readonly property color pearlGray: '#EAEAEC'
readonly property color translucentRichBrown: Qt.rgba(99/255, 51/255, 3/255, 0.26)
} }
} }
+1 -1
View File
@@ -252,7 +252,7 @@ PageType {
text: qsTr("Privacy Policy") text: qsTr("Privacy Policy")
clickedFunc: function() { clickedFunc: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy") Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("policy"))
} }
} }
} }
@@ -81,7 +81,7 @@ PageType {
actionButtonImage: "qrc:/images/controls/settings.svg" actionButtonImage: "qrc:/images/controls/settings.svg"
headerText: root.processedServer.name headerText: root.processedServer.name
descriptionText: qsTr("Locations for connection") descriptionText: qsTr("Location for connection")
actionButtonFunction: function() { actionButtonFunction: function() {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
@@ -0,0 +1,105 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import QtCore
import SortFilterProxyModel 0.2
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
ListViewType {
id: listView
anchors.fill: parent
anchors.topMargin: 20
anchors.bottomMargin: 24
model: ApiDevicesModel
header: ColumnLayout {
width: listView.width
BackButtonType {
id: backButton
}
HeaderType {
id: header
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Active Devices")
descriptionText: qsTr("Manage currently connected devices")
}
WarningType {
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.fillWidth: true
textString: qsTr("You can find the identifier on the Support tab or, for older versions of the app, "
+ "by tapping '+' and then the three dots at the top of the page.")
iconPath: "qrc:/images/controls/alert-circle.svg"
}
}
delegate: ColumnLayout {
width: listView.width
LabelWithButtonType {
Layout.fillWidth: true
Layout.topMargin: 6
text: osVersion + (isCurrentDevice ? qsTr(" (current device)") : "")
descriptionText: qsTr("Support tag: ") + "\n" + supportTag + "\n" + qsTr("Last updated: ") + lastUpdate
rightImageSource: "qrc:/images/controls/trash.svg"
clickedFunction: function() {
if (isCurrentDevice && ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot unlink device during active connection"))
return
}
var headerText = qsTr("Are you sure you want to unlink this device?")
var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
Qt.callLater(deactivateExternalDevice, supportTag, countryCode)
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {}
}
}
function deactivateExternalDevice(supportTag, countryCode) {
PageController.showBusyIndicator(true)
if (ApiConfigsController.deactivateExternalDevice(supportTag, countryCode)) {
ApiSettingsController.getAccountInfo(true)
}
PageController.showBusyIndicator(false)
}
}
@@ -99,7 +99,7 @@ PageType {
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("How to connect on another device") headerText: qsTr("How to connect on another device")
descriptionText: qsTr("Instructions on the Amnezia website") descriptionText: qsTr("Setup guides on the Amnezia website")
} }
} }
@@ -107,7 +107,6 @@ PageType {
width: listView.width width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: telegramButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 6 Layout.topMargin: 6
@@ -45,8 +45,8 @@ PageType {
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("Configuration files") headerText: qsTr("Configuration Files")
descriptionText: qsTr("To connect a router or AmneziaWG application") descriptionText: qsTr("For router setup or the AmneziaWG app")
} }
} }
@@ -58,6 +58,9 @@ PageType {
Layout.topMargin: 6 Layout.topMargin: 6
text: countryName text: countryName
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
descriptionColor: AmneziaStyle.color.vibrantRed
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg" rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
@@ -120,13 +123,13 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
headerText: qsTr("Configuration file ") + moreOptionsDrawer.countryName headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
} }
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Create a new") text: qsTr("Generate a new configuration file")
descriptionText: qsTr("The previously created one will stop working") descriptionText: qsTr("The previously created one will stop working")
clickedFunction: function() { clickedFunction: function() {
@@ -190,9 +193,15 @@ PageType {
} }
function showQuestion(isConfigIssue, countryCode, countryName) { function showQuestion(isConfigIssue, countryCode, countryName) {
var headerText = qsTr("Revoke the actual %1 configuration file?").arg(countryName) var headerText
var descriptionText = qsTr("The previously created file will no longer be valid. It will not be possible to connect using it.") if (isConfigIssue) {
var yesButtonText = qsTr("Continue") headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
} else {
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
}
var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it")
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
@@ -26,15 +26,15 @@ PageType {
QtObject { QtObject {
id: statusObject id: statusObject
readonly property string title: qsTr("Subscription status") readonly property string title: qsTr("Subscription Status")
readonly property string contentKey: "subscriptionStatus" readonly property string contentKey: "subscriptionStatus"
readonly property string objectImageSource: "qrc:/images/controls/map-pin.svg" readonly property string objectImageSource: "qrc:/images/controls/info.svg"
} }
QtObject { QtObject {
id: endDateObject id: endDateObject
readonly property string title: qsTr("Valid until") readonly property string title: qsTr("Valid Until")
readonly property string contentKey: "endDate" readonly property string contentKey: "endDate"
readonly property string objectImageSource: "qrc:/images/controls/history.svg" readonly property string objectImageSource: "qrc:/images/controls/history.svg"
} }
@@ -42,9 +42,9 @@ PageType {
QtObject { QtObject {
id: deviceCountObject id: deviceCountObject
readonly property string title: qsTr("Connected devices") readonly property string title: qsTr("Active Connections")
readonly property string contentKey: "connectedDevices" readonly property string contentKey: "connectedDevices"
readonly property string objectImageSource: "qrc:/images/controls/gauge.svg" readonly property string objectImageSource: "qrc:/images/controls/monitor.svg"
} }
property var processedServer property var processedServer
@@ -158,15 +158,32 @@ PageType {
readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible") readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible")
WarningType {
id: warning
Layout.topMargin: 32
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.fillWidth: true
backGroundColor: AmneziaStyle.color.translucentRichBrown
textString: qsTr("Configurations have been updated for some countries. Download and install the updated configuration files")
iconPath: "qrc:/images/controls/alert-circle.svg"
visible: ApiAccountInfoModel.data("hasExpiredWorker")
}
LabelWithButtonType { LabelWithButtonType {
id: vpnKey id: vpnKey
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: warning.visible ? 16 : 32
visible: false //footer.isVisibleForAmneziaFree visible: false //footer.isVisibleForAmneziaFree
text: qsTr("Subscription key") text: qsTr("Subscription Key")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
@@ -174,7 +191,7 @@ PageType {
shareConnectionDrawer.openTriggered() shareConnectionDrawer.openTriggered()
shareConnectionDrawer.isSelfHostedConfig = false; shareConnectionDrawer.isSelfHostedConfig = false;
shareConnectionDrawer.shareButtonText = qsTr("Save VPN key to file") shareConnectionDrawer.shareButtonText = qsTr("Save VPN key as a file")
shareConnectionDrawer.copyButtonText = qsTr("Copy VPN key") shareConnectionDrawer.copyButtonText = qsTr("Copy VPN key")
@@ -192,13 +209,13 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: warning.visible ? 16 : 32
visible: footer.isVisibleForAmneziaFree visible: footer.isVisibleForAmneziaFree
text: qsTr("Configuration files") text: qsTr("Configuration Files")
descriptionText: qsTr("To connect a router or AmneziaWG application") descriptionText: qsTr("Manage configuration files")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
@@ -211,6 +228,26 @@ PageType {
visible: footer.isVisibleForAmneziaFree visible: footer.isVisibleForAmneziaFree
} }
LabelWithButtonType {
Layout.fillWidth: true
visible: footer.isVisibleForAmneziaFree
text: qsTr("Active Devices")
descriptionText: qsTr("Manage currently connected devices")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
ApiSettingsController.updateApiDevicesModel()
PageController.goToPage(PageEnum.PageSettingsApiDevices)
}
}
DividerType {
visible: footer.isVisibleForAmneziaFree
}
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: footer.isVisibleForAmneziaFree ? 0 : 32 Layout.topMargin: footer.isVisibleForAmneziaFree ? 0 : 32
@@ -228,6 +265,8 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
visible: footer.isVisibleForAmneziaFree
text: qsTr("How to connect on another device") text: qsTr("How to connect on another device")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
@@ -236,7 +275,9 @@ PageType {
} }
} }
DividerType {} DividerType {
visible: footer.isVisibleForAmneziaFree
}
BasicButtonType { BasicButtonType {
id: resetButton id: resetButton
@@ -288,16 +329,17 @@ PageType {
pressedColor: AmneziaStyle.color.sheerWhite pressedColor: AmneziaStyle.color.sheerWhite
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
text: qsTr("Deactivate the subscription on this device") text: qsTr("Unlink this device")
clickedFunc: function() { clickedFunc: function() {
var headerText = qsTr("Deactivate the subscription on this device?") var headerText = qsTr("Are you sure you want to unlink this device?")
var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("The next time the “Connect” button is pressed, the device will be activated again")) PageController.showNotificationMessage(qsTr("Cannot unlink device during active connection"))
} else { } else {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
if (ApiConfigsController.deactivateDevice()) { if (ApiConfigsController.deactivateDevice()) {
@@ -309,7 +351,7 @@ PageType {
var noButtonFunction = function() { var noButtonFunction = function() {
} }
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
+58 -40
View File
@@ -16,20 +16,61 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { QtObject {
id: backButtonLayout id: telegram
anchors.top: parent.top readonly property string title: qsTr("Telegram")
anchors.left: parent.left readonly property string description: "@" + ApiAccountInfoModel.getTelegramBotLink()
anchors.right: parent.right readonly property string link: "https://t.me/" + ApiAccountInfoModel.getTelegramBotLink()
}
QtObject {
id: techSupport
readonly property string title: qsTr("Email")
readonly property string description: qsTr("support@amnezia.org")
readonly property string link: "mailto:support@amnezia.org"
}
QtObject {
id: paymentSupport
readonly property string title: qsTr("Email Billing & Orders")
readonly property string description: qsTr("help@vpnpay.io")
readonly property string link: "mailto:help@vpnpay.io"
}
QtObject {
id: site
readonly property string title: qsTr("Website")
readonly property string description: qsTr("amnezia.org")
readonly property string link: LanguageModel.getCurrentSiteUrl()
}
property list<QtObject> supportModel: [
telegram,
techSupport,
paymentSupport,
site
]
ListViewType {
id: listView
anchors.fill: parent
anchors.topMargin: 20 anchors.topMargin: 20
anchors.bottomMargin: 24
model: supportModel
header: ColumnLayout {
width: listView.width
BackButtonType { BackButtonType {
id: backButton id: backButton
} }
HeaderType { HeaderType {
id: header id: header
@@ -38,52 +79,28 @@ PageType {
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("Support") headerText: qsTr("Support")
descriptionText: qsTr("Our technical support specialists are ready to help you at any time") descriptionText: qsTr("Our technical support specialists are available to assist you at any time")
} }
}
delegate: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
text: title
readonly property string telegramBotLink: ApiAccountInfoModel.getTelegramBotLink() descriptionText: description
text: qsTr("Telegram")
descriptionText: "@" + telegramBotLink
rightImageSource: "qrc:/images/controls/external-link.svg" rightImageSource: "qrc:/images/controls/external-link.svg"
clickedFunction: function() { clickedFunction: function() {
Qt.openUrlExternally("https://t.me/" + telegramBotLink) Qt.openUrlExternally(link)
} }
} }
DividerType {} DividerType {}
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Mail")
descriptionText: qsTr("support@amnezia.org")
rightImageSource: "qrc:/images/controls/external-link.svg"
clickedFunction: function() {
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
}
} }
DividerType {}
LabelWithButtonType { footer: ColumnLayout {
Layout.fillWidth: true width: listView.width
text: qsTr("Site")
descriptionText: qsTr("amnezia.org")
rightImageSource: "qrc:/images/controls/external-link.svg"
clickedFunction: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
}
}
DividerType {}
LabelWithButtonType { LabelWithButtonType {
id: supportUuid id: supportUuid
@@ -106,4 +123,5 @@ PageType {
} }
} }
} }
}
} }
@@ -140,7 +140,7 @@ PageType {
} }
onClicked: { onClicked: {
if (!checkable) { if (!checkable) {
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection")) PageController.showNotificationMessage(qsTr("Cannot change KillSwitch settings during active connection"))
} }
} }
} }
@@ -175,7 +175,7 @@ PageType {
leftImageSource: "qrc:/images/controls/help-circle.svg" leftImageSource: "qrc:/images/controls/help-circle.svg"
onClicked: { onClicked: {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/starter-guide") Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("starter-guide"))
} }
} }
} }
+1 -1
View File
@@ -78,7 +78,7 @@ PageType {
height: containers.contentItem.height height: containers.contentItem.height
spacing: 16 spacing: 16
currentIndex: 1 currentIndex: 0
clip: true clip: true
interactive: false interactive: false
model: proxyContainersModel model: proxyContainersModel
+38 -3
View File
@@ -6,7 +6,6 @@
#include <QHostInfo> #include <QHostInfo>
#include <QJsonObject> #include <QJsonObject>
#include "core/controllers/serverController.h"
#include <configurators/cloak_configurator.h> #include <configurators/cloak_configurator.h>
#include <configurators/openvpn_configurator.h> #include <configurators/openvpn_configurator.h>
#include <configurators/shadowsocks_configurator.h> #include <configurators/shadowsocks_configurator.h>
@@ -14,7 +13,6 @@
#ifdef AMNEZIA_DESKTOP #ifdef AMNEZIA_DESKTOP
#include "core/ipcclient.h" #include "core/ipcclient.h"
#include "ipc.h"
#include <protocols/wireguardprotocol.h> #include <protocols/wireguardprotocol.h>
#endif #endif
@@ -54,7 +52,6 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
{ {
#ifdef AMNEZIA_DESKTOP #ifdef AMNEZIA_DESKTOP
auto container = m_settings->defaultContainer(m_settings->defaultServerIndex()); auto container = m_settings->defaultContainer(m_settings->defaultServerIndex());
@@ -86,6 +83,10 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
} }
} }
if (container != DockerContainer::Ipsec) {
IpcClient::Interface()->startNetworkCheck(m_vpnProtocol->vpnLocalAddress(), m_vpnProtocol->vpnLocalAddress());
}
} else if (state == Vpn::ConnectionState::Error) { } else if (state == Vpn::ConnectionState::Error) {
IpcClient::Interface()->flushDns(); IpcClient::Interface()->flushDns();
@@ -97,6 +98,8 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
} else if (state == Vpn::ConnectionState::Connecting) { } else if (state == Vpn::ConnectionState::Connecting) {
} else if (state == Vpn::ConnectionState::Disconnected) { } else if (state == Vpn::ConnectionState::Disconnected) {
auto result = IpcClient::Interface()->stopNetworkCheck();
result.waitForFinished(3000);
} }
} }
#endif #endif
@@ -219,6 +222,11 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
.arg(serverIndex) .arg(serverIndex)
.arg(ContainerProps::containerToString(container)); .arg(ContainerProps::containerToString(container));
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
if (m_IpcClient) {
m_IpcClient->close();
m_IpcClient = nullptr;
}
if (!m_IpcClient) { if (!m_IpcClient) {
m_IpcClient = new IpcClient(this); m_IpcClient = new IpcClient(this);
} }
@@ -237,6 +245,9 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
emit connectionStateChanged(Vpn::ConnectionState::Connecting); emit connectionStateChanged(Vpn::ConnectionState::Connecting);
m_vpnConfiguration = vpnConfiguration; m_vpnConfiguration = vpnConfiguration;
m_serverIndex = serverIndex;
m_serverCredentials = credentials;
m_dockerContainer = container;
#ifdef AMNEZIA_DESKTOP #ifdef AMNEZIA_DESKTOP
if (m_vpnProtocol) { if (m_vpnProtocol) {
@@ -275,12 +286,36 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
emit connectionStateChanged(Vpn::ConnectionState::Error); emit connectionStateChanged(Vpn::ConnectionState::Error);
} }
void VpnConnection::restartConnection()
{
this->disconnectFromVpn();
#ifdef Q_OS_LINUX
QThread::msleep(5000);
#endif
this->connectToVpn(m_serverIndex, m_serverCredentials, m_dockerContainer, m_vpnConfiguration);
}
void VpnConnection::createProtocolConnections() void VpnConnection::createProtocolConnections()
{ {
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(Vpn::ConnectionState)), this, connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(Vpn::ConnectionState)), this,
SLOT(onConnectionStateChanged(Vpn::ConnectionState))); SLOT(onConnectionStateChanged(Vpn::ConnectionState)));
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
#ifdef AMNEZIA_DESKTOP
connect(IpcClient::Interface().data(), &IpcInterfaceReplica::connectionLose,
this, [this]() {
qDebug() << "Connection Lose";
auto result = IpcClient::Interface()->stopNetworkCheck();
result.waitForFinished(3000);
this->restartConnection();
});
connect(IpcClient::Interface().data(), &IpcInterfaceReplica::networkChange,
this, [this]() {
qDebug() << "Network change";
this->restartConnection();
});
#endif
} }
void VpnConnection::appendKillSwitchConfig() void VpnConnection::appendKillSwitchConfig()
+5
View File
@@ -51,6 +51,7 @@ public slots:
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration); const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
void disconnectFromVpn(); void disconnectFromVpn();
void restartConnection();
void addRoutes(const QStringList &ips); void addRoutes(const QStringList &ips);
@@ -77,6 +78,10 @@ private:
QJsonObject m_routeMode; QJsonObject m_routeMode;
QString m_remoteAddress; QString m_remoteAddress;
ServerCredentials m_serverCredentials;
int m_serverIndex;
DockerContainer m_dockerContainer;
// Only for iOS for now, check counters // Only for iOS for now, check counters
QTimer m_checkTimer; QTimer m_checkTimer;
+5
View File
@@ -32,5 +32,10 @@ class IpcInterface
SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enablePeerTraffic( const QJsonObject &configStr) );
SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) );
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) ); SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );
SLOT( bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) );
SLOT( bool stopNetworkCheck() );
SIGNAL( connectionLose() );
SIGNAL( networkChange() );
}; };
+14 -1
View File
@@ -25,8 +25,8 @@
#endif #endif
IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent) IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent)
{ {
connect(&m_pingHelper, &PingHelper::connectionLose, this, &IpcServer::connectionLose);
} }
int IpcServer::createPrivilegedProcess() int IpcServer::createPrivilegedProcess()
@@ -188,6 +188,19 @@ void IpcServer::setLogsEnabled(bool enabled)
} }
} }
bool IpcServer::startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address)
{
m_pingHelper.start(serverIpv4Gateway, deviceIpv4Address);
return true;
}
bool IpcServer::stopNetworkCheck()
{
qDebug() << "stopNetworkCheck";
m_pingHelper.stop();
return true;
}
bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex)
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
+5
View File
@@ -6,6 +6,7 @@
#include <QRemoteObjectNode> #include <QRemoteObjectNode>
#include <QJsonObject> #include <QJsonObject>
#include "../client/daemon/interfaceconfig.h" #include "../client/daemon/interfaceconfig.h"
#include "../client/mozilla/pinghelper.h"
#include "ipc.h" #include "ipc.h"
#include "ipcserverprocess.h" #include "ipcserverprocess.h"
@@ -38,6 +39,8 @@ public:
virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override;
virtual bool disableKillSwitch() override; virtual bool disableKillSwitch() override;
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override; virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
virtual bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) override;
virtual bool stopNetworkCheck() override;
private: private:
int m_localpid = 0; int m_localpid = 0;
@@ -57,6 +60,8 @@ private:
}; };
QMap<int, ProcessDescriptor> m_processes; QMap<int, ProcessDescriptor> m_processes;
PingHelper m_pingHelper;
}; };
#endif // IPCSERVER_H #endif // IPCSERVER_H
+2 -2
View File
@@ -56,7 +56,6 @@ set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/controllerimpl.h ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/controllerimpl.h
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/dnspingsender.h ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/dnspingsender.h
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/localsocketcontroller.h
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/networkwatcher.h ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/networkwatcher.h
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/networkwatcherimpl.h ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/networkwatcherimpl.h
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/pinghelper.h ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/pinghelper.h
@@ -80,7 +79,6 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/shared/leakdetector.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/shared/leakdetector.cpp
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/dnspingsender.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/dnspingsender.cpp
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/localsocketcontroller.cpp
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/networkwatcher.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/networkwatcher.cpp
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/pinghelper.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/pinghelper.cpp
${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/pingsender.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/pingsender.cpp
@@ -212,6 +210,7 @@ if(LINUX)
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcher.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcher.h
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.h
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.h
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxpingsender.h
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.h
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dbustypeslinux.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dbustypeslinux.h
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.h
@@ -226,6 +225,7 @@ if(LINUX)
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcher.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxpingsender.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dnsutilslinux.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dnsutilslinux.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.cpp
+3 -2
View File
@@ -5,9 +5,7 @@
#include "ipc.h" #include "ipc.h"
#include "localserver.h" #include "localserver.h"
#include "utilities.h"
#include "router.h"
#include "logger.h" #include "logger.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@@ -47,6 +45,9 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
return; return;
} }
m_networkWatcher.initialize();
connect(&m_networkWatcher, &NetworkWatcher::sleepMode, &m_ipcServer, &IpcServer::networkChange);
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
// Signal handling for a proper shutdown. // Signal handling for a proper shutdown.
QObject::connect(qApp, &QCoreApplication::aboutToQuit, QObject::connect(qApp, &QCoreApplication::aboutToQuit,
+3 -1
View File
@@ -11,7 +11,7 @@
#include "ipcserver.h" #include "ipcserver.h"
#include "../../client/daemon/daemonlocalserver.h" #include "../../client/daemon/daemonlocalserver.h"
#include "../../client/mozilla/networkwatcher.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "windows/daemon/windowsdaemon.h" #include "windows/daemon/windowsdaemon.h"
@@ -41,6 +41,8 @@ public:
IpcProcessTun2Socks m_tun2socks; IpcProcessTun2Socks m_tun2socks;
QRemoteObjectHost m_serverNode; QRemoteObjectHost m_serverNode;
bool m_isRemotingEnabled = false; bool m_isRemotingEnabled = false;
NetworkWatcher m_networkWatcher;
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
DaemonLocalServer server{qApp}; DaemonLocalServer server{qApp};
LinuxDaemon daemon; LinuxDaemon daemon;