Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD
This commit is contained in:
commit
f0395847ac
65 changed files with 1902 additions and 1283 deletions
82
.github/workflows/deploy.yml
vendored
82
.github/workflows/deploy.yml
vendored
|
|
@ -20,6 +20,8 @@ jobs:
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install Qt'
|
- name: 'Install Qt'
|
||||||
|
|
@ -90,6 +92,8 @@ jobs:
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Get sources'
|
- name: 'Get sources'
|
||||||
|
|
@ -156,6 +160,8 @@ jobs:
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
|
|
@ -243,7 +249,7 @@ jobs:
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-MacOS:
|
Build-MacOS-old:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|
@ -255,6 +261,78 @@ jobs:
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 'Setup xcode'
|
||||||
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
|
with:
|
||||||
|
xcode-version: '15.4.0'
|
||||||
|
|
||||||
|
- name: 'Install Qt'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'mac'
|
||||||
|
target: 'desktop'
|
||||||
|
arch: 'clang_64'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtshadertools'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
setup-python: 'true'
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
|
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
|
||||||
|
run: |
|
||||||
|
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
|
||||||
|
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
|
||||||
|
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
|
||||||
|
|
||||||
|
- name: 'Get sources'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
fetch-depth: 10
|
||||||
|
|
||||||
|
- name: 'Setup ccache'
|
||||||
|
uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
|
- name: 'Build project'
|
||||||
|
run: |
|
||||||
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
|
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
|
||||||
|
bash deploy/build_macos.sh
|
||||||
|
|
||||||
|
- name: 'Upload installer artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: AmneziaVPN_MacOS_old_installer
|
||||||
|
path: AmneziaVPN.dmg
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
- name: 'Upload unpacked artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: AmneziaVPN_MacOS_old_unpacked
|
||||||
|
path: deploy/build/client/AmneziaVPN.app
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
# ------------------------------------------------------
|
||||||
|
|
||||||
|
Build-MacOS:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
QT_VERSION: 6.8.0
|
||||||
|
QIF_VERSION: 4.8.1
|
||||||
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
|
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
|
|
@ -324,6 +402,8 @@ jobs:
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install desktop Qt'
|
- name: 'Install desktop Qt'
|
||||||
|
|
|
||||||
2
.github/workflows/tag-deploy.yml
vendored
2
.github/workflows/tag-deploy.yml
vendored
|
|
@ -20,6 +20,8 @@ jobs:
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install desktop Qt'
|
- name: 'Install desktop Qt'
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -134,3 +134,7 @@ out/
|
||||||
|
|
||||||
# CMake files
|
# CMake files
|
||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
|
|
||||||
|
ios-ne-build.sh
|
||||||
|
macos-ne-build.sh
|
||||||
|
macos-signed-build.sh
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.8.6.0
|
project(${PROJECT} VERSION 4.8.7.2
|
||||||
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 2083)
|
set(APP_ANDROID_VERSION_CODE 2086)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit efad1a5b5cb8e8ab61e49ccdca18c9090a0da8d3
|
Subproject commit 0f3748efd7cc04e0c914304b68931f925bed1259
|
||||||
|
|
@ -31,6 +31,9 @@ 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}")
|
||||||
|
|
||||||
|
add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}")
|
||||||
|
add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}")
|
||||||
|
|
||||||
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()
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,22 @@ set_target_properties(${PROJECT} PROPERTIES
|
||||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
||||||
XCODE_EMBED_APP_EXTENSIONS networkextension
|
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||||
|
)
|
||||||
|
|
||||||
|
if(DEFINED DEPLOY)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
|
|
|
||||||
|
|
@ -126,13 +126,6 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
||||||
|
|
||||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
||||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
|
||||||
// Prevent ipv6 leak
|
|
||||||
if (NetworkUtilities::checkIpv6Enabled()) {
|
|
||||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||||
|
|
||||||
|
|
@ -141,7 +134,6 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||||
// Prevent ipv6 leak
|
// Prevent ipv6 leak
|
||||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
|
||||||
#endif
|
#endif
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
}
|
}
|
||||||
|
|
@ -184,7 +176,6 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString
|
||||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||||
|
|
||||||
// Prevent ipv6 leak
|
// Prevent ipv6 leak
|
||||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
|
|
||||||
// remove block-outside-dns for all exported configs
|
// remove block-outside-dns for all exported configs
|
||||||
|
|
|
||||||
|
|
@ -140,98 +140,83 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
{ DockerContainer::OpenVpn,
|
{ DockerContainer::OpenVpn,
|
||||||
QObject::tr(
|
QObject::tr("OpenVPN is one of the most popular and reliable VPN protocols. "
|
||||||
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
|
"It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, "
|
||||||
"It employs its unique security protocol, "
|
"and is continuously improved by the community due to its open-source nature. "
|
||||||
"leveraging the strength of SSL/TLS for encryption and key exchange. "
|
"It provides a good balance between speed and security but is easily recognized by DPI systems, "
|
||||||
"Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, "
|
"making it susceptible to blocking.\n"
|
||||||
"catering to a wide range of devices and operating systems. "
|
"\nFeatures:\n"
|
||||||
"Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, "
|
"* Available on all AmneziaVPN platforms\n"
|
||||||
"which continually reinforces its security. "
|
"* Normal battery consumption on mobile devices\n"
|
||||||
"With a strong balance of performance, security, and compatibility, "
|
"* Flexible customization for various devices and OS\n"
|
||||||
"OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n"
|
"* Operates over both TCP and UDP protocols") },
|
||||||
"* Available in the AmneziaVPN across all platforms\n"
|
|
||||||
"* Normal power consumption on mobile devices\n"
|
|
||||||
"* Flexible customisation to suit user needs to work with different operating systems and devices\n"
|
|
||||||
"* Recognised by DPI systems and therefore susceptible to blocking\n"
|
|
||||||
"* Can operate over both TCP and UDP network protocols.") },
|
|
||||||
{ DockerContainer::ShadowSocks,
|
{ DockerContainer::ShadowSocks,
|
||||||
QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. "
|
QObject::tr("Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. "
|
||||||
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection."
|
"Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. "
|
||||||
"However, certain traffic analysis systems might still detect a Shadowsocks connection. "
|
"Due to limited support in Amnezia, we recommend using the AmneziaWG protocol.\n"
|
||||||
"Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n"
|
"\nFeatures:\n"
|
||||||
"* Available in the AmneziaVPN only on desktop platforms\n"
|
"* Available in AmneziaVPN only on desktop platforms\n"
|
||||||
"* Configurable encryption protocol\n"
|
"* Customizable encryption protocol\n"
|
||||||
"* Detectable by some DPI systems\n"
|
"* Detectable by some DPI systems\n"
|
||||||
"* Works over TCP network protocol.") },
|
"* Operates over TCP protocol\n") },
|
||||||
{ DockerContainer::Cloak,
|
{ DockerContainer::Cloak,
|
||||||
QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for "
|
QObject::tr("This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.\n"
|
||||||
"protecting against detection.\n\n"
|
"\nOpenVPN securely encrypts all internet traffic between your device and the server.\n"
|
||||||
"OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client "
|
"\nThe Cloak plugin further protects the connection from DPI detection. "
|
||||||
"and the server.\n\n"
|
"It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. "
|
||||||
"Cloak protects OpenVPN from detection. \n\n"
|
"If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems.\n"
|
||||||
"Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, "
|
"\nIn regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.\n"
|
||||||
"and also protects the VPN from detection by Active Probing. This makes it very resistant to "
|
"\nFeatures:\n"
|
||||||
"being detected\n\n"
|
"* Available on all AmneziaVPN platforms\n"
|
||||||
"Immediately after receiving the first data packet, Cloak authenticates the incoming connection. "
|
|
||||||
"If authentication fails, the plugin masks the server as a fake website and your VPN becomes "
|
|
||||||
"invisible to analysis systems.\n\n"
|
|
||||||
"* Available in the AmneziaVPN across all platforms\n"
|
|
||||||
"* High power consumption on mobile devices\n"
|
"* High power consumption on mobile devices\n"
|
||||||
"* Flexible settings\n"
|
"* Flexible configuration options\n"
|
||||||
"* Not recognised by detection systems\n"
|
"* Undetectable by DPI systems\n"
|
||||||
"* Works over TCP network protocol, 443 port.\n") },
|
"* Operates over TCP protocol on port 443") },
|
||||||
{ DockerContainer::WireGuard,
|
{ DockerContainer::WireGuard,
|
||||||
QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n"
|
QObject::tr("WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. "
|
||||||
"WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption "
|
"It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. "
|
||||||
"settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n"
|
"However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking.\n"
|
||||||
"WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. "
|
"\nFeatures:\n"
|
||||||
"Unlike some other VPN protocols that employ obfuscation techniques, "
|
"* Available on all AmneziaVPN platforms\n"
|
||||||
"the consistent signature patterns of WireGuard packets can be more easily identified and "
|
"* Low power consumption on mobile devices\n"
|
||||||
"thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n"
|
"* Minimal configuration required\n"
|
||||||
"* Available in the AmneziaVPN across all platforms\n"
|
"* Easily detected by DPI systems (susceptible to blocking)\n"
|
||||||
"* Low power consumption\n"
|
"* Operates over UDP protocol") },
|
||||||
"* Minimum number of settings\n"
|
|
||||||
"* Easily recognised by DPI analysis systems, susceptible to blocking\n"
|
|
||||||
"* Works over UDP network protocol.") },
|
|
||||||
{ DockerContainer::Awg,
|
{ DockerContainer::Awg,
|
||||||
QObject::tr("A modern iteration of the popular VPN protocol, "
|
QObject::tr("AmneziaWG is a modern VPN protocol based on WireGuard, "
|
||||||
"AmneziaWG builds upon the foundation set by WireGuard, "
|
"combining simplified architecture with high performance across all devices. "
|
||||||
"retaining its simplified architecture and high-performance capabilities across devices.\n"
|
"It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, "
|
||||||
"While WireGuard is known for its efficiency, "
|
"making VPN traffic indistinguishable from regular internet traffic.\n"
|
||||||
"it had issues with being easily detected due to its distinct packet signatures. "
|
"\nAmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection.\n"
|
||||||
"AmneziaWG solves this problem by using better obfuscation methods, "
|
"\nFeatures:\n"
|
||||||
"making its traffic blend in with regular internet traffic.\n"
|
"* Available on all AmneziaVPN platforms\n"
|
||||||
"This means that AmneziaWG keeps the fast performance of the original "
|
"* Low battery consumption on mobile devices\n"
|
||||||
"while adding an extra layer of stealth, "
|
"* Minimal settings required\n"
|
||||||
"making it a great choice for those wanting a fast and discreet VPN connection.\n\n"
|
"* Undetectable by traffic analysis systems (DPI)\n"
|
||||||
"* Available in the AmneziaVPN across all platforms\n"
|
"* Operates over UDP protocol") },
|
||||||
"* Low power consumption\n"
|
|
||||||
"* Minimum number of settings\n"
|
|
||||||
"* Not recognised by traffic analysis systems\n"
|
|
||||||
"* Works over UDP network protocol.") },
|
|
||||||
{ DockerContainer::Xray,
|
{ DockerContainer::Xray,
|
||||||
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
|
QObject::tr("REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. "
|
||||||
"is designed to provide the highest level of protection against detection through its innovative approach to security and privacy.\n"
|
"REALITY identifies censorship systems during the TLS handshake, "
|
||||||
"It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, "
|
"redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. "
|
||||||
"thus presenting an authentic TLS certificate and data. \n"
|
"This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration."
|
||||||
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
|
"\nUnlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in \"friend-or-foe\" detection mechanism, "
|
||||||
"legitimate sites without the need for specific configurations. \n"
|
"effectively protecting against DPI and other traffic analysis methods.\n"
|
||||||
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
|
"\nFeatures:\n"
|
||||||
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security. "
|
"* Resistant to active probing and DPI detection\n"
|
||||||
"This makes REALITY a robust solution for maintaining internet freedom.")
|
"* No special configuration required to disguise traffic\n"
|
||||||
},
|
"* Highly effective in heavily censored regions\n"
|
||||||
|
"* Minimal battery consumption on devices\n"
|
||||||
|
"* Operates over TCP protocol") },
|
||||||
{ DockerContainer::Ipsec,
|
{ DockerContainer::Ipsec,
|
||||||
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
|
QObject::tr("IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. "
|
||||||
"One of its distinguishing features is its ability to swiftly switch between networks and devices, "
|
"It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. "
|
||||||
"making it particularly adaptive in dynamic network environments. \n"
|
"While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking.\n"
|
||||||
"While it offers a blend of security, stability, and speed, "
|
"\nFeatures:\n"
|
||||||
"it's essential to note that IKEv2 can be easily detected and is susceptible to blocking.\n\n"
|
"* Available in AmneziaVPN only on Windows\n"
|
||||||
"* Available in the AmneziaVPN only on Windows\n"
|
"* Low battery consumption on mobile devices\n"
|
||||||
"* Low power consumption, on mobile devices\n"
|
"* Minimal configuration required\n"
|
||||||
"* Minimal configuration\n"
|
"* Detectable by DPI analysis systems(easily blocked)\n"
|
||||||
"* Recognised by DPI analysis systems\n"
|
"* Operates over UDP protocol(ports 500 and 4500)") },
|
||||||
"* Works over UDP network protocol, ports 500 and 4500.") },
|
|
||||||
|
|
||||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||||
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,20 @@ namespace apiDefs
|
||||||
namespace key
|
namespace key
|
||||||
{
|
{
|
||||||
constexpr QLatin1String configVersion("config_version");
|
constexpr QLatin1String configVersion("config_version");
|
||||||
|
constexpr QLatin1String apiEndpoint("api_endpoint");
|
||||||
|
constexpr QLatin1String apiKey("api_key");
|
||||||
|
constexpr QLatin1String description("description");
|
||||||
|
constexpr QLatin1String name("name");
|
||||||
|
constexpr QLatin1String protocol("protocol");
|
||||||
|
|
||||||
constexpr QLatin1String apiConfig("api_config");
|
constexpr QLatin1String apiConfig("api_config");
|
||||||
constexpr QLatin1String stackType("stack_type");
|
constexpr QLatin1String stackType("stack_type");
|
||||||
constexpr QLatin1String serviceType("service_type");
|
constexpr QLatin1String serviceType("service_type");
|
||||||
|
constexpr QLatin1String cliVersion("cli_version");
|
||||||
|
|
||||||
constexpr QLatin1String vpnKey("vpn_key");
|
constexpr QLatin1String vpnKey("vpn_key");
|
||||||
|
constexpr QLatin1String config("config");
|
||||||
|
constexpr QLatin1String configs("configs");
|
||||||
|
|
||||||
constexpr QLatin1String installationUuid("installation_uuid");
|
constexpr QLatin1String installationUuid("installation_uuid");
|
||||||
constexpr QLatin1String workerLastUpdated("worker_last_updated");
|
constexpr QLatin1String workerLastUpdated("worker_last_updated");
|
||||||
|
|
@ -51,6 +59,10 @@ namespace apiDefs
|
||||||
constexpr QLatin1String website("website");
|
constexpr QLatin1String website("website");
|
||||||
constexpr QLatin1String websiteName("website_name");
|
constexpr QLatin1String websiteName("website_name");
|
||||||
constexpr QLatin1String telegram("telegram");
|
constexpr QLatin1String telegram("telegram");
|
||||||
|
|
||||||
|
constexpr QLatin1String id("id");
|
||||||
|
constexpr QLatin1String orderId("order_id");
|
||||||
|
constexpr QLatin1String migrationCode("migration_code");
|
||||||
}
|
}
|
||||||
|
|
||||||
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,24 @@
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const QByteArray AMNEZIA_CONFIG_SIGNATURE = QByteArray::fromHex("000000ff");
|
||||||
|
|
||||||
|
QString escapeUnicode(const QString &input)
|
||||||
|
{
|
||||||
|
QString output;
|
||||||
|
for (QChar c : input) {
|
||||||
|
if (c.unicode() < 0x20 || c.unicode() > 0x7E) {
|
||||||
|
output += QString("\\u%1").arg(QString::number(c.unicode(), 16).rightJustified(4, '0'));
|
||||||
|
} else {
|
||||||
|
output += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
|
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
|
||||||
{
|
{
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime now = QDateTime::currentDateTime();
|
||||||
|
|
@ -23,13 +41,21 @@ bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
|
||||||
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
|
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
|
||||||
{
|
{
|
||||||
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
|
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
|
||||||
|
|
||||||
switch (configVersion) {
|
switch (configVersion) {
|
||||||
case apiDefs::ConfigSource::Telegram: {
|
case apiDefs::ConfigSource::Telegram: {
|
||||||
|
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
|
||||||
|
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
|
||||||
|
|
||||||
|
auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString();
|
||||||
|
|
||||||
|
if (apiEndpoint.contains(premiumV1Endpoint)) {
|
||||||
|
return apiDefs::ConfigType::AmneziaPremiumV1;
|
||||||
|
} else if (apiEndpoint.contains(freeV2Endpoint)) {
|
||||||
|
return apiDefs::ConfigType::AmneziaFreeV2;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
case apiDefs::ConfigSource::AmneziaGateway: {
|
case apiDefs::ConfigSource::AmneziaGateway: {
|
||||||
constexpr QLatin1String stackPremium("prem");
|
|
||||||
constexpr QLatin1String stackFree("free");
|
|
||||||
|
|
||||||
constexpr QLatin1String servicePremium("amnezia-premium");
|
constexpr QLatin1String servicePremium("amnezia-premium");
|
||||||
constexpr QLatin1String serviceFree("amnezia-free");
|
constexpr QLatin1String serviceFree("amnezia-free");
|
||||||
constexpr QLatin1String serviceExternalPremium("external-premium");
|
constexpr QLatin1String serviceExternalPremium("external-premium");
|
||||||
|
|
@ -70,6 +96,9 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
||||||
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
||||||
qDebug() << reply->error();
|
qDebug() << reply->error();
|
||||||
return amnezia::ErrorCode::ApiConfigTimeoutError;
|
return amnezia::ErrorCode::ApiConfigTimeoutError;
|
||||||
|
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
|
||||||
|
qDebug() << reply->error();
|
||||||
|
return amnezia::ErrorCode::ApiUpdateRequestError;
|
||||||
} else {
|
} else {
|
||||||
QString err = reply->errorString();
|
QString err = reply->errorString();
|
||||||
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
@ -95,3 +124,41 @@ bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
|
||||||
apiDefs::ConfigType::ExternalPremium };
|
apiDefs::ConfigType::ExternalPremium };
|
||||||
return premiumTypes.contains(getConfigType(serverConfigObject));
|
return premiumTypes.contains(getConfigType(serverConfigObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
|
||||||
|
{
|
||||||
|
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QPair<QString, QVariant>> orderedFields;
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::name, serverConfigObject[apiDefs::key::name].toString()));
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::description, serverConfigObject[apiDefs::key::description].toString()));
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::configVersion, serverConfigObject[apiDefs::key::configVersion].toDouble()));
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::protocol, serverConfigObject[apiDefs::key::protocol].toString()));
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::apiEndpoint, serverConfigObject[apiDefs::key::apiEndpoint].toString()));
|
||||||
|
orderedFields.append(qMakePair(apiDefs::key::apiKey, serverConfigObject[apiDefs::key::apiKey].toString()));
|
||||||
|
|
||||||
|
QString vpnKeyStr = "{";
|
||||||
|
for (int i = 0; i < orderedFields.size(); ++i) {
|
||||||
|
const auto &pair = orderedFields[i];
|
||||||
|
if (pair.second.typeId() == QMetaType::Type::QString) {
|
||||||
|
vpnKeyStr += "\"" + pair.first + "\": \"" + pair.second.toString() + "\"";
|
||||||
|
} else if (pair.second.typeId() == QMetaType::Type::Double || pair.second.typeId() == QMetaType::Type::Int) {
|
||||||
|
vpnKeyStr += "\"" + pair.first + "\": " + QString::number(pair.second.toDouble(), 'f', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < orderedFields.size() - 1) {
|
||||||
|
vpnKeyStr += ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vpnKeyStr += "}";
|
||||||
|
|
||||||
|
QByteArray vpnKeyCompressed = escapeUnicode(vpnKeyStr).toUtf8();
|
||||||
|
vpnKeyCompressed = qCompress(vpnKeyCompressed, 6);
|
||||||
|
vpnKeyCompressed = vpnKeyCompressed.mid(4);
|
||||||
|
|
||||||
|
QByteArray signedData = AMNEZIA_CONFIG_SIGNATURE + vpnKeyCompressed;
|
||||||
|
|
||||||
|
return QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding)));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ namespace apiUtils
|
||||||
apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
|
apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
|
||||||
|
|
||||||
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply);
|
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply);
|
||||||
|
|
||||||
|
QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // APIUTILS_H
|
#endif // APIUTILS_H
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,9 @@ void CoreController::initControllers()
|
||||||
|
|
||||||
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
|
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
|
||||||
|
|
||||||
|
m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initAndroidController()
|
void CoreController::initAndroidController()
|
||||||
|
|
@ -220,6 +223,8 @@ void CoreController::initSignalHandlers()
|
||||||
initAutoConnectHandler();
|
initAutoConnectHandler();
|
||||||
initAmneziaDnsToggledHandler();
|
initAmneziaDnsToggledHandler();
|
||||||
initPrepareConfigHandler();
|
initPrepareConfigHandler();
|
||||||
|
initImportPremiumV2VpnKeyHandler();
|
||||||
|
initShowMigrationDrawerHandler();
|
||||||
initStrictKillSwitchHandler();
|
initStrictKillSwitchHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,10 +368,29 @@ void CoreController::initPrepareConfigHandler()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CoreController::initImportPremiumV2VpnKeyHandler()
|
||||||
|
{
|
||||||
|
connect(m_apiPremV1MigrationController.get(), &ApiPremV1MigrationController::importPremiumV2VpnKey, this, [this](const QString &vpnKey) {
|
||||||
|
m_importController->extractConfigFromData(vpnKey);
|
||||||
|
m_importController->importConfig();
|
||||||
|
|
||||||
|
emit m_apiPremV1MigrationController->migrationFinished();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreController::initShowMigrationDrawerHandler()
|
||||||
|
{
|
||||||
|
QTimer::singleShot(1000, this, [this]() {
|
||||||
|
if (m_apiPremV1MigrationController->isPremV1MigrationReminderActive() && m_apiPremV1MigrationController->hasConfigsToMigration()) {
|
||||||
|
m_apiPremV1MigrationController->showMigrationDrawer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void CoreController::initStrictKillSwitchHandler()
|
void CoreController::initStrictKillSwitchHandler()
|
||||||
{
|
{
|
||||||
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged,
|
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(),
|
||||||
m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged);
|
&VpnConnection::onKillSwitchModeChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<PageController> CoreController::pageController() const
|
QSharedPointer<PageController> CoreController::pageController() const
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "ui/controllers/api/apiConfigsController.h"
|
#include "ui/controllers/api/apiConfigsController.h"
|
||||||
#include "ui/controllers/api/apiSettingsController.h"
|
#include "ui/controllers/api/apiSettingsController.h"
|
||||||
|
#include "ui/controllers/api/apiPremV1MigrationController.h"
|
||||||
#include "ui/controllers/appSplitTunnelingController.h"
|
#include "ui/controllers/appSplitTunnelingController.h"
|
||||||
#include "ui/controllers/allowedDnsController.h"
|
#include "ui/controllers/allowedDnsController.h"
|
||||||
#include "ui/controllers/connectionController.h"
|
#include "ui/controllers/connectionController.h"
|
||||||
|
|
@ -82,6 +83,8 @@ private:
|
||||||
void initAutoConnectHandler();
|
void initAutoConnectHandler();
|
||||||
void initAmneziaDnsToggledHandler();
|
void initAmneziaDnsToggledHandler();
|
||||||
void initPrepareConfigHandler();
|
void initPrepareConfigHandler();
|
||||||
|
void initImportPremiumV2VpnKeyHandler();
|
||||||
|
void initShowMigrationDrawerHandler();
|
||||||
void initStrictKillSwitchHandler();
|
void initStrictKillSwitchHandler();
|
||||||
|
|
||||||
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
||||||
|
|
@ -109,6 +112,7 @@ private:
|
||||||
|
|
||||||
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
||||||
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
||||||
|
QScopedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationController;
|
||||||
|
|
||||||
QSharedPointer<ContainersModel> m_containersModel;
|
QSharedPointer<ContainersModel> m_containersModel;
|
||||||
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
|
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
|
|
||||||
#include "amnezia_application.h"
|
#include "amnezia_application.h"
|
||||||
#include "core/api/apiUtils.h"
|
#include "core/api/apiUtils.h"
|
||||||
#include "utilities.h"
|
|
||||||
#include "core/networkUtilities.h"
|
#include "core/networkUtilities.h"
|
||||||
|
#include "utilities.h"
|
||||||
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
#ifdef AMNEZIA_DESKTOP
|
||||||
#include "core/ipcclient.h"
|
#include "core/ipcclient.h"
|
||||||
|
|
@ -36,10 +36,17 @@ namespace
|
||||||
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
|
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
|
||||||
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
|
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
|
||||||
constexpr QLatin1String errorResponsePattern3("Account not found.");
|
constexpr QLatin1String errorResponsePattern3("Account not found.");
|
||||||
|
|
||||||
|
constexpr QLatin1String updateRequestResponsePattern("client version update is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent)
|
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
|
||||||
: QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment), m_requestTimeoutMsecs(requestTimeoutMsecs)
|
const bool isStrictKillSwitchEnabled, QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_gatewayEndpoint(gatewayEndpoint),
|
||||||
|
m_isDevEnvironment(isDevEnvironment),
|
||||||
|
m_requestTimeoutMsecs(requestTimeoutMsecs),
|
||||||
|
m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +65,7 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo
|
||||||
|
|
||||||
// bypass killSwitch exceptions for API-gateway
|
// bypass killSwitch exceptions for API-gateway
|
||||||
#ifdef AMNEZIA_DESKTOP
|
#ifdef AMNEZIA_DESKTOP
|
||||||
{
|
if (m_isStrictKillSwitchEnabled) {
|
||||||
QString host = QUrl(request.url()).host();
|
QString host = QUrl(request.url()).host();
|
||||||
QString ip = NetworkUtilities::getIPAddress(host);
|
QString ip = NetworkUtilities::getIPAddress(host);
|
||||||
if (!ip.isEmpty()) {
|
if (!ip.isEmpty()) {
|
||||||
|
|
@ -120,7 +127,7 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||||
|
|
||||||
// bypass killSwitch exceptions for API-gateway
|
// bypass killSwitch exceptions for API-gateway
|
||||||
#ifdef AMNEZIA_DESKTOP
|
#ifdef AMNEZIA_DESKTOP
|
||||||
{
|
if (m_isStrictKillSwitchEnabled) {
|
||||||
QString host = QUrl(request.url()).host();
|
QString host = QUrl(request.url()).host();
|
||||||
QString ip = NetworkUtilities::getIPAddress(host);
|
QString ip = NetworkUtilities::getIPAddress(host);
|
||||||
if (!ip.isEmpty()) {
|
if (!ip.isEmpty()) {
|
||||||
|
|
@ -306,6 +313,13 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray
|
||||||
qDebug() << reply->error();
|
qDebug() << reply->error();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
|
||||||
|
if (responseBody.contains(updateRequestResponsePattern)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
qDebug() << reply->error();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} else if (reply->error() != QNetworkReply::NetworkError::NoError) {
|
} else if (reply->error() != QNetworkReply::NetworkError::NoError) {
|
||||||
qDebug() << reply->error();
|
qDebug() << reply->error();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ class GatewayController : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent = nullptr);
|
explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
|
||||||
|
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
|
||||||
|
|
||||||
amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody);
|
amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody);
|
||||||
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
|
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
|
||||||
|
|
@ -30,6 +31,7 @@ private:
|
||||||
int m_requestTimeoutMsecs;
|
int m_requestTimeoutMsecs;
|
||||||
QString m_gatewayEndpoint;
|
QString m_gatewayEndpoint;
|
||||||
bool m_isDevEnvironment = false;
|
bool m_isDevEnvironment = false;
|
||||||
|
bool m_isStrictKillSwitchEnabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GATEWAYCONTROLLER_H
|
#endif // GATEWAYCONTROLLER_H
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||||
|
|
||||||
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
|
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
|
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path),
|
||||||
genVarsForScript(credentials, container)),
|
genVarsForScript(credentials, container)),
|
||||||
cbReadStd, cbReadStd);
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
|
|
@ -146,7 +146,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||||
return e;
|
return e;
|
||||||
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
|
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
|
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName),
|
||||||
genVarsForScript(credentials, container)),
|
genVarsForScript(credentials, container)),
|
||||||
cbReadStd, cbReadStd);
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
|
|
@ -154,7 +154,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path),
|
||||||
genVarsForScript(credentials, container)),
|
genVarsForScript(credentials, container)),
|
||||||
cbReadStd, cbReadStd);
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
|
|
@ -177,7 +177,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
||||||
|
|
||||||
errorCode = ErrorCode::NoError;
|
errorCode = ErrorCode::NoError;
|
||||||
|
|
||||||
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").arg(ContainerProps::containerToString(container)).arg(path);
|
QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path);
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
|
@ -383,6 +383,13 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (container == DockerContainer::Xray) {
|
||||||
|
if (oldProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)
|
||||||
|
!= newProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -453,6 +460,8 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
|
||||||
return ErrorCode::ServerDockerOnCgroupsV2;
|
return ErrorCode::ServerDockerOnCgroupsV2;
|
||||||
if (stdOut.contains("cgroup mountpoint does not exist"))
|
if (stdOut.contains("cgroup mountpoint does not exist"))
|
||||||
return ErrorCode::ServerCgroupMountpoint;
|
return ErrorCode::ServerCgroupMountpoint;
|
||||||
|
if (stdOut.contains("have reached") && stdOut.contains("pull rate limit"))
|
||||||
|
return ErrorCode::DockerPullRateLimit;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
@ -818,7 +827,7 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
||||||
|
|
||||||
if (stdOut.contains("Packet manager not found"))
|
if (stdOut.contains("Packet manager not found"))
|
||||||
return ErrorCode::ServerPacketManagerError;
|
return ErrorCode::ServerPacketManagerError;
|
||||||
if (stdOut.contains("fuser not installed"))
|
if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed"))
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
|
|
||||||
if (stdOut.isEmpty()) {
|
if (stdOut.isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ namespace amnezia
|
||||||
ServerUserPasswordRequired = 210,
|
ServerUserPasswordRequired = 210,
|
||||||
ServerDockerOnCgroupsV2 = 211,
|
ServerDockerOnCgroupsV2 = 211,
|
||||||
ServerCgroupMountpoint = 212,
|
ServerCgroupMountpoint = 212,
|
||||||
|
DockerPullRateLimit = 213,
|
||||||
|
|
||||||
// Ssh connection errors
|
// Ssh connection errors
|
||||||
SshRequestDeniedError = 300,
|
SshRequestDeniedError = 300,
|
||||||
|
|
@ -117,6 +118,8 @@ namespace amnezia
|
||||||
ApiServicesMissingError = 1107,
|
ApiServicesMissingError = 1107,
|
||||||
ApiConfigLimitError = 1108,
|
ApiConfigLimitError = 1108,
|
||||||
ApiNotFoundError = 1109,
|
ApiNotFoundError = 1109,
|
||||||
|
ApiMigrationError = 1110,
|
||||||
|
ApiUpdateRequestError = 1111,
|
||||||
|
|
||||||
// QFile errors
|
// QFile errors
|
||||||
OpenError = 1200,
|
OpenError = 1200,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ QString errorString(ErrorCode code) {
|
||||||
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
|
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
|
||||||
case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break;
|
case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break;
|
||||||
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
|
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
|
||||||
|
case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); 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;
|
||||||
|
|
@ -74,6 +75,8 @@ QString errorString(ErrorCode code) {
|
||||||
case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break;
|
case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break;
|
||||||
case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break;
|
case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break;
|
||||||
case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
|
case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
|
||||||
|
case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break;
|
||||||
|
case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break;
|
||||||
|
|
||||||
// QFile errors
|
// QFile errors
|
||||||
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
|
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
|
||||||
|
|
|
||||||
|
|
@ -149,8 +149,7 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||||
// set routing
|
// set routing
|
||||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||||
if (!wgutils()->updateRoutePrefix(ip)) {
|
if (!wgutils()->updateRoutePrefix(ip)) {
|
||||||
logger.debug() << "Routing configuration failed for"
|
logger.debug() << "Routing configuration failed for" << ip.toString();
|
||||||
<< logger.sensitive(ip.toString());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,21 @@ set_target_properties(networkextension PROPERTIES
|
||||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(DEPLOY)
|
||||||
|
set_target_properties(networkextension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(networkextension PROPERTIES
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
set_target_properties(networkextension PROPERTIES
|
set_target_properties(networkextension PROPERTIES
|
||||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) {
|
||||||
// Set ifr to interface
|
// Set ifr to interface
|
||||||
int ret = ioctl(sockfd, SIOCSIFADDR, &ifr);
|
int ret = ioctl(sockfd, SIOCSIFADDR, &ifr);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr)
|
logger.error() << "Failed to set IPv4: " << deviceAddr
|
||||||
<< "error:" << strerror(errno);
|
<< "error:" << strerror(errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +138,7 @@ bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) {
|
||||||
// Set ifr6 to the interface
|
// Set ifr6 to the interface
|
||||||
ret = ioctl(sockfd, SIOCSIFADDR, &ifr6);
|
ret = ioctl(sockfd, SIOCSIFADDR, &ifr6);
|
||||||
if (ret && (errno != EEXIST)) {
|
if (ret && (errno != EEXIST)) {
|
||||||
logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr)
|
logger.error() << "Failed to set IPv6: " << deviceAddr
|
||||||
<< "error:" << strerror(errno);
|
<< "error:" << strerror(errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) {
|
||||||
// Set ifr to interface
|
// Set ifr to interface
|
||||||
int ret = ioctl(sockfd, SIOCAIFADDR, &ifr);
|
int ret = ioctl(sockfd, SIOCAIFADDR, &ifr);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr)
|
logger.error() << "Failed to set IPv4: " << deviceAddr
|
||||||
<< "error:" << strerror(errno);
|
<< "error:" << strerror(errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +162,7 @@ bool IPUtilsMacos::addIP6AddressToDevice(const InterfaceConfig& config) {
|
||||||
// Set ifr to interface
|
// Set ifr to interface
|
||||||
int ret = ioctl(sockfd, SIOCAIFADDR_IN6, &ifr6);
|
int ret = ioctl(sockfd, SIOCAIFADDR_IN6, &ifr6);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr)
|
logger.error() << "Failed to set IPv6: " << deviceAddr
|
||||||
<< "error:" << strerror(errno);
|
<< "error:" << strerror(errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm,
|
||||||
for (const IPAddress& prefix : m_exclusionRoutes) {
|
for (const IPAddress& prefix : m_exclusionRoutes) {
|
||||||
if (prefix.address().protocol() == protocol) {
|
if (prefix.address().protocol() == protocol) {
|
||||||
logger.debug() << "Removing exclusion route to"
|
logger.debug() << "Removing exclusion route to"
|
||||||
<< logger.sensitive(prefix.toString());
|
<< prefix.toString();
|
||||||
rtmSendRoute(RTM_DELETE, prefix, rtm->rtm_index, nullptr);
|
rtmSendRoute(RTM_DELETE, prefix, rtm->rtm_index, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +259,7 @@ void MacosRouteMonitor::handleRtmUpdate(const struct rt_msghdr* rtm,
|
||||||
for (const IPAddress& prefix : m_exclusionRoutes) {
|
for (const IPAddress& prefix : m_exclusionRoutes) {
|
||||||
if (prefix.address().protocol() == protocol) {
|
if (prefix.address().protocol() == protocol) {
|
||||||
logger.debug() << "Updating exclusion route to"
|
logger.debug() << "Updating exclusion route to"
|
||||||
<< logger.sensitive(prefix.toString());
|
<< prefix.toString();
|
||||||
rtmSendRoute(rtm_type, prefix, ifindex, addrlist[1].constData());
|
rtmSendRoute(rtm_type, prefix, ifindex, addrlist[1].constData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -510,8 +510,7 @@ bool MacosRouteMonitor::deleteRoute(const IPAddress& prefix, int flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||||
logger.debug() << "Adding exclusion route for"
|
logger.debug() << "Adding exclusion route for" << prefix.toString();
|
||||||
<< logger.sensitive(prefix.toString());
|
|
||||||
|
|
||||||
if (m_exclusionRoutes.contains(prefix)) {
|
if (m_exclusionRoutes.contains(prefix)) {
|
||||||
logger.warning() << "Exclusion route already exists";
|
logger.warning() << "Exclusion route already exists";
|
||||||
|
|
@ -536,8 +535,7 @@ bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacosRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
bool MacosRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
logger.debug() << "Deleting exclusion route for"
|
logger.debug() << "Deleting exclusion route for" << prefix.toString();
|
||||||
<< logger.sensitive(prefix.toString());
|
|
||||||
|
|
||||||
m_exclusionRoutes.removeAll(prefix);
|
m_exclusionRoutes.removeAll(prefix);
|
||||||
if (prefix.address().protocol() == QAbstractSocket::IPv4Protocol) {
|
if (prefix.address().protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
|
|
||||||
|
|
@ -256,7 +256,7 @@ bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) {
|
||||||
|
|
||||||
for (const QString& addr : ranges) {
|
for (const QString& addr : ranges) {
|
||||||
logger.debug() << "Allow killswitch exclude: " << addr;
|
logger.debug() << "Allow killswitch exclude: " << addr;
|
||||||
if (!allowTrafficTo(QHostAddress(addr), LOW_WEIGHT + 1, "Allow killswitch bypass traffic")) {
|
if (!allowTrafficTo(QHostAddress(addr), HIGH_WEIGHT, "Allow killswitch bypass traffic")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -303,8 +303,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
|
||||||
data->Age++;
|
data->Age++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
logger.debug() << "Capturing route to"
|
logger.debug() << "Capturing route to" << prefix.toString();
|
||||||
<< logger.sensitive(prefix.toString());
|
|
||||||
|
|
||||||
// Clone the route and direct it into the VPN tunnel.
|
// Clone the route and direct it into the VPN tunnel.
|
||||||
data = new MIB_IPFORWARD_ROW2;
|
data = new MIB_IPFORWARD_ROW2;
|
||||||
|
|
@ -354,8 +353,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug() << "Removing route capture for"
|
logger.debug() << "Removing route capture for" << i.key().toString();
|
||||||
<< logger.sensitive(i.key().toString());
|
|
||||||
|
|
||||||
// Otherwise, this route is no longer in use.
|
// Otherwise, this route is no longer in use.
|
||||||
DWORD result = DeleteIpForwardEntry2(data);
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
|
|
@ -368,8 +366,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||||
logger.debug() << "Adding exclusion route for"
|
logger.debug() << "Adding exclusion route for" << prefix.toString();
|
||||||
<< logger.sensitive(prefix.toString());
|
|
||||||
|
|
||||||
// Silently ignore non-routeable addresses.
|
// Silently ignore non-routeable addresses.
|
||||||
QHostAddress addr = prefix.address();
|
QHostAddress addr = prefix.address();
|
||||||
|
|
@ -437,7 +434,7 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||||
|
|
||||||
bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
logger.debug() << "Deleting exclusion route for"
|
logger.debug() << "Deleting exclusion route for"
|
||||||
<< logger.sensitive(prefix.address().toString());
|
<< prefix.address().toString();
|
||||||
|
|
||||||
MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix);
|
MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix);
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
|
|
@ -447,7 +444,7 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
DWORD result = DeleteIpForwardEntry2(data);
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
||||||
logger.error() << "Failed to delete route to"
|
logger.error() << "Failed to delete route to"
|
||||||
<< logger.sensitive(prefix.toString())
|
<< prefix.toString()
|
||||||
<< "result:" << result;
|
<< "result:" << result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -465,7 +462,7 @@ void WindowsRouteMonitor::flushRouteTable(
|
||||||
DWORD result = DeleteIpForwardEntry2(data);
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
||||||
logger.error() << "Failed to delete route to"
|
logger.error() << "Failed to delete route to"
|
||||||
<< logger.sensitive(i.key().toString())
|
<< i.key().toString()
|
||||||
<< "result:" << result;
|
<< "result:" << result;
|
||||||
}
|
}
|
||||||
delete data;
|
delete data;
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
|
||||||
// Enable the windows firewall
|
// Enable the windows firewall
|
||||||
NET_IFINDEX ifindex;
|
NET_IFINDEX ifindex;
|
||||||
ConvertInterfaceLuidToIndex(&luid, &ifindex);
|
ConvertInterfaceLuidToIndex(&luid, &ifindex);
|
||||||
|
m_firewall->allowAllTraffic();
|
||||||
m_firewall->enableInterface(ifindex);
|
m_firewall->enableInterface(ifindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ ErrorCode OpenVpnProtocol::start()
|
||||||
return lastError();
|
return lastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
#ifdef AMNEZIA_DESKTOP
|
||||||
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList(NetworkUtilities::getIPAddress(
|
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList(NetworkUtilities::getIPAddress(
|
||||||
m_configData.value(amnezia::config_key::hostName).toString())));
|
m_configData.value(amnezia::config_key::hostName).toString())));
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -343,7 +343,7 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line)
|
||||||
// killSwitch toggle
|
// killSwitch toggle
|
||||||
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
|
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
|
||||||
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
|
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
|
||||||
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
|
IpcClient::Interface()->enableKillSwitch(m_configData, netInterfaces.at(i).index());
|
||||||
}
|
}
|
||||||
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
|
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
|
||||||
m_configData.insert("vpnGateway", m_vpnGateway);
|
m_configData.insert("vpnGateway", m_vpnGateway);
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,8 @@ namespace amnezia
|
||||||
|
|
||||||
constexpr char clientId[] = "clientId";
|
constexpr char clientId[] = "clientId";
|
||||||
|
|
||||||
|
constexpr char nameOverriddenByUser[] = "nameOverriddenByUser";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace protocols
|
namespace protocols
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ ErrorCode XrayProtocol::startTun2Sock()
|
||||||
// killSwitch toggle
|
// killSwitch toggle
|
||||||
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
|
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
|
||||||
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
|
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
|
||||||
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
|
IpcClient::Interface()->enableKillSwitch(m_configData, netInterfaces.at(i).index());
|
||||||
}
|
}
|
||||||
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
|
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
|
||||||
m_configData.insert("vpnGateway", m_vpnGateway);
|
m_configData.insert("vpnGateway", m_vpnGateway);
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,9 @@
|
||||||
<file>ui/qml/Pages2/PageSettingsApiNativeConfigs.qml</file>
|
<file>ui/qml/Pages2/PageSettingsApiNativeConfigs.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsApiDevices.qml</file>
|
<file>ui/qml/Pages2/PageSettingsApiDevices.qml</file>
|
||||||
<file>images/controls/monitor.svg</file>
|
<file>images/controls/monitor.svg</file>
|
||||||
|
<file>ui/qml/Components/ApiPremV1MigrationDrawer.qml</file>
|
||||||
|
<file>ui/qml/Components/ApiPremV1SubListDrawer.qml</file>
|
||||||
|
<file>ui/qml/Components/OtpCodeDrawer.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/countriesFlags">
|
<qresource prefix="/countriesFlags">
|
||||||
<file>images/flagKit/ZW.svg</file>
|
<file>images/flagKit/ZW.svg</file>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
if which apt-get > /dev/null 2>&1; then LOCK_FILE="/var/lib/dpkg/lock-frontend";\
|
if which apt-get > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/dpkg/lock-frontend";\
|
||||||
elif which dnf > /dev/null 2>&1; then LOCK_FILE="/var/run/dnf.pid";\
|
elif which dnf > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/cache/dnf/* /var/run/dnf/* /var/lib/dnf/* /var/lib/rpm/*";\
|
||||||
elif which yum > /dev/null 2>&1; then LOCK_FILE="/var/run/yum.pid";\
|
elif which yum > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/yum.pid";\
|
||||||
elif which pacman > /dev/null 2>&1; then LOCK_FILE="/var/lib/pacman/db.lck";\
|
elif which zypper > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/zypp.pid";\
|
||||||
|
elif which pacman > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/pacman/db.lck";\
|
||||||
else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\
|
else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\
|
||||||
if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; fi
|
if command -v $LOCK_CMD > /dev/null 2>&1; then sudo $LOCK_CMD $LOCK_FILE 2>/dev/null; else echo "$LOCK_CMD not installed"; fi
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\
|
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\
|
||||||
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\
|
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 yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\
|
||||||
|
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); opt="--version";\
|
||||||
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
|
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
|
||||||
else pm="uname"; opt="-a";\
|
else pm="uname"; opt="-a";\
|
||||||
fi;\
|
fi;\
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
|
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
|
||||||
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
|
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
|
||||||
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
|
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
|
||||||
|
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); silent_inst="-nq install"; check_pkgs="-nq refresh"; docker_pkg="docker"; dist="opensuse";\
|
||||||
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\
|
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\
|
||||||
else echo "Packet manager not found"; exit 1; fi;\
|
else echo "Packet manager not found"; exit 1; fi;\
|
||||||
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
|
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
|
||||||
|
|
|
||||||
|
|
@ -559,6 +559,16 @@ void Settings::disableHomeAdLabel()
|
||||||
setValue("Conf/homeAdLabelVisible", false);
|
setValue("Conf/homeAdLabelVisible", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Settings::isPremV1MigrationReminderActive()
|
||||||
|
{
|
||||||
|
return value("Conf/premV1MigrationReminderActive", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::disablePremV1MigrationReminder()
|
||||||
|
{
|
||||||
|
setValue("Conf/premV1MigrationReminderActive", false);
|
||||||
|
}
|
||||||
|
|
||||||
QStringList Settings::allowedDnsServers() const
|
QStringList Settings::allowedDnsServers() const
|
||||||
{
|
{
|
||||||
return value("Conf/allowedDnsServers").toStringList();
|
return value("Conf/allowedDnsServers").toStringList();
|
||||||
|
|
|
||||||
|
|
@ -174,11 +174,12 @@ public:
|
||||||
|
|
||||||
QLocale getAppLanguage()
|
QLocale getAppLanguage()
|
||||||
{
|
{
|
||||||
return value("Conf/appLanguage", QLocale()).toLocale();
|
QString localeStr = m_settings.value("Conf/appLanguage").toString();
|
||||||
|
return QLocale(localeStr);
|
||||||
};
|
};
|
||||||
void setAppLanguage(QLocale locale)
|
void setAppLanguage(QLocale locale)
|
||||||
{
|
{
|
||||||
setValue("Conf/appLanguage", locale);
|
setValue("Conf/appLanguage", locale.name());
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isScreenshotsEnabled() const
|
bool isScreenshotsEnabled() const
|
||||||
|
|
@ -229,6 +230,9 @@ public:
|
||||||
bool isHomeAdLabelVisible();
|
bool isHomeAdLabelVisible();
|
||||||
void disableHomeAdLabel();
|
void disableHomeAdLabel();
|
||||||
|
|
||||||
|
bool isPremV1MigrationReminderActive();
|
||||||
|
void disablePremV1MigrationReminder();
|
||||||
|
|
||||||
QStringList allowedDnsServers() const;
|
QStringList allowedDnsServers() const;
|
||||||
void setAllowedDnsServers(const QStringList &servers);
|
void setAllowedDnsServers(const QStringList &servers);
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,34 +0,0 @@
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
TextArea {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
topPadding: 16
|
|
||||||
leftPadding: 16
|
|
||||||
|
|
||||||
color: "#D7D8DB"
|
|
||||||
selectionColor: "#412102"
|
|
||||||
selectedTextColor: "#D7D8DB"
|
|
||||||
placeholderTextColor: "#878B91"
|
|
||||||
|
|
||||||
font.pixelSize: 16
|
|
||||||
font.weight: Font.Medium
|
|
||||||
font.family: "PT Root UI VF"
|
|
||||||
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onClicked: contextMenu.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextMenuType {
|
|
||||||
id: contextMenu
|
|
||||||
textObj: textField
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -63,7 +63,8 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
||||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||||
|
|
@ -76,6 +77,7 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
||||||
apiPayload[configKey::serverCountryCode] = serverCountryCode;
|
apiPayload[configKey::serverCountryCode] = serverCountryCode;
|
||||||
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
||||||
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
||||||
|
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody);
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody);
|
||||||
|
|
@ -94,7 +96,8 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
||||||
|
|
||||||
bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
|
bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
|
||||||
{
|
{
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
||||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||||
|
|
@ -107,6 +110,7 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
|
||||||
apiPayload[configKey::serverCountryCode] = serverCountryCode;
|
apiPayload[configKey::serverCountryCode] = serverCountryCode;
|
||||||
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
||||||
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
||||||
|
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody);
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody);
|
||||||
|
|
@ -140,7 +144,8 @@ void ApiConfigsController::copyVpnKeyToClipboard()
|
||||||
|
|
||||||
bool ApiConfigsController::fillAvailableServices()
|
bool ApiConfigsController::fillAvailableServices()
|
||||||
{
|
{
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
QJsonObject apiPayload;
|
QJsonObject apiPayload;
|
||||||
apiPayload[configKey::osVersion] = QSysInfo::productType();
|
apiPayload[configKey::osVersion] = QSysInfo::productType();
|
||||||
|
|
@ -171,7 +176,8 @@ bool ApiConfigsController::importServiceFromGateway()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
auto installationUuid = m_settings->getInstallationUuid(true);
|
auto installationUuid = m_settings->getInstallationUuid(true);
|
||||||
auto userCountryCode = m_apiServicesModel->getCountryCode();
|
auto userCountryCode = m_apiServicesModel->getCountryCode();
|
||||||
|
|
@ -184,6 +190,7 @@ bool ApiConfigsController::importServiceFromGateway()
|
||||||
apiPayload[configKey::userCountryCode] = userCountryCode;
|
apiPayload[configKey::userCountryCode] = userCountryCode;
|
||||||
apiPayload[configKey::serviceType] = serviceType;
|
apiPayload[configKey::serviceType] = serviceType;
|
||||||
apiPayload[configKey::uuid] = installationUuid;
|
apiPayload[configKey::uuid] = installationUuid;
|
||||||
|
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
|
||||||
|
|
@ -211,7 +218,8 @@ bool ApiConfigsController::importServiceFromGateway()
|
||||||
bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
|
bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
|
||||||
bool reloadServiceConfig)
|
bool reloadServiceConfig)
|
||||||
{
|
{
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
|
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||||
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
|
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
|
||||||
|
|
@ -228,6 +236,7 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
||||||
apiPayload[configKey::userCountryCode] = userCountryCode;
|
apiPayload[configKey::userCountryCode] = userCountryCode;
|
||||||
apiPayload[configKey::serviceType] = serviceType;
|
apiPayload[configKey::serviceType] = serviceType;
|
||||||
apiPayload[configKey::uuid] = installationUuid;
|
apiPayload[configKey::uuid] = installationUuid;
|
||||||
|
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||||
|
|
||||||
if (!newCountryCode.isEmpty()) {
|
if (!newCountryCode.isEmpty()) {
|
||||||
apiPayload[configKey::serverCountryCode] = newCountryCode;
|
apiPayload[configKey::serverCountryCode] = newCountryCode;
|
||||||
|
|
@ -252,6 +261,10 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
||||||
newServerConfig.insert(configKey::apiConfig, newApiConfig);
|
newServerConfig.insert(configKey::apiConfig, newApiConfig);
|
||||||
newServerConfig.insert(configKey::authData, authData);
|
newServerConfig.insert(configKey::authData, authData);
|
||||||
|
|
||||||
|
if (serverConfig.value(config_key::nameOverriddenByUser).toBool()) {
|
||||||
|
newServerConfig.insert(config_key::name, serverConfig.value(config_key::name));
|
||||||
|
newServerConfig.insert(config_key::nameOverriddenByUser, true);
|
||||||
|
}
|
||||||
m_serversModel->editServer(newServerConfig, serverIndex);
|
m_serversModel->editServer(newServerConfig, serverIndex);
|
||||||
if (reloadServiceConfig) {
|
if (reloadServiceConfig) {
|
||||||
emit reloadServerFromApiFinished(tr("API config reloaded"));
|
emit reloadServerFromApiFinished(tr("API config reloaded"));
|
||||||
|
|
@ -274,7 +287,8 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
|
||||||
QThread::msleep(10);
|
QThread::msleep(10);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
|
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||||
auto installationUuid = m_settings->getInstallationUuid(true);
|
auto installationUuid = m_settings->getInstallationUuid(true);
|
||||||
|
|
@ -304,7 +318,8 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
|
||||||
|
|
||||||
bool ApiConfigsController::deactivateDevice()
|
bool ApiConfigsController::deactivateDevice()
|
||||||
{
|
{
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||||
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
||||||
|
|
@ -323,6 +338,7 @@ bool ApiConfigsController::deactivateDevice()
|
||||||
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
||||||
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
||||||
apiPayload[configKey::uuid] = m_settings->getInstallationUuid(true);
|
apiPayload[configKey::uuid] = m_settings->getInstallationUuid(true);
|
||||||
|
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||||
|
|
@ -339,7 +355,8 @@ bool ApiConfigsController::deactivateDevice()
|
||||||
|
|
||||||
bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode)
|
bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode)
|
||||||
{
|
{
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||||
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
||||||
|
|
@ -358,6 +375,7 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q
|
||||||
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
||||||
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
||||||
apiPayload[configKey::uuid] = uuid;
|
apiPayload[configKey::uuid] = uuid;
|
||||||
|
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||||
|
|
|
||||||
133
client/ui/controllers/api/apiPremV1MigrationController.cpp
Normal file
133
client/ui/controllers/api/apiPremV1MigrationController.cpp
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
#include "apiPremV1MigrationController.h"
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "core/api/apiDefs.h"
|
||||||
|
#include "core/api/apiUtils.h"
|
||||||
|
#include "core/controllers/gatewayController.h"
|
||||||
|
|
||||||
|
ApiPremV1MigrationController::ApiPremV1MigrationController(const QSharedPointer<ServersModel> &serversModel,
|
||||||
|
const std::shared_ptr<Settings> &settings, QObject *parent)
|
||||||
|
: QObject(parent), m_serversModel(serversModel), m_settings(settings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiPremV1MigrationController::hasConfigsToMigration()
|
||||||
|
{
|
||||||
|
QJsonArray vpnKeys;
|
||||||
|
|
||||||
|
auto serversCount = m_serversModel->getServersCount();
|
||||||
|
for (size_t i = 0; i < serversCount; i++) {
|
||||||
|
auto serverConfigObject = m_serversModel->getServerConfig(i);
|
||||||
|
|
||||||
|
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString vpnKey = apiUtils::getPremiumV1VpnKey(serverConfigObject);
|
||||||
|
vpnKeys.append(vpnKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vpnKeys.isEmpty()) {
|
||||||
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
QJsonObject apiPayload;
|
||||||
|
|
||||||
|
apiPayload["configs"] = vpnKeys;
|
||||||
|
QByteArray responseBody;
|
||||||
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/is-active-subscription"), apiPayload, responseBody);
|
||||||
|
|
||||||
|
auto migrationsStatus = QJsonDocument::fromJson(responseBody).object();
|
||||||
|
for (const auto &migrationStatus : migrationsStatus) {
|
||||||
|
if (migrationStatus == "not_found") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiPremV1MigrationController::getSubscriptionList(const QString &email)
|
||||||
|
{
|
||||||
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
QJsonObject apiPayload;
|
||||||
|
|
||||||
|
apiPayload[apiDefs::key::email] = email;
|
||||||
|
QByteArray responseBody;
|
||||||
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/subscription-list"), apiPayload, responseBody);
|
||||||
|
|
||||||
|
if (errorCode == ErrorCode::NoError) {
|
||||||
|
m_email = email;
|
||||||
|
m_subscriptionsModel = QJsonDocument::fromJson(responseBody).array();
|
||||||
|
if (m_subscriptionsModel.isEmpty()) {
|
||||||
|
emit noSubscriptionToMigrate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit subscriptionsModelChanged();
|
||||||
|
} else {
|
||||||
|
emit errorOccurred(ErrorCode::ApiMigrationError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray ApiPremV1MigrationController::getSubscriptionModel()
|
||||||
|
{
|
||||||
|
return m_subscriptionsModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex)
|
||||||
|
{
|
||||||
|
QEventLoop wait;
|
||||||
|
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
QJsonObject apiPayload;
|
||||||
|
|
||||||
|
apiPayload[apiDefs::key::email] = m_email;
|
||||||
|
QByteArray responseBody;
|
||||||
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migration-code"), apiPayload, responseBody);
|
||||||
|
|
||||||
|
if (errorCode == ErrorCode::NoError) {
|
||||||
|
m_subscriptionIndex = subscriptionIndex;
|
||||||
|
emit otpSuccessfullySent();
|
||||||
|
} else {
|
||||||
|
emit errorOccurred(ErrorCode::ApiMigrationError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiPremV1MigrationController::migrate(const QString &migrationCode)
|
||||||
|
{
|
||||||
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
QJsonObject apiPayload;
|
||||||
|
|
||||||
|
apiPayload[apiDefs::key::email] = m_email;
|
||||||
|
apiPayload[apiDefs::key::orderId] = m_subscriptionsModel.at(m_subscriptionIndex).toObject().value(apiDefs::key::id).toString();
|
||||||
|
apiPayload[apiDefs::key::migrationCode] = migrationCode;
|
||||||
|
QByteArray responseBody;
|
||||||
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migrate"), apiPayload, responseBody);
|
||||||
|
|
||||||
|
if (errorCode == ErrorCode::NoError) {
|
||||||
|
auto responseObject = QJsonDocument::fromJson(responseBody).object();
|
||||||
|
QString premiumV2VpnKey = responseObject.value(apiDefs::key::config).toString();
|
||||||
|
|
||||||
|
emit importPremiumV2VpnKey(premiumV2VpnKey);
|
||||||
|
} else {
|
||||||
|
emit errorOccurred(ErrorCode::ApiMigrationError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiPremV1MigrationController::isPremV1MigrationReminderActive()
|
||||||
|
{
|
||||||
|
return m_settings->isPremV1MigrationReminderActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiPremV1MigrationController::disablePremV1MigrationReminder()
|
||||||
|
{
|
||||||
|
m_settings->disablePremV1MigrationReminder();
|
||||||
|
}
|
||||||
50
client/ui/controllers/api/apiPremV1MigrationController.h
Normal file
50
client/ui/controllers/api/apiPremV1MigrationController.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef APIPREMV1MIGRATIONCONTROLLER_H
|
||||||
|
#define APIPREMV1MIGRATIONCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "ui/models/servers_model.h"
|
||||||
|
|
||||||
|
class ApiPremV1MigrationController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ApiPremV1MigrationController(const QSharedPointer<ServersModel> &serversModel, const std::shared_ptr<Settings> &settings,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
|
Q_PROPERTY(QJsonArray subscriptionsModel READ getSubscriptionModel NOTIFY subscriptionsModelChanged)
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool hasConfigsToMigration();
|
||||||
|
void getSubscriptionList(const QString &email);
|
||||||
|
QJsonArray getSubscriptionModel();
|
||||||
|
void sendMigrationCode(const int subscriptionIndex);
|
||||||
|
void migrate(const QString &migrationCode);
|
||||||
|
|
||||||
|
bool isPremV1MigrationReminderActive();
|
||||||
|
void disablePremV1MigrationReminder();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void subscriptionsModelChanged();
|
||||||
|
|
||||||
|
void otpSuccessfullySent();
|
||||||
|
|
||||||
|
void importPremiumV2VpnKey(const QString &vpnKey);
|
||||||
|
|
||||||
|
void errorOccurred(ErrorCode errorCode);
|
||||||
|
|
||||||
|
void showMigrationDrawer();
|
||||||
|
void migrationFinished();
|
||||||
|
|
||||||
|
void noSubscriptionToMigrate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<ServersModel> m_serversModel;
|
||||||
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
|
||||||
|
QJsonArray m_subscriptionsModel;
|
||||||
|
int m_subscriptionIndex;
|
||||||
|
QString m_email;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APIPREMV1MIGRATIONCONTROLLER_H
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "core/api/apiUtils.h"
|
#include "core/api/apiUtils.h"
|
||||||
#include "core/controllers/gatewayController.h"
|
#include "core/controllers/gatewayController.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
@ -48,7 +49,8 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
||||||
wait.exec();
|
wait.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs);
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
auto processedIndex = m_serversModel->getProcessedServerIndex();
|
auto processedIndex = m_serversModel->getProcessedServerIndex();
|
||||||
auto serverConfig = m_serversModel->getServerConfig(processedIndex);
|
auto serverConfig = m_serversModel->getServerConfig(processedIndex);
|
||||||
|
|
@ -59,16 +61,15 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
||||||
apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString();
|
apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString();
|
||||||
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
|
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
|
||||||
apiPayload[configKey::authData] = authData;
|
apiPayload[configKey::authData] = authData;
|
||||||
|
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
|
|
||||||
if (apiUtils::isPremiumServer(serverConfig)) {
|
|
||||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody);
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
emit errorOccurred(errorCode);
|
emit errorOccurred(errorCode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object();
|
QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object();
|
||||||
m_apiAccountInfoModel->updateModel(accountInfo, serverConfig);
|
m_apiAccountInfoModel->updateModel(accountInfo, serverConfig);
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ void ExportController::generateOpenVpnConfig(const QString &clientName)
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
||||||
for (const QString &line : lines) {
|
for (const QString &line : std::as_const(lines)) {
|
||||||
m_config.append(line + "\n");
|
m_config.append(line + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,7 +163,7 @@ void ExportController::generateWireGuardConfig(const QString &clientName)
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
||||||
for (const QString &line : lines) {
|
for (const QString &line : std::as_const(lines)) {
|
||||||
m_config.append(line + "\n");
|
m_config.append(line + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,7 +183,7 @@ void ExportController::generateAwgConfig(const QString &clientName)
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
||||||
for (const QString &line : lines) {
|
for (const QString &line : std::as_const(lines)) {
|
||||||
m_config.append(line + "\n");
|
m_config.append(line + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,7 +211,7 @@ void ExportController::generateShadowSocksConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
||||||
for (const QString &line : lines) {
|
for (const QString &line : std::as_const(lines)) {
|
||||||
m_config.append(line + "\n");
|
m_config.append(line + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,7 +240,7 @@ void ExportController::generateCloakConfig()
|
||||||
nativeConfig.insert("ProxyMethod", "shadowsocks");
|
nativeConfig.insert("ProxyMethod", "shadowsocks");
|
||||||
|
|
||||||
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
||||||
for (const QString &line : lines) {
|
for (const QString &line : std::as_const(lines)) {
|
||||||
m_config.append(line + "\n");
|
m_config.append(line + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,7 +257,7 @@ void ExportController::generateXrayConfig(const QString &clientName)
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
||||||
for (const QString &line : lines) {
|
for (const QString &line : std::as_const(lines)) {
|
||||||
m_config.append(line + "\n");
|
m_config.append(line + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -665,27 +665,27 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
|
||||||
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();
|
||||||
|
|
||||||
const QRegularExpression regExp { "(\\w+-\\w+|\\w+)" };
|
|
||||||
const size_t dangerousTagsMaxCount = 3;
|
|
||||||
|
|
||||||
// https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst
|
// https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst
|
||||||
QStringList dangerousTags {
|
QStringList dangerousTags {
|
||||||
"up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify"
|
"up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify"
|
||||||
};
|
};
|
||||||
|
|
||||||
QStringList maliciousStrings;
|
QStringList maliciousStrings;
|
||||||
QStringList lines = protocolConfigJson.replace("\r", "").split("\n");
|
QStringList lines = protocolConfigJson.split('\n', Qt::SkipEmptyParts);
|
||||||
for (const QString &l : lines) {
|
|
||||||
QRegularExpressionMatch match = regExp.match(l);
|
for (const QString &rawLine : lines) {
|
||||||
if (dangerousTags.contains(match.captured(0))) {
|
QString line = rawLine.trimmed();
|
||||||
maliciousStrings << l;
|
|
||||||
|
QString command = line.section(' ', 0, 0, QString::SectionSkipEmpty);
|
||||||
|
if (dangerousTags.contains(command, Qt::CaseInsensitive)) {
|
||||||
|
maliciousStrings << rawLine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_maliciousWarningText = tr("This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious "
|
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. ");
|
"scripts, so only add it if you fully trust the provider of this config. ");
|
||||||
|
|
||||||
if (maliciousStrings.size() >= dangerousTagsMaxCount) {
|
if (!maliciousStrings.isEmpty()) {
|
||||||
m_maliciousWarningText.push_back(tr("<br>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));
|
||||||
|
|
|
||||||
|
|
@ -363,7 +363,8 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||||
|
|
||||||
QJsonObject config;
|
QJsonObject config;
|
||||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||||
for (auto protocol : ContainerProps::protocolsForContainer(container)) {
|
const auto &protocols = ContainerProps::protocolsForContainer(container);
|
||||||
|
for (const auto &protocol : protocols) {
|
||||||
QJsonObject containerConfig;
|
QJsonObject containerConfig;
|
||||||
if (protocol == mainProto) {
|
if (protocol == mainProto) {
|
||||||
containerConfig.insert(config_key::port, port);
|
containerConfig.insert(config_key::port, port);
|
||||||
|
|
@ -387,6 +388,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
containerConfig[config_key::subnet_address] = serverConfigMap.value("Address").remove("/24");
|
||||||
containerConfig[config_key::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount);
|
containerConfig[config_key::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount);
|
||||||
containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize);
|
containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize);
|
||||||
containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize);
|
containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize);
|
||||||
|
|
@ -398,6 +400,25 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||||
serverConfigMap.value(config_key::underloadPacketMagicHeader);
|
serverConfigMap.value(config_key::underloadPacketMagicHeader);
|
||||||
containerConfig[config_key::transportPacketMagicHeader] =
|
containerConfig[config_key::transportPacketMagicHeader] =
|
||||||
serverConfigMap.value(config_key::transportPacketMagicHeader);
|
serverConfigMap.value(config_key::transportPacketMagicHeader);
|
||||||
|
|
||||||
|
} else if (protocol == Proto::WireGuard) {
|
||||||
|
QString serverConfig = serverController->getTextFileFromContainer(container, credentials,
|
||||||
|
protocols::wireguard::serverConfigPath, errorCode);
|
||||||
|
|
||||||
|
QMap<QString, QString> serverConfigMap;
|
||||||
|
auto serverConfigLines = serverConfig.split("\n");
|
||||||
|
for (auto &line : serverConfigLines) {
|
||||||
|
auto trimmedLine = line.trimmed();
|
||||||
|
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
QStringList parts = trimmedLine.split(" = ");
|
||||||
|
if (parts.count() == 2) {
|
||||||
|
serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
containerConfig[config_key::subnet_address] = serverConfigMap.value("Address").remove("/24");
|
||||||
} else if (protocol == Proto::Sftp) {
|
} else if (protocol == Proto::Sftp) {
|
||||||
stdOut.clear();
|
stdOut.clear();
|
||||||
script = QString("sudo docker inspect --format '{{.Config.Cmd}}' %1").arg(name);
|
script = QString("sudo docker inspect --format '{{.Config.Cmd}}' %1").arg(name);
|
||||||
|
|
@ -432,6 +453,51 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||||
containerConfig.insert(config_key::userName, userName);
|
containerConfig.insert(config_key::userName, userName);
|
||||||
containerConfig.insert(config_key::password, password);
|
containerConfig.insert(config_key::password, password);
|
||||||
}
|
}
|
||||||
|
} else if (protocol == Proto::Xray) {
|
||||||
|
QString currentConfig = serverController->getTextFileFromContainer(
|
||||||
|
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(currentConfig.toUtf8());
|
||||||
|
qDebug() << doc;
|
||||||
|
if (doc.isNull() || !doc.isObject()) {
|
||||||
|
logger.error() << "Failed to parse server config JSON";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
QJsonObject serverConfig = doc.object();
|
||||||
|
|
||||||
|
if (!serverConfig.contains("inbounds")) {
|
||||||
|
logger.error() << "Server config missing 'inbounds' field";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray inbounds = serverConfig["inbounds"].toArray();
|
||||||
|
if (inbounds.isEmpty()) {
|
||||||
|
logger.error() << "Server config has empty 'inbounds' array";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject inbound = inbounds[0].toObject();
|
||||||
|
if (!inbound.contains("streamSettings")) {
|
||||||
|
logger.error() << "Inbound missing 'streamSettings' field";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject streamSettings = inbound["streamSettings"].toObject();
|
||||||
|
QJsonObject realitySettings = streamSettings["realitySettings"].toObject();
|
||||||
|
if (!realitySettings.contains("serverNames")) {
|
||||||
|
logger.error() << "Settings missing 'clients' field";
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString siteName = realitySettings["serverNames"][0].toString();
|
||||||
|
qDebug() << siteName;
|
||||||
|
|
||||||
|
containerConfig.insert(config_key::site, siteName);
|
||||||
}
|
}
|
||||||
|
|
||||||
config.insert(config_key::container, ContainerProps::containerToString(container));
|
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,13 @@ void SettingsController::clearLogs()
|
||||||
|
|
||||||
void SettingsController::backupAppConfig(const QString &fileName)
|
void SettingsController::backupAppConfig(const QString &fileName)
|
||||||
{
|
{
|
||||||
SystemController::saveFile(fileName, m_settings->backupAppConfig());
|
QByteArray data = m_settings->backupAppConfig();
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||||
|
QJsonObject config = doc.object();
|
||||||
|
|
||||||
|
config["Conf/autoStart"] = Autostart::isAutostart();
|
||||||
|
|
||||||
|
SystemController::saveFile(fileName, QJsonDocument(config).toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsController::restoreAppConfig(const QString &fileName)
|
void SettingsController::restoreAppConfig(const QString &fileName)
|
||||||
|
|
@ -140,9 +146,30 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
bool ok = m_settings->restoreAppConfig(data);
|
bool ok = m_settings->restoreAppConfig(data);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
QJsonObject newConfigData = QJsonDocument::fromJson(data).object();
|
||||||
|
|
||||||
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) || defined(Q_OS_MACX)
|
||||||
|
bool autoStart = false;
|
||||||
|
if (newConfigData.contains("Conf/autoStart")) {
|
||||||
|
autoStart = newConfigData["Conf/autoStart"].toBool();
|
||||||
|
}
|
||||||
|
toggleAutoStart(autoStart);
|
||||||
|
#endif
|
||||||
m_serversModel->resetModel();
|
m_serversModel->resetModel();
|
||||||
m_languageModel->changeLanguage(
|
m_languageModel->changeLanguage(
|
||||||
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
|
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
|
||||||
|
|
||||||
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
|
||||||
|
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
|
||||||
|
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toBool();
|
||||||
|
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode);
|
||||||
|
m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
|
||||||
|
#endif
|
||||||
|
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
|
||||||
|
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toBool();
|
||||||
|
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
|
||||||
|
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
|
||||||
|
|
||||||
emit restoreBackupFinished();
|
emit restoreBackupFinished();
|
||||||
} else {
|
} else {
|
||||||
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
|
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
|
||||||
|
|
@ -167,6 +194,8 @@ void SettingsController::clearSettings()
|
||||||
m_appSplitTunnelingModel->setRouteMode(Settings::AppsRouteMode::VpnAllExceptApps);
|
m_appSplitTunnelingModel->setRouteMode(Settings::AppsRouteMode::VpnAllExceptApps);
|
||||||
m_appSplitTunnelingModel->toggleSplitTunneling(false);
|
m_appSplitTunnelingModel->toggleSplitTunneling(false);
|
||||||
|
|
||||||
|
toggleAutoStart(false);
|
||||||
|
|
||||||
emit changeSettingsFinished(tr("All settings have been reset to default values"));
|
emit changeSettingsFinished(tr("All settings have been reset to default values"));
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#ifdef Q_OS_IOS
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
||||||
"Access all websites and online resources. Speeds up to %1 Mbps.")
|
"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("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan.");
|
||||||
if (!isServiceAvailable) {
|
if (!isServiceAvailable) {
|
||||||
description += tr("<p><a style=\"color: #EB5757;\">Not available in your region. If you have VPN enabled, disable it, "
|
description += tr("<p><a style=\"color: #EB5757;\">Not available in your region. If you have VPN enabled, disable it, "
|
||||||
"return to the previous screen, and try again.</a>");
|
"return to the previous screen, and try again.</a>");
|
||||||
|
|
@ -82,7 +82,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
||||||
return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. "
|
return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. "
|
||||||
"Access all websites and online resources.");
|
"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("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case IsServiceAvailableRole: {
|
case IsServiceAvailableRole: {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ bool XrayConfigModel::setData(const QModelIndex &index, const QVariant &value, i
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break;
|
case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break;
|
||||||
|
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit dataChanged(index, index, QList { role });
|
emit dataChanged(index, index, QList { role });
|
||||||
|
|
@ -34,6 +35,7 @@ QVariant XrayConfigModel::data(const QModelIndex &index, int role) const
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite);
|
case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite);
|
||||||
|
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::xray::defaultPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
@ -67,6 +69,7 @@ QHash<int, QByteArray> XrayConfigModel::roleNames() const
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
|
|
||||||
roles[SiteRole] = "site";
|
roles[SiteRole] = "site";
|
||||||
|
roles[PortRole] = "port";
|
||||||
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ class XrayConfigModel : public QAbstractListModel
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Roles {
|
enum Roles {
|
||||||
SiteRole
|
SiteRole,
|
||||||
|
PortRole
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit XrayConfigModel(QObject *parent = nullptr);
|
explicit XrayConfigModel(QObject *parent = nullptr);
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int
|
||||||
} else {
|
} else {
|
||||||
server.insert(config_key::description, value.toString());
|
server.insert(config_key::description, value.toString());
|
||||||
}
|
}
|
||||||
|
server.insert(config_key::nameOverriddenByUser, true);
|
||||||
m_settings->editServer(index.row(), server);
|
m_settings->editServer(index.row(), server);
|
||||||
m_servers.replace(index.row(), server);
|
m_servers.replace(index.row(), server);
|
||||||
if (index.row() == m_defaultServerIndex) {
|
if (index.row() == m_defaultServerIndex) {
|
||||||
|
|
@ -348,6 +349,25 @@ void ServersModel::removeServer()
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServersModel::removeServer(const int serverIndex)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_settings->removeServer(serverIndex);
|
||||||
|
m_servers = m_settings->serversArray();
|
||||||
|
|
||||||
|
if (m_settings->defaultServerIndex() == serverIndex) {
|
||||||
|
setDefaultServerIndex(0);
|
||||||
|
} else if (m_settings->defaultServerIndex() > serverIndex) {
|
||||||
|
setDefaultServerIndex(m_settings->defaultServerIndex() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_settings->serversCount() == 0) {
|
||||||
|
setDefaultServerIndex(-1);
|
||||||
|
}
|
||||||
|
setProcessedServerIndex(m_defaultServerIndex);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> ServersModel::roleNames() const
|
QHash<int, QByteArray> ServersModel::roleNames() const
|
||||||
{
|
{
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ public slots:
|
||||||
void addServer(const QJsonObject &server);
|
void addServer(const QJsonObject &server);
|
||||||
void editServer(const QJsonObject &server, const int serverIndex);
|
void editServer(const QJsonObject &server, const int serverIndex);
|
||||||
void removeServer();
|
void removeServer();
|
||||||
|
void removeServer(const int serverIndex);
|
||||||
|
|
||||||
QJsonObject getServerConfig(const int serverIndex);
|
QJsonObject getServerConfig(const int serverIndex);
|
||||||
|
|
||||||
|
|
|
||||||
194
client/ui/qml/Components/ApiPremV1MigrationDrawer.qml
Normal file
194
client/ui/qml/Components/ApiPremV1MigrationDrawer.qml
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import QtCore
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "./"
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
import "../Config"
|
||||||
|
import "../Components"
|
||||||
|
|
||||||
|
DrawerType2 {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
expandedHeight: parent.height * 0.9
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: ApiPremV1MigrationController
|
||||||
|
|
||||||
|
function onErrorOccurred(error, goToPageHome) {
|
||||||
|
PageController.showErrorMessage(error)
|
||||||
|
root.closeTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expandedStateContent: Item {
|
||||||
|
implicitHeight: root.expandedHeight
|
||||||
|
|
||||||
|
ListViewType {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
model: 1 // fake model to force the ListView to be created without a model
|
||||||
|
snapMode: ListView.NoSnap
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
|
Header2Type {
|
||||||
|
id: header
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 20
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Switch to the new Amnezia Premium subscription")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
|
ParagraphTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 24
|
||||||
|
Layout.bottomMargin: 24
|
||||||
|
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
textFormat: Text.RichText
|
||||||
|
text: {
|
||||||
|
var str = qsTr("We'll preserve all remaining days of your current subscription and give you an extra month as a thank you. ")
|
||||||
|
str += qsTr("This new subscription type will be actively developed with more locations and features added regularly. Currently available:")
|
||||||
|
str += "<ul style='margin-left: -16px;'>"
|
||||||
|
str += qsTr("<li>13 locations (with more coming soon)</li>")
|
||||||
|
str += qsTr("<li>Easier switching between countries in the app</li>")
|
||||||
|
str += qsTr("<li>Personal dashboard to manage your subscription</li>")
|
||||||
|
str += "</ul>"
|
||||||
|
str += qsTr("Old keys will be deactivated after switching.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldWithHeaderType {
|
||||||
|
id: emailLabel
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
borderColor: AmneziaStyle.color.mutedGray
|
||||||
|
headerTextColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
|
headerText: qsTr("Email")
|
||||||
|
textField.placeholderText: qsTr("mail@example.com")
|
||||||
|
|
||||||
|
|
||||||
|
textField.onFocusChanged: {
|
||||||
|
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: ApiPremV1MigrationController
|
||||||
|
|
||||||
|
function onNoSubscriptionToMigrate() {
|
||||||
|
emailLabel.errorText = qsTr("No old format subscriptions for a given email")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptionTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 16
|
||||||
|
|
||||||
|
color: AmneziaStyle.color.mutedGray
|
||||||
|
|
||||||
|
text: qsTr("Enter the email you used for your current subscription")
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiPremV1SubListDrawer {
|
||||||
|
id: apiPremV1SubListDrawer
|
||||||
|
parent: root
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
OtpCodeDrawer {
|
||||||
|
id: otpCodeDrawer
|
||||||
|
parent: root
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
id: yesButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 32
|
||||||
|
|
||||||
|
text: qsTr("Continue")
|
||||||
|
|
||||||
|
clickedFunc: function() {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
ApiPremV1MigrationController.getSubscriptionList(emailLabel.textField.text)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
id: noButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
defaultColor: AmneziaStyle.color.transparent
|
||||||
|
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||||
|
pressedColor: AmneziaStyle.color.sheerWhite
|
||||||
|
disabledColor: AmneziaStyle.color.mutedGray
|
||||||
|
textColor: AmneziaStyle.color.paleGray
|
||||||
|
borderWidth: 1
|
||||||
|
|
||||||
|
text: qsTr("Remind me later")
|
||||||
|
|
||||||
|
clickedFunc: function() {
|
||||||
|
root.closeTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: 32
|
||||||
|
Layout.bottomMargin: 32
|
||||||
|
implicitHeight: 32
|
||||||
|
|
||||||
|
defaultColor: "transparent"
|
||||||
|
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||||
|
pressedColor: AmneziaStyle.color.sheerWhite
|
||||||
|
textColor: AmneziaStyle.color.vibrantRed
|
||||||
|
|
||||||
|
text: qsTr("Don't remind me again")
|
||||||
|
|
||||||
|
clickedFunc: function() {
|
||||||
|
var headerText = qsTr("No more reminders? You can always switch to the new format in the server settings")
|
||||||
|
var yesButtonText = qsTr("Continue")
|
||||||
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
var yesButtonFunction = function() {
|
||||||
|
ApiPremV1MigrationController.disablePremV1MigrationReminder()
|
||||||
|
root.closeTriggered()
|
||||||
|
}
|
||||||
|
var noButtonFunction = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
client/ui/qml/Components/ApiPremV1SubListDrawer.qml
Normal file
89
client/ui/qml/Components/ApiPremV1SubListDrawer.qml
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
import "../Config"
|
||||||
|
|
||||||
|
DrawerType2 {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: ApiPremV1MigrationController
|
||||||
|
|
||||||
|
function onSubscriptionsModelChanged() {
|
||||||
|
if (ApiPremV1MigrationController.subscriptionsModel.length > 1) {
|
||||||
|
root.openTriggered()
|
||||||
|
} else {
|
||||||
|
sendMigrationCode(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendMigrationCode(index) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
ApiPremV1MigrationController.sendMigrationCode(index)
|
||||||
|
root.closeTriggered()
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
expandedHeight: parent.height * 0.9
|
||||||
|
|
||||||
|
expandedStateContent: Item {
|
||||||
|
implicitHeight: root.expandedHeight
|
||||||
|
|
||||||
|
ListViewType {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
model: ApiPremV1MigrationController.subscriptionsModel
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
|
Header2Type {
|
||||||
|
id: header
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 20
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Choose Subscription")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
implicitWidth: listView.width
|
||||||
|
implicitHeight: delegateContent.implicitHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: delegateContent
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: server
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Order ID: ") + modelData.id
|
||||||
|
|
||||||
|
descriptionText: qsTr("Purchase Date: ") + Qt.formatDateTime(new Date(modelData.created_at), "dd.MM.yyyy hh:mm")
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
sendMigrationCode(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
client/ui/qml/Components/OtpCodeDrawer.qml
Normal file
77
client/ui/qml/Components/OtpCodeDrawer.qml
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
import "../Config"
|
||||||
|
|
||||||
|
DrawerType2 {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: ApiPremV1MigrationController
|
||||||
|
|
||||||
|
function onOtpSuccessfullySent() {
|
||||||
|
root.openTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expandedHeight: parent.height * 0.6
|
||||||
|
|
||||||
|
expandedStateContent: Item {
|
||||||
|
implicitHeight: root.expandedHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Header2Type {
|
||||||
|
id: header
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 20
|
||||||
|
|
||||||
|
headerText: qsTr("OTP code was sent to your email")
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldWithHeaderType {
|
||||||
|
id: otpFiled
|
||||||
|
|
||||||
|
borderColor: AmneziaStyle.color.mutedGray
|
||||||
|
headerTextColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 16
|
||||||
|
headerText: qsTr("OTP Code")
|
||||||
|
textField.maximumLength: 30
|
||||||
|
checkEmptyText: true
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
id: saveButton
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 16
|
||||||
|
|
||||||
|
text: qsTr("Continue")
|
||||||
|
|
||||||
|
clickedFunc: function() {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
ApiPremV1MigrationController.migrate(otpFiled.textField.text)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
root.closeTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
@ -39,7 +41,7 @@ DrawerType2 {
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
text: headerText
|
text: root.headerText
|
||||||
}
|
}
|
||||||
|
|
||||||
ParagraphTextType {
|
ParagraphTextType {
|
||||||
|
|
@ -48,7 +50,7 @@ DrawerType2 {
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
text: descriptionText
|
text: root.descriptionText
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
|
|
@ -58,11 +60,11 @@ DrawerType2 {
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
text: yesButtonText
|
text: root.yesButtonText
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
if (yesButtonFunction && typeof yesButtonFunction === "function") {
|
if (root.yesButtonFunction && typeof root.yesButtonFunction === "function") {
|
||||||
yesButtonFunction()
|
root.yesButtonFunction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -80,11 +82,13 @@ DrawerType2 {
|
||||||
textColor: AmneziaStyle.color.paleGray
|
textColor: AmneziaStyle.color.paleGray
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
|
|
||||||
text: noButtonText
|
visible: root.noButtonText !== ""
|
||||||
|
|
||||||
|
text: root.noButtonText
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
if (noButtonFunction && typeof noButtonFunction === "function") {
|
if (root.noButtonFunction && typeof root.noButtonFunction === "function") {
|
||||||
noButtonFunction()
|
root.noButtonFunction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,31 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
|
||||||
|
target: ApiPremV1MigrationController
|
||||||
|
|
||||||
|
function onMigrationFinished() {
|
||||||
|
apiPremV1MigrationDrawer.closeTriggered()
|
||||||
|
|
||||||
|
var headerText = qsTr("You've successfully switched to the new Amnezia Premium subscription!")
|
||||||
|
var descriptionText = qsTr("Old keys will no longer work. Please use your new subscription key to connect. \nThank you for staying with us!")
|
||||||
|
var yesButtonText = qsTr("Continue")
|
||||||
|
var noButtonText = ""
|
||||||
|
|
||||||
|
var yesButtonFunction = function() {
|
||||||
|
}
|
||||||
|
var noButtonFunction = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onShowMigrationDrawer() {
|
||||||
|
apiPremV1MigrationDrawer.openTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
objectName: "homeColumnItem"
|
objectName: "homeColumnItem"
|
||||||
|
|
||||||
|
|
@ -429,4 +454,9 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApiPremV1MigrationDrawer {
|
||||||
|
id: apiPremV1MigrationDrawer
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,9 +93,9 @@ PageType {
|
||||||
var tmpText = textField.text
|
var tmpText = textField.text
|
||||||
tmpText = tmpText.toLocaleLowerCase()
|
tmpText = tmpText.toLocaleLowerCase()
|
||||||
|
|
||||||
var indexHttps = tmpText.indexOf("https://")
|
if (tmpText.startsWith("https://")) {
|
||||||
if (indexHttps === 0) {
|
|
||||||
tmpText = textField.text.substring(8)
|
tmpText = textField.text.substring(8)
|
||||||
|
site = tmpText
|
||||||
} else {
|
} else {
|
||||||
site = textField.text
|
site = textField.text
|
||||||
}
|
}
|
||||||
|
|
@ -103,8 +103,29 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextFieldWithHeaderType {
|
||||||
|
id: portTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 16
|
||||||
|
|
||||||
|
enabled: delegateItem.isEnabled
|
||||||
|
|
||||||
|
headerText: qsTr("Port")
|
||||||
|
textField.text: port
|
||||||
|
textField.maximumLength: 5
|
||||||
|
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||||
|
|
||||||
|
textField.onEditingFinished: {
|
||||||
|
if (textField.text !== port) {
|
||||||
|
port = textField.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkEmptyText: true
|
||||||
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: basicButton
|
id: saveButton
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
Layout.bottomMargin: 24
|
Layout.bottomMargin: 24
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
var headerText = qsTr("Are you sure you want to unlink 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 descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing \"Reload API config\" in subscription settings on device.")
|
||||||
var yesButtonText = qsTr("Continue")
|
var yesButtonText = qsTr("Continue")
|
||||||
var noButtonText = qsTr("Cancel")
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@ PageType {
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
var headerText = qsTr("Are you sure you want to unlink 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 descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing \"Reload API config\" in subscription settings on device.")
|
||||||
var yesButtonText = qsTr("Continue")
|
var yesButtonText = qsTr("Continue")
|
||||||
var noButtonText = qsTr("Cancel")
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ PageType {
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
visible: link !== ""
|
||||||
text: title
|
text: title
|
||||||
descriptionText: description
|
descriptionText: description
|
||||||
rightImageSource: "qrc:/images/controls/external-link.svg"
|
rightImageSource: "qrc:/images/controls/external-link.svg"
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ PageType {
|
||||||
if (!ConnectionController.isConnected) {
|
if (!ConnectionController.isConnected) {
|
||||||
SettingsController.isKillSwitchEnabled = checked
|
SettingsController.isKillSwitchEnabled = checked
|
||||||
} else {
|
} else {
|
||||||
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection"))
|
PageController.showNotificationMessage(qsTr("KillSwitch settings cannot be changed during an active connection"))
|
||||||
switcher.checked = SettingsController.isKillSwitchEnabled
|
switcher.checked = SettingsController.isKillSwitchEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +66,7 @@ PageType {
|
||||||
checked: !SettingsController.strictKillSwitchEnabled
|
checked: !SettingsController.strictKillSwitchEnabled
|
||||||
|
|
||||||
text: qsTr("Soft KillSwitch")
|
text: qsTr("Soft KillSwitch")
|
||||||
descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally")
|
descriptionText: qsTr("Internet access is blocked if the VPN disconnects unexpectedly")
|
||||||
|
|
||||||
onClicked: function() {
|
onClicked: function() {
|
||||||
SettingsController.strictKillSwitchEnabled = false
|
SettingsController.strictKillSwitchEnabled = false
|
||||||
|
|
@ -85,11 +85,11 @@ PageType {
|
||||||
checked: SettingsController.strictKillSwitchEnabled
|
checked: SettingsController.strictKillSwitchEnabled
|
||||||
|
|
||||||
text: qsTr("Strict KillSwitch")
|
text: qsTr("Strict KillSwitch")
|
||||||
descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started")
|
descriptionText: qsTr("Internet connection is blocked even when VPN is turned off manually or hasn't started")
|
||||||
|
|
||||||
onClicked: function() {
|
onClicked: function() {
|
||||||
var headerText = qsTr("Just a little heads-up")
|
var headerText = qsTr("Just a little heads-up")
|
||||||
var descriptionText = qsTr("If you disconnect from VPN or the VPN connection drops while the Strict Kill Switch is turned on, your internet access will be disabled. To restore it, connect to VPN, change the Kill Switch mode or turn the Kill Switch off.")
|
var descriptionText = qsTr("If the VPN disconnects or drops while Strict KillSwitch is enabled, internet access will be blocked. To restore access, reconnect VPN or disable/change the KillSwitch.")
|
||||||
var yesButtonText = qsTr("Continue")
|
var yesButtonText = qsTr("Continue")
|
||||||
var noButtonText = qsTr("Cancel")
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
|
@ -111,7 +111,7 @@ PageType {
|
||||||
|
|
||||||
enabled: true
|
enabled: true
|
||||||
text: qsTr("DNS Exceptions")
|
text: qsTr("DNS Exceptions")
|
||||||
descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered")
|
descriptionText: qsTr("DNS servers listed here will remain accessible when KillSwitch is active.")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ PageType {
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("DNS Exceptions")
|
headerText: qsTr("DNS Exceptions")
|
||||||
descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered")
|
descriptionText: qsTr("DNS servers listed here will remain accessible when KillSwitch is active")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -257,6 +257,24 @@ PageType {
|
||||||
DividerType {
|
DividerType {
|
||||||
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
|
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: labelWithButton6
|
||||||
|
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Switch to the new Amnezia Premium subscription")
|
||||||
|
textColor: AmneziaStyle.color.vibrantRed
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
PageController.goToPageHome()
|
||||||
|
ApiPremV1MigrationController.showMigrationDrawer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {
|
||||||
|
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,10 @@ wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSIO
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_armeabi-v7a.apk
|
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_armeabi-v7a.apk
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86.apk
|
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86.apk
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86_64.apk
|
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86_64.apk
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux.tar.zip
|
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux_x64.tar.zip
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos.dmg
|
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos.dmg
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos_old.dmg
|
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos_old.dmg
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_x64.exe
|
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_windows_x64.exe
|
||||||
|
|
||||||
cd ../
|
cd ../
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,9 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) {
|
||||||
|
|
||||||
bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) {
|
bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) {
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
if (configStr.value("splitTunnelType").toInt() != 0) {
|
||||||
|
WindowsFirewall::create(this)->allowAllTraffic();
|
||||||
|
}
|
||||||
return WindowsFirewall::create(this)->enableInterface(vpnAdapterIndex);
|
return WindowsFirewall::create(this)->enableInterface(vpnAdapterIndex);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue