Compare commits

..

42 Commits

Author SHA1 Message Date
albexk a297ef3808 Fix name resolving on Android 2024-09-16 15:35:27 +03:00
vladimir.kuznetsov 75768425ff moved awg/wg domain name processing to WireguardProtocol 2024-09-14 13:42:05 +04:00
vladimir.kuznetsov 8f6fef8552 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/awg-wg-name-resolving 2024-09-13 23:20:13 +04:00
vladimir.kuznetsov 1f72043281 added domain name resolving when connecting via xray 2024-09-13 23:13:19 +04:00
Pokamest Nikak c6b131aa4c Bump version to 4.8.0.1 2024-09-13 18:25:04 +01:00
pokamest 5e72bf945c Merge pull request #1064 from amnezia-vpn/fix/android-window-hiding
Fix window hiding on startup on Android
2024-09-13 18:21:49 +03:00
albexk eebf7eccec Fix window hiding on startup on Android 2024-09-13 18:14:25 +03:00
pokamest 168c293bfe Merge pull request #979 from amnezia-vpn/feature/update-tap
Update TAP-Windows driver
2024-09-13 15:00:31 +03:00
Nethius aae3cdcac1 added saving allowed_ips to the array of strings for old configs (#926)
* added saving allowed_ips to the array of strings for old configs

* Remove config string processing, add getting all AWG, WG parameters from JSON

* fixed checking of default routes when adding split tunneling from the application

* added check when processing siteBasedSplitTunneling
2024-09-13 10:53:21 +01:00
Nethius 96566f04ee feature/mtu connection config (#833)
* added the ability to change mtu for connection-only configs
* added replacing MTU with default when importing awg/wg configs in amnezia
2024-09-13 09:38:48 +01:00
vladimir.kuznetsov a050c36950 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/awg-wg-name-resolving 2024-09-11 21:48:23 +04:00
pokamest fff15fffe2 Bug fix for iOS 2024-09-11 09:51:07 -07:00
pokamest 4e5a03e7f1 Merge pull request #1059 from amnezia-vpn/chore/dev-key 2024-09-10 21:38:45 +03:00
vladimir.kuznetsov 7571bbc36e chore: added dev key to deploy workflow
- added m_isDevEnvironment initialization
2024-09-10 22:03:10 +04:00
pokamest db4a1a62e5 Merge pull request #1058 from amnezia-vpn/version-bump 2024-09-09 22:17:47 +03:00
albexk 581773ce03 Bump version to 4.8.0.0 2024-09-09 22:11:18 +03:00
albexk 46058f614e Add connection checking for WG/AWG via logs (#1056) 2024-09-09 22:08:06 +03:00
Nethius 9cab51fb00 added open service logs to logs page (#951)
* added open service logs to logs page
* redesign of log saving buttons
* hide service logs buttons for mobile platforms
* refactoring: moved logger to common folder
* feature: added the ability to enable logs to the start screen
2024-09-09 17:53:44 +01:00
Nethius 918be16372 feature: added isAvailable flag support (#1032)
* feature: added isAvailable flag support
* added the option to switch to dev env
2024-09-09 13:27:29 +01:00
albexk 175477d31f Android qt 6.7 (#1024)
* Up Gradle to 8.10

* Update Android dependencies

* Up Qt to 6.7.2

* Up qtkeychain to 0.14.3

* Move function of changing the color of the navigation bar to the android side

* Fix splashscreen and recent apps thumbnail backgrounds

* Android authentication refactoring

* Fix GitHub action

* Fix the extra circle around the connect button on Android

* Fix keyboard popup

* Increase the amount of requestNetwork attempts on Android 11
2024-09-09 12:36:33 +01:00
KsZnak cd70b7e619 Translation updated (ukrainian) (#1048)
* Update amneziavpn_uk_UA.ts
2024-09-06 15:54:47 +03:00
pokamest 22011e263e Merge pull request #1051 from amnezia-vpn/bugfix/startup-crush
fixed a possible unhandled exception
2024-09-06 15:53:59 +03:00
Shehab Ahmed 88a2b9a07a Update Arabic, Burmese translation (#1022)
Update Arabic and Burmese translation
2024-09-03 10:06:13 +01:00
KsZnak 248f487d4e Update amneziavpn_fa_IR.ts (#1005)
Persian language updated
2024-09-03 10:03:42 +01:00
pokamest 572ef09296 Merge pull request #1030 from amnezia-vpn/chore/screenshots-enabled-true
chore/screenshots enabled true
2024-08-30 15:56:10 +03:00
pokamest 03078236ab Merge pull request #1028 from amnezia-vpn/feature/copy-mail-button
feature: added 'copy mail' button on about page
2024-08-30 15:54:26 +03:00
Shehab Ahmed b39a0a1d94 fix start Minimized feature issue on linux, Closes #1016 (#1021)
fix start Minized feature issue on linux
2024-08-30 15:53:48 +03:00
vladimir.kuznetsov e94fc688ba chore: set screenshotsEnabled to true by default 2024-08-30 16:32:40 +04:00
vladimir.kuznetsov 558f613acc feature: added 'copy mail' button on about page 2024-08-30 16:19:11 +04:00
pokamest d800a95a1d Merge pull request #1003 from eltociear/patch-1
chore: update windowsservicemanager.h
2024-08-28 17:26:21 +03:00
pokamest b8f100d4fa Merge pull request #1015 from amnezia-vpn/Links-updated-4.7.0.0-in-readme
Update README.md
2024-08-28 17:08:56 +03:00
vladimir.kuznetsov 51618fb882 fixed a possible unhandled exception 2024-08-27 13:14:15 +03:00
KsZnak 14f537ba76 Update README.md
links updated 4.7.0.0
2024-08-26 16:41:25 +03:00
pokamest 3458ed78d7 Merge pull request #1004 from amnezia-vpn/Update-amneziavpn_ru_RU.ts
Update amneziavpn_ru_RU.ts
2024-08-23 14:17:56 -07:00
KsZnak 4bc571f609 Update amneziavpn_ru_RU.ts
Russian language updated
2024-08-23 22:07:40 +03:00
Ikko Eltociear Ashimine ee61f842e5 chore: update windowsservicemanager.h
controll -> control
2024-08-24 00:32:58 +09:00
Mykola Baibuz b83e74427e Update TAP-Windows driver 2024-08-15 19:51:49 +03:00
vladimir.kuznetsov 460f5eed31 added the ability to enter a domain name when configuring the server 2024-08-09 15:22:10 +04:00
vladimir.kuznetsov 0e4a494d4e added domain name and ipv6 processing when importing awg/wg configs 2024-08-09 15:21:38 +04:00
vladimir.kuznetsov 9184404eda Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/awg-wg-name-resolving 2024-08-09 09:20:15 +04:00
vladimir.kuznetsov 34d363d204 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/awg-wg-name-resolving 2024-08-05 09:56:42 +04:00
vladimir.kuznetsov 61d9a4fa93 added domain name resolving before connection for wg and awg protocols 2024-05-18 22:01:52 +02:00
110 changed files with 2664 additions and 2343 deletions
+12 -7
View File
@@ -16,6 +16,7 @@ jobs:
QT_VERSION: 6.6.2 QT_VERSION: 6.6.2
QIF_VERSION: 4.7 QIF_VERSION: 4.7
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
steps: steps:
- name: 'Install Qt' - name: 'Install Qt'
@@ -82,6 +83,7 @@ jobs:
QIF_VERSION: 4.7 QIF_VERSION: 4.7
BUILD_ARCH: 64 BUILD_ARCH: 64
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
steps: steps:
- name: 'Get sources' - name: 'Get sources'
@@ -144,6 +146,7 @@ jobs:
CC: cc CC: cc
CXX: c++ CXX: c++
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
steps: steps:
- name: 'Setup xcode' - name: 'Setup xcode'
@@ -235,6 +238,7 @@ jobs:
QT_VERSION: 6.4.3 QT_VERSION: 6.4.3
QIF_VERSION: 4.6 QIF_VERSION: 4.6
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
steps: steps:
- name: 'Setup xcode' - name: 'Setup xcode'
@@ -297,24 +301,25 @@ jobs:
env: env:
ANDROID_BUILD_PLATFORM: android-34 ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.6.2 QT_VERSION: 6.7.2
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools' QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
steps: steps:
- name: 'Install desktop Qt' - name: 'Install desktop Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v4
with: with:
version: ${{ env.QT_VERSION }} version: ${{ env.QT_VERSION }}
host: 'linux' host: 'linux'
target: 'desktop' target: 'desktop'
arch: 'gcc_64' arch: 'linux_gcc_64'
modules: ${{ env.QT_MODULES }} modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86_64 Qt' - name: 'Install android_x86_64 Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v4
with: with:
version: ${{ env.QT_VERSION }} version: ${{ env.QT_VERSION }}
host: 'linux' host: 'linux'
@@ -325,7 +330,7 @@ jobs:
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86 Qt' - name: 'Install android_x86 Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v4
with: with:
version: ${{ env.QT_VERSION }} version: ${{ env.QT_VERSION }}
host: 'linux' host: 'linux'
@@ -336,7 +341,7 @@ jobs:
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_armv7 Qt' - name: 'Install android_armv7 Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v4
with: with:
version: ${{ env.QT_VERSION }} version: ${{ env.QT_VERSION }}
host: 'linux' host: 'linux'
@@ -347,7 +352,7 @@ jobs:
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_arm64_v8a Qt' - name: 'Install android_arm64_v8a Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v4
with: with:
version: ${{ env.QT_VERSION }} version: ${{ env.QT_VERSION }}
host: 'linux' host: 'linux'
+1
View File
@@ -16,6 +16,7 @@ jobs:
QT_VERSION: 6.4.1 QT_VERSION: 6.4.1
QIF_VERSION: 4.5 QIF_VERSION: 4.5
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
steps: steps:
- name: 'Install desktop Qt' - name: 'Install desktop Qt'
+2 -2
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.7.0.0 project(${PROJECT} VERSION 4.8.0.1
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 57) set(APP_ANDROID_VERSION_CODE 59)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")
+4 -4
View File
@@ -10,10 +10,10 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
<br> <br>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.6.0.3/AmneziaVPN_4.6.0.3_x64.exe"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/win.png" width="150" style="max-width: 100%;"></a> <a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_4.7.0.0_x64.exe"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/win.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.6.0.3/AmneziaVPN_4.6.0.3.dmg"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/mac.png" width="150" style="max-width: 100%;"></a> <a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_4.7.0.0.dmg"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/mac.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.6.0.3/AmneziaVPN_Linux_4.6.0.3.tar.zip"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/lin.png" width="150" style="max-width: 100%;"></a> <a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_Linux_4.7.0.0.tar.zip"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/lin.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/tag/4.6.0.3"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/andr.png" width="150" style="max-width: 100%;"></a> <a href="https://github.com/amnezia-vpn/amnezia-client/releases/tag/4.7.0.0"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/andr.png" width="150" style="max-width: 100%;"></a>
<br> <br>
+6 -1
View File
@@ -27,6 +27,9 @@ add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}") add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}") add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}")
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
if(IOS) if(IOS)
set(PACKAGES ${PACKAGES} Multimedia) set(PACKAGES ${PACKAGES} Multimedia)
endif() endif()
@@ -110,6 +113,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake)
include_directories( include_directories(
${CMAKE_CURRENT_LIST_DIR}/../ipc ${CMAKE_CURRENT_LIST_DIR}/../ipc
${CMAKE_CURRENT_LIST_DIR}/../common/logger
${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
) )
@@ -131,7 +135,6 @@ set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h ${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h ${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
${CMAKE_CURRENT_LIST_DIR}/ui/pages.h ${CMAKE_CURRENT_LIST_DIR}/ui/pages.h
${CMAKE_CURRENT_LIST_DIR}/ui/property_helper.h
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h ${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h ${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
${CMAKE_CURRENT_BINARY_DIR}/version.h ${CMAKE_CURRENT_BINARY_DIR}/version.h
@@ -140,6 +143,7 @@ set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/core/serialization/serialization.h ${CMAKE_CURRENT_LIST_DIR}/core/serialization/serialization.h
${CMAKE_CURRENT_LIST_DIR}/core/serialization/transfer.h ${CMAKE_CURRENT_LIST_DIR}/core/serialization/transfer.h
${CMAKE_CURRENT_LIST_DIR}/core/enums/apiEnums.h ${CMAKE_CURRENT_LIST_DIR}/core/enums/apiEnums.h
${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.h
) )
# Mozilla headres # Mozilla headres
@@ -190,6 +194,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/core/serialization/trojan.cpp ${CMAKE_CURRENT_LIST_DIR}/core/serialization/trojan.cpp
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess.cpp ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess.cpp
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess_new.cpp ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess_new.cpp
${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.cpp
) )
# Mozilla sources # Mozilla sources
+1 -1
View File
@@ -164,7 +164,7 @@ void AmneziaApplication::init()
bool enabled = m_settings->isSaveLogs(); bool enabled = m_settings->isSaveLogs();
#ifndef Q_OS_ANDROID #ifndef Q_OS_ANDROID
if (enabled) { if (enabled) {
if (!Logger::init()) { if (!Logger::init(false)) {
qWarning() << "Initialization of debug subsystem failed"; qWarning() << "Initialization of debug subsystem failed";
} }
} }
+8 -5
View File
@@ -3,7 +3,6 @@
<manifest <manifest
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="org.amnezia.vpn"
android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionName="-- %%INSERT_VERSION_NAME%% --"
android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --"
android:installLocation="auto"> android:installLocation="auto">
@@ -46,7 +45,7 @@
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc" |fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="stateUnchanged|adjustResize"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
@@ -68,9 +67,6 @@
android:name="android.app.lib_name" android:name="android.app.lib_name"
android:value="-- %%INSERT_APP_LIB_NAME%% --" /> android:value="-- %%INSERT_APP_LIB_NAME%% --" />
<meta-data
android:name="android.app.extract_android_style"
android:value="minimal" />
</activity> </activity>
<activity <activity
@@ -88,6 +84,13 @@
android:exported="false" android:exported="false"
android:theme="@style/Translucent" /> android:theme="@style/Translucent" />
<activity android:name=".AuthActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="false"
android:theme="@style/Translucent" />
<activity <activity
android:name=".ImportConfigActivity" android:name=".ImportConfigActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
+12 -65
View File
@@ -1,81 +1,28 @@
package org.amnezia.vpn.protocol.awg package org.amnezia.vpn.protocol.awg
import org.amnezia.vpn.protocol.wireguard.Wireguard import org.amnezia.vpn.protocol.wireguard.Wireguard
import org.amnezia.vpn.util.optStringOrNull
import org.json.JSONObject import org.json.JSONObject
/**
* Config example:
* {
* "protocol": "awg",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "awg_config_data": {
* "H1": "969537490",
* "H2": "481688153",
* "H3": "2049399200",
* "H4": "52029755",
* "Jc": "3",
* "Jmax": "1000",
* "Jmin": "50",
* "S1": "49",
* "S2": "60",
* "client_ip": "10.8.1.1",
* "hostName": "100.100.100.0",
* "port": 12345,
* "client_pub_key": "clientPublicKeyBase64",
* "client_priv_key": "privateKeyBase64",
* "psk_key": "presharedKeyBase64",
* "server_pub_key": "publicKeyBase64",
* "config": "[Interface]
* Address = 10.8.1.1/32
* DNS = 1.1.1.1, 1.0.0.1
* PrivateKey = privateKeyBase64
* Jc = 3
* Jmin = 50
* Jmax = 1000
* S1 = 49
* S2 = 60
* H1 = 969537490
* H2 = 481688153
* H3 = 2049399200
* H4 = 52029755
*
* [Peer]
* PublicKey = publicKeyBase64
* PresharedKey = presharedKeyBase64
* AllowedIPs = 0.0.0.0/0, ::/0
* Endpoint = 100.100.100.0:12345
* PersistentKeepalive = 25
* "
* }
* }
*/
class Awg : Wireguard() { class Awg : Wireguard() {
override val ifName: String = "awg0" override val ifName: String = "awg0"
override fun parseConfig(config: JSONObject): AwgConfig { override fun parseConfig(config: JSONObject): AwgConfig {
val configDataJson = config.getJSONObject("awg_config_data") val configData = config.getJSONObject("awg_config_data")
val configData = parseConfigData(configDataJson.getString("config"))
return AwgConfig.build { return AwgConfig.build {
configWireguard(configData, configDataJson) configWireguard(config, configData)
configSplitTunneling(config) configSplitTunneling(config)
configAppSplitTunneling(config) configAppSplitTunneling(config)
configData["Jc"]?.let { setJc(it.toInt()) } configData.optStringOrNull("Jc")?.let { setJc(it.toInt()) }
configData["Jmin"]?.let { setJmin(it.toInt()) } configData.optStringOrNull("Jmin")?.let { setJmin(it.toInt()) }
configData["Jmax"]?.let { setJmax(it.toInt()) } configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
configData["S1"]?.let { setS1(it.toInt()) } configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
configData["S2"]?.let { setS2(it.toInt()) } configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
configData["H1"]?.let { setH1(it.toLong()) } configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
configData["H2"]?.let { setH2(it.toLong()) } configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
configData["H3"]?.let { setH3(it.toLong()) } configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
configData["H4"]?.let { setH4(it.toLong()) } configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
} }
} }
} }
+3
View File
@@ -3,3 +3,6 @@
// android.bundle.enableUncompressedNativeLibs is deprecated // android.bundle.enableUncompressedNativeLibs is deprecated
// disable adding gradle property android.bundle.enableUncompressedNativeLibs by androiddeployqt // disable adding gradle property android.bundle.enableUncompressedNativeLibs by androiddeployqt
useLegacyPackaging useLegacyPackaging
// package name for androiddeployqt
namespace = "org.amnezia.vpn"
+2
View File
@@ -115,9 +115,11 @@ dependencies {
implementation(project(":xray")) implementation(project(":xray"))
implementation(libs.androidx.core) implementation(libs.androidx.core)
implementation(libs.androidx.activity) implementation(libs.androidx.activity)
implementation(libs.androidx.fragment)
implementation(libs.kotlinx.coroutines) implementation(libs.kotlinx.coroutines)
implementation(libs.kotlinx.serialization.protobuf) implementation(libs.kotlinx.serialization.protobuf)
implementation(libs.bundles.androidx.camera) implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit) implementation(libs.google.mlkit)
implementation(libs.androidx.datastore) implementation(libs.androidx.datastore)
implementation(libs.androidx.biometric)
} }
@@ -5,36 +5,6 @@ import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.openvpn.OpenVpn import org.amnezia.vpn.protocol.openvpn.OpenVpn
import org.json.JSONObject import org.json.JSONObject
/**
* Config Example:
* {
* "protocol": "cloak",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "openvpn_config_data": {
* "config": "openVpnConfig"
* }
* "cloak_config_data": {
* "BrowserSig": "chrome",
* "EncryptionMethod": "aes-gcm",
* "NumConn": 1,
* "ProxyMethod": "openvpn",
* "PublicKey": "PublicKey=",
* "RemoteHost": "100.100.100.0",
* "RemotePort": "443",
* "ServerName": "servername",
* "StreamTimeout": 300,
* "Transport": "direct",
* "UID": "UID="
* }
* }
*/
class Cloak : OpenVpn() { class Cloak : OpenVpn() {
override fun parseConfig(config: JSONObject): ClientAPI_Config { override fun parseConfig(config: JSONObject): ClientAPI_Config {
+13 -9
View File
@@ -1,24 +1,28 @@
[versions] [versions]
agp = "8.2.0" agp = "8.5.2"
kotlin = "1.9.20" kotlin = "1.9.24"
androidx-core = "1.12.0" androidx-core = "1.13.1"
androidx-activity = "1.8.1" androidx-activity = "1.9.1"
androidx-annotation = "1.7.0" androidx-annotation = "1.8.2"
androidx-camera = "1.3.0" androidx-biometric = "1.2.0-alpha05"
androidx-camera = "1.3.4"
androidx-fragment = "1.8.2"
androidx-security-crypto = "1.1.0-alpha06" androidx-security-crypto = "1.1.0-alpha06"
androidx-datastore = "1.1.0-beta01" androidx-datastore = "1.1.1"
kotlinx-coroutines = "1.7.3" kotlinx-coroutines = "1.8.1"
kotlinx-serialization = "1.6.3" kotlinx-serialization = "1.6.3"
google-mlkit = "17.2.0" google-mlkit = "17.3.0"
[libraries] [libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" } androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" } androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" } androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
androidx-biometric = { module = "androidx.biometric:biometric-ktx", version.ref = "androidx-biometric" }
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" } androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" } androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" } androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" }
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidx-camera" } androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidx-camera" }
androidx-fragment = { module = "androidx.fragment:fragment-ktx", version.ref = "androidx-fragment" }
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" } androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
androidx-datastore = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" } androidx-datastore = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
Binary file not shown.
+1 -3
View File
@@ -1,7 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
+5 -2
View File
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
+12 -10
View File
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@@ -16,23 +16,6 @@ import org.amnezia.vpn.util.net.getLocalNetworks
import org.amnezia.vpn.util.net.parseInetAddress import org.amnezia.vpn.util.net.parseInetAddress
import org.json.JSONObject import org.json.JSONObject
/**
* Config Example:
* {
* "protocol": "openvpn",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "openvpn_config_data": {
* "config": "openVpnConfig"
* }
* }
*/
open class OpenVpn : Protocol() { open class OpenVpn : Protocol() {
private var openVpnClient: OpenVpnClient? = null private var openVpnClient: OpenVpnClient? = null
@@ -58,7 +41,7 @@ open class OpenVpn : Protocol() {
scope = CoroutineScope(Dispatchers.IO) scope = CoroutineScope(Dispatchers.IO)
} }
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val configBuilder = OpenVpnConfig.Builder() val configBuilder = OpenVpnConfig.Builder()
openVpnClient = OpenVpnClient( openVpnClient = OpenVpnClient(
@@ -42,7 +42,7 @@ abstract class Protocol {
protected abstract fun internalInit() protected abstract fun internalInit()
abstract fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) abstract suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
abstract fun stopVpn() abstract fun stopVpn()
+1 -1
View File
@@ -21,5 +21,5 @@ android {
} }
dependencies { dependencies {
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar")))) api(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
} }
+3
View File
@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="black">#FF0E0E11</color>
<style name="NoActionBar"> <style name="NoActionBar">
<item name="android:windowBackground">@color/black</item>
<item name="android:colorBackground">@color/black</item>
<item name="android:windowActionBar">false</item> <item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item> <item name="android:windowNoTitle">true</item>
</style> </style>
+1 -1
View File
@@ -22,7 +22,7 @@ dependencyResolutionManagement {
includeBuild("./gradle/plugins") includeBuild("./gradle/plugins")
plugins { plugins {
id("com.android.settings") version "8.2.0" id("com.android.settings") version "8.5.2"
id("settings-property-delegate") id("settings-property-delegate")
} }
@@ -158,6 +158,10 @@ class AmneziaActivity : QtActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Log.d(TAG, "Create Amnezia activity: $intent") Log.d(TAG, "Create Amnezia activity: $intent")
window.apply {
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
statusBarColor = getColor(R.color.black)
}
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
val proto = mainScope.async(Dispatchers.IO) { val proto = mainScope.async(Dispatchers.IO) {
VpnStateStore.getVpnState().vpnProto VpnStateStore.getVpnState().vpnProto
@@ -610,6 +614,14 @@ class AmneziaActivity : QtActivity() {
} }
} }
@Suppress("unused")
fun setNavigationBarColor(color: Int) {
Log.v(TAG, "Change navigation bar color: ${"#%08X".format(color)}")
mainScope.launch {
window.navigationBarColor = color
}
}
@Suppress("unused") @Suppress("unused")
fun minimizeApp() { fun minimizeApp() {
Log.v(TAG, "Minimize application") Log.v(TAG, "Minimize application")
@@ -684,6 +696,17 @@ class AmneziaActivity : QtActivity() {
.show() .show()
} }
@Suppress("unused")
fun requestAuthentication() {
Log.v(TAG, "Request authentication")
mainScope.launch {
qtInitialized.await()
Intent(this@AmneziaActivity, AuthActivity::class.java).also {
startActivity(it)
}
}
}
/** /**
* Utils methods * Utils methods
*/ */
@@ -31,6 +31,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
@@ -111,6 +112,10 @@ open class AmneziaVpnService : VpnService() {
get() = clientMessengers.any { it.value.name == ACTIVITY_MESSENGER_NAME } get() = clientMessengers.any { it.value.name == ACTIVITY_MESSENGER_NAME }
private val connectionExceptionHandler = CoroutineExceptionHandler { _, e -> private val connectionExceptionHandler = CoroutineExceptionHandler { _, e ->
connectionJob?.cancel()
connectionJob = null
disconnectionJob?.cancel()
disconnectionJob = null
protocolState.value = DISCONNECTED protocolState.value = DISCONNECTED
when (e) { when (e) {
is IllegalArgumentException, is IllegalArgumentException,
@@ -531,7 +536,7 @@ open class AmneziaVpnService : VpnService() {
protocolState.value = DISCONNECTING protocolState.value = DISCONNECTING
disconnectionJob = connectionScope.launch { disconnectionJob = connectionScope.launch {
connectionJob?.join() connectionJob?.cancelAndJoin()
connectionJob = null connectionJob = null
vpnProto?.protocol?.stopVpn() vpnProto?.protocol?.stopVpn()
@@ -0,0 +1,97 @@
package org.amnezia.vpn
import android.os.Build
import android.os.Bundle
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.AuthenticationResult
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import org.amnezia.vpn.qt.QtAndroidController
import org.amnezia.vpn.util.Log
private const val TAG = "AuthActivity"
private const val AUTHENTICATORS = BIOMETRIC_STRONG or DEVICE_CREDENTIAL
class AuthActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val biometricManager = BiometricManager.from(applicationContext)
when (biometricManager.canAuthenticate(AUTHENTICATORS)) {
BiometricManager.BIOMETRIC_SUCCESS -> {
showBiometricPrompt(biometricManager)
return
}
BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> {
Log.w(TAG, "Unknown biometric status")
showBiometricPrompt(biometricManager)
return
}
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> {
Log.e(TAG, "The specified options are incompatible with the current Android " +
"version ${Build.VERSION.SDK_INT}")
}
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
Log.w(TAG, "The hardware is unavailable")
}
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
Log.w(TAG, "No biometric or device credential is enrolled")
}
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
Log.w(TAG, "There is no suitable hardware")
}
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> {
Log.w(TAG, "A security vulnerability has been discovered with one or " +
"more hardware sensors")
}
}
QtAndroidController.onAuthResult(true)
finish()
}
private fun showBiometricPrompt(biometricManager: BiometricManager) {
val executor = ContextCompat.getMainExecutor(applicationContext)
val biometricPrompt = BiometricPrompt(this, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Log.d(TAG, "Authentication succeeded")
QtAndroidController.onAuthResult(true)
finish()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Log.w(TAG, "Authentication failed")
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Log.e(TAG, "Authentication error $errorCode: $errString")
QtAndroidController.onAuthResult(false)
finish()
}
})
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setAllowedAuthenticators(AUTHENTICATORS)
.setTitle("AmneziaVPN")
.setSubtitle(biometricManager.getStrings(AUTHENTICATORS)?.promptMessage)
.build()
biometricPrompt.authenticate(promptInfo)
}
}
@@ -1,24 +0,0 @@
package org.amnezia.vpn;
import android.content.Context;
import android.app.KeyguardManager;
import android.content.Intent;
import org.qtproject.qt.android.bindings.QtActivity;
import static android.content.Context.KEYGUARD_SERVICE;
public class AuthHelper extends QtActivity {
static final String TAG = "AuthHelper";
public static Intent getAuthIntent(Context context) {
KeyguardManager mKeyguardManager = (KeyguardManager)context.getSystemService(KEYGUARD_SERVICE);
if (mKeyguardManager.isDeviceSecure()) {
return mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
} else {
return null;
}
}
}
@@ -33,10 +33,10 @@ class ImportConfigActivity : ComponentActivity() {
intent?.let(::readConfig) intent?.let(::readConfig)
} }
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
Log.d(TAG, "onNewIntent: $intent") Log.d(TAG, "onNewIntent: $intent")
intent?.let(::readConfig) intent.let(::readConfig)
} }
private fun readConfig(intent: Intent) { private fun readConfig(intent: Intent) {
@@ -25,5 +25,7 @@ object QtAndroidController {
external fun onConfigImported(data: String) external fun onConfigImported(data: String)
external fun onAuthResult(result: Boolean)
external fun decodeQrCode(data: String): Boolean external fun decodeQrCode(data: String): Boolean
} }
@@ -0,0 +1,9 @@
package org.amnezia.vpn.util
import org.json.JSONArray
import org.json.JSONObject
inline fun <reified T> JSONArray.asSequence(): Sequence<T> =
(0..<length()).asSequence().map { get(it) as T }
fun JSONObject.optStringOrNull(name: String) = optString(name).ifEmpty { null }
@@ -88,7 +88,7 @@ class NetworkState(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler) connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val numberAttempts = 3 val numberAttempts = 300
var attemptCount = 0 var attemptCount = 0
while(true) { while(true) {
try { try {
@@ -35,7 +35,7 @@ fun getLocalNetworks(context: Context, ipv6: Boolean): List<InetNetwork> {
return emptyList() return emptyList()
} }
fun parseInetAddress(address: String): InetAddress = parseNumericAddressCompat(address) fun parseInetAddress(address: String): InetAddress = InetAddress.getByName(address)
private val parseNumericAddressCompat: (String) -> InetAddress = private val parseNumericAddressCompat: (String) -> InetAddress =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
@@ -1,7 +1,12 @@
package org.amnezia.vpn.protocol.wireguard package org.amnezia.vpn.protocol.wireguard
import android.net.VpnService.Builder import android.net.VpnService.Builder
import java.io.IOException
import java.util.Locale
import java.util.TreeMap import java.util.TreeMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.amnezia.awg.GoBackend import org.amnezia.awg.GoBackend
import org.amnezia.vpn.protocol.Protocol import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
@@ -9,46 +14,13 @@ import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.Statistics import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnStartException import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.asSequence
import org.amnezia.vpn.util.net.InetEndpoint import org.amnezia.vpn.util.net.InetEndpoint
import org.amnezia.vpn.util.net.InetNetwork import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.parseInetAddress import org.amnezia.vpn.util.net.parseInetAddress
import org.amnezia.vpn.util.optStringOrNull
import org.json.JSONObject import org.json.JSONObject
/**
* Config example:
* {
* "protocol": "wireguard",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "wireguard_config_data": {
* "client_ip": "10.8.1.1",
* "hostName": "100.100.100.0",
* "port": 12345,
* "client_pub_key": "clientPublicKeyBase64",
* "client_priv_key": "privateKeyBase64",
* "psk_key": "presharedKeyBase64",
* "server_pub_key": "publicKeyBase64",
* "config": "[Interface]
* Address = 10.8.1.1/32
* DNS = 1.1.1.1, 1.0.0.1
* PrivateKey = privateKeyBase64
*
* [Peer]
* PublicKey = publicKeyBase64
* PresharedKey = presharedKeyBase64
* AllowedIPs = 0.0.0.0/0, ::/0
* Endpoint = 100.100.100.0:12345
* PersistentKeepalive = 25
* "
* }
* }
*/
private const val TAG = "Wireguard" private const val TAG = "Wireguard"
open class Wireguard : Protocol() { open class Wireguard : Protocol() {
@@ -79,67 +51,88 @@ open class Wireguard : Protocol() {
if (!isInitialized) loadSharedLibrary(context, "wg-go") if (!isInitialized) loadSharedLibrary(context, "wg-go")
} }
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val wireguardConfig = parseConfig(config) val wireguardConfig = parseConfig(config)
val startTime = System.currentTimeMillis()
start(wireguardConfig, vpnBuilder, protect) start(wireguardConfig, vpnBuilder, protect)
waitForConnection(startTime)
state.value = CONNECTED state.value = CONNECTED
} }
private suspend fun waitForConnection(startTime: Long) {
Log.d(TAG, "Waiting for connection")
withContext(Dispatchers.IO) {
val time = String.format(Locale.ROOT,"%.3f", startTime / 1000.0)
try {
delay(1000)
var log = getLogcat(time)
Log.d(TAG, "First waiting log: $log")
// check that there is a connection log,
// to avoid infinite connection
if (!log.contains("Attaching to interface")) {
Log.w(TAG, "Logs do not contain a connection log")
return@withContext
}
while (!log.contains("Received handshake response")) {
delay(1000)
log = getLogcat(time)
}
} catch (e: IOException) {
Log.e(TAG, "Failed to get logcat: $e")
}
}
}
private fun getLogcat(time: String): String =
ProcessBuilder("logcat", "--buffer=main", "--format=raw", "*:S AmneziaWG/awg0", "-t", time)
.redirectErrorStream(true)
.start()
.inputStream.reader().readText()
protected open fun parseConfig(config: JSONObject): WireguardConfig { protected open fun parseConfig(config: JSONObject): WireguardConfig {
val configDataJson = config.getJSONObject("wireguard_config_data") val configData = config.getJSONObject("wireguard_config_data")
val configData = parseConfigData(configDataJson.getString("config"))
return WireguardConfig.build { return WireguardConfig.build {
configWireguard(configData, configDataJson) configWireguard(config, configData)
configSplitTunneling(config) configSplitTunneling(config)
configAppSplitTunneling(config) configAppSplitTunneling(config)
} }
} }
protected fun WireguardConfig.Builder.configWireguard(configData: Map<String, String>, configDataJson: JSONObject) { protected fun WireguardConfig.Builder.configWireguard(config: JSONObject, configData: JSONObject) {
configData["Address"]?.split(",")?.map { address -> configData.getString("client_ip").split(",").map { address ->
InetNetwork.parse(address.trim()) InetNetwork.parse(address.trim())
}?.forEach(::addAddress) }.forEach(::addAddress)
configData["DNS"]?.split(",")?.map { dns -> config.optStringOrNull("dns1")?.let { dns ->
parseInetAddress(dns.trim()) addDnsServer(parseInetAddress(dns.trim()))
}?.forEach(::addDnsServer) }
config.optStringOrNull("dns2")?.let { dns ->
addDnsServer(parseInetAddress(dns.trim()))
}
val defRoutes = hashSetOf( val defRoutes = hashSetOf(
InetNetwork("0.0.0.0", 0), InetNetwork("0.0.0.0", 0),
InetNetwork("::", 0) InetNetwork("::", 0)
) )
val routes = hashSetOf<InetNetwork>() val routes = hashSetOf<InetNetwork>()
configData["AllowedIPs"]?.split(",")?.map { route -> configData.getJSONArray("allowed_ips").asSequence<String>().map { route ->
InetNetwork.parse(route.trim()) InetNetwork.parse(route.trim())
}?.forEach(routes::add) }.forEach(routes::add)
// if the allowed IPs list contains at least one non-default route, disable global split tunneling // if the allowed IPs list contains at least one non-default route, disable global split tunneling
if (routes.any { it !in defRoutes }) disableSplitTunneling() if (routes.any { it !in defRoutes }) disableSplitTunneling()
addRoutes(routes) addRoutes(routes)
configDataJson.optString("mtu").let { mtu -> configData.optStringOrNull("mtu")?.let { setMtu(it.toInt()) }
if (mtu.isNotEmpty()) {
setMtu(mtu.toInt())
} else {
configData["MTU"]?.let { setMtu(it.toInt()) }
}
}
configData["Endpoint"]?.let { setEndpoint(InetEndpoint.parse(it)) } val host = configData.getString("hostName").let { parseInetAddress(it.trim()) }
configData["PersistentKeepalive"]?.let { setPersistentKeepalive(it.toInt()) } val port = configData.getInt("port")
configData["PrivateKey"]?.let { setPrivateKeyHex(it.base64ToHex()) } setEndpoint(InetEndpoint(host, port))
configData["PublicKey"]?.let { setPublicKeyHex(it.base64ToHex()) }
configData["PresharedKey"]?.let { setPreSharedKeyHex(it.base64ToHex()) }
}
protected fun parseConfigData(data: String): Map<String, String> { configData.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) }
val parsedData = TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER) configData.getString("client_priv_key").let { setPrivateKeyHex(it.base64ToHex()) }
data.lineSequence() configData.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) }
.filter { it.isNotEmpty() && !it.startsWith('[') } configData.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) }
.forEach { line ->
val attr = line.split("=", limit = 2)
parsedData[attr.first().trim()] = attr.last().trim()
}
return parsedData
} }
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) { private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
+1 -64
View File
@@ -20,69 +20,6 @@ import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.parseInetAddress import org.amnezia.vpn.util.net.parseInetAddress
import org.json.JSONObject import org.json.JSONObject
/**
* Config example:
* {
* "appSplitTunnelType": 0,
* "config_version": 0,
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "protocol": "xray",
* "splitTunnelApps": [],
* "splitTunnelSites": [],
* "splitTunnelType": 0,
* "xray_config_data": {
* "inbounds": [
* {
* "listen": "127.0.0.1",
* "port": 8080,
* "protocol": "socks",
* "settings": {
* "udp": true
* }
* }
* ],
* "log": {
* "loglevel": "error"
* },
* "outbounds": [
* {
* "protocol": "vless",
* "settings": {
* "vnext": [
* {
* "address": "100.100.100.0",
* "port": 443,
* "users": [
* {
* "encryption": "none",
* "flow": "xtls-rprx-vision",
* "id": "id"
* }
* ]
* }
* ]
* },
* "streamSettings": {
* "network": "tcp",
* "realitySettings": {
* "fingerprint": "chrome",
* "publicKey": "publicKey",
* "serverName": "google.com",
* "shortId": "id",
* "spiderX": ""
* },
* "security": "reality"
* }
* }
* ]
* }
* }
*
*/
private const val TAG = "Xray" private const val TAG = "Xray"
private const val LIBXRAY_TAG = "libXray" private const val LIBXRAY_TAG = "libXray"
@@ -109,7 +46,7 @@ class Xray : Protocol() {
} }
} }
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
if (isRunning) { if (isRunning) {
Log.w(TAG, "XRay already running") Log.w(TAG, "XRay already running")
return return
-2
View File
@@ -27,7 +27,6 @@ link_directories(${CMAKE_CURRENT_SOURCE_DIR}/platforms/android)
set(HEADERS ${HEADERS} set(HEADERS ${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.h
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h ${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.h ${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.h
) )
@@ -35,7 +34,6 @@ set(HEADERS ${HEADERS}
set(SOURCES ${SOURCES} set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.cpp
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp ${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.cpp
) )
@@ -187,6 +187,10 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
jConfig[config_key::server_pub_key] = connData.serverPubKey; jConfig[config_key::server_pub_key] = connData.serverPubKey;
jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu);
jConfig[config_key::persistent_keep_alive] = 25;
QJsonArray allowedIps { "0.0.0.0/0", "::/0" };
jConfig[config_key::allowed_ips] = allowedIps;
jConfig[config_key::clientId] = connData.clientPubKey; jConfig[config_key::clientId] = connData.clientPubKey;
return QJsonDocument(jConfig).toJson(); return QJsonDocument(jConfig).toJson();
+12 -9
View File
@@ -9,8 +9,8 @@
#include "QRsa.h" #include "QRsa.h"
#include "amnezia_application.h" #include "amnezia_application.h"
#include "core/enums/apiEnums.h"
#include "configurators/wireguard_configurator.h" #include "configurators/wireguard_configurator.h"
#include "core/enums/apiEnums.h"
#include "version.h" #include "version.h"
namespace namespace
@@ -42,7 +42,7 @@ namespace
constexpr char keyPayload[] = "key_payload"; constexpr char keyPayload[] = "key_payload";
} }
const QStringList proxyStorageUrl = {""}; const QStringList proxyStorageUrl = { "" };
ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply) ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
{ {
@@ -65,7 +65,8 @@ namespace
} }
} }
ApiController::ApiController(const QString &gatewayEndpoint, QObject *parent) : QObject(parent), m_gatewayEndpoint(gatewayEndpoint) ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent)
: QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment)
{ {
} }
@@ -143,7 +144,7 @@ QStringList ApiController::getProxyUrls()
QEventLoop wait; QEventLoop wait;
QList<QSslError> sslErrors; QList<QSslError> sslErrors;
QNetworkReply* reply; QNetworkReply *reply;
for (const auto &proxyStorageUrl : proxyStorageUrl) { for (const auto &proxyStorageUrl : proxyStorageUrl) {
request.setUrl(proxyStorageUrl); request.setUrl(proxyStorageUrl);
@@ -281,7 +282,7 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint)); request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint));
QNetworkReply* reply; QNetworkReply *reply;
reply = amnApp->manager()->get(request); reply = amnApp->manager()->get(request);
QEventLoop wait; QEventLoop wait;
@@ -300,7 +301,8 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; }); connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(); wait.exec();
if (reply->error() != QNetworkReply::NetworkError::TimeoutError && reply->error() != QNetworkReply::NetworkError::OperationCanceledError) { if (reply->error() != QNetworkReply::NetworkError::TimeoutError
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
break; break;
} }
reply->deleteLater(); reply->deleteLater();
@@ -355,7 +357,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
EVP_PKEY *publicKey = nullptr; EVP_PKEY *publicKey = nullptr;
try { try {
QByteArray key = PROD_AGW_PUBLIC_KEY; QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
QSimpleCrypto::QRsa rsa; QSimpleCrypto::QRsa rsa;
publicKey = rsa.getPublicKeyFromByteArray(key); publicKey = rsa.getPublicKeyFromByteArray(key);
} catch (...) { } catch (...) {
@@ -375,7 +377,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64()); requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64()); requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
QNetworkReply* reply = manager.post(request, QJsonDocument(requestBody).toJson()); QNetworkReply *reply = manager.post(request, QJsonDocument(requestBody).toJson());
QEventLoop wait; QEventLoop wait;
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
@@ -395,7 +397,8 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; }); connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(); wait.exec();
if (reply->error() != QNetworkReply::NetworkError::TimeoutError && reply->error() != QNetworkReply::NetworkError::OperationCanceledError) { if (reply->error() != QNetworkReply::NetworkError::TimeoutError
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
break; break;
} }
reply->deleteLater(); reply->deleteLater();
+2 -1
View File
@@ -14,7 +14,7 @@ class ApiController : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit ApiController(const QString &gatewayEndpoint, QObject *parent = nullptr); explicit ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent = nullptr);
public slots: public slots:
void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig); void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig);
@@ -44,6 +44,7 @@ private:
QString m_gatewayEndpoint; QString m_gatewayEndpoint;
QStringList m_proxyUrls; QStringList m_proxyUrls;
bool m_isDevEnvironment = false;
}; };
#endif // APICONTROLLER_H #endif // APICONTROLLER_H
+15 -12
View File
@@ -83,7 +83,6 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
} }
qDebug().noquote() << lineToExec; qDebug().noquote() << lineToExec;
Logger::appendSshLog("Run command:" + lineToExec);
error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr); error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
if (error != ErrorCode::NoError) { if (error != ErrorCode::NoError) {
@@ -100,13 +99,13 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr) const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr)
{ {
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh"; QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
Logger::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName); ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
if (e) if (e)
return e; return e;
QString runner = QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, (container == DockerContainer::Socks5Proxy ? "sh" : "bash")); QString runner =
QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, (container == DockerContainer::Socks5Proxy ? "sh" : "bash"));
e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr); e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName); QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
@@ -426,7 +425,7 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
if (errorCode) if (errorCode)
return errorCode; return errorCode;
errorCode = uploadFileToHost(credentials, amnezia::scriptData(ProtocolScriptType::dockerfile, container).toUtf8(),dockerFilePath); errorCode = uploadFileToHost(credentials, amnezia::scriptData(ProtocolScriptType::dockerfile, container).toUtf8(), dockerFilePath);
if (errorCode) if (errorCode)
return errorCode; return errorCode;
@@ -437,9 +436,10 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
return ErrorCode::NoError; return ErrorCode::NoError;
}; };
errorCode = runScript(credentials, errorCode =
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)), runScript(credentials,
cbReadStdOut); replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
cbReadStdOut);
if (errorCode) if (errorCode)
return errorCode; return errorCode;
@@ -621,13 +621,15 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
// Socks5 proxy vars // Socks5 proxy vars
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } }); vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
auto username = socks5ProxyConfig.value(config_key:: userName).toString(); auto username = socks5ProxyConfig.value(config_key::userName).toString();
auto password = socks5ProxyConfig.value(config_key::password).toString(); auto password = socks5ProxyConfig.value(config_key::password).toString();
QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : ""; QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : "";
vars.append({ { "$SOCKS5_USER", socks5user } }); vars.append({ { "$SOCKS5_USER", socks5user } });
vars.append({ { "$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong" } }); vars.append({ { "$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong" } });
QString serverIp = NetworkUtilities::getIPAddress(credentials.hostName); QString serverIp = (container != DockerContainer::Awg && container != DockerContainer::WireGuard && container != DockerContainer::Xray)
? NetworkUtilities::getIPAddress(credentials.hostName)
: credentials.hostName;
if (!serverIp.isEmpty()) { if (!serverIp.isEmpty()) {
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } }); vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
} else { } else {
@@ -713,7 +715,8 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
udpProtoScript.append("' | grep -i udp"); udpProtoScript.append("' | grep -i udp");
tcpProtoScript.append(" | grep LISTEN"); tcpProtoScript.append(" | grep LISTEN");
ErrorCode errorCode = runScript(credentials, replaceVars(tcpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr); ErrorCode errorCode =
runScript(credentials, replaceVars(tcpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return errorCode; return errorCode;
} }
@@ -100,7 +100,13 @@ QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QStr
protocolConfigString = configurator->processConfigWithLocalSettings(dns, isApiConfig, protocolConfigString); protocolConfigString = configurator->processConfigWithLocalSettings(dns, isApiConfig, protocolConfigString);
QJsonObject vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object(); QJsonObject vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object(); if (container == DockerContainer::Awg || container == DockerContainer::WireGuard) {
// add mtu for old configs
if (vpnConfigData[config_key::mtu].toString().isEmpty()) {
vpnConfigData[config_key::mtu] = container == DockerContainer::Awg ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
}
}
vpnConfiguration.insert(ProtocolProps::key_proto_config_data(proto), vpnConfigData); vpnConfiguration.insert(ProtocolProps::key_proto_config_data(proto), vpnConfigData);
} }
+4 -1
View File
@@ -109,7 +109,10 @@ QStringList NetworkUtilities::summarizeRoutes(const QStringList &ips, const QStr
QString NetworkUtilities::getIPAddress(const QString &host) QString NetworkUtilities::getIPAddress(const QString &host)
{ {
if (ipAddressRegExp().match(host).hasMatch()) { QHostAddress address(host);
if (QAbstractSocket::IPv4Protocol == address.protocol()) {
return host;
} else if (QAbstractSocket::IPv6Protocol == address.protocol()) {
return host; return host;
} }
-107
View File
@@ -1,107 +0,0 @@
#ifndef LOGGER_H
#define LOGGER_H
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QString>
#include <QTextStream>
#include "ui/property_helper.h"
#include "mozilla/shared/loglevel.h"
class Logger : public QObject
{
Q_OBJECT
AUTO_PROPERTY(QString, sshLog)
AUTO_PROPERTY(QString, allLog)
public:
static Logger& Instance();
static void appendSshLog(const QString &log);
static void appendAllLog(const QString &log);
static bool init();
static void deInit();
static bool setServiceLogsEnabled(bool enabled);
static bool openLogsFolder();
static bool openServiceLogsFolder();
static QString appLogFileNamePath();
static void clearLogs();
static void clearServiceLogs();
static void cleanUp();
static QString userLogsFilePath();
static QString getLogFile();
// compat with Mozilla logger
Logger(const QString &className) { m_className = className; }
const QString& className() const { return m_className; }
class Log {
public:
Log(Logger* logger, LogLevel level);
~Log();
Log& operator<<(uint64_t t);
Log& operator<<(const char* t);
Log& operator<<(const QString& t);
Log& operator<<(const QStringList& t);
Log& operator<<(const QByteArray& t);
Log& operator<<(const QJsonObject& t);
Log& operator<<(QTextStreamFunction t);
Log& operator<<(const void* t);
// Q_ENUM
template <typename T>
typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, Log&>::type
operator<<(T t) {
const QMetaObject* meta = qt_getEnumMetaObject(t);
const char* name = qt_getEnumName(t);
addMetaEnum(typename QFlags<T>::Int(t), meta, name);
return *this;
}
private:
void addMetaEnum(quint64 value, const QMetaObject* meta, const char* name);
Logger* m_logger;
LogLevel m_logLevel;
struct Data {
Data() : m_ts(&m_buffer, QIODevice::WriteOnly) {}
QString m_buffer;
QTextStream m_ts;
};
Data* m_data;
};
Log error();
Log warning();
Log info();
Log debug();
QString sensitive(const QString& input);
private:
Logger() {}
Logger(Logger const &) = delete;
Logger& operator= (Logger const&) = delete;
static QString userLogsDir();
static QFile m_file;
static QTextStream m_textStream;
static QString m_logFileName;
friend void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
// compat with Mozilla logger
QString m_className;
};
#endif // LOGGER_H
+1 -1
View File
@@ -149,7 +149,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
QJsonArray jsAllowedIPAddesses; QJsonArray jsAllowedIPAddesses;
QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray(); QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray();
QJsonArray defaultAllowedIP = QJsonArray::fromStringList(QString("0.0.0.0/0, ::/0").split(",")); QJsonArray defaultAllowedIP = { "0.0.0.0/0", "::/0" };
if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) { if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) {
// Use AllowedIP list from WG config because of higher priority // Use AllowedIP list from WG config because of higher priority
@@ -98,6 +98,7 @@ bool AndroidController::initialize()
{"onStatisticsUpdate", "(JJ)V", reinterpret_cast<void *>(onStatisticsUpdate)}, {"onStatisticsUpdate", "(JJ)V", reinterpret_cast<void *>(onStatisticsUpdate)},
{"onFileOpened", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onFileOpened)}, {"onFileOpened", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onFileOpened)},
{"onConfigImported", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onConfigImported)}, {"onConfigImported", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onConfigImported)},
{"onAuthResult", "(Z)V", reinterpret_cast<void *>(onAuthResult)},
{"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)} {"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)}
}; };
@@ -210,6 +211,11 @@ void AndroidController::setScreenshotsEnabled(bool enabled)
callActivityMethod("setScreenshotsEnabled", "(Z)V", enabled); callActivityMethod("setScreenshotsEnabled", "(Z)V", enabled);
} }
void AndroidController::setNavigationBarColor(unsigned int color)
{
callActivityMethod("setNavigationBarColor", "(I)V", color);
}
void AndroidController::minimizeApp() void AndroidController::minimizeApp()
{ {
callActivityMethod("minimizeApp", "()V"); callActivityMethod("minimizeApp", "()V");
@@ -265,6 +271,22 @@ void AndroidController::requestNotificationPermission()
callActivityMethod("requestNotificationPermission", "()V"); callActivityMethod("requestNotificationPermission", "()V");
} }
bool AndroidController::requestAuthentication()
{
QEventLoop wait;
bool result;
connect(this, &AndroidController::authenticationResult, this,
[&result, &wait](const bool &authResult){
qDebug() << "Android authentication result:" << authResult;
result = authResult;
wait.quit();
},
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
callActivityMethod("requestAuthentication", "()V");
wait.exec();
return result;
}
// Moving log processing to the Android side // Moving log processing to the Android side
jclass AndroidController::log; jclass AndroidController::log;
jmethodID AndroidController::logDebug; jmethodID AndroidController::logDebug;
@@ -462,6 +484,14 @@ void AndroidController::onConfigImported(JNIEnv *env, jobject thiz, jstring data
emit AndroidController::instance()->configImported(AndroidUtils::convertJString(env, data)); emit AndroidController::instance()->configImported(AndroidUtils::convertJString(env, data));
} }
// static
void AndroidController::onAuthResult(JNIEnv *env, jobject thiz, jboolean result)
{
Q_UNUSED(thiz);
emit AndroidController::instance()->authenticationResult(result);
}
// static // static
bool AndroidController::decodeQrCode(JNIEnv *env, jobject thiz, jstring data) bool AndroidController::decodeQrCode(JNIEnv *env, jobject thiz, jstring data)
{ {
@@ -41,11 +41,13 @@ public:
void exportLogsFile(const QString &fileName); void exportLogsFile(const QString &fileName);
void clearLogs(); void clearLogs();
void setScreenshotsEnabled(bool enabled); void setScreenshotsEnabled(bool enabled);
void setNavigationBarColor(unsigned int color);
void minimizeApp(); void minimizeApp();
QJsonArray getAppList(); QJsonArray getAppList();
QPixmap getAppIcon(const QString &package, QSize *size, const QSize &requestedSize); QPixmap getAppIcon(const QString &package, QSize *size, const QSize &requestedSize);
bool isNotificationPermissionGranted(); bool isNotificationPermissionGranted();
void requestNotificationPermission(); void requestNotificationPermission();
bool requestAuthentication();
static bool initLogging(); static bool initLogging();
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message); static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
@@ -63,6 +65,7 @@ signals:
void configImported(QString config); void configImported(QString config);
void importConfigFromOutside(QString config); void importConfigFromOutside(QString config);
void initConnectionState(Vpn::ConnectionState state); void initConnectionState(Vpn::ConnectionState state);
void authenticationResult(bool result);
private: private:
bool isWaitingStatus = true; bool isWaitingStatus = true;
@@ -89,6 +92,7 @@ private:
static void onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes); static void onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes);
static void onConfigImported(JNIEnv *env, jobject thiz, jstring data); static void onConfigImported(JNIEnv *env, jobject thiz, jstring data);
static void onFileOpened(JNIEnv *env, jobject thiz, jstring uri); static void onFileOpened(JNIEnv *env, jobject thiz, jstring uri);
static void onAuthResult(JNIEnv *env, jobject thiz, jboolean result);
static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data); static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data);
template <typename Ret, typename ...Args> template <typename Ret, typename ...Args>
@@ -1,16 +0,0 @@
#include "authResultReceiver.h"
AuthResultReceiver::AuthResultReceiver(QSharedPointer<AuthResultNotifier> &notifier) : m_notifier(notifier)
{
}
void AuthResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data)
{
qDebug() << "receiverRequestCode" << receiverRequestCode << "resultCode" << resultCode;
if (resultCode == -1) { // ResultOK
emit m_notifier->authSuccessful();
} else {
emit m_notifier->authFailed();
}
}
@@ -1,32 +0,0 @@
#ifndef AUTHRESULTRECEIVER_H
#define AUTHRESULTRECEIVER_H
#include <QJniObject>
#include <private/qandroidextras_p.h>
class AuthResultNotifier : public QObject
{
Q_OBJECT
public:
AuthResultNotifier(QObject *parent = nullptr) : QObject(parent) {};
signals:
void authFailed();
void authSuccessful();
};
/* Auth result handler for Android */
class AuthResultReceiver final : public QAndroidActivityResultReceiver
{
public:
AuthResultReceiver(QSharedPointer<AuthResultNotifier> &notifier);
void handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data) override;
private:
QSharedPointer<AuthResultNotifier> m_notifier;
};
#endif // AUTHRESULTRECEIVER_H
+1 -4
View File
@@ -351,8 +351,6 @@ void IosController::vpnStatusDidChange(void *pNotification)
} }
} }
} }
} else {
qDebug() << "Disconnect error is absent";
} }
}]; }];
} else { } else {
@@ -835,7 +833,7 @@ QString IosController::openFile() {
void IosController::requestInetAccess() { void IosController::requestInetAccess() {
NSURL *url = [NSURL URLWithString:@"http://captive.apple.com/generate_204"]; NSURL *url = [NSURL URLWithString:@"http://captive.apple.com/generate_204"];
if (url) { if (!url) {
qDebug() << "IosController::requestInetAccess URL error"; qDebug() << "IosController::requestInetAccess URL error";
return; return;
} }
@@ -847,7 +845,6 @@ void IosController::requestInetAccess() {
} else { } else {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
QString responseBody = QString::fromUtf8((const char*)data.bytes, data.length); QString responseBody = QString::fromUtf8((const char*)data.bytes, data.length);
qDebug() << "IosController::requestInetAccess server response:" << httpResponse.statusCode << "\n\n" <<responseBody;
} }
}]; }];
[task resume]; [task resume];
@@ -12,7 +12,7 @@
#include "Winsvc.h" #include "Winsvc.h"
/** /**
* @brief The WindowsServiceManager provides controll over the MozillaVPNBroker * @brief The WindowsServiceManager provides control over the MozillaVPNBroker
* service via SCM * service via SCM
*/ */
class WindowsServiceManager : public QObject { class WindowsServiceManager : public QObject {
+7 -2
View File
@@ -4,9 +4,8 @@
#include <QTcpSocket> #include <QTcpSocket>
#include <QThread> #include <QThread>
#include "logger.h"
#include "utilities.h"
#include "wireguardprotocol.h" #include "wireguardprotocol.h"
#include "core/networkUtilities.h"
#include "mozilla/localsocketcontroller.h" #include "mozilla/localsocketcontroller.h"
@@ -37,6 +36,12 @@ void WireguardProtocol::stop()
ErrorCode WireguardProtocol::startMzImpl() ErrorCode WireguardProtocol::startMzImpl()
{ {
QString protocolName = m_rawConfig.value("protocol").toString();
QJsonObject vpnConfigData = m_rawConfig.value(protocolName + "_config_data").toObject();
vpnConfigData[config_key::hostName] = NetworkUtilities::getIPAddress(vpnConfigData.value(config_key::hostName).toString());
m_rawConfig.insert(protocolName + "_config_data", vpnConfigData);
m_rawConfig[config_key::hostName] = NetworkUtilities::getIPAddress(m_rawConfig[config_key::hostName].toString());
m_impl->activate(m_rawConfig); m_impl->activate(m_rawConfig);
return ErrorCode::NoError; return ErrorCode::NoError;
} }
+5 -2
View File
@@ -43,7 +43,9 @@ ErrorCode XrayProtocol::start()
m_xrayCfgFile.setAutoRemove(false); m_xrayCfgFile.setAutoRemove(false);
#endif #endif
m_xrayCfgFile.open(); m_xrayCfgFile.open();
m_xrayCfgFile.write(QJsonDocument(m_xrayConfig).toJson()); QString config = QJsonDocument(m_xrayConfig).toJson();
config.replace(m_remoteHost, m_remoteAddress);
m_xrayCfgFile.write(config.toUtf8());
m_xrayCfgFile.close(); m_xrayCfgFile.close();
QStringList args = QStringList() << "-c" << m_xrayCfgFile.fileName() << "-format=json"; QStringList args = QStringList() << "-c" << m_xrayCfgFile.fileName() << "-format=json";
@@ -238,7 +240,8 @@ void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
} }
m_xrayConfig = xrayConfiguration; m_xrayConfig = xrayConfiguration;
m_localPort = QString(amnezia::protocols::xray::defaultLocalProxyPort).toInt(); m_localPort = QString(amnezia::protocols::xray::defaultLocalProxyPort).toInt();
m_remoteAddress = configuration.value(amnezia::config_key::hostName).toString(); m_remoteHost = configuration.value(amnezia::config_key::hostName).toString();
m_remoteAddress = NetworkUtilities::getIPAddress(m_remoteHost);
m_routeMode = configuration.value(amnezia::config_key::splitTunnelType).toInt(); m_routeMode = configuration.value(amnezia::config_key::splitTunnelType).toInt();
m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString(); m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString();
m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString(); m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString();
+1
View File
@@ -26,6 +26,7 @@ private:
static QString tun2SocksExecPath(); static QString tun2SocksExecPath();
private: private:
int m_localPort; int m_localPort;
QString m_remoteHost;
QString m_remoteAddress; QString m_remoteAddress;
int m_routeMode; int m_routeMode;
QJsonObject m_configData; QJsonObject m_configData;
+2
View File
@@ -199,6 +199,8 @@
<file>server_scripts/socks5_proxy/Dockerfile</file> <file>server_scripts/socks5_proxy/Dockerfile</file>
<file>server_scripts/socks5_proxy/configure_container.sh</file> <file>server_scripts/socks5_proxy/configure_container.sh</file>
<file>server_scripts/socks5_proxy/start.sh</file> <file>server_scripts/socks5_proxy/start.sh</file>
<file>ui/qml/Pages2/PageProtocolAwgClientSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file>
<file>ui/qml/Pages2/PageSetupWizardApiServicesList.qml</file> <file>ui/qml/Pages2/PageSetupWizardApiServicesList.qml</file>
<file>ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml</file> <file>ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml</file>
<file>ui/qml/Controls2/CardWithIconsType.qml</file> <file>ui/qml/Controls2/CardWithIconsType.qml</file>
+14 -2
View File
@@ -174,13 +174,25 @@ bool SecureQSettings::restoreAppConfig(const QByteArray &json)
QByteArray SecureQSettings::encryptText(const QByteArray &value) const QByteArray SecureQSettings::encryptText(const QByteArray &value) const
{ {
QSimpleCrypto::QBlockCipher cipher; QSimpleCrypto::QBlockCipher cipher;
return cipher.encryptAesBlockCipher(value, getEncKey(), getEncIv()); QByteArray result;
try {
result = cipher.encryptAesBlockCipher(value, getEncKey(), getEncIv());
} catch (...) { // todo change error handling in QSimpleCrypto?
qCritical() << "error when encrypting the settings value";
}
return result;
} }
QByteArray SecureQSettings::decryptText(const QByteArray &ba) const QByteArray SecureQSettings::decryptText(const QByteArray &ba) const
{ {
QSimpleCrypto::QBlockCipher cipher; QSimpleCrypto::QBlockCipher cipher;
return cipher.decryptAesBlockCipher(ba, getEncKey(), getEncIv()); QByteArray result;
try {
result = cipher.decryptAesBlockCipher(ba, getEncKey(), getEncIv());
} catch (...) { // todo change error handling in QSimpleCrypto?
qCritical() << "error when decrypting the settings value";
}
return result;
} }
bool SecureQSettings::encryptionRequired() const bool SecureQSettings::encryptionRequired() const
+1 -1
View File
@@ -13,5 +13,5 @@ sudo docker network connect amnezia-dns-net $CONTAINER_NAME
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi' sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
# Prevent to route packets outside of the container in case if server behind of the NAT # Prevent to route packets outside of the container in case if server behind of the NAT
sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up" #sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up"
+1 -1
View File
@@ -3,7 +3,7 @@
# This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts # This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts
echo "Container startup" echo "Container startup"
ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up #ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up
iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
+16 -1
View File
@@ -227,7 +227,7 @@ void Settings::setSaveLogs(bool enabled)
if (!isSaveLogs()) { if (!isSaveLogs()) {
Logger::deInit(); Logger::deInit();
} else { } else {
if (!Logger::init()) { if (!Logger::init(false)) {
qWarning() << "Initialization of debug subsystem failed"; qWarning() << "Initialization of debug subsystem failed";
} }
} }
@@ -519,7 +519,22 @@ void Settings::setGatewayEndpoint(const QString &endpoint)
m_gatewayEndpoint = endpoint; m_gatewayEndpoint = endpoint;
} }
void Settings::setDevGatewayEndpoint()
{
m_gatewayEndpoint = DEV_AGW_ENDPOINT;
}
QString Settings::getGatewayEndpoint() QString Settings::getGatewayEndpoint()
{ {
return m_gatewayEndpoint; return m_gatewayEndpoint;
} }
bool Settings::isDevGatewayEnv()
{
return m_isDevGatewayEnv;
}
void Settings::toggleDevGatewayEnv(bool enabled)
{
m_isDevGatewayEnv = enabled;
}
+5 -1
View File
@@ -183,7 +183,7 @@ public:
bool isScreenshotsEnabled() const bool isScreenshotsEnabled() const
{ {
return value("Conf/screenshotsEnabled", false).toBool(); return value("Conf/screenshotsEnabled", true).toBool();
} }
void setScreenshotsEnabled(bool enabled) void setScreenshotsEnabled(bool enabled)
{ {
@@ -217,7 +217,10 @@ public:
void resetGatewayEndpoint(); void resetGatewayEndpoint();
void setGatewayEndpoint(const QString &endpoint); void setGatewayEndpoint(const QString &endpoint);
void setDevGatewayEndpoint();
QString getGatewayEndpoint(); QString getGatewayEndpoint();
bool isDevGatewayEnv();
void toggleDevGatewayEnv(bool enabled);
signals: signals:
void saveLogsChanged(bool enabled); void saveLogsChanged(bool enabled);
@@ -234,6 +237,7 @@ private:
mutable SecureQSettings m_settings; mutable SecureQSettings m_settings;
QString m_gatewayEndpoint; QString m_gatewayEndpoint;
bool m_isDevGatewayEnv;
}; };
#endif // SETTINGS_H #endif // SETTINGS_H
+69 -109
View File
@@ -6,47 +6,47 @@
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="63"/> <location filename="../ui/models/apiServicesModel.cpp" line="63"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source> <source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation type="unfinished"></translation> <translation>شبكة VPN كلاسيكية للعمل المريح وتنزيل الملفات الكبيرة ومشاهدة مقاطع الفيديو. تعمل مع أي موقع. تصل السرعة إلى %1 ميجابت/ثانية</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="67"/> <location filename="../ui/models/apiServicesModel.cpp" line="67"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source> <source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation type="unfinished"></translation> <translation>شبكة VPN للولوج للمواقع المحظورة في بلاد ذو مستوي عالي من الرقابة علي الانترنت. </translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/> <location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source> <source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation type="unfinished"></translation> <translation>Amenzia Premium - شبكة VPN للعمل المريح, تحميل ملفات كبيرة الحجم, ومشاهدة مقاطع الفيديو ب جودة عالية. تعمل لجميع المواقع, حتي في البلاد ذو مستوي عالي من الرقابة علي الانترنت</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="75"/> <location filename="../ui/models/apiServicesModel.cpp" line="75"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source> <source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation type="unfinished"></translation> <translation>Amnezia Free هو VPN مجاني لتخطي الحظر في البلاد ذو مستوي عالي من الرقابة علي الانترنت</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="80"/> <location filename="../ui/models/apiServicesModel.cpp" line="80"/>
<source>%1 MBit/s</source> <source>%1 MBit/s</source>
<translation type="unfinished"></translation> <translation>%1 ميجابت/ثانية</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="87"/> <location filename="../ui/models/apiServicesModel.cpp" line="87"/>
<source>%1 days</source> <source>%1 days</source>
<translation type="unfinished"></translation> <translation>%1 ايام</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/> <location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source> <source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation type="unfinished"></translation> <translation>سيقوم VPN فقط بفتح المواقع المشهورة المحظورة في بلدك, مثل Instagram, Facebook, Twitter و مواقع اخري. المواقع الاخري ستٌفتح من عنوان ال IP الحقيقي الخاص بك, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;معلومات اخري علي الموقع.&lt;/a&gt;</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="104"/> <location filename="../ui/models/apiServicesModel.cpp" line="104"/>
<source>Free</source> <source>Free</source>
<translation type="unfinished"></translation> <translation>مجاني</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="106"/> <location filename="../ui/models/apiServicesModel.cpp" line="106"/>
<source>%1 $/month</source> <source>%1 $/month</source>
<translation type="unfinished"></translation> <translation>%1 دولار/الشهر</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -325,7 +325,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/controllers/installController.cpp" line="608"/> <location filename="../ui/controllers/installController.cpp" line="608"/>
<source>Api config removed</source> <source>Api config removed</source>
<translation type="unfinished"></translation> <translation>تم حذف تكوين Api</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/installController.cpp" line="630"/> <location filename="../ui/controllers/installController.cpp" line="630"/>
@@ -345,17 +345,17 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/controllers/installController.cpp" line="845"/> <location filename="../ui/controllers/installController.cpp" line="845"/>
<source>%1 installed successfully.</source> <source>%1 installed successfully.</source>
<translation type="unfinished"></translation> <translation>تم تحميل %1 بنجاح</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/installController.cpp" line="877"/> <location filename="../ui/controllers/installController.cpp" line="877"/>
<source>API config reloaded</source> <source>API config reloaded</source>
<translation type="unfinished"></translation> <translation>تمت إعادة تحميل تكوين API</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/installController.cpp" line="881"/> <location filename="../ui/controllers/installController.cpp" line="881"/>
<source>Successfully changed the country of connection to %1</source> <source>Successfully changed the country of connection to %1</source>
<translation type="unfinished"></translation> <translation>تم تغيير بلد الاتصال بنجاح إلى %1</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -441,7 +441,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageDevMenu.qml" line="74"/> <location filename="../ui/qml/Pages2/PageDevMenu.qml" line="74"/>
<source>Gateway endpoint</source> <source>Gateway endpoint</source>
<translation type="unfinished"></translation> <translation>نقطة نهاية البوابة</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -507,47 +507,47 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="147"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="147"/>
<source>Jc - Junk packet count</source> <source>Jc - Junk packet count</source>
<translation type="unfinished"></translation> <translation>Jc - عدد الحزم غير المرغوب فيها</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="172"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="172"/>
<source>Jmin - Junk packet minimum size</source> <source>Jmin - Junk packet minimum size</source>
<translation type="unfinished"></translation> <translation>Jmin - الحجم الادني للحزم الغير مرغوب فيها</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="193"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="193"/>
<source>Jmax - Junk packet maximum size</source> <source>Jmax - Junk packet maximum size</source>
<translation type="unfinished"></translation> <translation>Jmax - الحجم الاقصي للحزم الغير مرغوب فيها</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="214"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="214"/>
<source>S1 - Init packet junk size</source> <source>S1 - Init packet junk size</source>
<translation type="unfinished"></translation> <translation>S1 - حجم حزمة البيانات العشوائية الأولية</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="235"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="235"/>
<source>S2 - Response packet junk size</source> <source>S2 - Response packet junk size</source>
<translation type="unfinished"></translation> <translation>S2 - حجم حزمة الاستجابة غير المرغوب فيها</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="256"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="256"/>
<source>H1 - Init packet magic header</source> <source>H1 - Init packet magic header</source>
<translation type="unfinished"></translation> <translation>H1 - حزمة رأس سحرية مبدئية</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="277"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="277"/>
<source>H2 - Response packet magic header</source> <source>H2 - Response packet magic header</source>
<translation type="unfinished"></translation> <translation>H2 - رأس حزمة الاستجابة السحرية</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="298"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="298"/>
<source>H4 - Transport packet magic header</source> <source>H4 - Transport packet magic header</source>
<translation type="unfinished"></translation> <translation>H4 - رأس حزمة النقل السحرية</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="320"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="320"/>
<source>H3 - Underload packet magic header</source> <source>H3 - Underload packet magic header</source>
<translation type="unfinished"></translation> <translation>H3 - رأس حزمة السحر غير المحمل</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="363"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="363"/>
@@ -1164,7 +1164,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettings.qml" line="137"/> <location filename="../ui/qml/Pages2/PageSettings.qml" line="137"/>
<source>Dev console</source> <source>Dev console</source>
<translation type="unfinished"></translation> <translation>وحدة تحكم التطوير</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettings.qml" line="158"/> <location filename="../ui/qml/Pages2/PageSettings.qml" line="158"/>
@@ -1250,74 +1250,74 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="45"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="45"/>
<source>For the region</source> <source>For the region</source>
<translation type="unfinished"></translation> <translation>للمنطقة</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="54"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="54"/>
<source>Price</source> <source>Price</source>
<translation type="unfinished"></translation> <translation>السعر</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<source>Work period</source> <source>Work period</source>
<translation type="unfinished"></translation> <translation>مدة العمل</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<source>Speed</source> <source>Speed</source>
<translation type="unfinished"></translation> <translation>السرعة</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
<source>Support tag</source> <source>Support tag</source>
<translation type="unfinished"></translation> <translation>علامة الدعم</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<source>Copied</source> <source>Copied</source>
<translation type="unfinished"></translation> <translation>تم النسخ</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<source>Reload API config</source> <source>Reload API config</source>
<translation type="unfinished"></translation> <translation>إعادة تحميل تكوين API</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<source>Reload API config?</source> <source>Reload API config?</source>
<translation type="unfinished"></translation> <translation>إعادة تحميل تكوين API</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<source>Continue</source> <source>Continue</source>
<translation type="unfinished">واصل</translation> <translation>واصل</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">إلغاء</translation> <translation>إلغاء</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<source>Cannot reload API config during active connection</source> <source>Cannot reload API config during active connection</source>
<translation type="unfinished"></translation> <translation>لا يمكن إعادة تحميل تكوين API اثناء تواجد اتصال نشط</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<source>Remove from application</source> <source>Remove from application</source>
<translation type="unfinished"></translation> <translation>احذف من التطبيق</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<source>Remove from application?</source> <source>Remove from application?</source>
<translation type="unfinished"></translation> <translation>احذف من التطبيق؟</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<source>Cannot remove server during active connection</source> <source>Cannot remove server during active connection</source>
<translation type="unfinished">لا يمكن إزالة الخادم أثناء الاتصال النشط</translation> <translation>لا يمكن إزالة الخادم أثناء الاتصال النشط</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -1769,7 +1769,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="33"/> <location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="33"/>
<source>No new installed containers found</source> <source>No new installed containers found</source>
<translation>لم يتم العثور علي اي حاويات جديدة مٌثبتة</translation> <translation>لم يتم العثور علي اي خدمات مٌثبتة سابقاً</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="127"/> <location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="127"/>
@@ -1941,7 +1941,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="190"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="190"/>
<source>Remove %1 from server?</source> <source>Remove %1 from server?</source>
<translation type="unfinished"></translation> <translation>احذف %1 من الخادم؟</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="191"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="191"/>
@@ -2080,32 +2080,32 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="62"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="62"/>
<source>For the region</source> <source>For the region</source>
<translation type="unfinished"></translation> <translation>للمنطقة</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="71"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="71"/>
<source>Price</source> <source>Price</source>
<translation type="unfinished"></translation> <translation>السعر</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="80"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="80"/>
<source>Work period</source> <source>Work period</source>
<translation type="unfinished"></translation> <translation>مدة العمل</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="91"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="91"/>
<source>Speed</source> <source>Speed</source>
<translation type="unfinished"></translation> <translation>السرعة</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="100"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="100"/>
<source>Features</source> <source>Features</source>
<translation type="unfinished"></translation> <translation>المميزات</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="139"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="139"/>
<source>Connect</source> <source>Connect</source>
<translation type="unfinished">اتصل</translation> <translation>اتصل</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -2113,96 +2113,80 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="52"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="52"/>
<source>VPN by Amnezia</source> <source>VPN by Amnezia</source>
<translation type="unfinished"></translation> <translation>VPN بواسطة Amnezia</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="53"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="53"/>
<source>Choose a VPN service that suits your needs.</source> <source>Choose a VPN service that suits your needs.</source>
<translation type="unfinished"></translation> <translation>اختر خدمة VPN تلبي احتياجاتك</translation>
</message> </message>
</context> </context>
<context> <context>
<name>PageSetupWizardConfigSource</name> <name>PageSetupWizardConfigSource</name>
<message>
<source>Server connection</source>
<translation type="vanished">اتصال الخادم</translation>
</message>
<message>
<source>Do not use connection codes from untrusted sources, as they may be created to intercept your data.</source>
<translation type="vanished">لا تستخدم رموز اتصال من مصادر غير موثوقة, حيث قد يكون تم إنشاؤها لاعتراض بياناتك.</translation>
</message>
<message>
<source>What do you have?</source>
<translation type="vanished">ماذا لديك؟</translation>
</message>
<message>
<source>File with connection settings or backup</source>
<translation type="vanished">ملف إعدادات اتصال او نسخ احتياطي</translation>
</message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="57"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="57"/>
<source>Connection</source> <source>Connection</source>
<translation type="unfinished">الاتصال</translation> <translation>الاتصال</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="67"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="67"/>
<source>Insert the key, add a configuration file or scan the QR-code</source> <source>Insert the key, add a configuration file or scan the QR-code</source>
<translation type="unfinished"></translation> <translation>أدخل المفتاح، أضف ملف تكوين أو امسح رمز الاستجابة السريعة</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="77"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="77"/>
<source>Insert key</source> <source>Insert key</source>
<translation type="unfinished"></translation> <translation>أدخل مفتاح</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="78"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="78"/>
<source>Insert</source> <source>Insert</source>
<translation type="unfinished">ادخل</translation> <translation>أدخل</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="98"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="98"/>
<source>Continue</source> <source>Continue</source>
<translation type="unfinished">واصل</translation> <translation>واصل</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="116"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="116"/>
<source>Other connection options</source> <source>Other connection options</source>
<translation type="unfinished"></translation> <translation>اختيارات اتصال اخري</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="129"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="129"/>
<source>VPN by Amnezia</source> <source>VPN by Amnezia</source>
<translation type="unfinished"></translation> <translation>VPN بواسطة Amnezia</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="130"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="130"/>
<source>Connect to classic paid and free VPN services from Amnezia</source> <source>Connect to classic paid and free VPN services from Amnezia</source>
<translation type="unfinished"></translation> <translation>اتصل بخدمات VPN الكلاسيكية المدفوعة والمجانية من Amnezia</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="153"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="153"/>
<source>Self-hosted VPN</source> <source>Self-hosted VPN</source>
<translation type="unfinished"></translation> <translation>VPN ذاتية الاستضافة</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="154"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="154"/>
<source>Configure Amnezia VPN on your own server</source> <source>Configure Amnezia VPN on your own server</source>
<translation type="unfinished"></translation> <translation>قم بتكوين Amnezia VPN على الخادم الخاص بك</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="174"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="174"/>
<source>Restore from backup</source> <source>Restore from backup</source>
<translation type="unfinished">استرجاع من ملف يحتوي علي نسخة احتياطية</translation> <translation>استرجاع من ملف يحتوي علي نسخة احتياطية</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="180"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="180"/>
<source>Open backup file</source> <source>Open backup file</source>
<translation type="unfinished">افتح ملف نسخ احتياطي</translation> <translation>افتح ملف نسخ احتياطي</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="181"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="181"/>
<source>Backup files (*.backup)</source> <source>Backup files (*.backup)</source>
<translation type="unfinished">ملفات نٌسخ احتياطية (*.backup)</translation> <translation>ملفات نٌسخ احتياطية (*.backup)</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="198"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="198"/>
@@ -2222,11 +2206,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="248"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="248"/>
<source>I have nothing</source> <source>I have nothing</source>
<translation type="unfinished">ليس لدي اي شئ</translation> <translation>ليس لدي اي شئ</translation>
</message>
<message>
<source>Key as text</source>
<translation type="vanished">مفتاح كنص</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -2269,12 +2249,12 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/> <location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
<source>How to run your VPN server</source> <source>How to run your VPN server</source>
<translation type="unfinished"></translation> <translation>كيف تقوم بتشغيل خادم ال VPN الخاص بك</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/> <location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
<source>Where to get connection data, step-by-step instructions for buying a VPS</source> <source>Where to get connection data, step-by-step instructions for buying a VPS</source>
<translation type="unfinished"></translation> <translation>اين تحصل علي بيانات الاتصال, تعليمات خطوة ب خطوة لشراء VPS</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/> <location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
@@ -2392,7 +2372,7 @@ Already installed containers were found on the server. All installed containers
<translation>تثبيت</translation> <translation>تثبيت</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardProtocolSettings.qml" line="267"/> <location filename="../ui/qml/Pages2/PageSetupWizardProtocolSettings.qml" line="268"/>
<source>The port must be in the range of 1 to 65535</source> <source>The port must be in the range of 1 to 65535</source>
<translation>يجب أن يكون المنفذ في النطاق من 1 إلى 65535</translation> <translation>يجب أن يكون المنفذ في النطاق من 1 إلى 65535</translation>
</message> </message>
@@ -2420,30 +2400,10 @@ Already installed containers were found on the server. All installed containers
</context> </context>
<context> <context>
<name>PageSetupWizardStart</name> <name>PageSetupWizardStart</name>
<message>
<source>Settings restored from backup file</source>
<translation type="vanished">تم استرداد الإعدادات من ملف نسخة احتياطية</translation>
</message>
<message>
<source>Free service for creating a personal VPN on your server.</source>
<translation type="vanished">خدمة مجانية لأنشاء VPN شخصي علي الخادم الشخصي.</translation>
</message>
<message>
<source> Helps you access blocked content without revealing your privacy, even to VPN providers.</source>
<translation type="vanished"> يساعدك في الولوج للمحتوي المحظور بدون إظهار خصوصيات, حتي لمزود ال VPN.</translation>
</message>
<message>
<source>I have the data to connect</source>
<translation type="vanished">لدي البيانات المطلوبة للأتصال</translation>
</message>
<message>
<source>I have nothing</source>
<translation type="vanished">ليس لدي اي شئ</translation>
</message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="48"/> <location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="48"/>
<source>Let&apos;s get started</source> <source>Let&apos;s get started</source>
<translation type="unfinished"></translation> <translation>هيا نبدأ</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -2777,7 +2737,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageStart.qml" line="202"/> <location filename="../ui/qml/Pages2/PageStart.qml" line="202"/>
<source>Settings restored from backup file</source> <source>Settings restored from backup file</source>
<translation type="unfinished"></translation> <translation>تم تحميل الإعدادات من ملف نسخة احتياطية</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -3221,7 +3181,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../core/errorstrings.cpp" line="63"/> <location filename="../core/errorstrings.cpp" line="63"/>
<source>Missing AGW public key</source> <source>Missing AGW public key</source>
<translation type="unfinished"></translation> <translation>مفتاح AGW عام مفقود</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="66"/> <location filename="../core/errorstrings.cpp" line="66"/>
@@ -3299,7 +3259,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../containers/containers_defs.cpp" line="127"/> <location filename="../containers/containers_defs.cpp" line="127"/>
<source>XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.</source> <source>XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.</source>
<translation>الأشعة السينية مع الواقع - مناسبة للبلدان التي لديها أعلى مستوى من الرقابة على الإنترنت. إخفاء حركة المرور كحركة مرور على الويب على مستوى TLS، والحماية من الكشف عن طريق طرق التحقيق النشطة.</translation> <translation>XRay مع REALITY - مناسبة للبلدان التي لديها أعلى مستوى من الرقابة على الإنترنت. إخفاء حركة المرور كحركة مرور على الويب على مستوى TLS، والحماية من الكشف عن طريق طرق التحقيق النشطة.</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="130"/> <location filename="../containers/containers_defs.cpp" line="130"/>
+8 -9
View File
@@ -6,8 +6,7 @@
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="63"/> <location filename="../ui/models/apiServicesModel.cpp" line="63"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source> <source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation>Для перевода на персидский: <translation>برای کار راحت، دانلود فایلهای بزرگ و تماشای ویدیوها، از VPN کلاسیک استفاده کنید. این VPN برای هر سایتی کار میکند و سرعت آن تا %1 مگابیت بر ثانیه است.</translation>
وی پی ان کلاسیک برای کار راحت، دانلود فایلهای بزرگ و تماشای ویدیو. قابل استفاده برای هر سایتی. سرعت تا %1 مگابیت بر ثانیه.</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="67"/> <location filename="../ui/models/apiServicesModel.cpp" line="67"/>
@@ -119,7 +118,7 @@
<message> <message>
<location filename="../ui/controllers/connectionController.cpp" line="209"/> <location filename="../ui/controllers/connectionController.cpp" line="209"/>
<source>The selected protocol is not supported on the current platform</source> <source>The selected protocol is not supported on the current platform</source>
<translation>پروتکل انتخاب شده بر روی این پلتفرم پشتیبانی نمیشودپروتکل انتخاب شده در پلتفرم فعلی پشتیبانی نمیشود.</translation> <translation>پروتکل انتخابشده در پلتفرم فعلی پشتیبانی نمیشود.</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/connectionController.cpp" line="233"/> <location filename="../ui/controllers/connectionController.cpp" line="233"/>
@@ -1113,13 +1112,13 @@ Already installed containers were found on the server. All installed containers
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="162"/> <location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="162"/>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="189"/> <location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="189"/>
<source>Copied</source> <source>Copied</source>
<translation>کپی شدکپی شد</translation> <translation>کپی شد</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="125"/> <location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="125"/>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="260"/> <location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="260"/>
<source>Port</source> <source>Port</source>
<translation>پورتپورت</translation> <translation>پورت</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="149"/> <location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="149"/>
@@ -1385,7 +1384,7 @@ Already installed containers were found on the server. All installed containers
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<source>Cancel</source> <source>Cancel</source>
<translation>کنسللغو کنید</translation> <translation>لغو</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
@@ -1433,7 +1432,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="129"/> <location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="129"/>
<source>Mode</source> <source>Mode</source>
<translation>حالتمدل</translation> <translation>حالت</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="221"/> <location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="221"/>
@@ -1458,7 +1457,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="278"/> <location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="278"/>
<source>Open executable file</source> <source>Open executable file</source>
<translation></translation> <translation>فایل اجرایی را باز کنید</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="279"/> <location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="279"/>
@@ -1486,7 +1485,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="95"/> <location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="95"/>
<source>Enable notifications to show the VPN state in the status bar</source> <source>Enable notifications to show the VPN state in the status bar</source>
<translation></translation> <translation>اعلان ها را فعال کنید تا وضعیت VPN را در نوار وضعیت ببینید</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="117"/> <location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="117"/>
File diff suppressed because it is too large Load Diff
+53 -53
View File
@@ -6,47 +6,47 @@
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="63"/> <location filename="../ui/models/apiServicesModel.cpp" line="63"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source> <source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation type="unfinished"></translation> <translation>Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Работает для любых сайтов. Скорость до %1 Мбит/с</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="67"/> <location filename="../ui/models/apiServicesModel.cpp" line="67"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source> <source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation type="unfinished"></translation> <translation>VPN для доступа к заблокированным сайтам в регионах с высоким уровнем интернет-цензуры. </translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/> <location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source> <source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation type="unfinished"></translation> <translation>Amnezia Premium классический VPN для комфортной работы, загрузки больших файлов и просмотра видео в высоком разрешении. Работает на всех сайтах, даже в странах с самым высоким уровнем интернет-цензуры.</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="75"/> <location filename="../ui/models/apiServicesModel.cpp" line="75"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source> <source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation type="unfinished"></translation> <translation>Amnezia Free - это бесплатный VPN для обхода блокировок в странах с высоким уровнем интернет-цензуры</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="80"/> <location filename="../ui/models/apiServicesModel.cpp" line="80"/>
<source>%1 MBit/s</source> <source>%1 MBit/s</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="87"/> <location filename="../ui/models/apiServicesModel.cpp" line="87"/>
<source>%1 days</source> <source>%1 days</source>
<translation type="unfinished"></translation> <translation>%1 дней</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/> <location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source> <source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation type="unfinished"></translation> <translation>Через VPN будут открываться только популярные сайты, заблокированные в вашем регионе, такие как Instagram, Facebook, Twitter и другие. Остальные сайты будут открываться с вашего реального IP-адреса, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;подробности на сайте.&lt;/a&gt;</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="104"/> <location filename="../ui/models/apiServicesModel.cpp" line="104"/>
<source>Free</source> <source>Free</source>
<translation type="unfinished"></translation> <translation>Бесплатно</translation>
</message> </message>
<message> <message>
<location filename="../ui/models/apiServicesModel.cpp" line="106"/> <location filename="../ui/models/apiServicesModel.cpp" line="106"/>
<source>%1 $/month</source> <source>%1 $/month</source>
<translation type="unfinished"></translation> <translation>%1 $/месяц</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -329,7 +329,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/controllers/installController.cpp" line="608"/> <location filename="../ui/controllers/installController.cpp" line="608"/>
<source>Api config removed</source> <source>Api config removed</source>
<translation type="unfinished"></translation> <translation>Конфигурация API удалена</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/installController.cpp" line="630"/> <location filename="../ui/controllers/installController.cpp" line="630"/>
@@ -349,17 +349,17 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/controllers/installController.cpp" line="845"/> <location filename="../ui/controllers/installController.cpp" line="845"/>
<source>%1 installed successfully.</source> <source>%1 installed successfully.</source>
<translation type="unfinished"></translation> <translation>%1 успешно установлен.</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/installController.cpp" line="877"/> <location filename="../ui/controllers/installController.cpp" line="877"/>
<source>API config reloaded</source> <source>API config reloaded</source>
<translation type="unfinished"></translation> <translation>Конфигурация API перезагружена</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/installController.cpp" line="881"/> <location filename="../ui/controllers/installController.cpp" line="881"/>
<source>Successfully changed the country of connection to %1</source> <source>Successfully changed the country of connection to %1</source>
<translation type="unfinished"></translation> <translation>Изменение страны подключения на %1</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -1353,22 +1353,22 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="45"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="45"/>
<source>For the region</source> <source>For the region</source>
<translation type="unfinished"></translation> <translation>Для региона</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="54"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="54"/>
<source>Price</source> <source>Price</source>
<translation type="unfinished"></translation> <translation>Цена</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<source>Work period</source> <source>Work period</source>
<translation type="unfinished"></translation> <translation>Период работы</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<source>Speed</source> <source>Speed</source>
<translation type="unfinished"></translation> <translation>Скорость</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
@@ -1378,49 +1378,49 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<source>Copied</source> <source>Copied</source>
<translation type="unfinished">Скопировано</translation> <translation>Скопировано</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<source>Reload API config</source> <source>Reload API config</source>
<translation type="unfinished"></translation> <translation>Перезагрузить конфигурацию API</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<source>Reload API config?</source> <source>Reload API config?</source>
<translation type="unfinished"></translation> <translation>Перезагрузить конфигурацию API?</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<source>Continue</source> <source>Continue</source>
<translation type="unfinished">Продолжить</translation> <translation>Продолжить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Отменить</translation> <translation>Отменить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<source>Cannot reload API config during active connection</source> <source>Cannot reload API config during active connection</source>
<translation type="unfinished"></translation> <translation>Невозможно перзагрузить API конфигурацию при активном соединении</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<source>Remove from application</source> <source>Remove from application</source>
<translation type="unfinished"></translation> <translation>Удалить из приложения</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<source>Remove from application?</source> <source>Remove from application?</source>
<translation type="unfinished"></translation> <translation>Удалить из приложения?</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<source>Cannot remove server during active connection</source> <source>Cannot remove server during active connection</source>
<translation type="unfinished">Невозможно удалить сервер во время активного соединения</translation> <translation>Невозможно удалить сервер во время активного соединения</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -1714,7 +1714,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="154"/> <location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="154"/>
<source>KillSwitch</source> <source>KillSwitch</source>
<translation>Аварийный выключатель</translation> <translation>KillSwitch</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="155"/> <location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="155"/>
@@ -2247,32 +2247,32 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="62"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="62"/>
<source>For the region</source> <source>For the region</source>
<translation type="unfinished"></translation> <translation>Для региона</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="71"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="71"/>
<source>Price</source> <source>Price</source>
<translation type="unfinished"></translation> <translation>Цена</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="80"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="80"/>
<source>Work period</source> <source>Work period</source>
<translation type="unfinished"></translation> <translation>Период работы</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="91"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="91"/>
<source>Speed</source> <source>Speed</source>
<translation type="unfinished"></translation> <translation>Скорость</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="100"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="100"/>
<source>Features</source> <source>Features</source>
<translation type="unfinished"></translation> <translation>Особенности</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="139"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="139"/>
<source>Connect</source> <source>Connect</source>
<translation type="unfinished">Подключиться</translation> <translation>Подключиться</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -2280,12 +2280,12 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="52"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="52"/>
<source>VPN by Amnezia</source> <source>VPN by Amnezia</source>
<translation type="unfinished"></translation> <translation>VPN от Amnezia</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="53"/> <location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="53"/>
<source>Choose a VPN service that suits your needs.</source> <source>Choose a VPN service that suits your needs.</source>
<translation type="unfinished"></translation> <translation>Выберите VPN-сервис, который подходит именно вам.</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -2322,67 +2322,67 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="57"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="57"/>
<source>Connection</source> <source>Connection</source>
<translation type="unfinished">Соединение</translation> <translation>Соединение</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="67"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="67"/>
<source>Insert the key, add a configuration file or scan the QR-code</source> <source>Insert the key, add a configuration file or scan the QR-code</source>
<translation type="unfinished"></translation> <translation>Вставьте ключ, добавьте файл конфигурации или отсканируйте QR-код</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="77"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="77"/>
<source>Insert key</source> <source>Insert key</source>
<translation type="unfinished"></translation> <translation>Вставьте ключ</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="78"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="78"/>
<source>Insert</source> <source>Insert</source>
<translation type="unfinished">Вставить</translation> <translation>Вставить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="98"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="98"/>
<source>Continue</source> <source>Continue</source>
<translation type="unfinished">Продолжить</translation> <translation>Продолжить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="116"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="116"/>
<source>Other connection options</source> <source>Other connection options</source>
<translation type="unfinished"></translation> <translation>Другие варианты подключения</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="129"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="129"/>
<source>VPN by Amnezia</source> <source>VPN by Amnezia</source>
<translation type="unfinished"></translation> <translation>VPN от Amnezia</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="130"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="130"/>
<source>Connect to classic paid and free VPN services from Amnezia</source> <source>Connect to classic paid and free VPN services from Amnezia</source>
<translation type="unfinished"></translation> <translation>Подключайтесь к классическим платным и бесплатным VPN-сервисам от Amnezia</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="153"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="153"/>
<source>Self-hosted VPN</source> <source>Self-hosted VPN</source>
<translation type="unfinished"></translation> <translation>Self-hosted VPN</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="154"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="154"/>
<source>Configure Amnezia VPN on your own server</source> <source>Configure Amnezia VPN on your own server</source>
<translation type="unfinished"></translation> <translation>Настроить VPN на собственном сервере</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="174"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="174"/>
<source>Restore from backup</source> <source>Restore from backup</source>
<translation type="unfinished">Восстановить из резервной копии</translation> <translation>Восстановить из резервной копии</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="180"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="180"/>
<source>Open backup file</source> <source>Open backup file</source>
<translation type="unfinished">Открыть резервную копию</translation> <translation>Открыть резервную копию</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="181"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="181"/>
<source>Backup files (*.backup)</source> <source>Backup files (*.backup)</source>
<translation type="unfinished">Файлы резервных копий (*.backup)</translation> <translation>Файлы резервных копий (*.backup)</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="206"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="206"/>
@@ -2397,7 +2397,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="248"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="248"/>
<source>I have nothing</source> <source>I have nothing</source>
<translation type="unfinished">У меня ничего нет</translation> <translation>У меня ничего нет</translation>
</message> </message>
<message> <message>
<source>Key as text</source> <source>Key as text</source>
@@ -2470,12 +2470,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/> <location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
<source>How to run your VPN server</source> <source>How to run your VPN server</source>
<translation type="unfinished"></translation> <translation>Как создать VPN на собственном сервере</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/> <location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
<source>Where to get connection data, step-by-step instructions for buying a VPS</source> <source>Where to get connection data, step-by-step instructions for buying a VPS</source>
<translation type="unfinished"></translation> <translation>Где взять данные для подключения, пошаговые инстуркции по покупке VPS</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/> <location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
@@ -2655,7 +2655,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="48"/> <location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="48"/>
<source>Let&apos;s get started</source> <source>Let&apos;s get started</source>
<translation type="unfinished"></translation> <translation>Приступим</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -3016,7 +3016,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<message> <message>
<location filename="../ui/qml/Pages2/PageStart.qml" line="202"/> <location filename="../ui/qml/Pages2/PageStart.qml" line="202"/>
<source>Settings restored from backup file</source> <source>Settings restored from backup file</source>
<translation type="unfinished"></translation> <translation>Настройки восстановлены из бэкап файла</translation>
</message> </message>
</context> </context>
<context> <context>
File diff suppressed because it is too large Load Diff
@@ -10,9 +10,6 @@
#include "core/controllers/vpnConfigurationController.h" #include "core/controllers/vpnConfigurationController.h"
#include "systemController.h" #include "systemController.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/android_utils.h"
#endif
#include "qrcodegen.hpp" #include "qrcodegen.hpp"
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel, ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
@@ -24,12 +21,6 @@ ExportController::ExportController(const QSharedPointer<ServersModel> &serversMo
m_clientManagementModel(clientManagementModel), m_clientManagementModel(clientManagementModel),
m_settings(settings) m_settings(settings)
{ {
#ifdef Q_OS_ANDROID
m_authResultNotifier.reset(new AuthResultNotifier);
m_authResultReceiver.reset(new AuthResultReceiver(m_authResultNotifier));
connect(m_authResultNotifier.get(), &AuthResultNotifier::authFailed, this, [this]() { emit exportErrorOccurred(tr("Access error!")); });
connect(m_authResultNotifier.get(), &AuthResultNotifier::authSuccessful, this, &ExportController::generateFullAccessConfig);
#endif
} }
void ExportController::generateFullAccessConfig() void ExportController::generateFullAccessConfig()
@@ -63,26 +54,6 @@ void ExportController::generateFullAccessConfig()
emit exportConfigChanged(); emit exportConfigChanged();
} }
#if defined(Q_OS_ANDROID)
void ExportController::generateFullAccessConfigAndroid()
{
/* We use builtin keyguard for ssh key export protection on Android */
QJniObject activity = AndroidUtils::getActivity();
auto appContext = activity.callObjectMethod("getApplicationContext", "()Landroid/content/Context;");
if (appContext.isValid()) {
auto intent = QJniObject::callStaticObjectMethod("org/amnezia/vpn/AuthHelper", "getAuthIntent",
"(Landroid/content/Context;)Landroid/content/Intent;", appContext.object());
if (intent.isValid()) {
if (intent.object<jobject>() != nullptr) {
QtAndroidPrivate::startActivity(intent.object<jobject>(), 1, m_authResultReceiver.get());
}
} else {
generateFullAccessConfig();
}
}
}
#endif
void ExportController::generateConnectionConfig(const QString &clientName) void ExportController::generateConnectionConfig(const QString &clientName)
{ {
clearPreviousConfig(); clearPreviousConfig();
-11
View File
@@ -6,9 +6,6 @@
#include "ui/models/clientManagementModel.h" #include "ui/models/clientManagementModel.h"
#include "ui/models/containers_model.h" #include "ui/models/containers_model.h"
#include "ui/models/servers_model.h" #include "ui/models/servers_model.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/authResultReceiver.h"
#endif
class ExportController : public QObject class ExportController : public QObject
{ {
@@ -25,9 +22,6 @@ public:
public slots: public slots:
void generateFullAccessConfig(); void generateFullAccessConfig();
#if defined(Q_OS_ANDROID)
void generateFullAccessConfigAndroid();
#endif
void generateConnectionConfig(const QString &clientName); void generateConnectionConfig(const QString &clientName);
void generateOpenVpnConfig(const QString &clientName); void generateOpenVpnConfig(const QString &clientName);
void generateWireGuardConfig(const QString &clientName); void generateWireGuardConfig(const QString &clientName);
@@ -74,11 +68,6 @@ private:
QString m_config; QString m_config;
QString m_nativeConfigString; QString m_nativeConfigString;
QList<QString> m_qrCodes; QList<QString> m_qrCodes;
#ifdef Q_OS_ANDROID
QSharedPointer<AuthResultNotifier> m_authResultNotifier;
QSharedPointer<QAndroidActivityResultReceiver> m_authResultReceiver;
#endif
}; };
#endif // EXPORTCONTROLLER_H #endif // EXPORTCONTROLLER_H
+61 -22
View File
@@ -4,12 +4,12 @@
#include <QFileInfo> #include <QFileInfo>
#include <QQuickItem> #include <QQuickItem>
#include <QRandomGenerator> #include <QRandomGenerator>
#include <QUrlQuery>
#include <QStandardPaths> #include <QStandardPaths>
#include <QUrlQuery>
#include "utilities.h"
#include "core/serialization/serialization.h"
#include "core/errorstrings.h" #include "core/errorstrings.h"
#include "core/serialization/serialization.h"
#include "utilities.h"
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
#include "platforms/android/android_controller.h" #include "platforms/android/android_controller.h"
@@ -96,36 +96,40 @@ bool ImportController::extractConfigFromData(QString data)
if (config.startsWith("vless://")) { if (config.startsWith("vless://")) {
m_configType = ConfigTypes::Xray; m_configType = ConfigTypes::Xray;
m_config = extractXrayConfig(Utils::JsonToString(serialization::vless::Deserialize(config, &prefix, &errormsg), m_config = extractXrayConfig(
QJsonDocument::JsonFormat::Compact), prefix); Utils::JsonToString(serialization::vless::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
prefix);
return m_config.empty() ? false : true; return m_config.empty() ? false : true;
} }
if (config.startsWith("vmess://") && config.contains("@")) { if (config.startsWith("vmess://") && config.contains("@")) {
m_configType = ConfigTypes::Xray; m_configType = ConfigTypes::Xray;
m_config = extractXrayConfig(Utils::JsonToString(serialization::vmess_new::Deserialize(config, &prefix, &errormsg), m_config = extractXrayConfig(
QJsonDocument::JsonFormat::Compact), prefix); Utils::JsonToString(serialization::vmess_new::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
prefix);
return m_config.empty() ? false : true; return m_config.empty() ? false : true;
} }
if (config.startsWith("vmess://")) { if (config.startsWith("vmess://")) {
m_configType = ConfigTypes::Xray; m_configType = ConfigTypes::Xray;
m_config = extractXrayConfig(Utils::JsonToString(serialization::vmess::Deserialize(config, &prefix, &errormsg), m_config = extractXrayConfig(
QJsonDocument::JsonFormat::Compact), prefix); Utils::JsonToString(serialization::vmess::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
prefix);
return m_config.empty() ? false : true; return m_config.empty() ? false : true;
} }
if (config.startsWith("trojan://")) { if (config.startsWith("trojan://")) {
m_configType = ConfigTypes::Xray; m_configType = ConfigTypes::Xray;
m_config = extractXrayConfig(Utils::JsonToString(serialization::trojan::Deserialize(config, &prefix, &errormsg), m_config = extractXrayConfig(
QJsonDocument::JsonFormat::Compact), prefix); Utils::JsonToString(serialization::trojan::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
prefix);
return m_config.empty() ? false : true; return m_config.empty() ? false : true;
} }
if (config.startsWith("ss://") && !config.contains("plugin=")) { if (config.startsWith("ss://") && !config.contains("plugin=")) {
m_configType = ConfigTypes::ShadowSocks; m_configType = ConfigTypes::ShadowSocks;
m_config = extractXrayConfig(Utils::JsonToString(serialization::ss::Deserialize(config, &prefix, &errormsg), m_config = extractXrayConfig(
QJsonDocument::JsonFormat::Compact), prefix); Utils::JsonToString(serialization::ss::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), prefix);
return m_config.empty() ? false : true; return m_config.empty() ? false : true;
} }
@@ -173,6 +177,7 @@ bool ImportController::extractConfigFromData(QString data)
} }
case ConfigTypes::Amnezia: { case ConfigTypes::Amnezia: {
m_config = QJsonDocument::fromJson(config.toUtf8()).object(); m_config = QJsonDocument::fromJson(config.toUtf8()).object();
processAmneziaConfig(m_config);
if (!m_config.empty()) { if (!m_config.empty()) {
checkForMaliciousStrings(m_config); checkForMaliciousStrings(m_config);
return true; return true;
@@ -353,20 +358,19 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
QJsonObject lastConfig; QJsonObject lastConfig;
lastConfig[config_key::config] = data; lastConfig[config_key::config] = data;
const static QRegularExpression hostNameAndPortRegExp("Endpoint = (.*):([0-9]*)"); auto url { QUrl::fromUserInput(configMap.value("Endpoint")) };
QRegularExpressionMatch hostNameAndPortMatch = hostNameAndPortRegExp.match(data);
QString hostName; QString hostName;
QString port; QString port;
if (hostNameAndPortMatch.hasCaptured(1)) { if (!url.host().isEmpty()) {
hostName = hostNameAndPortMatch.captured(1); hostName = url.host();
} else { } else {
qDebug() << "Key parameter 'Endpoint' is missing"; qDebug() << "Key parameter 'Endpoint' is missing or has an invalid format";
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false); emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
return QJsonObject(); return QJsonObject();
} }
if (hostNameAndPortMatch.hasCaptured(2)) { if (url.port() != -1) {
port = hostNameAndPortMatch.captured(2); port = QString::number(url.port());
} else { } else {
port = protocols::wireguard::defaultPort; port = protocols::wireguard::defaultPort;
} }
@@ -395,7 +399,11 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
lastConfig[config_key::mtu] = configMap.value("MTU"); lastConfig[config_key::mtu] = configMap.value("MTU");
} }
QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(",")); if (!configMap.value("PersistentKeepalive").isEmpty()) {
lastConfig[config_key::persistent_keep_alive] = configMap.value("PersistentKeepalive");
}
QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(", "));
lastConfig[config_key::allowed_ips] = allowedIpsJsonArray; lastConfig[config_key::allowed_ips] = allowedIpsJsonArray;
@@ -419,6 +427,12 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
m_configType = ConfigTypes::Awg; m_configType = ConfigTypes::Awg;
} }
if (!configMap.value("MTU").isEmpty()) {
lastConfig[config_key::mtu] = configMap.value("MTU");
} else {
lastConfig[config_key::mtu] = protocolName == "awg" ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
}
QJsonObject wireguardConfig; QJsonObject wireguardConfig;
wireguardConfig[config_key::last_config] = QString(QJsonDocument(lastConfig).toJson()); wireguardConfig[config_key::last_config] = QString(QJsonDocument(lastConfig).toJson());
wireguardConfig[config_key::isThirdPartyConfig] = true; wireguardConfig[config_key::isThirdPartyConfig] = true;
@@ -488,7 +502,7 @@ QJsonObject ImportController::extractXrayConfig(const QString &data, const QStri
if (m_configType == ConfigTypes::ShadowSocks) { if (m_configType == ConfigTypes::ShadowSocks) {
config[config_key::defaultContainer] = "amnezia-ssxray"; config[config_key::defaultContainer] = "amnezia-ssxray";
} else { } else {
config[config_key::defaultContainer] = "amnezia-xray"; config[config_key::defaultContainer] = "amnezia-xray";
} }
if (description.isEmpty()) { if (description.isEmpty()) {
config[config_key::description] = m_settings->nextAvailableServerName(); config[config_key::description] = m_settings->nextAvailableServerName();
@@ -646,3 +660,28 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
} }
} }
} }
void ImportController::processAmneziaConfig(QJsonObject &config)
{
auto containers = config.value(config_key::containers).toArray();
for (auto i = 0; i < containers.size(); i++) {
auto container = containers.at(i).toObject();
auto dockerContainer = ContainerProps::containerFromString(container.value(config_key::container).toString());
if (dockerContainer == DockerContainer::Awg || dockerContainer == DockerContainer::WireGuard) {
auto containerConfig = container.value(ContainerProps::containerTypeToString(dockerContainer)).toObject();
auto protocolConfig = containerConfig.value(config_key::last_config).toString();
if (protocolConfig.isEmpty()) {
return;
}
QJsonObject jsonConfig = QJsonDocument::fromJson(protocolConfig.toUtf8()).object();
jsonConfig[config_key::mtu] = dockerContainer == DockerContainer::Awg ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
containerConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson());
container[ContainerProps::containerTypeToString(dockerContainer)] = containerConfig;
containers.replace(i, container);
config.insert(config_key::containers, containers);
}
}
}
+2
View File
@@ -68,6 +68,8 @@ private:
void checkForMaliciousStrings(const QJsonObject &protocolConfig); void checkForMaliciousStrings(const QJsonObject &protocolConfig);
void processAmneziaConfig(QJsonObject &config);
#if defined Q_OS_ANDROID || defined Q_OS_IOS #if defined Q_OS_ANDROID || defined Q_OS_IOS
void stopDecodingQr(); void stopDecodingQr();
#endif #endif
+4 -4
View File
@@ -799,7 +799,7 @@ void InstallController::addEmptyServer()
bool InstallController::fillAvailableServices() bool InstallController::fillAvailableServices()
{ {
ApiController apiController(m_settings->getGatewayEndpoint()); ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = apiController.getServicesList(responseBody); ErrorCode errorCode = apiController.getServicesList(responseBody);
@@ -821,7 +821,7 @@ bool InstallController::installServiceFromApi()
return false; return false;
} }
ApiController apiController(m_settings->getGatewayEndpoint()); ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
QJsonObject serverConfig; QJsonObject serverConfig;
ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(), ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(),
@@ -849,7 +849,7 @@ bool InstallController::installServiceFromApi()
bool InstallController::updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, bool InstallController::updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig) bool reloadServiceConfig)
{ {
ApiController apiController(m_settings->getGatewayEndpoint()); ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
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();
@@ -885,7 +885,7 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin
void InstallController::updateServiceFromTelegram(const int serverIndex) void InstallController::updateServiceFromTelegram(const int serverIndex)
{ {
ApiController *apiController = new ApiController(m_settings->getGatewayEndpoint()); ApiController *apiController = new ApiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto serverConfig = m_serversModel->getServerConfig(serverIndex);
+3 -22
View File
@@ -10,8 +10,6 @@
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
#include "platforms/android/android_controller.h" #include "platforms/android/android_controller.h"
#include "platforms/android/android_utils.h"
#include <QJniObject>
#endif #endif
#if defined Q_OS_MAC #if defined Q_OS_MAC
#include "ui/macos_util.h" #include "ui/macos_util.h"
@@ -22,18 +20,8 @@ PageController::PageController(const QSharedPointer<ServersModel> &serversModel,
: QObject(parent), m_serversModel(serversModel), m_settings(settings) : QObject(parent), m_serversModel(serversModel), m_settings(settings)
{ {
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
// Change color of navigation and status bar's
auto initialPageNavigationBarColor = getInitialPageNavigationBarColor(); auto initialPageNavigationBarColor = getInitialPageNavigationBarColor();
AndroidUtils::runOnAndroidThreadSync([&initialPageNavigationBarColor]() { AndroidController::instance()->setNavigationBarColor(initialPageNavigationBarColor);
QJniObject activity = AndroidUtils::getActivity();
QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;");
if (window.isValid()) {
window.callMethod<void>("addFlags", "(I)V", 0x80000000);
window.callMethod<void>("clearFlags", "(I)V", 0x04000000);
window.callMethod<void>("setStatusBarColor", "(I)V", 0xFF0E0E11);
window.callMethod<void>("setNavigationBarColor", "(I)V", initialPageNavigationBarColor);
}
});
#endif #endif
#if defined Q_OS_MACX #if defined Q_OS_MACX
@@ -115,14 +103,7 @@ unsigned int PageController::getInitialPageNavigationBarColor()
void PageController::updateNavigationBarColor(const int color) void PageController::updateNavigationBarColor(const int color)
{ {
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
// Change color of navigation bar AndroidController::instance()->setNavigationBarColor(color);
AndroidUtils::runOnAndroidThreadSync([&color]() {
QJniObject activity = AndroidUtils::getActivity();
QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;");
if (window.isValid()) {
window.callMethod<void>("setNavigationBarColor", "(I)V", color);
}
});
#endif #endif
} }
@@ -131,7 +112,7 @@ void PageController::showOnStartup()
if (!m_settings->isStartMinimized()) { if (!m_settings->isStartMinimized()) {
emit raiseMainWindow(); emit raiseMainWindow();
} else { } else {
#ifdef Q_OS_WIN #if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
emit hideMainWindow(); emit hideMainWindow();
#elif defined Q_OS_MACX #elif defined Q_OS_MACX
setDockIconVisible(false); setDockIconVisible(false);
+3
View File
@@ -59,6 +59,9 @@ namespace PageLoader
PageProtocolIKev2Settings, PageProtocolIKev2Settings,
PageProtocolRaw, PageProtocolRaw,
PageProtocolWireGuardClientSettings,
PageProtocolAwgClientSettings,
PageShareFullAccess, PageShareFullAccess,
PageDevMenu PageDevMenu
+43 -3
View File
@@ -88,7 +88,12 @@ void SettingsController::toggleLogging(bool enable)
void SettingsController::openLogsFolder() void SettingsController::openLogsFolder()
{ {
Logger::openLogsFolder(); Logger::openLogsFolder(false);
}
void SettingsController::openServiceLogsFolder()
{
Logger::openLogsFolder(true);
} }
void SettingsController::exportLogsFile(const QString &fileName) void SettingsController::exportLogsFile(const QString &fileName)
@@ -100,12 +105,21 @@ void SettingsController::exportLogsFile(const QString &fileName)
#endif #endif
} }
void SettingsController::exportServiceLogsFile(const QString &fileName)
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->exportLogsFile(fileName);
#else
SystemController::saveFile(fileName, Logger::getServiceLogFile());
#endif
}
void SettingsController::clearLogs() void SettingsController::clearLogs()
{ {
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
AndroidController::instance()->clearLogs(); AndroidController::instance()->clearLogs();
#else #else
Logger::clearLogs(); Logger::clearLogs(false);
Logger::clearServiceLogs(); Logger::clearServiceLogs();
#endif #endif
} }
@@ -283,5 +297,31 @@ void SettingsController::setGatewayEndpoint(const QString &endpoint)
QString SettingsController::getGatewayEndpoint() QString SettingsController::getGatewayEndpoint()
{ {
return m_settings->getGatewayEndpoint(); return m_settings->isDevGatewayEnv() ? "Dev endpoint" : m_settings->getGatewayEndpoint();
}
bool SettingsController::isDevGatewayEnv()
{
return m_settings->isDevGatewayEnv();
}
void SettingsController::toggleDevGatewayEnv(bool enabled)
{
m_settings->toggleDevGatewayEnv(enabled);
if (enabled) {
m_settings->setDevGatewayEndpoint();
} else {
m_settings->resetGatewayEndpoint();
}
emit gatewayEndpointChanged(m_settings->getGatewayEndpoint());
emit devGatewayEnvChanged(enabled);
}
bool SettingsController::isOnTv()
{
#ifdef Q_OS_ANDROID
return AndroidController::instance()->isOnTv();
#else
return false;
#endif
} }
@@ -27,6 +27,7 @@ public:
Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled) Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged) Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
Q_PROPERTY(bool isDevGatewayEnv READ isDevGatewayEnv WRITE toggleDevGatewayEnv NOTIFY devGatewayEnvChanged)
public slots: public slots:
void toggleAmneziaDns(bool enable); void toggleAmneziaDns(bool enable);
@@ -42,7 +43,9 @@ public slots:
void toggleLogging(bool enable); void toggleLogging(bool enable);
void openLogsFolder(); void openLogsFolder();
void openServiceLogsFolder();
void exportLogsFile(const QString &fileName); void exportLogsFile(const QString &fileName);
void exportServiceLogsFile(const QString &fileName);
void clearLogs(); void clearLogs();
void backupAppConfig(const QString &fileName); void backupAppConfig(const QString &fileName);
@@ -81,6 +84,10 @@ public slots:
void resetGatewayEndpoint(); void resetGatewayEndpoint();
void setGatewayEndpoint(const QString &endpoint); void setGatewayEndpoint(const QString &endpoint);
QString getGatewayEndpoint(); QString getGatewayEndpoint();
bool isDevGatewayEnv();
void toggleDevGatewayEnv(bool enabled);
bool isOnTv();
signals: signals:
void primaryDnsChanged(); void primaryDnsChanged();
@@ -103,6 +110,7 @@ signals:
void devModeEnabled(); void devModeEnabled();
void gatewayEndpointChanged(const QString &endpoint); void gatewayEndpointChanged(const QString &endpoint);
void devGatewayEnvChanged(bool enabled);
private: private:
QSharedPointer<ServersModel> m_serversModel; QSharedPointer<ServersModel> m_serversModel;
@@ -125,3 +125,12 @@ void SystemController::setQmlRoot(QObject *qmlRoot)
{ {
m_qmlRoot = qmlRoot; m_qmlRoot = qmlRoot;
} }
bool SystemController::isAuthenticated()
{
#ifdef Q_OS_ANDROID
return AndroidController::instance()->requestAuthentication();
#else
return true;
#endif
}
+1
View File
@@ -19,6 +19,7 @@ public slots:
void setQmlRoot(QObject *qmlRoot); void setQmlRoot(QObject *qmlRoot);
bool isAuthenticated();
signals: signals:
void fileDialogClosed(const bool isAccepted); void fileDialogClosed(const bool isAccepted);
+17 -2
View File
@@ -25,6 +25,8 @@ namespace
constexpr char availableCountries[] = "available_countries"; constexpr char availableCountries[] = "available_countries";
constexpr char storeEndpoint[] = "store_endpoint"; constexpr char storeEndpoint[] = "store_endpoint";
constexpr char isAvailable[] = "is_available";
} }
namespace serviceType namespace serviceType
@@ -63,8 +65,12 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
return tr("Classic VPN for comfortable work, downloading large files and watching videos. " return tr("Classic VPN for comfortable work, downloading large files and watching videos. "
"Works for any sites. Speed up to %1 MBit/s") "Works for any sites. Speed up to %1 MBit/s")
.arg(speed); .arg(speed);
} else { } else if (serviceType == serviceType::amneziaFree){
return tr("VPN to access blocked sites in regions with high levels of Internet censorship. "); QString description = tr("VPN to access blocked sites in regions with high levels of Internet censorship. ");
if (service.value(configKey::isAvailable).isBool() && !service.value(configKey::isAvailable).toBool()) {
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 description;
} }
} }
case ServiceDescriptionRole: { case ServiceDescriptionRole: {
@@ -75,6 +81,14 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
return tr("Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship"); return tr("Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship");
} }
} }
case IsServiceAvailableRole: {
if (serviceType == serviceType::amneziaFree) {
if (service.value(configKey::isAvailable).isBool() && !service.value(configKey::isAvailable).toBool()) {
return false;
}
}
return true;
}
case SpeedRole: { case SpeedRole: {
auto speed = serviceInfo.value(configKey::speed).toString(); auto speed = serviceInfo.value(configKey::speed).toString();
return tr("%1 MBit/s").arg(speed); return tr("%1 MBit/s").arg(speed);
@@ -193,6 +207,7 @@ QHash<int, QByteArray> ApiServicesModel::roleNames() const
roles[NameRole] = "name"; roles[NameRole] = "name";
roles[CardDescriptionRole] = "cardDescription"; roles[CardDescriptionRole] = "cardDescription";
roles[ServiceDescriptionRole] = "serviceDescription"; roles[ServiceDescriptionRole] = "serviceDescription";
roles[IsServiceAvailableRole] = "isServiceAvailable";
roles[SpeedRole] = "speed"; roles[SpeedRole] = "speed";
roles[WorkPeriodRole] = "workPeriod"; roles[WorkPeriodRole] = "workPeriod";
roles[RegionRole] = "region"; roles[RegionRole] = "region";
+1
View File
@@ -13,6 +13,7 @@ public:
NameRole = Qt::UserRole + 1, NameRole = Qt::UserRole + 1,
CardDescriptionRole, CardDescriptionRole,
ServiceDescriptionRole, ServiceDescriptionRole,
IsServiceAvailableRole,
SpeedRole, SpeedRole,
WorkPeriodRole, WorkPeriodRole,
RegionRole, RegionRole,
+136 -82
View File
@@ -21,17 +21,30 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in
} }
switch (role) { switch (role) {
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; case Roles::PortRole: m_serverProtocolConfig.insert(config_key::port, value.toString()); break;
case Roles::MtuRole: m_protocolConfig.insert(config_key::mtu, value.toString()); break;
case Roles::JunkPacketCountRole: m_protocolConfig.insert(config_key::junkPacketCount, value.toString()); break; case Roles::ClientMtuRole: m_clientProtocolConfig.insert(config_key::mtu, value.toString()); break;
case Roles::JunkPacketMinSizeRole: m_protocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break; case Roles::ClientJunkPacketCountRole: m_clientProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break;
case Roles::JunkPacketMaxSizeRole: m_protocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break; case Roles::ClientJunkPacketMinSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break;
case Roles::InitPacketJunkSizeRole: m_protocolConfig.insert(config_key::initPacketJunkSize, value.toString()); break; case Roles::ClientJunkPacketMaxSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break;
case Roles::ResponsePacketJunkSizeRole: m_protocolConfig.insert(config_key::responsePacketJunkSize, value.toString()); break;
case Roles::InitPacketMagicHeaderRole: m_protocolConfig.insert(config_key::initPacketMagicHeader, value.toString()); break; case Roles::ServerJunkPacketCountRole: m_serverProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break;
case Roles::ResponsePacketMagicHeaderRole: m_protocolConfig.insert(config_key::responsePacketMagicHeader, value.toString()); break; case Roles::ServerJunkPacketMinSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break;
case Roles::UnderloadPacketMagicHeaderRole: m_protocolConfig.insert(config_key::underloadPacketMagicHeader, value.toString()); break; case Roles::ServerJunkPacketMaxSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break;
case Roles::TransportPacketMagicHeaderRole: m_protocolConfig.insert(config_key::transportPacketMagicHeader, value.toString()); break; case Roles::ServerInitPacketJunkSizeRole: m_serverProtocolConfig.insert(config_key::initPacketJunkSize, value.toString()); break;
case Roles::ServerResponsePacketJunkSizeRole:
m_serverProtocolConfig.insert(config_key::responsePacketJunkSize, value.toString());
break;
case Roles::ServerInitPacketMagicHeaderRole: m_serverProtocolConfig.insert(config_key::initPacketMagicHeader, value.toString()); break;
case Roles::ServerResponsePacketMagicHeaderRole:
m_serverProtocolConfig.insert(config_key::responsePacketMagicHeader, value.toString());
break;
case Roles::ServerUnderloadPacketMagicHeaderRole:
m_serverProtocolConfig.insert(config_key::underloadPacketMagicHeader, value.toString());
break;
case Roles::ServerTransportPacketMagicHeaderRole:
m_serverProtocolConfig.insert(config_key::transportPacketMagicHeader, value.toString());
break;
} }
emit dataChanged(index, index, QList { role }); emit dataChanged(index, index, QList { role });
@@ -45,17 +58,22 @@ QVariant AwgConfigModel::data(const QModelIndex &index, int role) const
} }
switch (role) { switch (role) {
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(); case Roles::PortRole: return m_serverProtocolConfig.value(config_key::port).toString();
case Roles::MtuRole: return m_protocolConfig.value(config_key::mtu).toString();
case Roles::JunkPacketCountRole: return m_protocolConfig.value(config_key::junkPacketCount); case Roles::ClientMtuRole: return m_clientProtocolConfig.value(config_key::mtu);
case Roles::JunkPacketMinSizeRole: return m_protocolConfig.value(config_key::junkPacketMinSize); case Roles::ClientJunkPacketCountRole: return m_clientProtocolConfig.value(config_key::junkPacketCount);
case Roles::JunkPacketMaxSizeRole: return m_protocolConfig.value(config_key::junkPacketMaxSize); case Roles::ClientJunkPacketMinSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMinSize);
case Roles::InitPacketJunkSizeRole: return m_protocolConfig.value(config_key::initPacketJunkSize); case Roles::ClientJunkPacketMaxSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMaxSize);
case Roles::ResponsePacketJunkSizeRole: return m_protocolConfig.value(config_key::responsePacketJunkSize);
case Roles::InitPacketMagicHeaderRole: return m_protocolConfig.value(config_key::initPacketMagicHeader); case Roles::ServerJunkPacketCountRole: return m_serverProtocolConfig.value(config_key::junkPacketCount);
case Roles::ResponsePacketMagicHeaderRole: return m_protocolConfig.value(config_key::responsePacketMagicHeader); case Roles::ServerJunkPacketMinSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMinSize);
case Roles::UnderloadPacketMagicHeaderRole: return m_protocolConfig.value(config_key::underloadPacketMagicHeader); case Roles::ServerJunkPacketMaxSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMaxSize);
case Roles::TransportPacketMagicHeaderRole: return m_protocolConfig.value(config_key::transportPacketMagicHeader); case Roles::ServerInitPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::initPacketJunkSize);
case Roles::ServerResponsePacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::responsePacketJunkSize);
case Roles::ServerInitPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::initPacketMagicHeader);
case Roles::ServerResponsePacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::responsePacketMagicHeader);
case Roles::ServerUnderloadPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::underloadPacketMagicHeader);
case Roles::ServerTransportPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::transportPacketMagicHeader);
} }
return QVariant(); return QVariant();
@@ -68,51 +86,63 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
m_fullConfig = config; m_fullConfig = config;
QJsonObject protocolConfig = config.value(config_key::awg).toObject(); QJsonObject serverProtocolConfig = config.value(config_key::awg).toObject();
auto defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::Awg), Proto::Awg); auto defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::Awg), Proto::Awg);
m_protocolConfig.insert(config_key::transport_proto, protocolConfig.value(config_key::transport_proto).toString(defaultTransportProto)); m_serverProtocolConfig.insert(config_key::transport_proto,
m_protocolConfig[config_key::last_config] = protocolConfig.value(config_key::last_config); serverProtocolConfig.value(config_key::transport_proto).toString(defaultTransportProto));
m_protocolConfig[config_key::port] = protocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); m_serverProtocolConfig[config_key::last_config] = serverProtocolConfig.value(config_key::last_config);
m_protocolConfig[config_key::mtu] = protocolConfig.value(config_key::mtu).toString(protocols::awg::defaultMtu); m_serverProtocolConfig[config_key::port] = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort);
m_protocolConfig[config_key::junkPacketCount] = m_serverProtocolConfig[config_key::junkPacketCount] =
protocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
m_protocolConfig[config_key::junkPacketMinSize] = m_serverProtocolConfig[config_key::junkPacketMinSize] =
protocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); serverProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize);
m_protocolConfig[config_key::junkPacketMaxSize] = m_serverProtocolConfig[config_key::junkPacketMaxSize] =
protocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); serverProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize);
m_protocolConfig[config_key::initPacketJunkSize] = m_serverProtocolConfig[config_key::initPacketJunkSize] =
protocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
m_protocolConfig[config_key::responsePacketJunkSize] = m_serverProtocolConfig[config_key::responsePacketJunkSize] =
protocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
m_protocolConfig[config_key::initPacketMagicHeader] = m_serverProtocolConfig[config_key::initPacketMagicHeader] =
protocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
m_protocolConfig[config_key::responsePacketMagicHeader] = m_serverProtocolConfig[config_key::responsePacketMagicHeader] =
protocolConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader); serverProtocolConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader);
m_protocolConfig[config_key::underloadPacketMagicHeader] = m_serverProtocolConfig[config_key::underloadPacketMagicHeader] =
protocolConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader); serverProtocolConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader);
m_protocolConfig[config_key::transportPacketMagicHeader] = m_serverProtocolConfig[config_key::transportPacketMagicHeader] =
protocolConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader); serverProtocolConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader);
auto lastConfig = m_serverProtocolConfig.value(config_key::last_config).toString();
QJsonObject clientProtocolConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object();
m_clientProtocolConfig[config_key::mtu] = clientProtocolConfig[config_key::mtu].toString(protocols::awg::defaultMtu);
m_clientProtocolConfig[config_key::junkPacketCount] =
clientProtocolConfig.value(config_key::junkPacketCount).toString(m_serverProtocolConfig[config_key::junkPacketCount].toString());
m_clientProtocolConfig[config_key::junkPacketMinSize] =
clientProtocolConfig.value(config_key::junkPacketMinSize).toString(m_serverProtocolConfig[config_key::junkPacketMinSize].toString());
m_clientProtocolConfig[config_key::junkPacketMaxSize] =
clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(m_serverProtocolConfig[config_key::junkPacketMaxSize].toString());
endResetModel(); endResetModel();
} }
QJsonObject AwgConfigModel::getConfig() QJsonObject AwgConfigModel::getConfig()
{ {
const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject()); const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject());
const AwgConfig newConfig(m_protocolConfig); const AwgConfig newConfig(m_serverProtocolConfig);
if (!oldConfig.hasEqualServerSettings(newConfig)) { if (!oldConfig.hasEqualServerSettings(newConfig)) {
m_protocolConfig.remove(config_key::last_config); m_serverProtocolConfig.remove(config_key::last_config);
} else { } else {
auto lastConfig = m_protocolConfig.value(config_key::last_config).toString(); auto lastConfig = m_serverProtocolConfig.value(config_key::last_config).toString();
QJsonObject jsonConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); QJsonObject jsonConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object();
jsonConfig[config_key::mtu] = newConfig.mtu; jsonConfig[config_key::mtu] = m_clientProtocolConfig[config_key::mtu];
jsonConfig[config_key::junkPacketCount] = m_clientProtocolConfig[config_key::junkPacketCount];
jsonConfig[config_key::junkPacketMinSize] = m_clientProtocolConfig[config_key::junkPacketMinSize];
jsonConfig[config_key::junkPacketMaxSize] = m_clientProtocolConfig[config_key::junkPacketMaxSize];
m_protocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson()); m_serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson());
} }
m_fullConfig.insert(config_key::awg, m_protocolConfig); m_fullConfig.insert(config_key::awg, m_serverProtocolConfig);
return m_fullConfig; return m_fullConfig;
} }
@@ -126,50 +156,73 @@ bool AwgConfigModel::isPacketSizeEqual(const int s1, const int s2)
return (AwgConstant::messageInitiationSize + s1 == AwgConstant::messageResponseSize + s2); return (AwgConstant::messageInitiationSize + s1 == AwgConstant::messageResponseSize + s2);
} }
bool AwgConfigModel::isServerSettingsEqual()
{
const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject());
const AwgConfig newConfig(m_serverProtocolConfig);
return oldConfig.hasEqualServerSettings(newConfig);
}
QHash<int, QByteArray> AwgConfigModel::roleNames() const QHash<int, QByteArray> AwgConfigModel::roleNames() const
{ {
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
roles[PortRole] = "port"; roles[PortRole] = "port";
roles[MtuRole] = "mtu";
roles[JunkPacketCountRole] = "junkPacketCount"; roles[ClientMtuRole] = "clientMtu";
roles[JunkPacketMinSizeRole] = "junkPacketMinSize"; roles[ClientJunkPacketCountRole] = "clientJunkPacketCount";
roles[JunkPacketMaxSizeRole] = "junkPacketMaxSize"; roles[ClientJunkPacketMinSizeRole] = "clientJunkPacketMinSize";
roles[InitPacketJunkSizeRole] = "initPacketJunkSize"; roles[ClientJunkPacketMaxSizeRole] = "clientJunkPacketMaxSize";
roles[ResponsePacketJunkSizeRole] = "responsePacketJunkSize";
roles[InitPacketMagicHeaderRole] = "initPacketMagicHeader"; roles[ServerJunkPacketCountRole] = "serverJunkPacketCount";
roles[ResponsePacketMagicHeaderRole] = "responsePacketMagicHeader"; roles[ServerJunkPacketMinSizeRole] = "serverJunkPacketMinSize";
roles[UnderloadPacketMagicHeaderRole] = "underloadPacketMagicHeader"; roles[ServerJunkPacketMaxSizeRole] = "serverJunkPacketMaxSize";
roles[TransportPacketMagicHeaderRole] = "transportPacketMagicHeader"; roles[ServerInitPacketJunkSizeRole] = "serverInitPacketJunkSize";
roles[ServerResponsePacketJunkSizeRole] = "serverResponsePacketJunkSize";
roles[ServerInitPacketMagicHeaderRole] = "serverInitPacketMagicHeader";
roles[ServerResponsePacketMagicHeaderRole] = "serverResponsePacketMagicHeader";
roles[ServerUnderloadPacketMagicHeaderRole] = "serverUnderloadPacketMagicHeader";
roles[ServerTransportPacketMagicHeaderRole] = "serverTransportPacketMagicHeader";
return roles; return roles;
} }
AwgConfig::AwgConfig(const QJsonObject &jsonConfig) AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig)
{ {
port = jsonConfig.value(config_key::port).toString(protocols::awg::defaultPort); auto lastConfig = serverProtocolConfig.value(config_key::last_config).toString();
mtu = jsonConfig.value(config_key::mtu).toString(protocols::awg::defaultMtu); QJsonObject clientProtocolConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object();
junkPacketCount = jsonConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); clientMtu = clientProtocolConfig[config_key::mtu].toString(protocols::awg::defaultMtu);
junkPacketMinSize = jsonConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); clientJunkPacketCount = clientProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
junkPacketMaxSize = jsonConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); clientJunkPacketMinSize = clientProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize);
initPacketJunkSize = jsonConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); clientJunkPacketMaxSize = clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize);
responsePacketJunkSize = jsonConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
initPacketMagicHeader = jsonConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); port = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort);
responsePacketMagicHeader = serverJunkPacketCount = serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
jsonConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader); serverJunkPacketMinSize = serverProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize);
underloadPacketMagicHeader = serverJunkPacketMaxSize = serverProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize);
jsonConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader); serverInitPacketJunkSize = serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
transportPacketMagicHeader = serverResponsePacketJunkSize =
jsonConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader); serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
serverInitPacketMagicHeader =
serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
serverResponsePacketMagicHeader =
serverProtocolConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader);
serverUnderloadPacketMagicHeader =
serverProtocolConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader);
serverTransportPacketMagicHeader =
serverProtocolConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader);
} }
bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const
{ {
if (port != other.port || junkPacketCount != other.junkPacketCount || junkPacketMinSize != other.junkPacketMinSize if (port != other.port || serverJunkPacketCount != other.serverJunkPacketCount
|| junkPacketMaxSize != other.junkPacketMaxSize || initPacketJunkSize != other.initPacketJunkSize || serverJunkPacketMinSize != other.serverJunkPacketMinSize || serverJunkPacketMaxSize != other.serverJunkPacketMaxSize
|| responsePacketJunkSize != other.responsePacketJunkSize || initPacketMagicHeader != other.initPacketMagicHeader || serverInitPacketJunkSize != other.serverInitPacketJunkSize || serverResponsePacketJunkSize != other.serverResponsePacketJunkSize
|| responsePacketMagicHeader != other.responsePacketMagicHeader || underloadPacketMagicHeader != other.underloadPacketMagicHeader || serverInitPacketMagicHeader != other.serverInitPacketMagicHeader
|| transportPacketMagicHeader != other.transportPacketMagicHeader) { || serverResponsePacketMagicHeader != other.serverResponsePacketMagicHeader
|| serverUnderloadPacketMagicHeader != other.serverUnderloadPacketMagicHeader
|| serverTransportPacketMagicHeader != other.serverTransportPacketMagicHeader) {
return false; return false;
} }
return true; return true;
@@ -177,7 +230,8 @@ bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const
bool AwgConfig::hasEqualClientSettings(const AwgConfig &other) const bool AwgConfig::hasEqualClientSettings(const AwgConfig &other) const
{ {
if (mtu != other.mtu) { if (clientMtu != other.clientMtu || clientJunkPacketCount != other.clientJunkPacketCount
|| clientJunkPacketMinSize != other.clientJunkPacketMinSize || clientJunkPacketMaxSize != other.clientJunkPacketMaxSize) {
return false; return false;
} }
return true; return true;
+34 -21
View File
@@ -16,16 +16,21 @@ struct AwgConfig
AwgConfig(const QJsonObject &jsonConfig); AwgConfig(const QJsonObject &jsonConfig);
QString port; QString port;
QString mtu;
QString junkPacketCount; QString clientMtu;
QString junkPacketMinSize; QString clientJunkPacketCount;
QString junkPacketMaxSize; QString clientJunkPacketMinSize;
QString initPacketJunkSize; QString clientJunkPacketMaxSize;
QString responsePacketJunkSize;
QString initPacketMagicHeader; QString serverJunkPacketCount;
QString responsePacketMagicHeader; QString serverJunkPacketMinSize;
QString underloadPacketMagicHeader; QString serverJunkPacketMaxSize;
QString transportPacketMagicHeader; QString serverInitPacketJunkSize;
QString serverResponsePacketJunkSize;
QString serverInitPacketMagicHeader;
QString serverResponsePacketMagicHeader;
QString serverUnderloadPacketMagicHeader;
QString serverTransportPacketMagicHeader;
bool hasEqualServerSettings(const AwgConfig &other) const; bool hasEqualServerSettings(const AwgConfig &other) const;
bool hasEqualClientSettings(const AwgConfig &other) const; bool hasEqualClientSettings(const AwgConfig &other) const;
@@ -39,16 +44,21 @@ class AwgConfigModel : public QAbstractListModel
public: public:
enum Roles { enum Roles {
PortRole = Qt::UserRole + 1, PortRole = Qt::UserRole + 1,
MtuRole,
JunkPacketCountRole, ClientMtuRole,
JunkPacketMinSizeRole, ClientJunkPacketCountRole,
JunkPacketMaxSizeRole, ClientJunkPacketMinSizeRole,
InitPacketJunkSizeRole, ClientJunkPacketMaxSizeRole,
ResponsePacketJunkSizeRole,
InitPacketMagicHeaderRole, ServerJunkPacketCountRole,
ResponsePacketMagicHeaderRole, ServerJunkPacketMinSizeRole,
UnderloadPacketMagicHeaderRole, ServerJunkPacketMaxSizeRole,
TransportPacketMagicHeaderRole ServerInitPacketJunkSizeRole,
ServerResponsePacketJunkSizeRole,
ServerInitPacketMagicHeaderRole,
ServerResponsePacketMagicHeaderRole,
ServerUnderloadPacketMagicHeaderRole,
ServerTransportPacketMagicHeaderRole
}; };
explicit AwgConfigModel(QObject *parent = nullptr); explicit AwgConfigModel(QObject *parent = nullptr);
@@ -65,12 +75,15 @@ public slots:
bool isHeadersEqual(const QString &h1, const QString &h2, const QString &h3, const QString &h4); bool isHeadersEqual(const QString &h1, const QString &h2, const QString &h3, const QString &h4);
bool isPacketSizeEqual(const int s1, const int s2); bool isPacketSizeEqual(const int s1, const int s2);
bool isServerSettingsEqual();
protected: protected:
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
private: private:
DockerContainer m_container; DockerContainer m_container;
QJsonObject m_protocolConfig; QJsonObject m_serverProtocolConfig;
QJsonObject m_clientProtocolConfig;
QJsonObject m_fullConfig; QJsonObject m_fullConfig;
}; };
@@ -21,8 +21,8 @@ bool WireGuardConfigModel::setData(const QModelIndex &index, const QVariant &val
} }
switch (role) { switch (role) {
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; case Roles::PortRole: m_serverProtocolConfig.insert(config_key::port, value.toString()); break;
case Roles::MtuRole: m_protocolConfig.insert(config_key::mtu, value.toString()); break; case Roles::ClientMtuRole: m_clientProtocolConfig.insert(config_key::mtu, value.toString()); break;
} }
emit dataChanged(index, index, QList { role }); emit dataChanged(index, index, QList { role });
@@ -36,8 +36,8 @@ QVariant WireGuardConfigModel::data(const QModelIndex &index, int role) const
} }
switch (role) { switch (role) {
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(); case Roles::PortRole: return m_serverProtocolConfig.value(config_key::port).toString();
case Roles::MtuRole: return m_protocolConfig.value(config_key::mtu).toString(); case Roles::ClientMtuRole: return m_clientProtocolConfig.value(config_key::mtu);
} }
return QVariant(); return QVariant();
@@ -49,17 +49,18 @@ void WireGuardConfigModel::updateModel(const QJsonObject &config)
m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); m_container = ContainerProps::containerFromString(config.value(config_key::container).toString());
m_fullConfig = config; m_fullConfig = config;
QJsonObject protocolConfig = config.value(config_key::wireguard).toObject(); QJsonObject serverProtocolConfig = config.value(config_key::wireguard).toObject();
auto defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::WireGuard), Proto::WireGuard); auto defaultTransportProto =
m_protocolConfig.insert(config_key::transport_proto, ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::WireGuard), Proto::WireGuard);
protocolConfig.value(config_key::transport_proto).toString(defaultTransportProto)); m_serverProtocolConfig.insert(config_key::transport_proto,
m_protocolConfig[config_key::last_config] = protocolConfig.value(config_key::last_config); serverProtocolConfig.value(config_key::transport_proto).toString(defaultTransportProto));
m_protocolConfig[config_key::port] = m_serverProtocolConfig[config_key::last_config] = serverProtocolConfig.value(config_key::last_config);
protocolConfig.value(config_key::port).toString(protocols::wireguard::defaultPort); m_serverProtocolConfig[config_key::port] = serverProtocolConfig.value(config_key::port).toString(protocols::wireguard::defaultPort);
m_protocolConfig[config_key::mtu] = auto lastConfig = m_serverProtocolConfig.value(config_key::last_config).toString();
protocolConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); QJsonObject clientProtocolConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object();
m_clientProtocolConfig[config_key::mtu] = clientProtocolConfig[config_key::mtu].toString(protocols::wireguard::defaultMtu);
endResetModel(); endResetModel();
} }
@@ -67,36 +68,47 @@ void WireGuardConfigModel::updateModel(const QJsonObject &config)
QJsonObject WireGuardConfigModel::getConfig() QJsonObject WireGuardConfigModel::getConfig()
{ {
const WgConfig oldConfig(m_fullConfig.value(config_key::wireguard).toObject()); const WgConfig oldConfig(m_fullConfig.value(config_key::wireguard).toObject());
const WgConfig newConfig(m_protocolConfig); const WgConfig newConfig(m_serverProtocolConfig);
if (!oldConfig.hasEqualServerSettings(newConfig)) { if (!oldConfig.hasEqualServerSettings(newConfig)) {
m_protocolConfig.remove(config_key::last_config); m_serverProtocolConfig.remove(config_key::last_config);
} else { } else {
auto lastConfig = m_protocolConfig.value(config_key::last_config).toString(); auto lastConfig = m_serverProtocolConfig.value(config_key::last_config).toString();
QJsonObject jsonConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); QJsonObject jsonConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object();
jsonConfig[config_key::mtu] = newConfig.mtu; jsonConfig[config_key::mtu] = m_clientProtocolConfig[config_key::mtu];
m_protocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson()); m_serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson());
} }
m_fullConfig.insert(config_key::wireguard, m_protocolConfig); m_fullConfig.insert(config_key::wireguard, m_serverProtocolConfig);
return m_fullConfig; return m_fullConfig;
} }
bool WireGuardConfigModel::isServerSettingsEqual()
{
const WgConfig oldConfig(m_fullConfig.value(config_key::wireguard).toObject());
const WgConfig newConfig(m_serverProtocolConfig);
return oldConfig.hasEqualServerSettings(newConfig);
}
QHash<int, QByteArray> WireGuardConfigModel::roleNames() const QHash<int, QByteArray> WireGuardConfigModel::roleNames() const
{ {
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
roles[PortRole] = "port"; roles[PortRole] = "port";
roles[MtuRole] = "mtu"; roles[ClientMtuRole] = "clientMtu";
return roles; return roles;
} }
WgConfig::WgConfig(const QJsonObject &jsonConfig) WgConfig::WgConfig(const QJsonObject &serverProtocolConfig)
{ {
port = jsonConfig.value(config_key::port).toString(protocols::wireguard::defaultPort); auto lastConfig = serverProtocolConfig.value(config_key::last_config).toString();
mtu = jsonConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); QJsonObject clientProtocolConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object();
clientMtu = clientProtocolConfig[config_key::mtu].toString(protocols::wireguard::defaultMtu);
port = serverProtocolConfig.value(config_key::port).toString(protocols::wireguard::defaultPort);
} }
bool WgConfig::hasEqualServerSettings(const WgConfig &other) const bool WgConfig::hasEqualServerSettings(const WgConfig &other) const
@@ -109,7 +121,7 @@ bool WgConfig::hasEqualServerSettings(const WgConfig &other) const
bool WgConfig::hasEqualClientSettings(const WgConfig &other) const bool WgConfig::hasEqualClientSettings(const WgConfig &other) const
{ {
if (mtu != other.mtu) { if (clientMtu != other.clientMtu) {
return false; return false;
} }
return true; return true;
@@ -11,7 +11,7 @@ struct WgConfig
WgConfig(const QJsonObject &jsonConfig); WgConfig(const QJsonObject &jsonConfig);
QString port; QString port;
QString mtu; QString clientMtu;
bool hasEqualServerSettings(const WgConfig &other) const; bool hasEqualServerSettings(const WgConfig &other) const;
bool hasEqualClientSettings(const WgConfig &other) const; bool hasEqualClientSettings(const WgConfig &other) const;
@@ -25,7 +25,7 @@ class WireGuardConfigModel : public QAbstractListModel
public: public:
enum Roles { enum Roles {
PortRole = Qt::UserRole + 1, PortRole = Qt::UserRole + 1,
MtuRole ClientMtuRole
}; };
explicit WireGuardConfigModel(QObject *parent = nullptr); explicit WireGuardConfigModel(QObject *parent = nullptr);
@@ -39,12 +39,15 @@ public slots:
void updateModel(const QJsonObject &config); void updateModel(const QJsonObject &config);
QJsonObject getConfig(); QJsonObject getConfig();
bool isServerSettingsEqual();
protected: protected:
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
private: private:
DockerContainer m_container; DockerContainer m_container;
QJsonObject m_protocolConfig; QJsonObject m_serverProtocolConfig;
QJsonObject m_clientProtocolConfig;
QJsonObject m_fullConfig; QJsonObject m_fullConfig;
}; };
+26 -4
View File
@@ -16,9 +16,11 @@ QHash<int, QByteArray> ProtocolsModel::roleNames() const
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
roles[ProtocolNameRole] = "protocolName"; roles[ProtocolNameRole] = "protocolName";
roles[ProtocolPageRole] = "protocolPage"; roles[ServerProtocolPageRole] = "serverProtocolPage";
roles[ClientProtocolPageRole] = "clientProtocolPage";
roles[ProtocolIndexRole] = "protocolIndex"; roles[ProtocolIndexRole] = "protocolIndex";
roles[RawConfigRole] = "rawConfig"; roles[RawConfigRole] = "rawConfig";
roles[IsClientProtocolExistsRole] = "isClientProtocolExists";
return roles; return roles;
} }
@@ -34,8 +36,10 @@ QVariant ProtocolsModel::data(const QModelIndex &index, int role) const
amnezia::Proto proto = ProtocolProps::protoFromString(m_content.keys().at(index.row())); amnezia::Proto proto = ProtocolProps::protoFromString(m_content.keys().at(index.row()));
return ProtocolProps::protocolHumanNames().value(proto); return ProtocolProps::protocolHumanNames().value(proto);
} }
case ProtocolPageRole: case ServerProtocolPageRole:
return static_cast<int>(protocolPage(ProtocolProps::protoFromString(m_content.keys().at(index.row())))); return static_cast<int>(serverProtocolPage(ProtocolProps::protoFromString(m_content.keys().at(index.row()))));
case ClientProtocolPageRole:
return static_cast<int>(clientProtocolPage(ProtocolProps::protoFromString(m_content.keys().at(index.row()))));
case ProtocolIndexRole: return ProtocolProps::protoFromString(m_content.keys().at(index.row())); case ProtocolIndexRole: return ProtocolProps::protoFromString(m_content.keys().at(index.row()));
case RawConfigRole: { case RawConfigRole: {
auto protocolConfig = m_content.value(ContainerProps::containerTypeToString(m_container)).toObject(); auto protocolConfig = m_content.value(ContainerProps::containerTypeToString(m_container)).toObject();
@@ -50,6 +54,15 @@ QVariant ProtocolsModel::data(const QModelIndex &index, int role) const
} }
return rawConfig; return rawConfig;
} }
case IsClientProtocolExistsRole: {
auto protocolConfig = m_content.value(ContainerProps::containerTypeToString(m_container)).toObject();
auto lastConfigJsonDoc =
QJsonDocument::fromJson(protocolConfig.value(config_key::last_config).toString().toUtf8());
auto lastConfigJson = lastConfigJsonDoc.object();
auto configString = lastConfigJson.value(config_key::config).toString();
return !configString.isEmpty();
}
} }
return QVariant(); return QVariant();
@@ -70,7 +83,7 @@ QJsonObject ProtocolsModel::getConfig()
return config; return config;
} }
PageLoader::PageEnum ProtocolsModel::protocolPage(Proto protocol) const PageLoader::PageEnum ProtocolsModel::serverProtocolPage(Proto protocol) const
{ {
switch (protocol) { switch (protocol) {
case Proto::OpenVpn: return PageLoader::PageEnum::PageProtocolOpenVpnSettings; case Proto::OpenVpn: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
@@ -90,3 +103,12 @@ PageLoader::PageEnum ProtocolsModel::protocolPage(Proto protocol) const
default: return PageLoader::PageEnum::PageProtocolOpenVpnSettings; default: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
} }
} }
PageLoader::PageEnum ProtocolsModel::clientProtocolPage(Proto protocol) const
{
switch (protocol) {
case Proto::WireGuard: return PageLoader::PageEnum::PageProtocolWireGuardClientSettings;
case Proto::Awg: return PageLoader::PageEnum::PageProtocolAwgClientSettings;
default: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
}
}
+6 -3
View File
@@ -13,9 +13,11 @@ class ProtocolsModel : public QAbstractListModel
public: public:
enum Roles { enum Roles {
ProtocolNameRole = Qt::UserRole + 1, ProtocolNameRole = Qt::UserRole + 1,
ProtocolPageRole, ServerProtocolPageRole,
ClientProtocolPageRole,
ProtocolIndexRole, ProtocolIndexRole,
RawConfigRole RawConfigRole,
IsClientProtocolExistsRole
}; };
ProtocolsModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr); ProtocolsModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
@@ -33,7 +35,8 @@ protected:
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
private: private:
PageLoader::PageEnum protocolPage(Proto protocol) const; PageLoader::PageEnum serverProtocolPage(Proto protocol) const;
PageLoader::PageEnum clientProtocolPage(Proto protocol) const;
std::shared_ptr<Settings> m_settings; std::shared_ptr<Settings> m_settings;
-27
View File
@@ -1,27 +0,0 @@
#ifndef PROPERTY_HELPER_H
#define PROPERTY_HELPER_H
#include <QObject>
#define AUTO_PROPERTY(TYPE, NAME) \
Q_PROPERTY(TYPE NAME READ NAME WRITE set_ ## NAME NOTIFY NAME ## Changed ) \
public: \
TYPE NAME() const { return m_ ## NAME ; } \
void set_ ## NAME(TYPE value) { \
if (m_ ## NAME == value) return; \
m_ ## NAME = value; \
emit NAME ## Changed(value); \
} \
Q_SIGNAL void NAME ## Changed(TYPE value);\
private: \
TYPE m_ ## NAME{};
#define READONLY_PROPERTY(TYPE, NAME) \
Q_PROPERTY(TYPE NAME READ NAME CONSTANT ) \
public: \
TYPE NAME() const { return m_ ## NAME ; } \
private: \
void NAME(TYPE value) {m_ ## NAME = value; } \
TYPE m_ ## NAME{};
#endif // PROPERTY_HELPER_H
+6 -5
View File
@@ -14,6 +14,7 @@ Button {
property string defaultButtonColor: AmneziaStyle.color.paleGray property string defaultButtonColor: AmneziaStyle.color.paleGray
property string progressButtonColor: AmneziaStyle.color.paleGray property string progressButtonColor: AmneziaStyle.color.paleGray
property string connectedButtonColor: AmneziaStyle.color.goldenApricot property string connectedButtonColor: AmneziaStyle.color.goldenApricot
property bool buttonActiveFocus: activeFocus && (Qt.platform.os !== "android" || SettingsController.isOnTv())
implicitWidth: 190 implicitWidth: 190
implicitHeight: 190 implicitHeight: 190
@@ -50,14 +51,14 @@ Button {
verticalOffset: 0 verticalOffset: 0
radius: 10 radius: 10
samples: 25 samples: 25
color: root.activeFocus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.goldenApricot color: root.buttonActiveFocus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.goldenApricot
source: backgroundCircle source: backgroundCircle
} }
ShapePath { ShapePath {
fillColor: AmneziaStyle.color.transparent fillColor: AmneziaStyle.color.transparent
strokeColor: AmneziaStyle.color.paleGray strokeColor: AmneziaStyle.color.paleGray
strokeWidth: root.activeFocus ? 1 : 0 strokeWidth: root.buttonActiveFocus ? 1 : 0
capStyle: ShapePath.RoundCap capStyle: ShapePath.RoundCap
PathAngleArc { PathAngleArc {
@@ -81,14 +82,14 @@ Button {
return defaultButtonColor return defaultButtonColor
} }
} }
strokeWidth: root.activeFocus ? 2 : 3 strokeWidth: root.buttonActiveFocus ? 2 : 3
capStyle: ShapePath.RoundCap capStyle: ShapePath.RoundCap
PathAngleArc { PathAngleArc {
centerX: backgroundCircle.width / 2 centerX: backgroundCircle.width / 2
centerY: backgroundCircle.height / 2 centerY: backgroundCircle.height / 2
radiusX: 93 - (root.activeFocus ? 2 : 0) radiusX: 93 - (root.buttonActiveFocus ? 2 : 0)
radiusY: 93 - (root.activeFocus ? 2 : 0) radiusY: 93 - (root.buttonActiveFocus ? 2 : 0)
startAngle: 0 startAngle: 0
sweepAngle: 360 sweepAngle: 360
} }
@@ -79,6 +79,7 @@ Button {
visible: text !== "" visible: text !== ""
color: AmneziaStyle.color.mutedGray color: AmneziaStyle.color.mutedGray
textFormat: Text.RichText
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
@@ -20,7 +20,8 @@ Item {
property string buttonImageSource property string buttonImageSource
property string rightImageSource property string rightImageSource
property string leftImageSource property string leftImageSource
property bool isLeftImageHoverEnabled: true //todo separete this qml file to 3 property bool isLeftImageHoverEnabled: true
property bool isSmallLeftImage: false
property alias rightButton: rightImage property alias rightButton: rightImage
property alias eyeButton: eyeImage property alias eyeButton: eyeImage
@@ -114,9 +115,9 @@ Item {
visible: leftImageSource ? true : false visible: leftImageSource ? true : false
Layout.preferredHeight: rightImageSource || !isLeftImageHoverEnabled ? leftImage.implicitHeight : 56 Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56
Layout.preferredWidth: rightImageSource || !isLeftImageHoverEnabled ? leftImage.implicitWidth : 56 Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56
Layout.rightMargin: rightImageSource || !isLeftImageHoverEnabled ? 16 : 0 Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0
radius: 12 radius: 12
color: AmneziaStyle.color.transparent color: AmneziaStyle.color.transparent
+1 -2
View File
@@ -102,8 +102,7 @@ Switch {
contentItem: ColumnLayout { contentItem: ColumnLayout {
id: content id: content
anchors.top: parent.top anchors.verticalCenter: parent.verticalCenter
anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
ListItemTitleType { ListItemTitleType {
+15
View File
@@ -89,6 +89,21 @@ PageType {
// KeyNavigation.tab: saveButton // KeyNavigation.tab: saveButton
} }
SwitcherType {
id: switcher
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.topMargin: 16
text: qsTr("Dev gateway environment")
checked: SettingsController.isDevGatewayEnv
onToggled: function() {
SettingsController.isDevGatewayEnv = checked
}
}
} }
} }
} }
@@ -0,0 +1,312 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
defaultActiveFocusItem: listview.currentItem.mtuTextField.textField
Item {
id: focusItem
onFocusChanged: {
if (activeFocus) {
fl.ensureVisible(focusItem)
}
}
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listview.currentItem.mtuTextField.textField
}
}
FlickableType {
id: fl
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: AwgConfigModel
delegate: Item {
id: delegateItem
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias mtuTextField: mtuTextField
property bool isSaveButtonEnabled: mtuTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === ""
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
HeaderType {
Layout.fillWidth: true
headerText: qsTr("AmneziaWG settings")
}
TextFieldWithHeaderType {
id: mtuTextField
Layout.fillWidth: true
Layout.topMargin: 40
headerText: qsTr("MTU")
textFieldText: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: {
if (textFieldText !== clientMtu) {
clientMtu = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketCountTextField.textField
}
TextFieldWithHeaderType {
id: junkPacketCountTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jc - Junk packet count"
textFieldText: clientJunkPacketCount
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketCount) {
clientJunkPacketCount = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketMinSizeTextField.textField
}
TextFieldWithHeaderType {
id: junkPacketMinSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jmin - Junk packet minimum size"
textFieldText: clientJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketMinSize) {
clientJunkPacketMinSize = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
}
TextFieldWithHeaderType {
id: junkPacketMaxSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jmax - Junk packet maximum size"
textFieldText: clientJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketMaxSize) {
clientJunkPacketMaxSize = textFieldText
}
}
checkEmptyText: true
Keys.onTabPressed: saveButton.forceActiveFocus()
}
Header2TextType {
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Server settings")
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 8
enabled: false
headerText: qsTr("Port")
textFieldText: port
}
TextFieldWithHeaderType {
id: initPacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "S1 - Init packet junk size"
textFieldText: serverInitPacketJunkSize
}
TextFieldWithHeaderType {
id: responsePacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "S2 - Response packet junk size"
textFieldText: serverResponsePacketJunkSize
}
TextFieldWithHeaderType {
id: initPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "H1 - Init packet magic header"
textFieldText: serverInitPacketMagicHeader
}
TextFieldWithHeaderType {
id: responsePacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "H2 - Response packet magic header"
textFieldText: serverResponsePacketMagicHeader
}
TextFieldWithHeaderType {
id: underloadPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
enabled: false
headerText: "H3 - Underload packet magic header"
textFieldText: serverUnderloadPacketMagicHeader
}
TextFieldWithHeaderType {
id: transportPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "H4 - Transport packet magic header"
textFieldText: serverTransportPacketMagicHeader
}
}
}
}
}
}
BasicButtonType {
id: saveButton
anchors.right: root.right
anchors.left: root.left
anchors.bottom: root.bottom
anchors.topMargin: 24
anchors.bottomMargin: 24
anchors.rightMargin: 16
anchors.leftMargin: 16
enabled: listview.currentItem.isSaveButtonEnabled
text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
forceActiveFocus()
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("Only the settings for this device will be changed")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
}
@@ -57,8 +57,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView { ListView {
id: listview id: listview
@@ -71,12 +69,12 @@ PageType {
model: AwgConfigModel model: AwgConfigModel
delegate: Item { delegate: Item {
id: _delegate id: delegateItem
implicitWidth: listview.width implicitWidth: listview.width
implicitHeight: col.implicitHeight implicitHeight: col.implicitHeight
property alias portTextField:portTextField property alias portTextField: portTextField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
ColumnLayout { ColumnLayout {
id: col id: col
@@ -101,6 +99,8 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 40
enabled: delegateItem.isEnabled
headerText: qsTr("Port") headerText: qsTr("Port")
textFieldText: port textFieldText: port
textField.maximumLength: 5 textField.maximumLength: 5
@@ -115,27 +115,6 @@ PageType {
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: mtuTextField.textField
}
TextFieldWithHeaderType {
id: mtuTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("MTU")
textFieldText: mtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: {
if (textFieldText === "") {
textFieldText = "0"
}
if (textFieldText !== mtu) {
mtu = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketCountTextField.textField KeyNavigation.tab: junkPacketCountTextField.textField
} }
@@ -145,7 +124,7 @@ PageType {
Layout.topMargin: 16 Layout.topMargin: 16
headerText: qsTr("Jc - Junk packet count") headerText: qsTr("Jc - Junk packet count")
textFieldText: junkPacketCount textFieldText: serverJunkPacketCount
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl parentFlickable: fl
@@ -154,8 +133,8 @@ PageType {
textFieldText = "0" textFieldText = "0"
} }
if (textFieldText !== junkPacketCount) { if (textFieldText !== serverJunkPacketCount) {
junkPacketCount = textFieldText serverJunkPacketCount = textFieldText
} }
} }
@@ -170,13 +149,13 @@ PageType {
Layout.topMargin: 16 Layout.topMargin: 16
headerText: qsTr("Jmin - Junk packet minimum size") headerText: qsTr("Jmin - Junk packet minimum size")
textFieldText: junkPacketMinSize textFieldText: serverJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== junkPacketMinSize) { if (textFieldText !== serverJunkPacketMinSize) {
junkPacketMinSize = textFieldText serverJunkPacketMinSize = textFieldText
} }
} }
@@ -191,13 +170,13 @@ PageType {
Layout.topMargin: 16 Layout.topMargin: 16
headerText: qsTr("Jmax - Junk packet maximum size") headerText: qsTr("Jmax - Junk packet maximum size")
textFieldText: junkPacketMaxSize textFieldText: serverJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== junkPacketMaxSize) { if (textFieldText !== serverJunkPacketMaxSize) {
junkPacketMaxSize = textFieldText serverJunkPacketMaxSize = textFieldText
} }
} }
@@ -212,13 +191,13 @@ PageType {
Layout.topMargin: 16 Layout.topMargin: 16
headerText: qsTr("S1 - Init packet junk size") headerText: qsTr("S1 - Init packet junk size")
textFieldText: initPacketJunkSize textFieldText: serverInitPacketJunkSize
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== initPacketJunkSize) { if (textFieldText !== serverInitPacketJunkSize) {
initPacketJunkSize = textFieldText serverInitPacketJunkSize = textFieldText
} }
} }
@@ -233,13 +212,13 @@ PageType {
Layout.topMargin: 16 Layout.topMargin: 16
headerText: qsTr("S2 - Response packet junk size") headerText: qsTr("S2 - Response packet junk size")
textFieldText: responsePacketJunkSize textFieldText: serverResponsePacketJunkSize
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== responsePacketJunkSize) { if (textFieldText !== serverResponsePacketJunkSize) {
responsePacketJunkSize = textFieldText serverResponsePacketJunkSize = textFieldText
} }
} }
@@ -254,13 +233,13 @@ PageType {
Layout.topMargin: 16 Layout.topMargin: 16
headerText: qsTr("H1 - Init packet magic header") headerText: qsTr("H1 - Init packet magic header")
textFieldText: initPacketMagicHeader textFieldText: serverInitPacketMagicHeader
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== initPacketMagicHeader) { if (textFieldText !== serverInitPacketMagicHeader) {
initPacketMagicHeader = textFieldText serverInitPacketMagicHeader = textFieldText
} }
} }
@@ -275,13 +254,13 @@ PageType {
Layout.topMargin: 16 Layout.topMargin: 16
headerText: qsTr("H2 - Response packet magic header") headerText: qsTr("H2 - Response packet magic header")
textFieldText: responsePacketMagicHeader textFieldText: serverResponsePacketMagicHeader
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== responsePacketMagicHeader) { if (textFieldText !== serverResponsePacketMagicHeader) {
responsePacketMagicHeader = textFieldText serverResponsePacketMagicHeader = textFieldText
} }
} }
@@ -296,13 +275,13 @@ PageType {
Layout.topMargin: 16 Layout.topMargin: 16
headerText: qsTr("H4 - Transport packet magic header") headerText: qsTr("H4 - Transport packet magic header")
textFieldText: transportPacketMagicHeader textFieldText: serverTransportPacketMagicHeader
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== transportPacketMagicHeader) { if (textFieldText !== serverTransportPacketMagicHeader) {
transportPacketMagicHeader = textFieldText serverTransportPacketMagicHeader = textFieldText
} }
} }
@@ -318,12 +297,12 @@ PageType {
parentFlickable: fl parentFlickable: fl
headerText: qsTr("H3 - Underload packet magic header") headerText: qsTr("H3 - Underload packet magic header")
textFieldText: underloadPacketMagicHeader textFieldText: serverUnderloadPacketMagicHeader
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== underloadPacketMagicHeader) { if (textFieldText !== serverUnderloadPacketMagicHeader) {
underloadPacketMagicHeader = textFieldText serverUnderloadPacketMagicHeader = textFieldText
} }
} }
@@ -356,18 +335,22 @@ PageType {
Keys.onTabPressed: lastItemTabClicked(focusItem) Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text, forceActiveFocus()
transportPacketMagicHeaderTextField.textField.text,
responsePacketMagicHeaderTextField.textField.text,
initPacketMagicHeaderTextField.textField.text)) {
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique"))
return
}
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text), if (delegateItem.isEnabled) {
parseInt(responsePacketJunkSizeTextField.textField.text))) { if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text,
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)")) transportPacketMagicHeaderTextField.textField.text,
return responsePacketMagicHeaderTextField.textField.text,
initPacketMagicHeaderTextField.textField.text)) {
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique"))
return
}
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
parseInt(responsePacketJunkSizeTextField.textField.text))) {
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)"))
return
}
} }
var headerText = qsTr("Save settings?") var headerText = qsTr("Save settings?")
@@ -376,8 +359,6 @@ PageType {
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
forceActiveFocus()
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return return
@@ -0,0 +1,179 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
defaultActiveFocusItem: listview.currentItem.mtuTextField.textField
Item {
id: focusItem
onFocusChanged: {
if (activeFocus) {
fl.ensureVisible(focusItem)
}
}
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listview.currentItem.mtuTextField.textField
}
}
FlickableType {
id: fl
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: WireGuardConfigModel
delegate: Item {
id: delegateItem
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias mtuTextField: mtuTextField
property bool isSaveButtonEnabled: mtuTextField.errorText === ""
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
HeaderType {
Layout.fillWidth: true
headerText: qsTr("WG settings")
}
TextFieldWithHeaderType {
id: mtuTextField
Layout.fillWidth: true
Layout.topMargin: 40
headerText: qsTr("MTU")
textFieldText: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: {
if (textFieldText !== clientMtu) {
clientMtu = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: saveButton
}
Header2TextType {
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Server settings")
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 8
enabled: false
headerText: qsTr("Port")
textFieldText: port
}
}
}
}
}
}
BasicButtonType {
id: saveButton
anchors.right: root.right
anchors.left: root.left
anchors.bottom: root.bottom
anchors.topMargin: 24
anchors.bottomMargin: 24
anchors.rightMargin: 16
anchors.leftMargin: 16
enabled: listview.currentItem.isSaveButtonEnabled
text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
forceActiveFocus()
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("Only the settings for this device will be changed")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
}
@@ -72,7 +72,10 @@ PageType {
} }
delegate: Item { delegate: Item {
id: delegateItem
property alias focusItemId: portTextField.textField property alias focusItemId: portTextField.textField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
implicitWidth: listview.width implicitWidth: listview.width
implicitHeight: col.implicitHeight implicitHeight: col.implicitHeight
@@ -99,12 +102,14 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 40
enabled: delegateItem.isEnabled
headerText: qsTr("Port") headerText: qsTr("Port")
textFieldText: port textFieldText: port
textField.maximumLength: 5 textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 } textField.validator: IntValidator { bottom: 1; top: 65535 }
KeyNavigation.tab: mtuTextField.textField KeyNavigation.tab: saveButton
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== port) { if (textFieldText !== port) {
@@ -115,52 +120,41 @@ PageType {
checkEmptyText: true checkEmptyText: true
} }
TextFieldWithHeaderType {
id: mtuTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("MTU")
textFieldText: mtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
KeyNavigation.tab: saveButton
textField.onEditingFinished: {
if (textFieldText === "") {
textFieldText = "0"
}
if (textFieldText !== mtu) {
mtu = textFieldText
}
}
checkEmptyText: true
}
BasicButtonType { BasicButtonType {
id: saveButton id: saveButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 24 Layout.bottomMargin: 24
enabled: mtuTextField.errorText === "" && enabled: portTextField.errorText === ""
portTextField.errorText === ""
text: qsTr("Save") text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem) Keys.onTabPressed: lastItemTabClicked(focusItem)
onClicked: { onClicked: function() {
forceActiveFocus() forceActiveFocus()
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { var headerText = qsTr("Save settings?")
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
return var yesButtonText = qsTr("Continue")
} var noButtonText = qsTr("Cancel")
PageController.goToPage(PageEnum.PageSetupWizardInstalling); var yesButtonFunction = function() {
InstallController.updateContainer(WireGuardConfigModel.getConfig()) if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
focusItem.forceActiveFocus() PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveRestartButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: saveButton.clicked() Keys.onEnterPressed: saveButton.clicked()
+3 -1
View File
@@ -120,7 +120,7 @@ PageType {
id: mailButton id: mailButton
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Mail") text: qsTr("support@amnezia.org")
descriptionText: qsTr("For reviews and bug reports") descriptionText: qsTr("For reviews and bug reports")
leftImageSource: "qrc:/images/controls/mail.svg" leftImageSource: "qrc:/images/controls/mail.svg"
@@ -128,6 +128,8 @@ PageType {
parentFlickable: fl parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
GC.copyToClipBoard(text)
PageController.showNotificationMessage(qsTr("Copied"))
} }
} }
+178 -121
View File
@@ -16,18 +16,6 @@ import "../Controls2/TextTypes"
PageType { PageType {
id: root id: root
Connections {
target: SettingsController
function onLoggingStateChanged() {
if (SettingsController.isLoggingEnabled) {
var message = qsTr("Logging is enabled. Note that logs will be automatically \
disabled after 14 days, and all log files will be deleted.")
PageController.showNotificationMessage(message)
}
}
}
defaultActiveFocusItem: focusItem defaultActiveFocusItem: focusItem
Item { Item {
@@ -58,13 +46,12 @@ disabled after 14 days, and all log files will be deleted.")
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.leftMargin: 16 spacing: 0
anchors.rightMargin: 16
spacing: 16
HeaderType { HeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Logging") headerText: qsTr("Logging")
descriptionText: qsTr("Enabling this function will save application's logs automatically. " + descriptionText: qsTr("Enabling this function will save application's logs automatically. " +
@@ -75,11 +62,13 @@ disabled after 14 days, and all log files will be deleted.")
id: switcher id: switcher
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Save logs") text: qsTr("Enable logs")
checked: SettingsController.isLoggingEnabled checked: SettingsController.isLoggingEnabled
KeyNavigation.tab: openFolderButton //KeyNavigation.tab: openFolderButton
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isLoggingEnabled) { if (checked !== SettingsController.isLoggingEnabled) {
SettingsController.isLoggingEnabled = checked SettingsController.isLoggingEnabled = checked
@@ -87,132 +76,200 @@ disabled after 14 days, and all log files will be deleted.")
} }
} }
RowLayout { DividerType {}
LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: -8
ColumnLayout { text: qsTr("Clear logs")
Layout.alignment: Qt.AlignBaseline leftImageSource: "qrc:/images/controls/trash.svg"
Layout.preferredWidth: GC.isMobile() ? 0 : root.width / 3 isSmallLeftImage: true
visible: !GC.isMobile()
ImageButtonType { // KeyNavigation.tab: labelWithButton3
id: openFolderButton
Layout.alignment: Qt.AlignHCenter
implicitWidth: 56 clickedFunction: function() {
implicitHeight: 56 var headerText = qsTr("Clear logs?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
image: "qrc:/images/controls/folder-open.svg" var yesButtonFunction = function() {
KeyNavigation.tab: saveButton PageController.showBusyIndicator(true)
SettingsController.clearLogs()
onClicked: SettingsController.openLogsFolder() PageController.showBusyIndicator(false)
Keys.onReturnPressed: openFolderButton.clicked() PageController.showNotificationMessage(qsTr("Logs have been cleaned up"))
Keys.onEnterPressed: openFolderButton.clicked() if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
} }
var noButtonFunction = function() {
CaptionTextType { if (!GC.isMobile()) {
horizontalAlignment: Text.AlignHCenter focusItem.forceActiveFocus()
Layout.fillWidth: true
text: qsTr("Open folder with logs")
color: AmneziaStyle.color.paleGray
}
}
ColumnLayout {
Layout.alignment: Qt.AlignBaseline
Layout.preferredWidth: root.width / ( GC.isMobile() ? 2 : 3 )
ImageButtonType {
id: saveButton
Layout.alignment: Qt.AlignHCenter
implicitWidth: 56
implicitHeight: 56
image: "qrc:/images/controls/save.svg"
KeyNavigation.tab: clearButton
Keys.onReturnPressed: saveButton.clicked()
Keys.onEnterPressed: saveButton.clicked()
onClicked: {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN",
true,
".log")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SettingsController.exportLogsFile(fileName)
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs file saved"))
}
} }
} }
CaptionTextType { showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
horizontalAlignment: Text.AlignHCenter }
Layout.fillWidth: true }
text: qsTr("Save logs to file") ListItemTitleType {
color: AmneziaStyle.color.paleGray Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Client logs")
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
color: AmneziaStyle.color.mutedGray
text: qsTr("AmneziaVPN logs")
}
LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Open logs folder")
leftImageSource: "qrc:/images/controls/folder-open.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
SettingsController.openLogsFolder()
}
}
DividerType {}
LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Export logs")
leftImageSource: "qrc:/images/controls/save.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN",
true,
".log")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SettingsController.exportLogsFile(fileName)
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs file saved"))
} }
} }
}
ColumnLayout { DividerType {}
Layout.alignment: Qt.AlignBaseline
Layout.preferredWidth: root.width / ( GC.isMobile() ? 2 : 3 )
ImageButtonType { ListItemTitleType {
id: clearButton visible: !GC.isMobile()
Layout.alignment: Qt.AlignHCenter
implicitWidth: 56 Layout.fillWidth: true
implicitHeight: 56 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
image: "qrc:/images/controls/delete.svg" text: qsTr("Service logs")
Keys.onTabPressed: lastItemTabClicked(focusItem) }
Keys.onReturnPressed: clearButton.clicked() ParagraphTextType {
Keys.onEnterPressed: clearButton.clicked() visible: !GC.isMobile()
onClicked: function() {
var headerText = qsTr("Clear logs?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { Layout.fillWidth: true
PageController.showBusyIndicator(true) Layout.topMargin: 8
SettingsController.clearLogs() Layout.leftMargin: 16
PageController.showBusyIndicator(false) Layout.rightMargin: 16
PageController.showNotificationMessage(qsTr("Logs have been cleaned up"))
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) color: AmneziaStyle.color.mutedGray
} text: qsTr("AmneziaVPN-service logs")
}
LabelWithButtonType {
// id: labelWithButton2
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Open logs folder")
leftImageSource: "qrc:/images/controls/folder-open.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
SettingsController.openServiceLogsFolder()
}
}
DividerType {
visible: !GC.isMobile()
}
LabelWithButtonType {
// id: labelWithButton2
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Export logs")
leftImageSource: "qrc:/images/controls/save.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN-service.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN-service",
true,
".log")
} }
if (fileName !== "") {
CaptionTextType { PageController.showBusyIndicator(true)
horizontalAlignment: Text.AlignHCenter SettingsController.exportServiceLogsFile(fileName)
Layout.fillWidth: true PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs file saved"))
text: qsTr("Clear logs")
color: AmneziaStyle.color.paleGray
} }
} }
} }
DividerType {
visible: !GC.isMobile()
}
} }
} }
} }
@@ -79,7 +79,7 @@ PageType {
} }
delegate: Item { delegate: Item {
property var focusItem: button.rightButton property var focusItem: clientSettings.rightButton
implicitWidth: protocols.width implicitWidth: protocols.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight
@@ -89,13 +89,49 @@ PageType {
anchors.fill: parent anchors.fill: parent
property bool isClientSettingsVisible: protocolIndex === ProtocolEnum.WireGuard || protocolIndex === ProtocolEnum.Awg
property bool isServerSettingsVisible: ServersModel.isProcessedServerHasWriteAccess()
LabelWithButtonType { LabelWithButtonType {
id: button id: clientSettings
Layout.fillWidth: true Layout.fillWidth: true
text: protocolName text: protocolName + qsTr(" connection settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
visible: delegateContent.isClientSettingsVisible
clickedFunction: function() {
if (isClientProtocolExists) {
switch (protocolIndex) {
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
PageController.goToPage(clientProtocolPage);
} else {
PageController.showNotificationMessage(qsTr("Click the \"connect\" button to create a connection configuration"))
}
}
MouseArea {
anchors.fill: clientSettings
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: delegateContent.isClientSettingsVisible
}
LabelWithButtonType {
id: serverSettings
Layout.fillWidth: true
text: protocolName + qsTr(" server settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
visible: delegateContent.isServerSettingsVisible
clickedFunction: function() { clickedFunction: function() {
switch (protocolIndex) { switch (protocolIndex) {
@@ -109,17 +145,19 @@ PageType {
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break; case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break; case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break;
} }
PageController.goToPage(protocolPage); PageController.goToPage(serverProtocolPage);
} }
MouseArea { MouseArea {
anchors.fill: button anchors.fill: serverSettings
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
enabled: false enabled: false
} }
} }
DividerType {} DividerType {
visible: delegateContent.isServerSettingsVisible
}
} }
} }
} }
@@ -132,11 +170,11 @@ PageType {
visible: root.isClearCacheVisible visible: root.isClearCacheVisible
KeyNavigation.tab: removeButton KeyNavigation.tab: removeButton
text: qsTr("Clear %1 profile").arg(ContainersModel.getProcessedContainerName()) text: qsTr("Clear profile")
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName()) var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("") var descriptionText = qsTr("The connection configuration will be deleted for this device only")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")
@@ -183,7 +221,7 @@ PageType {
visible: ServersModel.isProcessedServerHasWriteAccess() visible: ServersModel.isProcessedServerHasWriteAccess()
Keys.onTabPressed: lastItemTabClicked(focusItem) Keys.onTabPressed: lastItemTabClicked(focusItem)
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() text: qsTr("Remove ")
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() { clickedFunction: function() {
@@ -88,8 +88,10 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
onClicked: { onClicked: {
ApiServicesModel.setServiceIndex(index) if (isServiceAvailable) {
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo) ApiServicesModel.setServiceIndex(index)
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
}
} }
} }
} }
@@ -55,6 +55,51 @@ PageType {
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("Connection") headerText: qsTr("Connection")
actionButtonImage: PageController.isStartPageVisible() ? "qrc:/images/controls/more-vertical.svg" : ""
actionButtonFunction: function() {
moreActionsDrawer.open()
}
DrawerType2 {
id: moreActionsDrawer
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.35
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 32
headerText: qsTr("Settings")
}
SwitcherType {
id: switcher
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Enable logs")
checked: SettingsController.isLoggingEnabled
onCheckedChanged: {
if (checked !== SettingsController.isLoggingEnabled) {
SettingsController.isLoggingEnabled = checked
}
}
}
}
}
} }
ParagraphTextType { ParagraphTextType {
@@ -119,8 +164,6 @@ PageType {
CardWithIconsType { CardWithIconsType {
id: apiInstalling id: apiInstalling
visible: false
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
@@ -60,9 +60,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("Server IP address [:port]") headerText: qsTr("Server IP address [:port]")
textFieldPlaceholderText: qsTr("255.255.255.255:22") textFieldPlaceholderText: qsTr("255.255.255.255:22")
textField.validator: RegularExpressionValidator {
regularExpression: InstallController.ipAddressPortRegExp()
}
textField.onFocusChanged: { textField.onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '') textField.text = textField.text.replace(/^\s+|\s+$/g, '')
+11 -10
View File
@@ -140,22 +140,23 @@ PageType {
Keys.onTabPressed: lastItemTabClicked(focusItem) Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
PageController.showBusyIndicator(true)
if (Qt.platform.os === "android" && !SystemController.isAuthenticated()) {
PageController.showBusyIndicator(false)
ExportController.exportErrorOccurred(qsTr("Access error!"))
return
} else {
ExportController.generateFullAccessConfig()
}
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.open() shareConnectionDrawer.open()
shareConnectionDrawer.contentVisible = false shareConnectionDrawer.contentVisible = true
PageController.showBusyIndicator(true)
if (Qt.platform.os === "android") {
ExportController.generateFullAccessConfigAndroid();
} else {
ExportController.generateFullAccessConfig();
}
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
shareConnectionDrawer.contentVisible = true
} }
} }
} }
+8
View File
@@ -202,6 +202,14 @@ PageType {
PageController.showNotificationMessage(qsTr("Settings restored from backup file")) PageController.showNotificationMessage(qsTr("Settings restored from backup file"))
PageController.goToPageHome() PageController.goToPageHome()
} }
function onLoggingStateChanged() {
if (SettingsController.isLoggingEnabled) {
var message = qsTr("Logging is enabled. Note that logs will be automatically" +
"disabled after 14 days, and all log files will be deleted.")
PageController.showNotificationMessage(message)
}
}
} }
StackViewType { StackViewType {

Some files were not shown because too many files have changed in this diff Show More