Compare commits

..

1 Commits

Author SHA1 Message Date
ronoaer e6ecfaf171 adapted awg-questiondrawer with drawer2type 2023-10-21 16:11:54 +08:00
90 changed files with 1679 additions and 2664 deletions
-1
View File
@@ -3,4 +3,3 @@ deploy/data/windows/x64/tap/windows_10/OemVista.inf eol=crlf
deploy/data/windows/x32/tap/windows_7/OemVista.inf eol=crlf deploy/data/windows/x32/tap/windows_7/OemVista.inf eol=crlf
deploy/data/windows/x32/tap/windows_10/OemVista.inf eol=crlf deploy/data/windows/x32/tap/windows_10/OemVista.inf eol=crlf
client/3rd/* linguist-vendored client/3rd/* linguist-vendored
client/android/gradlew.bat eol=crlf
+45 -87
View File
@@ -1,12 +1,7 @@
name: 'Deploy workflow' name: 'Deploy workflow'
on:
push:
branches:
- '**'
env: on: [push]
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ # https://download.qt.io/static/mirrorlist/
jobs: jobs:
Build-Linux-Ubuntu: Build-Linux-Ubuntu:
@@ -30,7 +25,7 @@ jobs:
setup-python: 'true' setup-python: 'true'
tools: 'tools_ifw' tools: 'tools_ifw'
set-env: 'true' set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z'
- name: 'Get sources' - name: 'Get sources'
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -94,7 +89,7 @@ jobs:
setup-python: 'true' setup-python: 'true'
tools: 'tools_ifw' tools: 'tools_ifw'
set-env: 'true' set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z'
- name: 'Setup mvsc' - name: 'Setup mvsc'
uses: ilammy/msvc-dev-cmd@v1 uses: ilammy/msvc-dev-cmd@v1
@@ -124,14 +119,15 @@ jobs:
# ------------------------------------------------------ # ------------------------------------------------------
Build-iOS: Build-IOS:
name: 'Build-iOS' name: 'Build-IOS'
runs-on: macos-12 runs-on: macos-12
env: env:
QT_VERSION: 6.5.2 QT_VERSION: 6.5.2
steps: steps:
# Just select XCode
- name: 'Setup xcode' - name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1 uses: maxim-lobanov/setup-xcode@v1
with: with:
@@ -147,7 +143,6 @@ jobs:
arch: 'clang_64' arch: 'clang_64'
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
set-env: 'true' set-env: 'true'
extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install iOS Qt' - name: 'Install iOS Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
@@ -159,7 +154,7 @@ jobs:
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
setup-python: 'true' setup-python: 'true'
set-env: 'true' set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z'
- name: 'Install go' - name: 'Install go'
uses: actions/setup-go@v3 uses: actions/setup-go@v3
@@ -179,7 +174,7 @@ jobs:
- name: 'Setup ccache' - name: 'Setup ccache'
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1.2
- name: 'Install dependencies' - name: Install dependencies
run: pip install jsonschema jinja2 run: pip install jsonschema jinja2
- name: 'Build project' - name: 'Build project'
@@ -237,7 +232,7 @@ jobs:
setup-python: 'true' setup-python: 'true'
tools: 'tools_ifw' tools: 'tools_ifw'
set-env: 'true' set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z'
- name: 'Get sources' - name: 'Get sources'
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -273,10 +268,21 @@ jobs:
name: 'Build-Android' name: 'Build-Android'
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
include:
- abi: 'x86_64'
qt_arch: 'android_x86_64'
- abi: 'x86'
qt_arch: 'android_x86'
- abi: 'armeabi-v7a'
qt_arch: 'android_armv7'
- abi: 'arm64-v8a'
qt_arch: 'android_arm64_v8a'
env: env:
ANDROID_BUILD_PLATFORM: android-34 QT_VERSION: 6.5.2
QT_VERSION: 6.5.3 ANDROID_BUILD_PLATFORM: android-33
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
steps: steps:
- name: 'Install desktop Qt' - name: 'Install desktop Qt'
@@ -286,58 +292,29 @@ jobs:
host: 'linux' host: 'linux'
target: 'desktop' target: 'desktop'
arch: 'gcc_64' arch: 'gcc_64'
modules: ${{ env.QT_MODULES }} modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}' setup-python: 'true'
set-env: 'true'
extra: '--external 7z'
- name: 'Install android_x86_64 Qt' - name: 'Install android Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
with: with:
version: ${{ env.QT_VERSION }} version: ${{ env.QT_VERSION }}
host: 'linux' host: 'linux'
target: 'android' target: 'android'
arch: 'android_x86_64' arch: ${{ matrix.qt_arch }}
modules: ${{ env.QT_MODULES }} modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}' setup-python: 'true'
set-env: 'true'
- name: 'Install android_x86 Qt' extra: '--external 7z'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_x86'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_armv7 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_armv7'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_arm64_v8a Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_arm64_v8a'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Grant execute permission for qt-cmake' - name: 'Grant execute permission for qt-cmake'
shell: bash shell: bash
run: | run: |
chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/android_x86_64/bin/qt-cmake chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/${{ matrix.qt_arch }}/bin/qt-cmake
- name: 'Get sources' - name: 'Get sources'
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -351,14 +328,15 @@ jobs:
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: '17' java-version: '11'
cache: 'gradle' cache: 'gradle'
- name: 'Setup Android NDK' - name: 'Setup Android NDK'
id: setup-ndk id: setup-ndk
uses: nttld/setup-ndk@v1 uses: nttld/setup-ndk@v1
with: with:
ndk-version: 'r26b' ndk-version: 'r25c'
local-cache: 'true'
- name: 'Decode keystore secret to file' - name: 'Decode keystore secret to file'
env: env:
@@ -371,36 +349,16 @@ jobs:
env: env:
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
QT_HOST_PATH: ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64 QT_HOST_PATH: ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64
ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }} QT_ANDROID_KEYSTORE_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }} QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
QT_ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
shell: bash shell: bash
run: ./deploy/build_android.sh --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }} run: ./deploy/build_android.sh --apk ${{ matrix.abi }} --platform ${{ env.ANDROID_BUILD_PLATFORM }}
- name: 'Upload x86_64 apk' - name: 'Upload apk'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: AmneziaVPN-android-x86_64 name: AmneziaVPN-android-${{ matrix.abi }}
path: deploy/build/AmneziaVPN-x86_64-release.apk path: deploy/build/AmneziaVPN-${{ matrix.abi }}-release-signed.apk
retention-days: 7
- name: 'Upload x86 apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-x86
path: deploy/build/AmneziaVPN-x86-release.apk
retention-days: 7
- name: 'Upload arm64-v8a apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-arm64-v8a
path: deploy/build/AmneziaVPN-arm64-v8a-release.apk
retention-days: 7
- name: 'Upload armeabi-v7a apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-armeabi-v7a
path: deploy/build/AmneziaVPN-armeabi-v7a-release.apk
retention-days: 7 retention-days: 7
-64
View File
@@ -1,64 +0,0 @@
name: 'Upload a new version'
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
jobs:
upload:
runs-on: ubuntu-latest
name: upload
steps:
- name: Checkout CMakeLists.txt
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
sparse-checkout: |
CMakeLists.txt
sparse-checkout-cone-mode: false
- name: Verify git tag
run: |
GIT_TAG=${{ github.ref_name }}
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
if [[ "$GIT_TAG" == "$CMAKE_TAG" ]]; then
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are the same. Continuing..."
else
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..."
exit 1
fi
- name: Download artifacts from the "${{ github.ref_name }}" tag
uses: robinraju/release-downloader@v1.8
with:
tag: ${{ github.ref_name }}
fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*"
out-file-path: ${{ github.ref_name }}
- name: Upload beta version
uses: jakejarvis/s3-sync-action@master
if: contains(github.event.base_ref, 'dev')
with:
args: --include "AmneziaVPN*" --delete
env:
AWS_S3_BUCKET: updates
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
SOURCE_DIR: ${{ github.ref_name }}
DEST_DIR: beta/${{ github.ref_name }}
- name: Upload stable version
uses: jakejarvis/s3-sync-action@master
if: contains(github.event.base_ref, 'master')
with:
args: --include "AmneziaVPN*" --delete
env:
AWS_S3_BUCKET: updates
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
SOURCE_DIR: ${{ github.ref_name }}
DEST_DIR: stable/${{ github.ref_name }}
+1 -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.1.0.1 project(${PROJECT} VERSION 4.0.8.5
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/" HOMEPAGE_URL "https://amnezia.org/"
) )
@@ -11,7 +11,6 @@ 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 39)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")
+13 -8
View File
@@ -139,8 +139,7 @@ void AmneziaApplication::init()
&ConnectionController::openConnection); &ConnectionController::openConnection);
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(), connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection); &ConnectionController::closeConnection);
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
&NotificationHandler::onTranslationsUpdated);
m_engine->load(url); m_engine->load(url);
m_systemController->setQmlRoot(m_engine->rootObjects().value(0)); m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
@@ -227,13 +226,14 @@ void AmneziaApplication::loadTranslator()
updateTranslator(locale); updateTranslator(locale);
} }
void AmneziaApplication::updateTranslator(const QLocale &locale) void AmneziaApplication::updateTranslator(const QLocale &locale)
{ {
if (!m_translator->isEmpty()) { if (!m_translator->isEmpty()) {
QCoreApplication::removeTranslator(m_translator.get()); QCoreApplication::removeTranslator(m_translator.get());
} }
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm"; QString strFileName = QString(":/translations/amneziavpn")+QLatin1String("_")+locale.name()+".qm";
if (m_translator->load(strFileName)) { if (m_translator->load(strFileName)) {
if (QCoreApplication::installTranslator(m_translator.get())) { if (QCoreApplication::installTranslator(m_translator.get())) {
m_settings->setAppLanguage(locale); m_settings->setAppLanguage(locale);
@@ -288,8 +288,6 @@ void AmneziaApplication::initModels()
&ContainersModel::setCurrentlyProcessedServerIndex); &ContainersModel::setCurrentlyProcessedServerIndex);
connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(), connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(),
&ContainersModel::setCurrentlyProcessedServerIndex); &ContainersModel::setCurrentlyProcessedServerIndex);
connect(m_containersModel.get(), &ContainersModel::containersModelUpdated, m_serversModel.get(),
&ServersModel::updateContainersConfig);
m_languageModel.reset(new LanguageModel(m_settings, this)); m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get()); m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
@@ -298,7 +296,15 @@ void AmneziaApplication::initModels()
m_sitesModel.reset(new SitesModel(m_settings, this)); m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get()); m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
connect(m_containersModel.get(), &ContainersModel::defaultContainerChanged, this, [this]() {
if (m_containersModel->getDefaultContainer() == DockerContainer::WireGuard
&& m_sitesModel->getRouteMode() != Settings::RouteMode::VpnAllSites) {
m_sitesModel->setRouteMode(Settings::RouteMode::VpnAllSites);
emit m_pageController->showNotificationMessage(
tr("Split tunneling for WireGuard is not implemented, the option was disabled"));
}
});
m_protocolsModel.reset(new ProtocolsModel(m_settings, this)); m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get()); m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
@@ -331,8 +337,7 @@ void AmneziaApplication::initControllers()
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection)); m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get()); m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
&ConnectionController::onTranslationsUpdated);
m_pageController.reset(new PageController(m_serversModel, m_settings)); m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get()); m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
+100 -91
View File
@@ -1,52 +1,60 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!-- Leave package attribute for androiddeployqt --> <manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.amnezia.vpn" package="org.amnezia.vpn"
xmlns:android="http://schemas.android.com/apk/res/android"
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">
<uses-feature android:name="android.hardware.camera" android:required="false" /> <uses-permission android:name="android.permission.INTERNET"/>
<uses-feature android:name="android.hardware.camera.any" android:required="false" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<!-- The following comment will be replaced upon deployment with default features based on the dependencies <!-- Enable when VPN-per-app mode will be implemented -->
of the application. Remove the comment if you do not require these default features. --> <!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
<uses-permission android:name="android.permission.CAMERA"/>
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES --> <!-- %%INSERT_FEATURES -->
<uses-permission android:name="android.permission.INTERNET" /> <supports-screens
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> android:largeScreens="true"
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> android:normalScreens="true"
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> android:anyDensity="true"
<uses-permission android:name="android.permission.CAMERA" /> android:smallScreens="true"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Enable when VPN-per-app mode will be implemented -->
<!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
<application <application
android:name=".qt.AmneziaApp" android:name=".qt.AmneziaApp"
android:hardwareAccelerated="true"
android:label="-- %%INSERT_APP_NAME%% --" android:label="-- %%INSERT_APP_NAME%% --"
android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true"
android:allowNativeHeapPointerTagging="false" android:allowNativeHeapPointerTagging="false"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:icon="@drawable/icon" android:icon="@drawable/icon"
android:roundIcon="@drawable/icon_round" android:roundIcon="@drawable/icon_round">
android:theme="@style/Theme.AppCompat.NoActionBar">
<activity <activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:name=".qt.VPNActivity" android:name=".qt.VPNActivity"
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density android:label="-- %%INSERT_APP_NAME%% --"
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc" android:screenOrientation="unspecified"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:windowSoftInputMode="adjustResize"
android:exported="true"> android:exported="true">
<!-- android:theme="@style/splashScreenTheme"-->
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="org.amnezia.vpn.qt.IMPORT_CONFIG" /> <action android:name="org.amnezia.vpn.qt.IMPORT_CONFIG" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
@@ -54,70 +62,73 @@
<meta-data <meta-data
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 <meta-data
android:name="android.app.extract_android_style" android:name="android.app.extract_android_style"
android:value="minimal" /> android:value="minimal" />
</activity>
<meta-data
android:name="android.app.background_running"
android:value="false"/>
<meta-data
android:name="android.app.arguments"
android:value="-- %%INSERT_APP_ARGUMENTS%% --" />
</activity>
<activity <activity
android:name=".qt.CameraActivity" android:name=".qt.CameraActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".qt.ImportConfigActivity" android:name=".qt.ImportConfigActivity"
android:exported="true"> android:exported="true" >
<intent-filter android:label="AmneziaVPN"> <intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file"/>
<data android:scheme="content"/>
<data android:scheme="file" /> <data android:mimeType="*/*"/>
<data android:scheme="content" /> <data android:host="*"/>
<data android:mimeType="*/*" /> <data android:pathPattern=".*\\.vpn"/>
<data android:host="*" /> <data android:pathPattern=".*\\..*\\.vpn"/>
<data android:pathPattern=".*\\.vpn" /> <data android:pathPattern=".*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\.vpn" /> <data android:pathPattern=".*\\..*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\.vpn" /> <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn" />
</intent-filter> </intent-filter>
<intent-filter android:label="AmneziaVPN"> <intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file"/>
<data android:scheme="content"/>
<data android:scheme="file" /> <data android:mimeType="*/*"/>
<data android:scheme="content" /> <data android:host="*"/>
<data android:mimeType="*/*" /> <data android:pathPattern=".*\\.cfg"/>
<data android:host="*" /> <data android:pathPattern=".*\\..*\\.cfg"/>
<data android:pathPattern=".*\\.cfg" /> <data android:pathPattern=".*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\.cfg" /> <data android:pathPattern=".*\\..*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\.cfg" /> <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg" />
</intent-filter> </intent-filter>
<intent-filter android:label="AmneziaVPN"> <intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file"/>
<data android:scheme="content"/>
<data android:scheme="file" /> <data android:mimeType="*/*"/>
<data android:scheme="content" /> <data android:host="*"/>
<data android:mimeType="*/*" /> <data android:pathPattern=".*\\.conf"/>
<data android:host="*" /> <data android:pathPattern=".*\\..*\\.conf"/>
<data android:pathPattern=".*\\.conf" /> <data android:pathPattern=".*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\.conf" /> <data android:pathPattern=".*\\..*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\.conf" /> <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf" />
</intent-filter> </intent-filter>
</activity> </activity>
@@ -125,32 +136,30 @@
android:name=".VPNService" android:name=".VPNService"
android:process=":QtOnlyProcess" android:process=":QtOnlyProcess"
android:permission="android.permission.BIND_VPN_SERVICE" android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse" android:foregroundServiceType="connectedDevice"
android:exported="true"> android:exported="true">
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<intent-filter>
<action android:name="android.net.VpnService" /> <intent-filter>
</intent-filter> <action android:name="android.net.VpnService"/>
</intent-filter>
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
</service> </service>
<service <service
android:name=".qt.VPNPermissionHelper" android:name=".qt.VPNPermissionHelper"
android:permission="android.permission.BIND_VPN_SERVICE" android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse" android:foregroundServiceType="connectedDevice"
android:exported="true"> android:exported="true">
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
</service> </service>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices --> <!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="org.amnezia.vpn.fileprovider" android:authorities="org.amnezia.vpn.fileprovider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider" /> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider"/>
</provider> </provider>
</application> </application>
+152 -4
View File
@@ -1,5 +1,153 @@
// dummy file for androiddeployqt apply plugin: 'com.github.ben-manes.versions'
buildscript {
ext{
kotlin_version = "1.7.22"
// for libwg
appcompatVersion = '1.1.0'
annotationsVersion = '1.0.1'
databindingVersion = '3.3.1'
jsr305Version = '3.0.2'
streamsupportVersion = '1.7.0'
threetenabpVersion = '1.1.1'
groupName = 'org.amnezia.vpn'
minSdkVer = '24'
cmakeMinVersion = "3.25.0+"
}
repositories {
google()
jcenter()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.8.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlinx-serialization'
apply plugin: 'kotlin-kapt'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation group: 'org.json', name: 'json', version: '20220924'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation "androidx.security:security-crypto:1.1.0-alpha03"
implementation "androidx.security:security-identity-credential:1.0.0-alpha02"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
implementation project(path: ':shadowsocks')
// CameraX core library using the camera2 implementation
def camerax_version = "1.2.1"
implementation("androidx.camera:camera-core:${camerax_version}")
implementation("androidx.camera:camera-camera2:${camerax_version}")
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
implementation("androidx.camera:camera-view:${camerax_version}")
implementation("androidx.camera:camera-extensions:${camerax_version}")
def camerax_ml_version = "1.2.0-beta02"
def ml_kit_version = "17.0.3"
implementation("androidx.camera:camera-mlkit-vision:${camerax_ml_version}")
implementation("com.google.mlkit:barcode-scanning:${ml_kit_version}")
}
androidExtensions {
experimental = true
}
android {
/*******************************************************
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
* - qtAndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
* are defined in gradle.properties file. This file is
* updated by QtCreator and androiddeployqt tools.
* Changing them manually might break the compilation!
*******************************************************/
compileSdkVersion androidCompileSdkVersion.toInteger()
buildToolsVersion androidBuildToolsVersion
ndkVersion androidNdkVersion
// Extract native libraries from the APK
packagingOptions.jniLibs.useLegacyPackaging true
dexOptions {
javaMaxHeapSize "3g"
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qtAndroidDir + '/res', 'res']
resources.srcDirs = ['resources']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
androidTest.assets.srcDirs += files("${qtAndroidDir}/schemas".toString())
}
}
tasks.withType(JavaCompile) {
options.incremental = true
}
compileOptions {
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8
lintOptions {
abortOnError false
}
// Do not compress Qt binary resources file
aaptOptions {
noCompress 'rcc'
}
defaultConfig {
resConfig "en"
minSdkVersion = 24
targetSdkVersion = 34
versionCode 37 // Change to a higher number
versionName "4.0.8" // Change to a higher number
javaCompileOptions.annotationProcessorOptions.arguments = [
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()
]
}
}
// android.bundle.enableUncompressedNativeLibs is deprecated
// disable adding gradle property android.bundle.enableUncompressedNativeLibs by androiddeployqt
useLegacyPackaging
-92
View File
@@ -1,92 +0,0 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
id("property-delegate")
}
kotlin {
jvmToolchain(17)
}
// get values from gradle or local properties
val qtTargetSdkVersion: String by gradleProperties
val qtTargetAbiList: String by gradleProperties
val qtAndroidDir: String by gradleProperties
android {
namespace = "org.amnezia.vpn"
buildFeatures {
buildConfig = true
viewBinding = true
}
androidResources {
// don't compress Qt binary resources file
noCompress += "rcc"
}
packaging {
// compress .so binary libraries
jniLibs.useLegacyPackaging = true
}
defaultConfig {
applicationId = "org.amnezia.vpn"
targetSdk = qtTargetSdkVersion.toInt()
// keeps language resources for only the locales specified below
resourceConfigurations += listOf("en", "ru", "b+zh+Hans")
}
sourceSets {
getByName("main") {
manifest.srcFile("AndroidManifest.xml")
java.setSrcDirs(listOf("$qtAndroidDir/src", "src"))
res.setSrcDirs(listOf("$qtAndroidDir/res", "res"))
assets.setSrcDirs(listOf("assets"))
jniLibs.setSrcDirs(listOf("libs"))
}
}
signingConfigs {
register("release") {
storeFile = providers.environmentVariable("ANDROID_KEYSTORE_PATH").orNull?.let { file(it) }
storePassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
keyAlias = providers.environmentVariable("ANDROID_KEYSTORE_KEY_ALIAS").orNull
keyPassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
}
}
buildTypes {
release {
// exclude coroutine debug resource from release build
packaging {
resources.excludes += "DebugProbesKt.bin"
}
signingConfig = signingConfigs["release"]
}
}
splits {
abi {
isEnable = true
reset()
include(*qtTargetAbiList.split(',').toTypedArray())
isUniversalApk = false
}
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.security.crypto)
implementation(libs.kotlinx.coroutines)
implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit)
implementation(project(":shadowsocks"))
// todo: remove after finish refactoring
implementation(libs.androidx.constraintlayout)
}
+22 -41
View File
@@ -1,46 +1,27 @@
# Specifies the JVM arguments used for the daemon process # Project-wide Gradle settings.
org.gradle.jvmargs=-Xms512m -Xmx4g -XX:+UseParallelGC -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError \ # For more details on how to configure your build environment visit
-Dfile.encoding=UTF-8 # http://www.gradle.org/docs/current/userguide/build_environment.html
org.gradle.caching=true # Specifies the JVM arguments used for the daemon process.
org.gradle.parallel=true # The setting is particularly useful for tweaking memory settings.
org.gradle.configureondemand=true org.gradle.jvmargs=-Xmx1536m
# Gradle caching allows reusing the build artifacts from a previous
# build with the same inputs. However, over time, the cache size will
# grow. Uncomment the following line to enable it.
#org.gradle.caching=true
# Use AndroidX library instead of a Support Library
android.useAndroidX=true android.useAndroidX=true
# Disable adding android:testOnly attribute to the manifest # Automatically convert third-party libraries to use AndroidX
android.injected.testOnly=false android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete": # Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
# Disable providing custom values to resources from buildscript by default android.bundle.enableUncompressedNativeLibs=false
android.defaults.buildfeatures.resvalues=false androidBuildToolsVersion=30.0.2
# Disable compileShaders tasks by default androidCompileSdkVersion=30
android.defaults.buildfeatures.shaders=false org.gradle.caching=true
# Disable Android resource processing for libraries by default org.gradle.parallel=true
android.library.defaults.buildfeatures.androidresources=false android.enableJetifier=true
android.injected.testOnly=false
# Qt variables kapt.use.worker.api=false
# At build time androiddeployqt replaces these values with: kapt.incremental.apt=false
# androidCompileSdkVersion - androiddeployqt --android-platform parameter
# androidBuildToolsVersion - QT_ANDROID_SDK_BUILD_TOOLS_REVISION cmake target parameter
# qtMinSdkVersion - QT_ANDROID_MIN_SDK_VERSION cmake target parameter
# qtTargetSdkVersion - QT_ANDROID_TARGET_SDK_VERSION cmake target parameter
# androidNdkVersion - version from ANDROID_NDK_ROOT environment variable
# qtTargetAbiList - qt-cmake QT_ANDROID_ABIS parameter
# qtAndroidDir - path to qt binding java source code
# buildDir - hardcoded "build" value in androiddeployqt
# For development copy and set local values for these parameters in local.properties
#androidCompileSdkVersion=android-34
#androidBuildToolsVersion=34.0.0
#qtMinSdkVersion=24
#qtTargetSdkVersion=34
#androidNdkVersion=26.1.10909125
#qtTargetAbiList=x86_64
#qtAndroidDir=/QT_BASE/android_ABI/src/android/java
#buildDir=build
# Note about qtAndroidDir:
# Some IDEs (for example, IntelliJ IDEA) may index all data from a common root of the project and qtAndroidDir.
# Therefore, it's recommended to copy qt android files to a directory inside the project
# and specify the path to that directory in qtAndroidDir.
-35
View File
@@ -1,35 +0,0 @@
[versions]
agp = "8.1.3"
kotlin = "1.9.20"
androidx-core = "1.12.0"
androidx-appcompat = "1.6.1"
androidx-camera = "1.2.3"
androidx-security-crypto = "1.1.0-alpha06"
kotlinx-coroutines = "1.7.3"
google-mlkit = "17.2.0"
[libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
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-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" }
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidx-camera" }
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
google-mlkit = { module = "com.google.mlkit:barcode-scanning", version.ref = "google-mlkit" }
# todo: remove after finish refactoring
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" }
[bundles]
androidx-camera = [
"androidx-camera-core",
"androidx-camera-lifecycle",
"androidx-camera-view",
"androidx-camera-camera2"
]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
@@ -1,25 +0,0 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}
kotlin {
jvmToolchain(17)
}
gradlePlugin {
plugins {
register("settingsGradlePropertyDelegate") {
id = "settings-property-delegate"
implementationClass = "SettingsPropertyDelegate"
}
register("projectGradlePropertyDelegate") {
id = "property-delegate"
implementationClass = "ProjectPropertyDelegate"
}
}
}
@@ -1,49 +0,0 @@
import java.io.File
import java.io.FileInputStream
import java.io.InputStreamReader
import java.util.Properties
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
import org.gradle.api.provider.ProviderFactory
private fun localProperties(rootDir: File) = Properties().apply {
val localProperties = File(rootDir, "local.properties")
if (localProperties.isFile) {
InputStreamReader(FileInputStream(localProperties), Charsets.UTF_8).use {
load(it)
}
}
}
private class PropertyDelegate(
rootDir: File,
private val providers: ProviderFactory,
private val localProperties: Properties = localProperties(rootDir)
) : ReadOnlyProperty<Any?, String> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String =
providers.gradleProperty(property.name).orNull ?: localProperties.getProperty(property.name)
}
private lateinit var settingsPropertyDelegate: ReadOnlyProperty<Any?, String>
private lateinit var projectPropertyDelegate: ReadOnlyProperty<Any?, String>
class SettingsPropertyDelegate : Plugin<Settings> {
override fun apply(settings: Settings) {
settingsPropertyDelegate = PropertyDelegate(settings.rootDir, settings.providers)
}
}
class ProjectPropertyDelegate : Plugin<Project> {
override fun apply(project: Project) {
projectPropertyDelegate = PropertyDelegate(project.rootDir, project.providers)
}
}
val Settings.gradleProperties: ReadOnlyProperty<Any?, String>
get() = settingsPropertyDelegate
val Project.gradleProperties: ReadOnlyProperty<Any?, String>
get() = projectPropertyDelegate
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
+115 -192
View File
@@ -1,127 +1,78 @@
#!/bin/sh #!/usr/bin/env sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
# ##
# Gradle start up script for POSIX generated by Gradle. ## Gradle start up script for UN*X
# ##
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (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
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
app_path=$0 PRG="$0"
# Need this for relative symlinks.
# Need this for daisy-chained symlinks. while [ -h "$PRG" ] ; do
while ls=`ls -ld "$PRG"`
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path link=`expr "$ls" : '.*-> \(.*\)$'`
[ -h "$app_path" ] if expr "$link" : '/.*' > /dev/null; then
do PRG="$link"
ls=$( ls -ld "$app_path" ) else
link=${ls#*' -> '} PRG=`dirname "$PRG"`"/$link"
case $link in #( fi
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
# This is normally unused APP_NAME="Gradle"
# shellcheck disable=SC2034 APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit DEFAULT_JVM_OPTS=""
# 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"
warn () { warn () {
echo "$*" echo "$*"
} >&2 }
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} >&2 }
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "$( uname )" in #( case "`uname`" in
CYGWIN* ) cygwin=true ;; #( CYGWIN* )
Darwin* ) darwin=true ;; #( cygwin=true
MSYS* | MINGW* ) msys=true ;; #( ;;
NONSTOP* ) nonstop=true ;; Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java JAVACMD="$JAVA_HOME/jre/sh/java"
else else
JAVACMD=$JAVA_HOME/bin/java JAVACMD="$JAVA_HOME/bin/java"
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -130,120 +81,92 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD="java"
if ! command -v java >/dev/null 2>&1 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
case $MAX_FD in #( MAX_FD_LIMIT=`ulimit -H -n`
max*) if [ $? -eq 0 ] ; then
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
# shellcheck disable=SC2039,SC3045 MAX_FD="$MAX_FD_LIMIT"
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi fi
# Roll the args list around exactly as many times as the number of ulimit -n $MAX_FD
# args, so each arg winds up back in the position where it started, but if [ $? -ne 0 ] ; then
# possibly modified. warn "Could not set maximum file descriptor limit: $MAX_FD"
# fi
# NB: a `for` loop captures its iteration list before it begins, so else
# changing the positional parameters here affects neither the number of warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
# iterations, nor the values presented in `arg`. fi
shift # remove old arg fi
set -- "$@" "$arg" # push replacement arg
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Collect all arguments for the java command, following the shell quoting and substitution rules
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# Collect all arguments for the java command: # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
# and any embedded shellness will be escaped. cd "$(dirname "$0")"
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"
+24 -32
View File
@@ -1,20 +1,4 @@
@rem @if "%DEBUG%" == "" @echo off
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@@ -25,23 +9,19 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" set DEFAULT_JVM_OPTS=
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe 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%" == "0" goto init
echo. echo.
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.
@@ -55,7 +35,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto init
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -65,26 +45,38 @@ echo location of your Java installation.
goto fail goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd if "%ERRORLEVEL%"=="0" goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL% if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
if %EXIT_CODE% equ 0 set EXIT_CODE=1 exit /b 1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal
+21
View File
@@ -0,0 +1,21 @@
pluginManagement {
repositories {
google()
mavenCentral()
jcenter()
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter()
maven { url 'https://jitpack.io' }
}
}
include ':shadowsocks'
-58
View File
@@ -1,58 +0,0 @@
import com.android.build.api.dsl.SettingsExtension
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
// for jsocks todo: remove after finish refactoring
maven("https://jitpack.io")
}
includeBuild("./gradle/plugins")
}
@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// for jsocks todo: remove after finish refactoring
maven("https://jitpack.io")
}
}
includeBuild("./gradle/plugins")
plugins {
id("com.android.settings") version "8.1.3"
id("settings-property-delegate")
}
rootProject.name = "AmneziaVPN"
rootProject.buildFileName = "build.gradle.kts"
include(":shadowsocks")
// get values from gradle or local properties
val androidBuildToolsVersion: String by gradleProperties
val androidCompileSdkVersion: String by gradleProperties
val androidNdkVersion: String by gradleProperties
val qtMinSdkVersion: String by gradleProperties
// set default values for all modules
configure<SettingsExtension> {
buildToolsVersion = androidBuildToolsVersion
compileSdk = androidCompileSdkVersion.substringAfter('-').toInt()
minSdk = qtMinSdkVersion.toInt()
ndkVersion = androidNdkVersion
}
// stop Gradle running by androiddeployqt
gradle.taskGraph.whenReady {
if (providers.environmentVariable("ANDROIDDEPLOYQT_RUN").isPresent
&& !providers.systemProperty("explicitRun").isPresent) {
allTasks.forEach { it.enabled = false }
}
}
+12 -10
View File
@@ -1,17 +1,14 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-parcelize'
//apply plugin: 'com.novoda.bintray-release' //apply plugin: 'com.novoda.bintray-release'
android { android {
buildFeatures { compileSdkVersion 30
aidl true
androidResources true
}
defaultConfig { defaultConfig {
namespace "org.amnezia.vpn.shadowsocks.core" minSdkVersion 24
targetSdkVersion 30
versionCode 1 versionCode 1
versionName "1.0.0" versionName "1.0.0"
@@ -33,6 +30,10 @@ android {
} }
} }
androidExtensions {
experimental = true
}
//def lifecycleVersion = '2.0.0' //def lifecycleVersion = '2.0.0'
def roomVersion = "2.4.3" def roomVersion = "2.4.3"
//def preferencexVersion = '1.0.0' //def preferencexVersion = '1.0.0'
@@ -45,9 +46,10 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
implementation "androidx.core:core-ktx:1.2.0" implementation "androidx.core:core-ktx:1.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1" implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.5.1" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.room:room-runtime:$roomVersion" // runtime implementation "androidx.room:room-runtime:$roomVersion" // runtime
implementation "androidx.preference:preference:1.1.0" implementation "androidx.preference:preference:1.1.0"
implementation "androidx.work:work-runtime-ktx:2.7.1" implementation "androidx.work:work-runtime-ktx:2.7.1"
@@ -1,5 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest 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.shadowsocks.core"
tools:ignore="MissingLeanbackLauncher"> tools:ignore="MissingLeanbackLauncher">
<uses-feature <uses-feature
@@ -65,7 +65,6 @@ class VpnRequestActivity : AppCompatActivity() {
Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_LONG).show() Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_LONG).show()
} }
finish() finish()
super.onActivityResult(requestCode, resultCode, data)
} }
override fun onDestroy() { override fun onDestroy() {
@@ -28,7 +28,6 @@ import kotlinx.coroutines.channels.sendBlocking
import java.io.IOException import java.io.IOException
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.channels.* import java.nio.channels.*
import kotlinx.coroutines.channels.trySendBlocking
class ChannelMonitor : Thread("ChannelMonitor") { class ChannelMonitor : Thread("ChannelMonitor") {
private data class Registration(val channel: SelectableChannel, private data class Registration(val channel: SelectableChannel,
@@ -53,7 +52,7 @@ class ChannelMonitor : Thread("ChannelMonitor") {
registerInternal(this, SelectionKey.OP_READ) { registerInternal(this, SelectionKey.OP_READ) {
val junk = ByteBuffer.allocateDirect(1) val junk = ByteBuffer.allocateDirect(1)
while (read(junk) > 0) { while (read(junk) > 0) {
pendingRegistrations.tryReceive().getOrNull()!!.apply { pendingRegistrations.poll()!!.apply {
try { try {
result.complete(registerInternal(channel, ops, listener)) result.complete(registerInternal(channel, ops, listener))
} catch (e: Exception) { } catch (e: Exception) {
@@ -113,7 +112,7 @@ class ChannelMonitor : Thread("ChannelMonitor") {
(key.attachment() as (SelectionKey) -> Unit)(key) (key.attachment() as (SelectionKey) -> Unit)(key)
} }
} }
closeChannel.trySendBlocking(Unit) closeChannel.sendBlocking(Unit)
} }
fun close(scope: CoroutineScope) { fun close(scope: CoroutineScope) {
@@ -33,7 +33,6 @@ import kotlinx.coroutines.channels.sendBlocking
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import kotlinx.coroutines.channels.trySendBlocking
abstract class LocalSocketListener(name: String, socketFile: File) : Thread(name) { abstract class LocalSocketListener(name: String, socketFile: File) : Thread(name) {
private val localSocket = LocalSocket().apply { private val localSocket = LocalSocket().apply {
@@ -61,7 +60,7 @@ abstract class LocalSocketListener(name: String, socketFile: File) : Thread(name
} }
} }
} }
closeChannel.trySendBlocking(Unit) closeChannel.sendBlocking(Unit)
} }
open fun shutdown(scope: CoroutineScope) { open fun shutdown(scope: CoroutineScope) {
@@ -1,509 +0,0 @@
/*
* Copyright (C) 2012-2017 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package com.wireguard.config;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import androidx.annotation.NonNull;
/**
* Class that represents a range of IP addresses. This range could be a proper subnet, but that's
* not necessarily the case (see {@code getPrefix} and {@code toSubnets}).
*/
public class IPRange implements Comparable<IPRange>
{
private final byte[] mBitmask = { (byte)0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
private byte[] mFrom;
private byte[] mTo;
private Integer mPrefix;
/**
* Determine if the range is a proper subnet and, if so, what the network prefix is.
*/
private void determinePrefix()
{
boolean matching = true;
mPrefix = mFrom.length * 8;
for (int i = 0; i < mFrom.length; i++)
{
for (int bit = 0; bit < 8; bit++)
{
if (matching)
{
if ((mFrom[i] & mBitmask[bit]) != (mTo[i] & mBitmask[bit]))
{
mPrefix = (i * 8) + bit;
matching = false;
}
}
else
{
if ((mFrom[i] & mBitmask[bit]) != 0 || (mTo[i] & mBitmask[bit]) == 0)
{
mPrefix = null;
return;
}
}
}
}
}
private IPRange(byte[] from, byte[] to)
{
mFrom = from;
mTo = to;
determinePrefix();
}
public IPRange(String from, String to) throws UnknownHostException
{
this(Utils.parseInetAddress(from), Utils.parseInetAddress(to));
}
public IPRange(InetAddress from, InetAddress to)
{
initializeFromRange(from, to);
}
private void initializeFromRange(InetAddress from, InetAddress to)
{
byte[] fa = from.getAddress(), ta = to.getAddress();
if (fa.length != ta.length)
{
throw new IllegalArgumentException("Invalid range");
}
if (compareAddr(fa, ta) < 0)
{
mFrom = fa;
mTo = ta;
}
else
{
mTo = fa;
mFrom = ta;
}
determinePrefix();
}
public IPRange(String base, int prefix) throws UnknownHostException
{
this(Utils.parseInetAddress(base), prefix);
}
public IPRange(InetAddress base, int prefix)
{
this(base.getAddress(), prefix);
}
private IPRange(byte[] from, int prefix)
{
initializeFromCIDR(from, prefix);
}
private void initializeFromCIDR(byte[] from, int prefix)
{
if (from.length != 4 && from.length != 16)
{
throw new IllegalArgumentException("Invalid address");
}
if (prefix < 0 || prefix > from.length * 8)
{
throw new IllegalArgumentException("Invalid prefix");
}
byte[] to = from.clone();
byte mask = (byte)(0xff << (8 - prefix % 8));
int i = prefix / 8;
if (i < from.length)
{
from[i] = (byte)(from[i] & mask);
to[i] = (byte)(to[i] | ~mask);
Arrays.fill(from, i+1, from.length, (byte)0);
Arrays.fill(to, i+1, to.length, (byte)0xff);
}
mFrom = from;
mTo = to;
mPrefix = prefix;
}
public IPRange(String cidr) throws UnknownHostException
{
/* only verify the basic structure */
if (!cidr.matches("(?i)^(([0-9.]+)|([0-9a-f:]+))(-(([0-9.]+)|([0-9a-f:]+))|(/\\d+))?$"))
{
throw new IllegalArgumentException("Invalid CIDR or range notation");
}
if (cidr.contains("-"))
{
String[] parts = cidr.split("-");
InetAddress from = InetAddress.getByName(parts[0]);
InetAddress to = InetAddress.getByName(parts[1]);
initializeFromRange(from, to);
}
else
{
String[] parts = cidr.split("/");
InetAddress addr = InetAddress.getByName(parts[0]);
byte[] base = addr.getAddress();
int prefix = base.length * 8;
if (parts.length > 1)
{
prefix = Integer.parseInt(parts[1]);
}
initializeFromCIDR(base, prefix);
}
}
/**
* Returns the first address of the range. The network ID in case this is a proper subnet.
*/
public InetAddress getFrom()
{
try
{
return InetAddress.getByAddress(mFrom);
}
catch (UnknownHostException ignored)
{
return null;
}
}
/**
* Returns the last address of the range.
*/
public InetAddress getTo()
{
try
{
return InetAddress.getByAddress(mTo);
}
catch (UnknownHostException ignored)
{
return null;
}
}
/**
* If this range is a proper subnet returns its prefix, otherwise returns null.
*/
public Integer getPrefix()
{
return mPrefix;
}
@Override
public int compareTo(@NonNull IPRange other)
{
int cmp = compareAddr(mFrom, other.mFrom);
if (cmp == 0)
{ /* smaller ranges first */
cmp = compareAddr(mTo, other.mTo);
}
return cmp;
}
@Override
public boolean equals(Object o)
{
if (o == null || !(o instanceof IPRange))
{
return false;
}
return this == o || compareTo((IPRange)o) == 0;
}
@Override
public String toString()
{
try
{
if (mPrefix != null)
{
return InetAddress.getByAddress(mFrom).getHostAddress() + "/" + mPrefix;
}
return InetAddress.getByAddress(mFrom).getHostAddress() + "-" +
InetAddress.getByAddress(mTo).getHostAddress();
}
catch (UnknownHostException ignored)
{
return super.toString();
}
}
private int compareAddr(byte a[], byte b[])
{
if (a.length != b.length)
{
return (a.length < b.length) ? -1 : 1;
}
for (int i = 0; i < a.length; i++)
{
if (a[i] != b[i])
{
if (((int)a[i] & 0xff) < ((int)b[i] & 0xff))
{
return -1;
}
else
{
return 1;
}
}
}
return 0;
}
/**
* Check if this range fully contains the given range.
*/
public boolean contains(IPRange range)
{
return compareAddr(mFrom, range.mFrom) <= 0 && compareAddr(range.mTo, mTo) <= 0;
}
/**
* Check if this and the given range overlap.
*/
public boolean overlaps(IPRange range)
{
return !(compareAddr(mTo, range.mFrom) < 0 || compareAddr(range.mTo, mFrom) < 0);
}
private byte[] dec(byte[] addr)
{
for (int i = addr.length - 1; i >= 0; i--)
{
if (--addr[i] != (byte)0xff)
{
break;
}
}
return addr;
}
private byte[] inc(byte[] addr)
{
for (int i = addr.length - 1; i >= 0; i--)
{
if (++addr[i] != 0)
{
break;
}
}
return addr;
}
/**
* Remove the given range from the current range. Returns a list of resulting ranges (these are
* not proper subnets). At most two ranges are returned, in case the given range is contained in
* this but does not equal it, which would result in an empty list (which is also the case if
* this range is fully contained in the given range).
*/
public List<IPRange> remove(IPRange range)
{
ArrayList<IPRange> list = new ArrayList<>();
if (!overlaps(range))
{ /* | this | or | this |
* | range | | range | */
list.add(this);
}
else if (!range.contains(this))
{ /* we are not completely removed, so none of these cases applies:
* | this | or | this | or | this |
* | range | | range | | range | */
if (compareAddr(mFrom, range.mFrom) < 0 && compareAddr(range.mTo, mTo) < 0)
{ /* the removed range is completely within our boundaries:
* | this |
* | range | */
list.add(new IPRange(mFrom, dec(range.mFrom.clone())));
list.add(new IPRange(inc(range.mTo.clone()), mTo));
}
else
{ /* one end is within our boundaries the other at or outside it:
* | this | or | this | or | this | or | this |
* | range | | range | | range | | range | */
byte[] from = compareAddr(mFrom, range.mFrom) < 0 ? mFrom : inc(range.mTo.clone());
byte[] to = compareAddr(mTo, range.mTo) > 0 ? mTo : dec(range.mFrom.clone());
list.add(new IPRange(from, to));
}
}
return list;
}
private boolean adjacent(IPRange range)
{
if (compareAddr(mTo, range.mFrom) < 0)
{
byte[] to = inc(mTo.clone());
return compareAddr(to, range.mFrom) == 0;
}
byte[] from = dec(mFrom.clone());
return compareAddr(from, range.mTo) == 0;
}
/**
* Merge two adjacent or overlapping ranges, returns null if it's not possible to merge them.
*/
public IPRange merge(IPRange range)
{
if (overlaps(range))
{
if (contains(range))
{
return this;
}
else if (range.contains(this))
{
return range;
}
}
else if (!adjacent(range))
{
return null;
}
byte[] from = compareAddr(mFrom, range.mFrom) < 0 ? mFrom : range.mFrom;
byte[] to = compareAddr(mTo, range.mTo) > 0 ? mTo : range.mTo;
return new IPRange(from, to);
}
/**
* Split the given range into a sorted list of proper subnets.
*/
public List<IPRange> toSubnets()
{
ArrayList<IPRange> list = new ArrayList<>();
if (mPrefix != null)
{
list.add(this);
}
else
{
int i = 0, bit = 0, prefix, netmask, common_byte, common_bit;
int from_cur, from_prev = 0, to_cur, to_prev = 1;
boolean from_full = true, to_full = true;
byte[] from = mFrom.clone();
byte[] to = mTo.clone();
/* find a common prefix */
while (i < from.length && (from[i] & mBitmask[bit]) == (to[i] & mBitmask[bit]))
{
if (++bit == 8)
{
bit = 0;
i++;
}
}
prefix = i * 8 + bit;
/* at this point we know that the addresses are either equal, or that the
* current bits in the 'from' and 'to' addresses are 0 and 1, respectively.
* we now look at the rest of the bits as two binary trees (0=left, 1=right)
* where 'from' and 'to' are both leaf nodes. all leaf nodes between these
* nodes are addresses contained in the range. to collect them as subnets
* we follow the trees from both leaf nodes to their root node and record
* all complete subtrees (right for from, left for to) we come across as
* subnets. in that process host bits are zeroed out. if both addresses
* are equal we won't enter the loop below.
* 0_____|_____1 for the 'from' address we assume we start on a
* 0__|__ 1 0__|__1 left subtree (0) and follow the left edges until
* _|_ _|_ _|_ _|_ we reach the root of this subtree, which is
* | | | | | | | | either the root of this whole 'from'-subtree
* 0 1 0 1 0 1 0 1 (causing us to leave the loop) or the root node
* of the right subtree (1) of another node (which actually could be the
* leaf node we start from). that whole subtree gets recorded as subnet.
* next we follow the right edges to the root of that subtree which again is
* either the 'from'-root or the root node in the left subtree (0) of
* another node. the complete right subtree of that node is the next subnet
* we record. from there we assume that we are in that right subtree and
* recursively follow right edges to its root. for the 'to' address the
* procedure is exactly the same but with left and right reversed.
*/
if (++bit == 8)
{
bit = 0;
i++;
}
common_byte = i;
common_bit = bit;
netmask = from.length * 8;
for (i = from.length - 1; i >= common_byte; i--)
{
int bit_min = (i == common_byte) ? common_bit : 0;
for (bit = 7; bit >= bit_min; bit--)
{
byte mask = mBitmask[bit];
from_cur = from[i] & mask;
if (from_prev == 0 && from_cur != 0)
{ /* 0 -> 1: subnet is the whole current (right) subtree */
list.add(new IPRange(from.clone(), netmask));
from_full = false;
}
else if (from_prev != 0 && from_cur == 0)
{ /* 1 -> 0: invert bit to switch to right subtree and add it */
from[i] ^= mask;
list.add(new IPRange(from.clone(), netmask));
from_cur = 1;
}
/* clear the current bit */
from[i] &= ~mask;
from_prev = from_cur;
to_cur = to[i] & mask;
if (to_prev != 0 && to_cur == 0)
{ /* 1 -> 0: subnet is the whole current (left) subtree */
list.add(new IPRange(to.clone(), netmask));
to_full = false;
}
else if (to_prev == 0 && to_cur != 0)
{ /* 0 -> 1: invert bit to switch to left subtree and add it */
to[i] ^= mask;
list.add(new IPRange(to.clone(), netmask));
to_cur = 0;
}
/* clear the current bit */
to[i] &= ~mask;
to_prev = to_cur;
netmask--;
}
}
if (from_full && to_full)
{ /* full subnet (from=to or from=0.. and to=1.. after common prefix) - not reachable
* due to the shortcut at the top */
list.add(new IPRange(from.clone(), prefix));
}
else if (from_full)
{ /* full from subnet (from=0.. after prefix) */
list.add(new IPRange(from.clone(), prefix + 1));
}
else if (to_full)
{ /* full to subnet (to=1.. after prefix) */
list.add(new IPRange(to.clone(), prefix + 1));
}
}
Collections.sort(list);
return list;
}
}
@@ -1,223 +0,0 @@
/*
* Copyright (C) 2012-2017 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package com.wireguard.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
/**
* Class that represents a set of IP address ranges (not necessarily proper subnets) and allows
* modifying the set and enumerating the resulting subnets.
*/
public class IPRangeSet implements Iterable<IPRange>
{
private TreeSet<IPRange> mRanges = new TreeSet<>();
/**
* Parse the given string (space separated ranges in CIDR or range notation) and return the
* resulting set or {@code null} if the string was invalid. An empty set is returned if the given string
* is {@code null}.
*/
public static IPRangeSet fromString(String ranges)
{
IPRangeSet set = new IPRangeSet();
if (ranges != null)
{
for (String range : ranges.split("\\s+"))
{
try
{
set.add(new IPRange(range));
}
catch (Exception unused)
{ /* besides due to invalid strings exceptions might get thrown if the string
* contains a hostname (NetworkOnMainThreadException) */
return null;
}
}
}
return set;
}
/**
* Add a range to this set. Automatically gets merged with existing ranges.
*/
public void add(IPRange range)
{
if (mRanges.contains(range))
{
return;
}
reinsert:
while (true)
{
Iterator<IPRange> iterator = mRanges.iterator();
while (iterator.hasNext())
{
IPRange existing = iterator.next();
IPRange replacement = existing.merge(range);
if (replacement != null)
{
iterator.remove();
range = replacement;
continue reinsert;
}
}
mRanges.add(range);
break;
}
}
/**
* Add all ranges from the given set.
*/
public void add(IPRangeSet ranges)
{
if (ranges == this)
{
return;
}
for (IPRange range : ranges.mRanges)
{
add(range);
}
}
/**
* Add all ranges from the given collection to this set.
*/
public void addAll(Collection<? extends IPRange> coll)
{
for (IPRange range : coll)
{
add(range);
}
}
/**
* Remove the given range from this set. Existing ranges are automatically adjusted.
*/
public void remove(IPRange range)
{
ArrayList <IPRange> additions = new ArrayList<>();
Iterator<IPRange> iterator = mRanges.iterator();
while (iterator.hasNext())
{
IPRange existing = iterator.next();
List<IPRange> result = existing.remove(range);
if (result.size() == 0)
{
iterator.remove();
}
else if (!result.get(0).equals(existing))
{
iterator.remove();
additions.addAll(result);
}
}
mRanges.addAll(additions);
}
/**
* Remove the given ranges from ranges in this set.
*/
public void remove(IPRangeSet ranges)
{
if (ranges == this)
{
mRanges.clear();
return;
}
for (IPRange range : ranges.mRanges)
{
remove(range);
}
}
/**
* Get all the subnets derived from all the ranges in this set.
*/
public Iterable<IPRange> subnets()
{
return new Iterable<IPRange>()
{
@Override
public Iterator<IPRange> iterator()
{
return new Iterator<IPRange>()
{
private Iterator<IPRange> mIterator = mRanges.iterator();
private List<IPRange> mSubnets;
@Override
public boolean hasNext()
{
return (mSubnets != null && mSubnets.size() > 0) || mIterator.hasNext();
}
@Override
public IPRange next()
{
if (mSubnets == null || mSubnets.size() == 0)
{
IPRange range = mIterator.next();
mSubnets = range.toSubnets();
}
return mSubnets.remove(0);
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
};
}
@Override
public Iterator<IPRange> iterator()
{
return mRanges.iterator();
}
/**
* Returns the number of ranges, not subnets.
*/
public int size()
{
return mRanges.size();
}
@Override
public String toString()
{ /* we could use TextUtils, but that causes the unit tests to fail */
StringBuilder sb = new StringBuilder();
for (IPRange range : mRanges)
{
if (sb.length() > 0)
{
sb.append(" ");
}
sb.append(range.toString());
}
return sb.toString();
}
}
@@ -31,7 +31,7 @@ public final class InetEndpoint {
private final boolean isResolved; private final boolean isResolved;
private final Object lock = new Object(); private final Object lock = new Object();
private final int port; private final int port;
private long lastResolution; private Instant lastResolution = Instant.EPOCH;
@Nullable private InetEndpoint resolved; @Nullable private InetEndpoint resolved;
private InetEndpoint(final String host, final boolean isResolved, final int port) { private InetEndpoint(final String host, final boolean isResolved, final int port) {
@@ -89,7 +89,7 @@ public final class InetEndpoint {
return Optional.of(this); return Optional.of(this);
synchronized (lock) { synchronized (lock) {
// TODO(zx2c4): Implement a real timeout mechanism using DNS TTL // TODO(zx2c4): Implement a real timeout mechanism using DNS TTL
if (System.currentTimeMillis() - lastResolution > 60000L) { if (Duration.between(lastResolution, Instant.now()).toMinutes() > 1) {
try { try {
// Prefer v4 endpoints over v6 to work around DNS64 and IPv6 NAT issues. // Prefer v4 endpoints over v6 to work around DNS64 and IPv6 NAT issues.
final InetAddress[] candidates = InetAddress.getAllByName(host); final InetAddress[] candidates = InetAddress.getAllByName(host);
@@ -101,7 +101,7 @@ public final class InetEndpoint {
} }
} }
resolved = new InetEndpoint(address.getHostAddress(), true, port); resolved = new InetEndpoint(address.getHostAddress(), true, port);
lastResolution = System.currentTimeMillis(); lastResolution = Instant.now();
} catch (final UnknownHostException e) { } catch (final UnknownHostException e) {
resolved = null; resolved = null;
} }
@@ -1,77 +0,0 @@
/*
* Copyright (C) 2014-2019 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package com.wireguard.config;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Utils
{
static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
/**
* Converts the given byte array to a hexadecimal string encoding.
*
* @param bytes byte array to convert
* @return hex string
*/
public static String bytesToHex(byte[] bytes)
{
char[] hex = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++)
{
int value = bytes[i];
hex[i*2] = HEXDIGITS[(value & 0xf0) >> 4];
hex[i*2+1] = HEXDIGITS[ value & 0x0f];
}
return new String(hex);
}
/**
* Validate the given proposal string
*
* @param ike true for IKE, false for ESP
* @param proposal proposal string
* @return true if valid
*/
public native static boolean isProposalValid(boolean ike, String proposal);
/**
* Parse an IP address without doing a name lookup
*
* @param address IP address string
* @return address bytes if valid
*/
private native static byte[] parseInetAddressBytes(String address);
/**
* Parse an IP address without doing a name lookup (as compared to InetAddress.fromName())
*
* @param address IP address string
* @return address if valid
* @throws UnknownHostException if address is invalid
*/
public static InetAddress parseInetAddress(String address) throws UnknownHostException
{
byte[] bytes = parseInetAddressBytes(address);
if (bytes == null)
{
throw new UnknownHostException();
}
return InetAddress.getByAddress(bytes);
}
}
@@ -12,7 +12,6 @@ import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Parcel import android.os.Parcel
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import org.amnezia.vpn.shadowsocks.core.R
import org.json.JSONObject import org.json.JSONObject
object NotificationUtil { object NotificationUtil {
@@ -103,7 +102,7 @@ object NotificationUtil {
val pendingIntent = PendingIntent.getActivity(service, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) val pendingIntent = PendingIntent.getActivity(service, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
// Build our notification // Build our notification
sNotificationBuilder?.let { sNotificationBuilder?.let {
it.setSmallIcon(R.drawable.ic_amnezia_round) it.setSmallIcon(org.amnezia.vpn.R.drawable.ic_amnezia_round)
.setContentTitle(header) .setContentTitle(header)
.setContentText(message) .setContentText(message)
.setOnlyAlertOnce(true) .setOnlyAlertOnce(true)
@@ -16,8 +16,6 @@ import com.wireguard.crypto.Key
import org.json.JSONObject import org.json.JSONObject
import java.util.Base64 import java.util.Base64
import com.wireguard.config.*
import net.openvpn.ovpn3.ClientAPI_Config import net.openvpn.ovpn3.ClientAPI_Config
import net.openvpn.ovpn3.ClientAPI_EvalConfig import net.openvpn.ovpn3.ClientAPI_EvalConfig
import net.openvpn.ovpn3.ClientAPI_Event import net.openvpn.ovpn3.ClientAPI_Event
@@ -74,8 +72,6 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
val jsonVpnConfig = mService.getVpnConfig() val jsonVpnConfig = mService.getVpnConfig()
val ovpnConfig = jsonVpnConfig.getJSONObject("openvpn_config_data").getString("config") val ovpnConfig = jsonVpnConfig.getJSONObject("openvpn_config_data").getString("config")
val splitTunnelType = jsonVpnConfig.getInt("splitTunnelType")
val splitTunnelSites = jsonVpnConfig.getJSONArray("splitTunnelSites")
val resultingConfig = StringBuilder() val resultingConfig = StringBuilder()
resultingConfig.append(ovpnConfig) resultingConfig.append(ovpnConfig)
@@ -83,32 +79,29 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
if (jsonVpnConfig.getString("protocol") == "cloak") { if (jsonVpnConfig.getString("protocol") == "cloak") {
val cloakConfigJson: JSONObject = jsonVpnConfig.getJSONObject("cloak_config_data") val cloakConfigJson: JSONObject = jsonVpnConfig.getJSONObject("cloak_config_data")
if (cloakConfigJson.has("NumConn")) { if (cloakConfigJson.keySet().contains("NumConn")) {
cloakConfigJson.put("NumConn", 1) cloakConfigJson.put("NumConn", 1)
} }
if (cloakConfigJson.has("ProxyMethod")) { if (cloakConfigJson.keySet().contains("ProxyMethod")) {
cloakConfigJson.put("ProxyMethod", "openvpn") cloakConfigJson.put("ProxyMethod", "openvpn")
} }
if (cloakConfigJson.has("port")) { if (cloakConfigJson.keySet().contains("port")) {
val portValue = cloakConfigJson.get("port") val portValue = cloakConfigJson.get("port")
cloakConfigJson.remove("port") cloakConfigJson.remove("port")
cloakConfigJson.put("RemotePort", portValue) cloakConfigJson.put("RemotePort", portValue)
} }
if (cloakConfigJson.has("remote")) { if (cloakConfigJson.keySet().contains("remote")) {
val hostValue = cloakConfigJson.get("remote") val hostValue = cloakConfigJson.get("remote")
cloakConfigJson.remove("remote") cloakConfigJson.remove("remote")
cloakConfigJson.put("RemoteHost", hostValue) cloakConfigJson.put("RemoteHost", hostValue)
} }
val cloakConfigData = jsonVpnConfig.getJSONObject("cloak_config_data").toString().toByteArray() val cloakConfig = Base64.getEncoder().encodeToString(
val cloakConfig = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { jsonVpnConfig.getJSONObject("cloak_config_data").toString().toByteArray()
Base64.getEncoder().encodeToString(cloakConfigData) )
} else {
android.util.Base64.encodeToString(cloakConfigData, android.util.Base64.DEFAULT)
}
resultingConfig.append("\n<cloak>\n") resultingConfig.append("\n<cloak>\n")
resultingConfig.append(cloakConfig) resultingConfig.append(cloakConfig)
@@ -122,7 +115,6 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
eval_config(config) eval_config(config)
val status = connect() val status = connect()
if (status.getError()) { if (status.getError()) {
Log.i(tag, "connect() error: " + status.getError() + ": " + status.getMessage()) Log.i(tag, "connect() error: " + status.getError() + ": " + status.getMessage())
} }
@@ -147,31 +139,6 @@ class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runna
override fun tun_builder_establish(): Int { override fun tun_builder_establish(): Int {
Log.v(tag, "tun_builder_establish") Log.v(tag, "tun_builder_establish")
val jsonVpnConfig = mService.getVpnConfig()
val splitTunnelType = jsonVpnConfig.getInt("splitTunnelType")
val splitTunnelSites = jsonVpnConfig.getJSONArray("splitTunnelSites")
if (splitTunnelType == 1) {
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
val ipRange = IPRange(site)
mService.addRoute(ipRange.getFrom().getHostAddress(), ipRange.getPrefix())
}
}
if (splitTunnelType == 2) {
val ipRangeSet = IPRangeSet.fromString("0.0.0.0/0")
ipRangeSet.remove(IPRange("127.0.0.0/8"))
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
ipRangeSet.remove(IPRange(site))
}
ipRangeSet.subnets().forEach {
mService.addRoute(it.getFrom().getHostAddress(), it.getPrefix())
Thread.sleep(10)
}
mService.addRoute("2000::", 3)
}
return mService.establish()!!.detachFd() return mService.establish()!!.detachFd()
} }
@@ -564,7 +564,6 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
return parseData return parseData
} }
/** /**
* Create a Wireguard [Config] from a [json] string - * Create a Wireguard [Config] from a [json] string -
* The [json] will be created in AndroidVpnProtocol.cpp * The [json] will be created in AndroidVpnProtocol.cpp
@@ -572,67 +571,29 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
private fun buildWireguardConfig(obj: JSONObject, type: String): Config { private fun buildWireguardConfig(obj: JSONObject, type: String): Config {
val confBuilder = Config.Builder() val confBuilder = Config.Builder()
val wireguardConfigData = obj.getJSONObject(type) val wireguardConfigData = obj.getJSONObject(type)
val splitTunnelType = obj.getInt("splitTunnelType")
val splitTunnelSites = obj.getJSONArray("splitTunnelSites")
val config = parseConfigData(wireguardConfigData.getString("config")) val config = parseConfigData(wireguardConfigData.getString("config"))
val peerBuilder = Peer.Builder() val peerBuilder = Peer.Builder()
val peerConfig = config["Peer"]!! val peerConfig = config["Peer"]!!
peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"])) peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"]))
peerConfig["PresharedKey"]?.let { peerBuilder.setPreSharedKey(Key.fromBase64(it)) } peerConfig["PresharedKey"]?.let {
peerBuilder.setPreSharedKey(Key.fromBase64(it))
val allIpString = peerConfig["AllowedIPs"]
var allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
/* default value in template */
if (allIpString == "0.0.0.0/0, ::/0") {
allowedIPList = emptyList()
} }
val allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
if (allowedIPList.isEmpty() && (splitTunnelType == 0)) { if (allowedIPList.isEmpty()) {
/* AllowedIP is empty and splitTunnel is turnoff */ val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
/* use VPN for whole Internet */ peerBuilder.addAllowedIp(internet)
val internetV4 = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
peerBuilder.addAllowedIp(internetV4)
val internetV6 = InetNetwork.parse("::/0") // aka The whole internet.
peerBuilder.addAllowedIp(internetV6)
} else { } else {
if (!allowedIPList.isEmpty()) { allowedIPList.forEach {
/* We have predefined AllowedIP in WG config */ val network = InetNetwork.parse(it.trim())
/* It's have higher priority than system SplitTunnel */ peerBuilder.addAllowedIp(network)
allowedIPList.forEach {
val network = InetNetwork.parse(it.trim())
peerBuilder.addAllowedIp(network)
}
} else {
if (splitTunnelType == 1) {
/* Use system SplitTunnel */
/* VPN connection used only for defined IPs */
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
val internet = InetNetwork.parse(site)
peerBuilder.addAllowedIp(internet)
}
}
if (splitTunnelType == 2) {
/* Use system SplitTunnel */
/* VPN connection used for all Internet exclude defined IPs */
val ipRangeSet = IPRangeSet.fromString("0.0.0.0/0")
ipRangeSet.remove(IPRange("127.0.0.0/8"))
for (i in 0 until splitTunnelSites.length()) {
val site = splitTunnelSites.getString(i)
ipRangeSet.remove(IPRange(site))
}
val allowedIps = ipRangeSet.subnets().joinToString(", ") + ", 2000::/3"
peerBuilder.parseAllowedIPs(allowedIps)
}
} }
} }
val endpointConfig = peerConfig["Endpoint"] val endpointConfig = peerConfig["Endpoint"]
val endpoint = InetEndpoint.parse(endpointConfig) val endpoint = InetEndpoint.parse(endpointConfig)
peerBuilder.setEndpoint(endpoint) peerBuilder.setEndpoint(endpoint)
peerConfig["PersistentKeepalive"]?.let { peerBuilder.setPersistentKeepalive(it.toInt()) } peerConfig["PersistentKeepalive"]?.let {
peerBuilder.setPersistentKeepalive(it.toInt())
}
confBuilder.addPeer(peerBuilder.build()) confBuilder.addPeer(peerBuilder.build())
val ifaceBuilder = Interface.Builder() val ifaceBuilder = Interface.Builder()
@@ -642,7 +603,7 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
ifaceConfig["DNS"]!!.split(",").forEach { ifaceConfig["DNS"]!!.split(",").forEach {
ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address) ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address)
} }
ifaceBuilder.parsePrivateKey(ifaceConfig["PrivateKey"]) ifaceBuilder.parsePrivateKey(ifaceConfig["PrivateKey"])
if (type == "awg_config_data") { if (type == "awg_config_data") {
ifaceBuilder.parseJc(ifaceConfig["Jc"]) ifaceBuilder.parseJc(ifaceConfig["Jc"])
@@ -663,13 +624,14 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
ifaceBuilder.parseH1("0") ifaceBuilder.parseH1("0")
ifaceBuilder.parseH2("0") ifaceBuilder.parseH2("0")
ifaceBuilder.parseH3("0") ifaceBuilder.parseH3("0")
ifaceBuilder.parseH4("0") ifaceBuilder.parseH4("0")
} }
/*val jExcludedApplication = obj.getJSONArray("excludedApps") /*val jExcludedApplication = obj.getJSONArray("excludedApps")
(0 until jExcludedApplication.length()).toList().forEach { (0 until jExcludedApplication.length()).toList().forEach {
val appName = jExcludedApplication.get(it).toString() val appName = jExcludedApplication.get(it).toString()
ifaceBuilder.excludeApplication(appName) ifaceBuilder.excludeApplication(appName)
}*/ }*/
confBuilder.setInterface(ifaceBuilder.build()) confBuilder.setInterface(ifaceBuilder.build())
return confBuilder.build() return confBuilder.build()
@@ -784,13 +746,13 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
private fun startWireGuard(type: String) { private fun startWireGuard(type: String) {
val wireguard_conf = buildWireguardConfig(mConfig!!, type + "_config_data") val wireguard_conf = buildWireguardConfig(mConfig!!, type + "_config_data")
Log.i(tag, "startWireGuard: wireguard_conf : $wireguard_conf")
if (currentTunnelHandle != -1) { if (currentTunnelHandle != -1) {
Log.e(tag, "Tunnel already up") Log.e(tag, "Tunnel already up")
// Turn the tunnel down because this might be a switch // Turn the tunnel down because this might be a switch
GoBackend.wgTurnOff(currentTunnelHandle) GoBackend.wgTurnOff(currentTunnelHandle)
} }
val wgConfig: String = wireguard_conf.toWgUserspaceString() val wgConfig: String = wireguard_conf.toWgUserspaceString()
val builder = Builder() val builder = Builder()
setupBuilder(wireguard_conf, builder) setupBuilder(wireguard_conf, builder)
builder.setSession("Amnezia") builder.setSession("Amnezia")
+33 -16
View File
@@ -1,20 +1,4 @@
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build") message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
set(APP_ANDROID_MIN_SDK 24)
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
"The minimum API level supported by the application or library" FORCE)
set_target_properties(${PROJECT} PROPERTIES
QT_ANDROID_VERSION_NAME ${CMAKE_PROJECT_VERSION}
QT_ANDROID_VERSION_CODE ${APP_ANDROID_VERSION_CODE}
QT_ANDROID_MIN_SDK_VERSION ${APP_ANDROID_MIN_SDK}
QT_ANDROID_TARGET_SDK_VERSION 34
QT_ANDROID_SDK_BUILD_TOOLS_REVISION 34.0.0
QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android
)
set(QT_ANDROID_MULTI_ABI_FORWARD_VARS "QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL;CMAKE_BUILD_TYPE")
# We need to include qtprivate api's # We need to include qtprivate api's
# As QAndroidBinder is not yet implemented with a public api # As QAndroidBinder is not yet implemented with a public api
set(LIBS ${LIBS} Qt6::CorePrivate) set(LIBS ${LIBS} Qt6::CorePrivate)
@@ -39,6 +23,39 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp ${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
) )
add_custom_command(
TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/android/AndroidManifest.xml
${CMAKE_CURRENT_SOURCE_DIR}/android/build.gradle
${CMAKE_CURRENT_SOURCE_DIR}/android/gradle/wrapper/gradle-wrapper.jar
${CMAKE_CURRENT_SOURCE_DIR}/android/gradle/wrapper/gradle-wrapper.properties
${CMAKE_CURRENT_SOURCE_DIR}/android/gradlew
${CMAKE_CURRENT_SOURCE_DIR}/android/gradlew.bat
${CMAKE_CURRENT_SOURCE_DIR}/android/gradle.properties
${CMAKE_CURRENT_SOURCE_DIR}/android/res/values/libs.xml
${CMAKE_CURRENT_SOURCE_DIR}/android/res/xml/fileprovider.xml
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/AuthHelper.java
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/IPCContract.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/NotificationUtil.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/OpenVPNThreadv3.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/Prefs.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/VPNLogger.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/VPNService.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/VPNServiceBinder.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/AmneziaApp.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/PackageManagerHelper.java
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/VPNActivity.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/VPNClientBinder.kt
${CMAKE_CURRENT_SOURCE_DIR}/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt
${CMAKE_CURRENT_BINARY_DIR}
)
set_property(TARGET ${PROJECT} PROPERTY
QT_ANDROID_PACKAGE_SOURCE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/android
)
foreach(abi IN ITEMS ${QT_ANDROID_ABIS}) foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
set_property(TARGET ${PROJECT} PROPERTY QT_ANDROID_EXTRA_LIBS set_property(TARGET ${PROJECT} PROPERTY QT_ANDROID_EXTRA_LIBS
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/android/${abi}/libwg.so ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/android/${abi}/libwg.so
+26 -14
View File
@@ -17,31 +17,43 @@ QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials,
QString config = WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, errorCode); QString config = WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, errorCode);
QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object(); QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object();
QString awgConfig = jsonConfig.value(config_key::config).toString();
QMap<QString, QString> configMap; ServerController serverController(m_settings);
auto configLines = awgConfig.split("\n"); QString serverConfig = serverController.getTextFileFromContainer(container, credentials, protocols::awg::serverConfigPath, errorCode);
for (auto &line : configLines) {
QMap<QString, QString> serverConfigMap;
auto serverConfigLines = serverConfig.split("\n");
for (auto &line : serverConfigLines) {
auto trimmedLine = line.trimmed(); auto trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) { if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
continue; continue;
} else { } else {
QStringList parts = trimmedLine.split(" = "); QStringList parts = trimmedLine.split(" = ");
if (parts.count() == 2) { if (parts.count() == 2) {
configMap.insert(parts[0].trimmed(), parts[1].trimmed()); serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed());
} }
} }
} }
jsonConfig[config_key::junkPacketCount] = configMap.value(config_key::junkPacketCount); config.replace("$JUNK_PACKET_COUNT", serverConfigMap.value(config_key::junkPacketCount));
jsonConfig[config_key::junkPacketMinSize] = configMap.value(config_key::junkPacketMinSize); config.replace("$JUNK_PACKET_MIN_SIZE", serverConfigMap.value(config_key::junkPacketMinSize));
jsonConfig[config_key::junkPacketMaxSize] = configMap.value(config_key::junkPacketMaxSize); config.replace("$JUNK_PACKET_MAX_SIZE", serverConfigMap.value(config_key::junkPacketMaxSize));
jsonConfig[config_key::initPacketJunkSize] = configMap.value(config_key::initPacketJunkSize); config.replace("$INIT_PACKET_JUNK_SIZE", serverConfigMap.value(config_key::initPacketJunkSize));
jsonConfig[config_key::responsePacketJunkSize] = configMap.value(config_key::responsePacketJunkSize); config.replace("$RESPONSE_PACKET_JUNK_SIZE", serverConfigMap.value(config_key::responsePacketJunkSize));
jsonConfig[config_key::initPacketMagicHeader] = configMap.value(config_key::initPacketMagicHeader); config.replace("$INIT_PACKET_MAGIC_HEADER", serverConfigMap.value(config_key::initPacketMagicHeader));
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader); config.replace("$RESPONSE_PACKET_MAGIC_HEADER", serverConfigMap.value(config_key::responsePacketMagicHeader));
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader); config.replace("$UNDERLOAD_PACKET_MAGIC_HEADER", serverConfigMap.value(config_key::underloadPacketMagicHeader));
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader); config.replace("$TRANSPORT_PACKET_MAGIC_HEADER", serverConfigMap.value(config_key::transportPacketMagicHeader));
jsonConfig[config_key::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount);
jsonConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize);
jsonConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize);
jsonConfig[config_key::initPacketJunkSize] = serverConfigMap.value(config_key::initPacketJunkSize);
jsonConfig[config_key::responsePacketJunkSize] = serverConfigMap.value(config_key::responsePacketJunkSize);
jsonConfig[config_key::initPacketMagicHeader] = serverConfigMap.value(config_key::initPacketMagicHeader);
jsonConfig[config_key::responsePacketMagicHeader] = serverConfigMap.value(config_key::responsePacketMagicHeader);
jsonConfig[config_key::underloadPacketMagicHeader] = serverConfigMap.value(config_key::underloadPacketMagicHeader);
jsonConfig[config_key::transportPacketMagicHeader] = serverConfigMap.value(config_key::transportPacketMagicHeader);
return QJsonDocument(jsonConfig).toJson(); return QJsonDocument(jsonConfig).toJson();
} }
@@ -131,13 +131,10 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig)
config.append("block-ipv6\n"); config.append("block-ipv6\n");
} }
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
// no redirect-gateway // no redirect-gateway
} }
if (m_settings->routeMode() == Settings::VpnAllExceptSites) { if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#ifndef Q_OS_ANDROID
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n"); config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
#endif
// Prevent ipv6 leak // Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n"); config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
config.append("block-ipv6\n"); config.append("block-ipv6\n");
+5 -30
View File
@@ -167,8 +167,11 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
return ErrorCode::ServerContainerMissingError; return ErrorCode::ServerContainerMissingError;
} }
runScript(credentials, runScript(credentials,
replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container))); replaceVars(QString("sudo shred %1").arg(tmpFileName), genVarsForScript(credentials, container)));
runScript(credentials, replaceVars(QString("sudo rm %1").arg(tmpFileName), genVarsForScript(credentials, container)));
return e; return e;
} }
@@ -831,34 +834,6 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential
containerConfig.insert(config_key::port, port); containerConfig.insert(config_key::port, port);
containerConfig.insert(config_key::transport_proto, transportProto); containerConfig.insert(config_key::transport_proto, transportProto);
if (protocol == Proto::Awg) {
QString serverConfig = getTextFileFromContainer(container, credentials, protocols::awg::serverConfigPath, &errorCode);
QMap<QString, QString> serverConfigMap;
auto serverConfigLines = serverConfig.split("\n");
for (auto &line : serverConfigLines) {
auto trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
continue;
} else {
QStringList parts = trimmedLine.split(" = ");
if (parts.count() == 2) {
serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed());
}
}
}
containerConfig[config_key::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount);
containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize);
containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize);
containerConfig[config_key::initPacketJunkSize] = serverConfigMap.value(config_key::initPacketJunkSize);
containerConfig[config_key::responsePacketJunkSize] = serverConfigMap.value(config_key::responsePacketJunkSize);
containerConfig[config_key::initPacketMagicHeader] = serverConfigMap.value(config_key::initPacketMagicHeader);
containerConfig[config_key::responsePacketMagicHeader] = serverConfigMap.value(config_key::responsePacketMagicHeader);
containerConfig[config_key::underloadPacketMagicHeader] = serverConfigMap.value(config_key::underloadPacketMagicHeader);
containerConfig[config_key::transportPacketMagicHeader] = serverConfigMap.value(config_key::transportPacketMagicHeader);
}
config.insert(config_key::container, ContainerProps::containerToString(container)); config.insert(config_key::container, ContainerProps::containerToString(container));
} }
config.insert(ProtocolProps::protoToString(protocol), containerConfig); config.insert(ProtocolProps::protoToString(protocol), containerConfig);
+10 -70
View File
@@ -115,12 +115,8 @@ void LocalSocketController::daemonConnected() {
} }
void LocalSocketController::activate(const QJsonObject &rawConfig) { void LocalSocketController::activate(const QJsonObject &rawConfig) {
QString protocolName = rawConfig.value("protocol").toString(); QString protocolName = rawConfig.value("protocol").toString();
int splitTunnelType = rawConfig.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = rawConfig.value("splitTunnelSites").toArray();
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject(); QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
QJsonObject json; QJsonObject json;
@@ -141,79 +137,23 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
QJsonArray jsAllowedIPAddesses; QJsonArray jsAllowedIPAddesses;
QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray(); QJsonObject range_ipv4;
QJsonArray defaultAllowedIP = QJsonArray::fromStringList(QString("0.0.0.0/0, ::/0").split(",")); range_ipv4.insert("address", "0.0.0.0");
range_ipv4.insert("range", 0);
range_ipv4.insert("isIpv6", false);
jsAllowedIPAddesses.append(range_ipv4);
if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) { QJsonObject range_ipv6;
// Use AllowedIP list from WG config bacouse of higer priority range_ipv6.insert("address", "::");
range_ipv6.insert("range", 0);
for (auto v : plainAllowedIP) { range_ipv6.insert("isIpv6", true);
QString ipRange = v.toString(); jsAllowedIPAddesses.append(range_ipv6);
qDebug() << "ipRange " << ipRange;
if (ipRange.split('/').size() > 1){
QJsonObject range;
range.insert("address", ipRange.split('/')[0]);
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
} else {
QJsonObject range;
range.insert("address",ipRange);
range.insert("range", 32);
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
}
}
} else {
// Use APP split tunnel
if (splitTunnelType == 0 || splitTunnelType == 2) {
QJsonObject range_ipv4;
range_ipv4.insert("address", "0.0.0.0");
range_ipv4.insert("range", 0);
range_ipv4.insert("isIpv6", false);
jsAllowedIPAddesses.append(range_ipv4);
QJsonObject range_ipv6;
range_ipv6.insert("address", "::");
range_ipv6.insert("range", 0);
range_ipv6.insert("isIpv6", true);
jsAllowedIPAddesses.append(range_ipv6);
}
if (splitTunnelType == 1) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
qDebug() << "ipRange " << ipRange;
if (ipRange.split('/').size() > 1){
QJsonObject range;
range.insert("address", ipRange.split('/')[0]);
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
} else {
QJsonObject range;
range.insert("address",ipRange);
range.insert("range", 32);
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
}
}
}
}
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses); json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
QJsonArray jsExcludedAddresses; QJsonArray jsExcludedAddresses;
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName)); jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
jsExcludedAddresses.append(ipRange);
}
}
json.insert("excludedAddresses", jsExcludedAddresses); json.insert("excludedAddresses", jsExcludedAddresses);
-2
View File
@@ -28,8 +28,6 @@ struct MessageKey
static const char *host; static const char *host;
static const char *port; static const char *port;
static const char *isOnDemand; static const char *isOnDemand;
static const char *SplitTunnelType;
static const char *SplitTunnelSites;
}; };
class IosController : public QObject class IosController : public QObject
+1 -18
View File
@@ -29,9 +29,6 @@ const char* MessageKey::errorCode = "errorCode";
const char* MessageKey::host = "host"; const char* MessageKey::host = "host";
const char* MessageKey::port = "port"; const char* MessageKey::port = "port";
const char* MessageKey::isOnDemand = "is-on-demand"; const char* MessageKey::isOnDemand = "is-on-demand";
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) { Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
switch (status) { switch (status) {
@@ -354,13 +351,6 @@ void IosController::startTunnel()
{ {
m_rxBytes = 0; m_rxBytes = 0;
m_txBytes = 0; m_txBytes = 0;
int STT = m_rawConfig["splitTunnelType"].toInt();
QJsonArray splitTunnelSites = m_rawConfig["splitTunnelSites"].toArray();
QJsonDocument doc;
doc.setArray(splitTunnelSites);
QString STS(doc.toJson());
[m_currentTunnel setEnabled:YES]; [m_currentTunnel setEnabled:YES];
[m_currentTunnel saveToPreferencesWithCompletionHandler:^(NSError *saveError) { [m_currentTunnel saveToPreferencesWithCompletionHandler:^(NSError *saveError) {
@@ -386,15 +376,8 @@ void IosController::startTunnel()
NSString *actionValue = [NSString stringWithUTF8String:Action::start]; NSString *actionValue = [NSString stringWithUTF8String:Action::start];
NSString *tunnelIdKey = [NSString stringWithUTF8String:MessageKey::tunnelId]; NSString *tunnelIdKey = [NSString stringWithUTF8String:MessageKey::tunnelId];
NSString *tunnelIdValue = !m_tunnelId.isEmpty() ? m_tunnelId.toNSString() : @""; NSString *tunnelIdValue = !m_tunnelId.isEmpty() ? m_tunnelId.toNSString() : @"";
NSString *SplitTunnelTypeKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelType];
NSString *SplitTunnelTypeValue = [NSString stringWithFormat:@"%d",STT];
NSString *SplitTunnelSitesKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelSites];
NSString *SplitTunnelSitesValue = STS.toNSString();
NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue, NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue};
SplitTunnelTypeKey: SplitTunnelTypeValue, SplitTunnelSitesKey: SplitTunnelSitesValue};
sendVpnExtensionMessage(message); sendVpnExtensionMessage(message);
+75 -174
View File
@@ -15,7 +15,7 @@ struct Constants {
static let ovpnConfigKey = "ovpn" static let ovpnConfigKey = "ovpn"
static let wireGuardConfigKey = "wireguard" static let wireGuardConfigKey = "wireguard"
static let loggerTag = "NET" static let loggerTag = "NET"
static let kActionStart = "start" static let kActionStart = "start"
static let kActionRestart = "restart" static let kActionRestart = "restart"
static let kActionStop = "stop" static let kActionStop = "stop"
@@ -29,8 +29,6 @@ struct Constants {
static let kMessageKeyHost = "host" static let kMessageKeyHost = "host"
static let kMessageKeyPort = "port" static let kMessageKeyPort = "port"
static let kMessageKeyOnDemand = "is-on-demand" static let kMessageKeyOnDemand = "is-on-demand"
static let kMessageKeySplitTunnelType = "SplitTunnelType"
static let kMessageKeySplitTunnelSites = "SplitTunnelSites"
} }
class PacketTunnelProvider: NEPacketTunnelProvider { class PacketTunnelProvider: NEPacketTunnelProvider {
@@ -40,7 +38,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
wg_log(logLevel.osLogLevel, message: message) wg_log(logLevel.osLogLevel, message: message)
} }
}() }()
private lazy var ovpnAdapter: OpenVPNAdapter = { private lazy var ovpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter() let adapter = OpenVPNAdapter()
adapter.delegate = self adapter.delegate = self
@@ -51,11 +49,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility) private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility)
private var openVPNConfig: Data? = nil private var openVPNConfig: Data? = nil
private var SplitTunnelType: String? = nil
private var SplitTunnelSites: String? = nil
let vpnReachability = OpenVPNReachability() let vpnReachability = OpenVPNReachability()
var startHandler: ((Error?) -> Void)? var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)? var stopHandler: (() -> Void)?
var protoType: TunnelProtoType = .none var protoType: TunnelProtoType = .none
@@ -67,34 +63,26 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
} }
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
let tmpStr = String(data: messageData, encoding: .utf8)!
wg_log(.error, message: tmpStr)
guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else { guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else {
Logger.global?.log(message: "Failed to serialize message from app") Logger.global?.log(message: "Failed to serialize message from app")
return return
} }
guard let completionHandler = completionHandler else { guard let completionHandler = completionHandler else {
Logger.global?.log(message: "Missing message completion handler") Logger.global?.log(message: "Missing message completion handler")
return return
} }
guard let action = message[Constants.kMessageKeyAction] as? String else { guard let action = message[Constants.kMessageKeyAction] as? String else {
Logger.global?.log(message: "Missing action key in app message") Logger.global?.log(message: "Missing action key in app message")
completionHandler(nil) completionHandler(nil)
return return
} }
if action == Constants.kActionStatus { if action == Constants.kActionStatus {
handleStatusAppMessage(messageData, completionHandler: completionHandler) handleStatusAppMessage(messageData, completionHandler: completionHandler)
} }
if action == Constants.kActionStart {
SplitTunnelType = message[Constants.kMessageKeySplitTunnelType] as? String
SplitTunnelSites = message[Constants.kMessageKeySplitTunnelSites] as? String
}
let callbackWrapper: (NSNumber?) -> Void = { errorCode in let callbackWrapper: (NSNumber?) -> Void = { errorCode in
//let tunnelId = self.tunnelConfig?.id ?? "" //let tunnelId = self.tunnelConfig?.id ?? ""
let response: [String: Any] = [ let response: [String: Any] = [
@@ -102,11 +90,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
Constants.kMessageKeyErrorCode: errorCode ?? NSNull(), Constants.kMessageKeyErrorCode: errorCode ?? NSNull(),
Constants.kMessageKeyTunnelId: 0 Constants.kMessageKeyTunnelId: 0
] ]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: [])) completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
} }
} }
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) { override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
dispatchQueue.async { dispatchQueue.async {
let activationAttemptId = options?[Constants.kActivationAttemptId] as? String let activationAttemptId = options?[Constants.kActivationAttemptId] as? String
@@ -130,8 +118,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
switch self.protoType { switch self.protoType {
case .wireguard: case .wireguard:
self.startWireguard(activationAttemptId: activationAttemptId, self.startWireguard(activationAttemptId: activationAttemptId,
errorNotifier: errorNotifier, errorNotifier: errorNotifier,
completionHandler: completionHandler) completionHandler: completionHandler)
case .openvpn: case .openvpn:
self.startOpenVPN(completionHandler: completionHandler) self.startOpenVPN(completionHandler: completionHandler)
case .shadowsocks: case .shadowsocks:
@@ -168,7 +156,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler) handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
case .shadowsocks: case .shadowsocks:
break break
// handleShadowSocksAppMessage(messageData, completionHandler: completionHandler) // handleShadowSocksAppMessage(messageData, completionHandler: completionHandler)
case .none: case .none:
break break
@@ -180,13 +168,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
errorNotifier: ErrorNotifier, errorNotifier: ErrorNotifier,
completionHandler: @escaping (Error?) -> Void) { completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol, guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration, let providerConfiguration = protocolConfiguration.providerConfiguration,
let wgConfig: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else { let wgConfig: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
wg_log(.error, message: "Can't start WireGuard config missing") wg_log(.error, message: "Can't start WireGuard config missing")
completionHandler(nil) completionHandler(nil)
return return
} }
let wgConfigStr = String(data: wgConfig, encoding: .utf8)! let wgConfigStr = String(data: wgConfig, encoding: .utf8)!
@@ -195,49 +182,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil) completionHandler(nil)
return return
} }
if (tunnelConfiguration.peers.first!.allowedIPs.map { $0.stringRepresentation }.joined(separator: ", ") == "0.0.0.0/0, ::/0") {
if (SplitTunnelType == "1") {
for index in tunnelConfiguration.peers.indices {
tunnelConfiguration.peers[index].allowedIPs.removeAll()
var allowedIPs = [IPAddressRange]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSArray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for allowedIPString in STSArray {
if let allowedIP = IPAddressRange(from: allowedIPString) {
allowedIPs.append(allowedIP)
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
tunnelConfiguration.peers[index].allowedIPs = allowedIPs
}
} else {
if (SplitTunnelType == "2")
{
for index in tunnelConfiguration.peers.indices {
var excludeIPs = [IPAddressRange]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for excludeIPString in STSarray {
if let excludeIP = IPAddressRange(from: excludeIPString) {
excludeIPs.append(excludeIP)
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
tunnelConfiguration.peers[index].excludeIPs = excludeIPs
}
}
}
}
wg_log(.info, message: "Starting wireguard tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app")) wg_log(.info, message: "Starting wireguard tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
// Start the tunnel // Start the tunnel
@@ -248,30 +193,30 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil) completionHandler(nil)
return return
} }
switch adapterError { switch adapterError {
case .cannotLocateTunnelFileDescriptor: case .cannotLocateTunnelFileDescriptor:
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor") wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor) errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor) completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
case .dnsResolution(let dnsErrors): case .dnsResolution(let dnsErrors):
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address } let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
.joined(separator: ", ") .joined(separator: ", ")
wg_log(.error, message: "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)") wg_log(.error, message: "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure) errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
completionHandler(PacketTunnelProviderError.dnsResolutionFailure) completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
case .setNetworkSettings(let error): case .setNetworkSettings(let error):
wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)") wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings) errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings) completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
case .startWireGuardBackend(let errorCode): case .startWireGuardBackend(let errorCode):
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)") wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend) errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
completionHandler(PacketTunnelProviderError.couldNotStartBackend) completionHandler(PacketTunnelProviderError.couldNotStartBackend)
case .invalidState: case .invalidState:
// Must never happen // Must never happen
fatalError() fatalError()
@@ -281,27 +226,27 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) { private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol, guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration, let providerConfiguration = protocolConfiguration.providerConfiguration,
let ovpnConfiguration: Data = providerConfiguration[Constants.ovpnConfigKey] as? Data else { let ovpnConfiguration: Data = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
// TODO: handle errors properly // TODO: handle errors properly
wg_log(.error, message: "Can't start startOpenVPN()") wg_log(.error, message: "Can't start startOpenVPN()")
return return
} }
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler) setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
} }
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
wg_log(.info, staticMessage: "Stopping tunnel") wg_log(.info, staticMessage: "Stopping tunnel")
wgAdapter.stop { error in wgAdapter.stop { error in
ErrorNotifier.removeLastErrorFile() ErrorNotifier.removeLastErrorFile()
if let error = error { if let error = error {
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)") wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
} }
completionHandler() completionHandler()
#if os(macOS) #if os(macOS)
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107). // HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
// Remove it when they finally fix this upstream and the fix has been rolled out to // Remove it when they finally fix this upstream and the fix has been rolled out to
@@ -318,7 +263,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
} }
ovpnAdapter.disconnect() ovpnAdapter.disconnect()
} }
func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return } guard let completionHandler = completionHandler else { return }
wgAdapter.getRuntimeConfiguration { settings in wgAdapter.getRuntimeConfiguration { settings in
@@ -333,8 +278,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
for component in components{ for component in components{
let pair = component.components(separatedBy: "=") let pair = component.components(separatedBy: "=")
if pair.count == 2 { if pair.count == 2 {
settingsDictionary[pair[0]] = pair[1] settingsDictionary[pair[0]] = pair[1]
} }
} }
let response: [String: Any] = [ let response: [String: Any] = [
@@ -364,7 +309,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil) completionHandler(nil)
return return
} }
do { do {
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString) let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
@@ -373,7 +318,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil) completionHandler(nil)
return return
} }
self.wgAdapter.getRuntimeConfiguration { settings in self.wgAdapter.getRuntimeConfiguration { settings in
var data: Data? var data: Data?
if let settings = settings { if let settings = settings {
@@ -389,76 +334,76 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil) completionHandler(nil)
} }
} }
private func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { private func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return } guard let completionHandler = completionHandler else { return }
let bytesin = ovpnAdapter.transportStatistics.bytesIn let bytesin = ovpnAdapter.transportStatistics.bytesIn
let bytesout = ovpnAdapter.transportStatistics.bytesOut let bytesout = ovpnAdapter.transportStatistics.bytesOut
let response: [String: Any] = [ let response: [String: Any] = [
"rx_bytes" : bytesin, "rx_bytes" : bytesin,
"tx_bytes" : bytesout "tx_bytes" : bytesout
] ]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: [])) completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
} }
// TODO review // TODO review
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data, withShadowSocks viaSS: Bool = false, completionHandler: @escaping (Error?) -> Void) { private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data, withShadowSocks viaSS: Bool = false, completionHandler: @escaping (Error?) -> Void) {
wg_log(.info, message: "setupAndlaunchOpenVPN") wg_log(.info, message: "setupAndlaunchOpenVPN")
let str = String(decoding: ovpnConfiguration, as: UTF8.self) let str = String(decoding: ovpnConfiguration, as: UTF8.self)
let configuration = OpenVPNConfiguration() let configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnConfiguration configuration.fileContent = ovpnConfiguration
if(str.contains("cloak")){ if(str.contains("cloak")){
configuration.setPTCloak(); configuration.setPTCloak();
} }
let evaluation: OpenVPNConfigurationEvaluation let evaluation: OpenVPNConfigurationEvaluation
do { do {
evaluation = try ovpnAdapter.apply(configuration: configuration) evaluation = try ovpnAdapter.apply(configuration: configuration)
} catch { } catch {
completionHandler(error) completionHandler(error)
return return
} }
if !evaluation.autologin { if !evaluation.autologin {
wg_log(.info, message: "Implement login with user credentials") wg_log(.info, message: "Implement login with user credentials")
} }
vpnReachability.startTracking { [weak self] status in vpnReachability.startTracking { [weak self] status in
guard status == .reachableViaWiFi else { return } guard status == .reachableViaWiFi else { return }
self?.ovpnAdapter.reconnect(afterTimeInterval: 5) self?.ovpnAdapter.reconnect(afterTimeInterval: 5)
} }
startHandler = completionHandler startHandler = completionHandler
ovpnAdapter.connect(using: packetFlow) ovpnAdapter.connect(using: packetFlow)
// let ifaces = Interface.allInterfaces() // let ifaces = Interface.allInterfaces()
// .filter { $0.family == .ipv4 } // .filter { $0.family == .ipv4 }
// .map { iface in iface.name } // .map { iface in iface.name }
// wg_log(.error, message: "Available TUN Interfaces: \(ifaces)") // wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
} }
// MARK: -- Network observing methods // MARK: -- Network observing methods
private func startListeningForNetworkChanges() { private func startListeningForNetworkChanges() {
stopListeningForNetworkChanges() stopListeningForNetworkChanges()
addObserver(self, forKeyPath: Constants.kDefaultPathKey, options: .old, context: nil) addObserver(self, forKeyPath: Constants.kDefaultPathKey, options: .old, context: nil)
} }
private func stopListeningForNetworkChanges() { private func stopListeningForNetworkChanges() {
removeObserver(self, forKeyPath: Constants.kDefaultPathKey) removeObserver(self, forKeyPath: Constants.kDefaultPathKey)
} }
override func observeValue(forKeyPath keyPath: String?, override func observeValue(forKeyPath keyPath: String?,
of object: Any?, of object: Any?,
change: [NSKeyValueChangeKey : Any]?, change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) { context: UnsafeMutableRawPointer?) {
guard Constants.kDefaultPathKey != keyPath else { return } guard Constants.kDefaultPathKey != keyPath else { return }
// Since iOS 11, we have observed that this KVO event fires repeatedly when connecting over Wifi, // Since iOS 11, we have observed that this KVO event fires repeatedly when connecting over Wifi,
// even though the underlying network has not changed (i.e. `isEqualToPath` returns false), // even though the underlying network has not changed (i.e. `isEqualToPath` returns false),
@@ -467,28 +412,28 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
guard let lastPath: NWPath = change?[.oldKey] as? NWPath, guard let lastPath: NWPath = change?[.oldKey] as? NWPath,
let defPath = defaultPath, let defPath = defaultPath,
lastPath != defPath || lastPath.description != defPath.description else { lastPath != defPath || lastPath.description != defPath.description else {
return return
} }
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let `self` = self, self.defaultPath != nil else { return } guard let `self` = self, self.defaultPath != nil else { return }
self.handle(networkChange: self.defaultPath!) { _ in } self.handle(networkChange: self.defaultPath!) { _ in }
} }
} }
private func handle(networkChange changePath: NWPath, completion: @escaping (Error?) -> Void) { private func handle(networkChange changePath: NWPath, completion: @escaping (Error?) -> Void) {
wg_log(.info, message: "Tunnel restarted.") wg_log(.info, message: "Tunnel restarted.")
startTunnel(options: nil, completionHandler: completion) startTunnel(options: nil, completionHandler: completion)
} }
private func startEmptyTunnel(completionHandler: @escaping (Error?) -> Void) { private func startEmptyTunnel(completionHandler: @escaping (Error?) -> Void) {
dispatchPrecondition(condition: .onQueue(dispatchQueue)) dispatchPrecondition(condition: .onQueue(dispatchQueue))
let emptyTunnelConfiguration = TunnelConfiguration( let emptyTunnelConfiguration = TunnelConfiguration(
name: nil, name: nil,
interface: InterfaceConfiguration(privateKey: PrivateKey()), interface: InterfaceConfiguration(privateKey: PrivateKey()),
peers: [] peers: []
) )
wgAdapter.start(tunnelConfiguration: emptyTunnelConfiguration) { error in wgAdapter.start(tunnelConfiguration: emptyTunnelConfiguration) { error in
self.dispatchQueue.async { self.dispatchQueue.async {
if let error { if let error {
@@ -500,9 +445,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
} }
} }
} }
let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1") let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1")
self.setTunnelNetworkSettings(settings) { error in self.setTunnelNetworkSettings(settings) { error in
completionHandler(error) completionHandler(error)
} }
@@ -533,50 +478,6 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
// send empty string to NEDNSSettings.matchDomains // send empty string to NEDNSSettings.matchDomains
networkSettings?.dnsSettings?.matchDomains = [""] networkSettings?.dnsSettings?.matchDomains = [""]
if (SplitTunnelType == "1") {
var ipv4IncludedRoutes = [NEIPv4Route]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for allowedIPString in STSarray {
if let allowedIP = IPAddressRange(from: allowedIPString){
ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(allowedIP.address)", subnetMask: "\(allowedIP.subnetMask())"))
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
} else {
if (SplitTunnelType == "2")
{
var ipv4ExcludedRoutes = [NEIPv4Route]()
var ipv4IncludedRoutes = [NEIPv4Route]()
var ipv6IncludedRoutes = [NEIPv6Route]()
let STSdata = Data(SplitTunnelSites!.utf8)
do {
let STSarray = try JSONSerialization.jsonObject(with: STSdata) as! [String]
for excludeIPString in STSarray {
if let excludeIP = IPAddressRange(from: excludeIPString) {
ipv4ExcludedRoutes.append(NEIPv4Route(destinationAddress: "\(excludeIP.address)", subnetMask: "\(excludeIP.subnetMask())"))
}
}
} catch {
wg_log(.error,message: "Parse JSONSerialization Error")
}
if let allIPv4 = IPAddressRange(from: "0.0.0.0/0"){
ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(allIPv4.address)", subnetMask: "\(allIPv4.subnetMask())"))
}
if let allIPv6 = IPAddressRange(from: "::/0") {
ipv6IncludedRoutes.append(NEIPv6Route(destinationAddress: "\(allIPv6.address)", networkPrefixLength: NSNumber(value: allIPv6.networkPrefixLength)))
}
networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
networkSettings?.ipv6Settings?.includedRoutes = ipv6IncludedRoutes
networkSettings?.ipv4Settings?.excludedRoutes = ipv4ExcludedRoutes
}
}
// Set the network settings for the current tunneling session. // Set the network settings for the current tunneling session.
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler) setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
} }
@@ -637,7 +538,7 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
wg_log(.info, message: logMessage) wg_log(.info, message: logMessage)
} }
} }
extension WireGuardLogLevel { extension WireGuardLogLevel {
var osLogLevel: OSLogType { var osLogLevel: OSLogType {
switch self { switch self {
@@ -158,15 +158,15 @@ bool LinuxRouteMonitor::rtmSendRoute(int action, int flags, int type,
return false; return false;
} }
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index); nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index);
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 1);
} }
if (rtm->rtm_type == RTN_THROW) { if (rtm->rtm_type == RTN_THROW) {
struct in_addr ip4; int index = if_nametoindex(getgatewayandiface().toUtf8());
inet_pton(AF_INET, getgatewayandiface().toUtf8(), &ip4); if (index <= 0) {
nlmsg_append_attr(nlmsg, sizeof(buf), RTA_GATEWAY, &ip4, sizeof(ip4)); logger.error() << "if_nametoindex() failed:" << strerror(errno);
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 0); return false;
rtm->rtm_type = RTN_UNICAST; }
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index);
} }
struct sockaddr_nl nladdr; struct sockaddr_nl nladdr;
@@ -334,7 +334,7 @@ QString LinuxRouteMonitor::getgatewayandiface()
} }
} }
close(sock); close(sock);
return gateway_address; return interface;
} }
static bool buildAllowedIp(wg_allowedip* ip, static bool buildAllowedIp(wg_allowedip* ip,
@@ -236,17 +236,6 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
} }
} }
if (!config.m_excludedAddresses.empty()) {
for (const QString& i : config.m_excludedAddresses) {
logger.debug() << "range: " << i;
if (!allowTrafficToRange(i, HIGH_WEIGHT,
"Allow Ecxlude route", config.m_serverPublicKey)) {
return false;
}
}
}
result = FwpmTransactionCommit0(m_sessionHandle); result = FwpmTransactionCommit0(m_sessionHandle);
if (result != ERROR_SUCCESS) { if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionCommit0 failed with error:" << result; logger.error() << "FwpmTransactionCommit0 failed with error:" << result;
@@ -422,8 +411,8 @@ bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
} }
bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port, bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
int weight, const QString& title, int weight, const QString& title,
const QString& peer) { const QString& peer) {
bool isIPv4 = targetIP.protocol() == QAbstractSocket::IPv4Protocol; bool isIPv4 = targetIP.protocol() == QAbstractSocket::IPv4Protocol;
GUID layerOut = GUID layerOut =
isIPv4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6; isIPv4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
@@ -484,57 +473,6 @@ bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
return true; return true;
} }
bool WindowsFirewall::allowTrafficToRange(const IPAddress& addr, uint8_t weight,
const QString& title,
const QString& peer) {
QString description("Allow traffic %1 %2 ");
auto lower = addr.address();
auto upper = addr.broadcastAddress();
const bool isV4 = addr.type() == QAbstractSocket::IPv4Protocol;
const GUID layerKeyOut =
isV4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
const GUID layerKeyIn = isV4 ? FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
: FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
// Assemble the Filter base
FWPM_FILTER0 filter;
memset(&filter, 0, sizeof(filter));
filter.action.type = FWP_ACTION_PERMIT;
filter.weight.type = FWP_UINT8;
filter.weight.uint8 = weight;
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
FWPM_FILTER_CONDITION0 cond[1] = {0};
FWP_RANGE0 ipRange;
QByteArray lowIpV6Buffer;
QByteArray highIpV6Buffer;
importAddress(lower, ipRange.valueLow, &lowIpV6Buffer);
importAddress(upper, ipRange.valueHigh, &highIpV6Buffer);
cond[0].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
cond[0].matchType = FWP_MATCH_RANGE;
cond[0].conditionValue.type = FWP_RANGE_TYPE;
cond[0].conditionValue.rangeValue = &ipRange;
filter.numFilterConditions = 1;
filter.filterCondition = cond;
filter.layerKey = layerKeyOut;
if (!enableFilter(&filter, title, description.arg("to").arg(addr.toString()),
peer)) {
return false;
}
filter.layerKey = layerKeyIn;
if (!enableFilter(&filter, title,
description.arg("from").arg(addr.toString()), peer)) {
return false;
}
return true;
}
bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) { bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
// Allow outbound DHCPv4 // Allow outbound DHCPv4
{ {
@@ -52,9 +52,6 @@ class WindowsFirewall final : public QObject {
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title); bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight, bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight,
const QString& title, const QString& peer = QString()); const QString& title, const QString& peer = QString());
bool allowTrafficToRange(const IPAddress& addr, uint8_t weight,
const QString& title,
const QString& peer);
bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight, bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
const QString& title); const QString& title);
bool allowDHCPTraffic(uint8_t weight, const QString& title); bool allowDHCPTraffic(uint8_t weight, const QString& title);
-4
View File
@@ -43,7 +43,6 @@ namespace amnezia
constexpr char server_priv_key[] = "server_priv_key"; constexpr char server_priv_key[] = "server_priv_key";
constexpr char server_pub_key[] = "server_pub_key"; constexpr char server_pub_key[] = "server_pub_key";
constexpr char psk_key[] = "psk_key"; constexpr char psk_key[] = "psk_key";
constexpr char allowed_ips[] = "allowed_ips";
constexpr char client_ip[] = "client_ip"; // internal ip address constexpr char client_ip[] = "client_ip"; // internal ip address
@@ -79,9 +78,6 @@ namespace amnezia
constexpr char sftp[] = "sftp"; constexpr char sftp[] = "sftp";
constexpr char awg[] = "awg"; constexpr char awg[] = "awg";
constexpr char splitTunnelSites[] = "splitTunnelSites";
constexpr char splitTunnelType[] = "splitTunnelType";
} }
namespace protocols namespace protocols
+190 -3
View File
@@ -16,6 +16,8 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
m_configFile.setFileName(QDir::tempPath() + QDir::separator() + serviceName() + ".conf"); m_configFile.setFileName(QDir::tempPath() + QDir::separator() + serviceName() + ".conf");
writeWireguardConfiguration(configuration); writeWireguardConfiguration(configuration);
// MZ
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
m_impl.reset(new LocalSocketController()); m_impl.reset(new LocalSocketController());
connect(m_impl.get(), &ControllerImpl::connected, this, connect(m_impl.get(), &ControllerImpl::connected, this,
[this](const QString &pubkey, const QDateTime &connectionTimestamp) { [this](const QString &pubkey, const QDateTime &connectionTimestamp) {
@@ -24,6 +26,7 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
connect(m_impl.get(), &ControllerImpl::disconnected, this, connect(m_impl.get(), &ControllerImpl::disconnected, this,
[this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); }); [this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); });
m_impl->initialize(nullptr, nullptr); m_impl->initialize(nullptr, nullptr);
#endif
} }
WireguardProtocol::~WireguardProtocol() WireguardProtocol::~WireguardProtocol()
@@ -34,10 +37,68 @@ WireguardProtocol::~WireguardProtocol()
void WireguardProtocol::stop() void WireguardProtocol::stop()
{ {
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
stopMzImpl(); stopMzImpl();
return; return;
#endif
if (!QFileInfo::exists(Utils::wireguardExecPath())) {
qCritical() << "Wireguard executable missing!";
setLastError(ErrorCode::ExecutableMissing);
return;
}
m_wireguardStopProcess = IpcClient::CreatePrivilegedProcess();
if (!m_wireguardStopProcess) {
qCritical() << "IpcProcess replica is not created!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return;
}
m_wireguardStopProcess->waitForSource(1000);
if (!m_wireguardStopProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return;
}
m_wireguardStopProcess->setProgram(PermittedProcess::Wireguard);
m_wireguardStopProcess->setArguments(stopArgs());
qDebug() << stopArgs().join(" ");
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop errorOccurred" << error;
setConnectionState(Vpn::ConnectionState::Disconnected);
});
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::stateChanged, this,
[this](QProcess::ProcessState newState) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop stateChanged" << newState;
});
#ifdef Q_OS_LINUX
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardRunning();
if (result.returnValue()) {
setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
} else {
qCritical() << "IPC client not initialized";
setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
#endif
m_wireguardStopProcess->start();
m_wireguardStopProcess->waitForFinished(10000);
setConnectionState(Vpn::ConnectionState::Disconnected);
} }
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
ErrorCode WireguardProtocol::startMzImpl() ErrorCode WireguardProtocol::startMzImpl()
{ {
m_impl->activate(m_rawConfig); m_impl->activate(m_rawConfig);
@@ -49,6 +110,7 @@ ErrorCode WireguardProtocol::stopMzImpl()
m_impl->deactivate(); m_impl->deactivate();
return ErrorCode::NoError; return ErrorCode::NoError;
} }
#endif
void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configuration) void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configuration)
{ {
@@ -62,8 +124,21 @@ void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configura
m_configFile.write(jConfig.value(config_key::config).toString().toUtf8()); m_configFile.write(jConfig.value(config_key::config).toString().toUtf8());
m_configFile.close(); m_configFile.close();
#if 0
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->copyWireguardConfig(m_configFile.fileName());
if (result.returnValue()) {
qCritical() << "Failed to copy wireguard config";
return;
}
} else {
qCritical() << "IPC client not initialized";
return;
}
m_configFileName = "/etc/wireguard/wg99.conf";
#else
m_configFileName = m_configFile.fileName(); m_configFileName = m_configFile.fileName();
#endif
m_isConfigLoaded = true; m_isConfigLoaded = true;
@@ -77,9 +152,15 @@ QString WireguardProtocol::configPath() const
return m_configFileName; return m_configFileName;
} }
QString WireguardProtocol::serviceName() const void WireguardProtocol::updateRouteGateway(QString line)
{ {
return "AmneziaVPN.WireGuard0"; // TODO: fix for macos
line = line.split("ROUTE_GATEWAY", Qt::SkipEmptyParts).at(1);
if (!line.contains("/"))
return;
m_routeGateway = line.split("/", Qt::SkipEmptyParts).first();
m_routeGateway.replace(" ", "");
qDebug() << "Set VPN route gateway" << m_routeGateway;
} }
ErrorCode WireguardProtocol::start() ErrorCode WireguardProtocol::start()
@@ -89,6 +170,112 @@ ErrorCode WireguardProtocol::start()
return lastError(); return lastError();
} }
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
return startMzImpl(); return startMzImpl();
#endif
if (!QFileInfo::exists(Utils::wireguardExecPath())) {
setLastError(ErrorCode::ExecutableMissing);
return lastError();
}
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardConfigExists(configPath());
if (result.returnValue()) {
setLastError(ErrorCode::ConfigMissing);
return lastError();
}
} else {
qCritical() << "IPC client not initialized";
setLastError(ErrorCode::InternalError);
return lastError();
}
setConnectionState(Vpn::ConnectionState::Connecting);
m_wireguardStartProcess = IpcClient::CreatePrivilegedProcess();
if (!m_wireguardStartProcess) {
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_wireguardStartProcess->waitForSource(1000);
if (!m_wireguardStartProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_wireguardStartProcess->setProgram(PermittedProcess::Wireguard);
m_wireguardStartProcess->setArguments(startArgs());
qDebug() << startArgs().join(" ");
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol errorOccurred" << error;
setConnectionState(Vpn::ConnectionState::Disconnected);
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::stateChanged, this,
[this](QProcess::ProcessState newState) {
qDebug() << "WireguardProtocol::WireguardProtocol stateChanged" << newState;
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::finished, this,
[this]() { setConnectionState(Vpn::ConnectionState::Connected); });
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyRead, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAll();
reply.waitForFinished(1000);
qDebug() << "WireguardProtocol::WireguardProtocol readyRead" << reply.returnValue();
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyReadStandardOutput, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAllStandardOutput();
reply.waitForFinished(1000);
qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardOutput" << reply.returnValue();
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyReadStandardError, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAllStandardError();
reply.waitForFinished(10);
qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardError" << reply.returnValue();
});
m_wireguardStartProcess->start();
m_wireguardStartProcess->waitForFinished(10000);
return ErrorCode::NoError;
} }
void WireguardProtocol::updateVpnGateway(const QString &line)
{
}
QString WireguardProtocol::serviceName() const
{
return "AmneziaVPN.WireGuard0";
}
QStringList WireguardProtocol::stopArgs()
{
#ifdef Q_OS_WIN
return { "--remove", configPath() };
#elif defined Q_OS_LINUX
return { "down", "wg99" };
#else
return {};
#endif
}
QStringList WireguardProtocol::startArgs()
{
#ifdef Q_OS_WIN
return { "--add", configPath() };
#elif defined Q_OS_LINUX
return { "up", "wg99" };
#else
return {};
#endif
}
+13
View File
@@ -8,6 +8,7 @@
#include <QTimer> #include <QTimer>
#include "vpnprotocol.h" #include "vpnprotocol.h"
#include "core/ipcclient.h"
#include "mozilla/controllerimpl.h" #include "mozilla/controllerimpl.h"
@@ -22,21 +23,33 @@ public:
ErrorCode start() override; ErrorCode start() override;
void stop() override; void stop() override;
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
ErrorCode startMzImpl(); ErrorCode startMzImpl();
ErrorCode stopMzImpl(); ErrorCode stopMzImpl();
#endif
private: private:
QString configPath() const; QString configPath() const;
void writeWireguardConfiguration(const QJsonObject &configuration); void writeWireguardConfiguration(const QJsonObject &configuration);
void updateRouteGateway(QString line);
void updateVpnGateway(const QString &line);
QString serviceName() const; QString serviceName() const;
QStringList stopArgs();
QStringList startArgs();
private: private:
QString m_configFileName; QString m_configFileName;
QFile m_configFile; QFile m_configFile;
QSharedPointer<PrivilegedProcess> m_wireguardStartProcess;
QSharedPointer<PrivilegedProcess> m_wireguardStopProcess;
bool m_isConfigLoaded = false; bool m_isConfigLoaded = false;
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
QScopedPointer<ControllerImpl> m_impl; QScopedPointer<ControllerImpl> m_impl;
#endif
}; };
#endif // WIREGUARDPROTOCOL_H #endif // WIREGUARDPROTOCOL_H
+1
View File
@@ -216,6 +216,7 @@
<file>ui/qml/Pages2/PageServiceDnsSettings.qml</file> <file>ui/qml/Pages2/PageServiceDnsSettings.qml</file>
<file>ui/qml/Controls2/TopCloseButtonType.qml</file> <file>ui/qml/Controls2/TopCloseButtonType.qml</file>
<file>images/controls/x-circle.svg</file> <file>images/controls/x-circle.svg</file>
<file>ui/qml/Controls2/Drawer2Type.qml</file>
<file>ui/qml/Pages2/PageProtocolAwgSettings.qml</file> <file>ui/qml/Pages2/PageProtocolAwgSettings.qml</file>
<file>server_scripts/awg/template.conf</file> <file>server_scripts/awg/template.conf</file>
<file>server_scripts/awg/start.sh</file> <file>server_scripts/awg/start.sh</file>
+4
View File
@@ -233,6 +233,10 @@ QString Settings::routeModeString(RouteMode mode) const
Settings::RouteMode Settings::routeMode() const Settings::RouteMode Settings::routeMode() const
{ {
// TODO implement for mobiles
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
return RouteMode::VpnAllSites;
#endif
return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt()); return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt());
} }
+38 -70
View File
@@ -4,13 +4,9 @@
<context> <context>
<name>AmneziaApplication</name> <name>AmneziaApplication</name>
<message> <message>
<location filename="../amnezia_application.cpp" line="304"/>
<source>Split tunneling for WireGuard is not implemented, the option was disabled</source> <source>Split tunneling for WireGuard is not implemented, the option was disabled</source>
<translation type="vanished">Раздельное туннелирование для &quot;Wireguard&quot; не реализовано,опция отключена</translation> <translation>Раздельное туннелирование для &quot;Wireguard&quot; не реализовано,опция отключена</translation>
</message>
<message>
<location filename="../amnezia_application.cpp" line="305"/>
<source>Split tunneling for %1 is not implemented, the option was disabled</source>
<translation>Раздельное туннелирование для %1 не реализовано, опция отключена</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -124,7 +120,7 @@
<message> <message>
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="58"/> <location filename="../ui/qml/Components/HomeContainersListView.qml" line="58"/>
<source>Unable change protocol while there is an active connection</source> <source>Unable change protocol while there is an active connection</source>
<translation>Невозможно изменить протокол при активном соединении</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="69"/> <location filename="../ui/qml/Components/HomeContainersListView.qml" line="69"/>
@@ -278,7 +274,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageHome.qml" line="490"/> <location filename="../ui/qml/Pages2/PageHome.qml" line="490"/>
<source>Unable change server while there is an active connection</source> <source>Unable change server while there is an active connection</source>
<translation>Невозможно изменить сервер при активном соединении</translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
@@ -350,12 +346,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="279"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="279"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation>Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source> <source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="vanished">Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation> <translation>Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="280"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="280"/>
@@ -412,7 +404,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="84"/> <location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="84"/>
<source>VPN Addresses Subnet</source> <source>VPN Addresses Subnet</source>
<translation>Подсеть для VPN</translation> <translation>VPN Адреса Подсеть</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="98"/> <location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="98"/>
@@ -579,12 +571,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="368"/> <location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="368"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation>Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source> <source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="vanished">Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation> <translation>Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="369"/> <location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="369"/>
@@ -631,12 +619,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="179"/> <location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="179"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation>Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source> <source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="obsolete">Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation> <translation type="unfinished">Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="180"/> <location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="180"/>
@@ -686,7 +670,7 @@ Already installed containers were found on the server. All installed containers
<location filename="../ui/qml/Pages2/PageServiceDnsSettings.qml" line="52"/> <location filename="../ui/qml/Pages2/PageServiceDnsSettings.qml" line="52"/>
<source>A DNS service is installed on your server, and it is only accessible via VPN. <source>A DNS service is installed on your server, and it is only accessible via VPN.
</source> </source>
<translation>На вашем сервере установлен DNS-сервис, доступ к нему возможен только через VPN. <translation>На вашем сервере устанавливается DNS-сервис, доступ к нему возможен только через VPN.
</translation> </translation>
</message> </message>
<message> <message>
@@ -1148,7 +1132,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="41"/> <location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="41"/>
<source>Connection</source> <source>Connection</source>
<translation>Соединение</translation> <translation>Подключение</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="50"/> <location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="50"/>
@@ -1294,7 +1278,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="127"/> <location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="127"/>
<source>Save logs to file</source> <source>Save logs to file</source>
<translation>Сохранить логи в файл</translation> <translation>Сохранять логи в файл</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="145"/> <location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="145"/>
@@ -1332,7 +1316,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="87"/> <location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="87"/>
<source>Clear Amnezia cache</source> <source>Clear Amnezia cache</source>
<translation>Очистить кэш Amnezia</translation> <translation>Очистить кэш Amnezia на сервере</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="88"/> <location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="88"/>
@@ -1456,12 +1440,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="117"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="117"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation>Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться.</translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source> <source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="vanished">Все пользователи, которым вы поделились VPN, больше не смогут к нему подключаться.</translation> <translation>Все пользователи, которым вы поделились VPN, больше не смогут к нему подключаться.</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="118"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="118"/>
@@ -1500,75 +1480,75 @@ Already installed containers were found on the server. All installed containers
<translation>Раздельное VPN-туннелирование</translation> <translation>Раздельное VPN-туннелирование</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="121"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="128"/>
<source>Mode</source> <source>Mode</source>
<translation>Режим</translation> <translation>Режим</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="199"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="206"/>
<source>Remove </source> <source>Remove </source>
<translation>Удалить </translation> <translation>Удалить </translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="200"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="207"/>
<source>Continue</source> <source>Continue</source>
<translation>Продолжить</translation> <translation>Продолжить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="201"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="208"/>
<source>Cancel</source> <source>Cancel</source>
<translation>Отменить</translation> <translation>Отменить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="248"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="255"/>
<source>Site or IP</source> <source>Site or IP</source>
<translation>Сайт или IP</translation> <translation>Сайт или IP</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="292"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="299"/>
<source>Import/Export Sites</source> <source>Import/Export Sites</source>
<translation>Импорт/экспорт Сайтов</translation> <translation>Импорт/экспорт Сайтов</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="298"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="305"/>
<source>Import</source> <source>Import</source>
<translation>Импорт</translation> <translation>Импорт</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="310"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="317"/>
<source>Save site list</source> <source>Save site list</source>
<translation>Сохранить список сайтов</translation> <translation>Сохранить список сайтов</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="317"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="324"/>
<source>Save sites</source> <source>Save sites</source>
<translation>Сохранить</translation> <translation>Сохранить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="318"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="325"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="385"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="392"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="400"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="407"/>
<source>Sites files (*.json)</source> <source>Sites files (*.json)</source>
<translation>Sites files (*.json)</translation> <translation>Sites files (*.json)</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="375"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="382"/>
<source>Import a list of sites</source> <source>Import a list of sites</source>
<translation>Импортировать список с сайтами</translation> <translation>Импортировать список с сайтами</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="381"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="388"/>
<source>Replace site list</source> <source>Replace site list</source>
<translation>Заменить список сайтов</translation> <translation>Заменить список сайтов</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="384"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="391"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="399"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="406"/>
<source>Open sites file</source> <source>Open sites file</source>
<translation>Открыть список с сайтами</translation> <translation>Открыть список с сайтами</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="396"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="403"/>
<source>Add imported sites to existing ones</source> <source>Add imported sites to existing ones</source>
<translation>Добавить импортированные сайты к существующим</translation> <translation>Добавить импортированные сайты к существующим</translation>
</message> </message>
@@ -1904,8 +1884,9 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>WireGuard нативный формат</translation> <translation>WireGuard нативный формат</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="121"/>
<source>VPN Access</source> <source>VPN Access</source>
<translation type="vanished">VPN-Доступ</translation> <translation>VPN-Доступ</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="146"/> <location filename="../ui/qml/Pages2/PageShare.qml" line="146"/>
@@ -1913,12 +1894,14 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>Соединение</translation> <translation>Соединение</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="174"/>
<source>VPN access without the ability to manage the server</source> <source>VPN access without the ability to manage the server</source>
<translation type="vanished">Доступ к VPN, без возможности управления сервером</translation> <translation>Доступ к VPN, без возможности управления сервером</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="175"/>
<source>Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings.</source> <source>Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings.</source>
<translation type="vanished">Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки.</translation> <translation>Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки.</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="190"/> <location filename="../ui/qml/Pages2/PageShare.qml" line="190"/>
@@ -1961,26 +1944,11 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<source>For the AmneziaVPN app</source> <source>For the AmneziaVPN app</source>
<translation>Для AmneziaVPN</translation> <translation>Для AmneziaVPN</translation>
</message> </message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="121"/>
<source>Share VPN Access</source>
<translation>Поделиться VPN</translation>
</message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="158"/> <location filename="../ui/qml/Pages2/PageShare.qml" line="158"/>
<source>Full access</source> <source>Full access</source>
<translation>Полный доступ</translation> <translation>Полный доступ</translation>
</message> </message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="174"/>
<source>Share VPN access without the ability to manage the server</source>
<translation>Поделиться доступом к VPN, без возможности управления сервером</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="175"/>
<source>Share access to server management. The user with whom you share full access to the server will be able to add and remove any protocols and services to the server, as well as change settings.</source>
<translation>Поделиться доступом к управлению сервером. Пользователь, с которым вы делитесь полным доступом к серверу, сможет добавлять и удалять любые протоколы и службы на сервере, а также изменять настройки.</translation>
</message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="251"/> <location filename="../ui/qml/Pages2/PageShare.qml" line="251"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="252"/> <location filename="../ui/qml/Pages2/PageShare.qml" line="252"/>
@@ -2603,9 +2571,9 @@ OpenVPN обеспечивает безопасное VPN-соединение
Cloak защищает OpenVPN от обнаружения и блокировок. Cloak защищает OpenVPN от обнаружения и блокировок.
Cloak может изменять метаданные пакетов. Он полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает его очень устойчивым к обнаружению Cloak может изменять метаданные пакетов. Он полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает ее очень устойчивой к обнаружению
Сразу же после получения первого пакета данных Cloak проверяет подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под поддельный сайт, и ваш VPN становится невидимым для аналитических систем. Сразу же после получения первого пакета данных Cloak проверяет подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под поддельный сайт, и ваша VPN становится невидимой для аналитических систем.
Если в вашем регионе существует экстремальный уровень цензуры в Интернете, мы советуем вам при первом подключении использовать только OpenVPN через Cloak Если в вашем регионе существует экстремальный уровень цензуры в Интернете, мы советуем вам при первом подключении использовать только OpenVPN через Cloak
+29 -61
View File
@@ -4,13 +4,9 @@
<context> <context>
<name>AmneziaApplication</name> <name>AmneziaApplication</name>
<message> <message>
<location filename="../amnezia_application.cpp" line="304"/>
<source>Split tunneling for WireGuard is not implemented, the option was disabled</source> <source>Split tunneling for WireGuard is not implemented, the option was disabled</source>
<translation type="vanished">WireGuard协议的VPN分离</translation> <translation>WireGuard协议的VPN分离</translation>
</message>
<message>
<location filename="../amnezia_application.cpp" line="305"/>
<source>Split tunneling for %1 is not implemented, the option was disabled</source>
<translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
@@ -135,7 +131,7 @@
<message> <message>
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="58"/> <location filename="../ui/qml/Components/HomeContainersListView.qml" line="58"/>
<source>Unable change protocol while there is an active connection</source> <source>Unable change protocol while there is an active connection</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Components/HomeContainersListView.qml" line="69"/> <location filename="../ui/qml/Components/HomeContainersListView.qml" line="69"/>
@@ -384,12 +380,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="279"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="279"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation></translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source> <source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="vanished">使</translation> <translation>使</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="280"/> <location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="280"/>
@@ -613,12 +605,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="368"/> <location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="368"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation></translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source> <source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="vanished">使</translation> <translation>使</translation>
</message> </message>
<message> <message>
<source>All users with whom you shared a connection will no longer be able to connect to it</source> <source>All users with whom you shared a connection will no longer be able to connect to it</source>
@@ -673,12 +661,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="179"/> <location filename="../ui/qml/Pages2/PageProtocolRaw.qml" line="179"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation></translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source> <source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="vanished">使</translation> <translation>使</translation>
</message> </message>
<message> <message>
<source> from server?</source> <source> from server?</source>
@@ -1537,12 +1521,8 @@ And if you don&apos;t like the app, all the more support it - the donation will
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="117"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="117"/>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
<translation></translation>
</message>
<message>
<source>All users who you shared a connection with will no longer be able to connect to it.</source> <source>All users who you shared a connection with will no longer be able to connect to it.</source>
<translation type="vanished">使</translation> <translation>使</translation>
</message> </message>
<message> <message>
<source> from server?</source> <source> from server?</source>
@@ -1606,75 +1586,75 @@ And if you don&apos;t like the app, all the more support it - the donation will
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="121"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="128"/>
<source>Mode</source> <source>Mode</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="199"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="206"/>
<source>Remove </source> <source>Remove </source>
<translation> </translation> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="200"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="207"/>
<source>Continue</source> <source>Continue</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="201"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="208"/>
<source>Cancel</source> <source>Cancel</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="248"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="255"/>
<source>Site or IP</source> <source>Site or IP</source>
<translation>IP地址</translation> <translation>IP地址</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="292"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="299"/>
<source>Import/Export Sites</source> <source>Import/Export Sites</source>
<translation>/</translation> <translation>/</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="298"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="305"/>
<source>Import</source> <source>Import</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="310"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="317"/>
<source>Save site list</source> <source>Save site list</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="317"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="324"/>
<source>Save sites</source> <source>Save sites</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="318"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="325"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="385"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="392"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="400"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="407"/>
<source>Sites files (*.json)</source> <source>Sites files (*.json)</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="375"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="382"/>
<source>Import a list of sites</source> <source>Import a list of sites</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="381"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="388"/>
<source>Replace site list</source> <source>Replace site list</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="384"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="391"/>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="399"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="406"/>
<source>Open sites file</source> <source>Open sites file</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="396"/> <location filename="../ui/qml/Pages2/PageSettingsSplitTunneling.qml" line="403"/>
<source>Add imported sites to existing ones</source> <source>Add imported sites to existing ones</source>
<translation></translation> <translation></translation>
</message> </message>
@@ -2026,22 +2006,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="121"/> <location filename="../ui/qml/Pages2/PageShare.qml" line="121"/>
<source>Share VPN Access</source>
<translation> VPN 访</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="174"/>
<source>Share VPN access without the ability to manage the server</source>
<translation> VPN 访</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="175"/>
<source>Share access to server management. The user with whom you share full access to the server will be able to add and remove any protocols and services to the server, as well as change settings.</source>
<translation>访访</translation>
</message>
<message>
<source>VPN Access</source> <source>VPN Access</source>
<translation type="vanished">访VPN</translation> <translation>访VPN</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="146"/> <location filename="../ui/qml/Pages2/PageShare.qml" line="146"/>
@@ -2054,12 +2020,14 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>访</translation> <translation>访</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="174"/>
<source>VPN access without the ability to manage the server</source> <source>VPN access without the ability to manage the server</source>
<translation type="vanished">访VPN</translation> <translation>访VPN</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="175"/>
<source>Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings.</source> <source>Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings.</source>
<translation type="vanished">访VPN外</translation> <translation>访VPN外</translation>
</message> </message>
<message> <message>
<source>Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the servers, as well as change settings.</source> <source>Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the servers, as well as change settings.</source>
@@ -261,10 +261,6 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
// return QJsonObject(); // return QJsonObject();
// } // }
QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(","));
lastConfig[config_key::allowed_ips] = allowedIpsJsonArray;
QString protocolName = "wireguard"; QString protocolName = "wireguard";
if (!configMap.value(config_key::junkPacketCount).isEmpty() if (!configMap.value(config_key::junkPacketCount).isEmpty()
&& !configMap.value(config_key::junkPacketMinSize).isEmpty() && !configMap.value(config_key::junkPacketMinSize).isEmpty()
+11 -4
View File
@@ -22,6 +22,10 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i
DockerContainer container = ContainerProps::allContainers().at(index.row()); DockerContainer container = ContainerProps::allContainers().at(index.row());
switch (role) { switch (role) {
case NameRole:
// return ContainerProps::containerHumanNames().value(container);
case DescriptionRole:
// return ContainerProps::containerDescriptions().value(container);
case ConfigRole: { case ConfigRole: {
m_settings->setContainerConfig(m_currentlyProcessedServerIndex, container, value.toJsonObject()); m_settings->setContainerConfig(m_currentlyProcessedServerIndex, container, value.toJsonObject());
m_containers = m_settings->containers(m_currentlyProcessedServerIndex); m_containers = m_settings->containers(m_currentlyProcessedServerIndex);
@@ -31,15 +35,19 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i
break; break;
} }
} }
case ServiceTypeRole:
// return ContainerProps::containerService(container);
case DockerContainerRole:
// return container;
case IsInstalledRole:
// return m_settings->containers(m_currentlyProcessedServerIndex).contains(container);
case IsDefaultRole: { //todo remove case IsDefaultRole: { //todo remove
m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container); m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container);
m_defaultContainerIndex = container; m_defaultContainerIndex = container;
emit defaultContainerChanged(); emit defaultContainerChanged();
} }
default: break;
} }
emit containersModelUpdated();
emit dataChanged(index, index); emit dataChanged(index, index);
return true; return true;
} }
@@ -109,9 +117,8 @@ QString ContainersModel::getDefaultContainerName()
return ContainerProps::containerHumanNames().value(m_defaultContainerIndex); return ContainerProps::containerHumanNames().value(m_defaultContainerIndex);
} }
void ContainersModel::setDefaultContainer(int index) void ContainersModel::setDefaultContainer(DockerContainer container)
{ {
auto container = static_cast<DockerContainer>(index);
m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container); m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container);
m_defaultContainerIndex = container; m_defaultContainerIndex = container;
emit defaultContainerChanged(); emit defaultContainerChanged();
+1 -2
View File
@@ -46,7 +46,7 @@ public:
public slots: public slots:
DockerContainer getDefaultContainer(); DockerContainer getDefaultContainer();
QString getDefaultContainerName(); QString getDefaultContainerName();
void setDefaultContainer(int index); void setDefaultContainer(DockerContainer container);
void setCurrentlyProcessedServerIndex(const int index); void setCurrentlyProcessedServerIndex(const int index);
@@ -73,7 +73,6 @@ protected:
signals: signals:
void defaultContainerChanged(); void defaultContainerChanged();
void containersModelUpdated();
private: private:
QMap<DockerContainer, QJsonObject> m_containers; QMap<DockerContainer, QJsonObject> m_containers;
-6
View File
@@ -193,12 +193,6 @@ bool ServersModel::isDefaultServerConfigContainsAmneziaDns()
return primaryDns == protocols::dns::amneziaDnsIp; return primaryDns == protocols::dns::amneziaDnsIp;
} }
void ServersModel::updateContainersConfig()
{
auto server = m_settings->server(m_currentlyProcessedServerIndex);
m_servers.replace(m_currentlyProcessedServerIndex, server);
}
QHash<int, QByteArray> ServersModel::roleNames() const QHash<int, QByteArray> ServersModel::roleNames() const
{ {
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
-2
View File
@@ -59,8 +59,6 @@ public slots:
bool isDefaultServerConfigContainsAmneziaDns(); bool isDefaultServerConfigContainsAmneziaDns();
void updateContainersConfig();
protected: protected:
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
+1 -23
View File
@@ -3,14 +3,7 @@
SitesModel::SitesModel(std::shared_ptr<Settings> settings, QObject *parent) SitesModel::SitesModel(std::shared_ptr<Settings> settings, QObject *parent)
: QAbstractListModel(parent), m_settings(settings) : QAbstractListModel(parent), m_settings(settings)
{ {
auto routeMode = m_settings->routeMode(); m_currentRouteMode = m_settings->routeMode();
if (routeMode == Settings::RouteMode::VpnAllSites) {
m_isSplitTunnelingEnabled = false;
m_currentRouteMode = Settings::RouteMode::VpnOnlyForwardSites;
} else {
m_isSplitTunnelingEnabled = true;
m_currentRouteMode = routeMode;
}
fillSites(); fillSites();
} }
@@ -100,21 +93,6 @@ void SitesModel::setRouteMode(int routeMode)
emit routeModeChanged(); emit routeModeChanged();
} }
bool SitesModel::isSplitTunnelingEnabled()
{
return m_isSplitTunnelingEnabled;
}
void SitesModel::toggleSplitTunneling(bool enabled)
{
if (enabled) {
setRouteMode(m_currentRouteMode);
} else {
m_settings->setRouteMode(Settings::RouteMode::VpnAllSites);
}
m_isSplitTunnelingEnabled = enabled;
}
QVector<QPair<QString, QString> > SitesModel::getCurrentSites() QVector<QPair<QString, QString> > SitesModel::getCurrentSites()
{ {
return m_sites; return m_sites;
-4
View File
@@ -31,9 +31,6 @@ public slots:
int getRouteMode(); int getRouteMode();
void setRouteMode(int routeMode); void setRouteMode(int routeMode);
bool isSplitTunnelingEnabled();
void toggleSplitTunneling(bool enabled);
QVector<QPair<QString, QString>> getCurrentSites(); QVector<QPair<QString, QString>> getCurrentSites();
signals: signals:
@@ -47,7 +44,6 @@ private:
std::shared_ptr<Settings> m_settings; std::shared_ptr<Settings> m_settings;
bool m_isSplitTunnelingEnabled;
Settings::RouteMode m_currentRouteMode; Settings::RouteMode m_currentRouteMode;
QVector<QPair<QString, QString>> m_sites; QVector<QPair<QString, QString>> m_sites;
@@ -8,13 +8,16 @@ import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
DrawerType { Drawer2Type {
id: root id: root
width: parent.width width: parent.width
height: parent.height * 0.4375 height: parent.height
contentHeight: parent.height * 0.4375
ColumnLayout { ColumnLayout {
parent: root.contentParent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -63,7 +63,7 @@ ListView {
isDefault = true isDefault = true
menuContent.currentIndex = index menuContent.currentIndex = index
containersDropDown.menuVisible = false containersDropDown.menu.close()
} else { } else {
if (!isSupported && isInstalled) { if (!isSupported && isInstalled) {
PageController.showErrorMessage(qsTr("The selected protocol is not supported on the current platform")) PageController.showErrorMessage(qsTr("The selected protocol is not supported on the current platform"))
+7 -3
View File
@@ -5,7 +5,7 @@ import QtQuick.Layouts
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
DrawerType { Drawer2Type {
id: root id: root
property string headerText property string headerText
@@ -15,12 +15,14 @@ DrawerType {
property var yesButtonFunction property var yesButtonFunction
property var noButtonFunction property var noButtonFunction
property real drawerHeight: 0.5
width: parent.width width: parent.width
height: content.implicitHeight + 32 height: parent.height
contentHeight: parent.height * drawerHeight
ColumnLayout { ColumnLayout {
id: content parent: root.contentParent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
@@ -29,6 +31,8 @@ DrawerType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
// visible: false
spacing: 8 spacing: 8
Header2TextType { Header2TextType {
@@ -5,15 +5,18 @@ import QtQuick.Layouts
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
DrawerType { Drawer2Type {
id: root id: root
width: parent.width width: parent.width
height: parent.height * 0.9 height: parent.height
contentHeight: parent.height * 0.9
ColumnLayout { ColumnLayout {
id: backButton id: backButton
parent: root.contentParent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -28,6 +31,7 @@ DrawerType {
} }
FlickableType { FlickableType {
parent: root.contentParent
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -16,7 +16,7 @@ import "../Controls2/TextTypes"
import "../Config" import "../Config"
import "../Components" import "../Components"
DrawerType { Drawer2Type {
id: root id: root
property alias headerText: header.headerText property alias headerText: header.headerText
@@ -28,9 +28,10 @@ DrawerType {
property string configFileName: "amnezia_config.vpn" property string configFileName: "amnezia_config.vpn"
width: parent.width width: parent.width
height: parent.height * 0.9 height: parent.height
contentHeight: parent.height * 0.9
onClosed: { onDrawerClosed: {
configExtension = ".vpn" configExtension = ".vpn"
configCaption = qsTr("Save AmneziaVPN config") configCaption = qsTr("Save AmneziaVPN config")
configFileName = "amnezia_config" configFileName = "amnezia_config"
@@ -41,6 +42,9 @@ DrawerType {
Header2Type { Header2Type {
id: header id: header
parent: root.contentParent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -50,6 +54,8 @@ DrawerType {
} }
FlickableType { FlickableType {
parent: root.contentParent
anchors.top: header.bottom anchors.top: header.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.height + 32 contentHeight: content.height + 32
@@ -126,30 +132,37 @@ DrawerType {
text: qsTr("Show connection settings") text: qsTr("Show connection settings")
onClicked: { onClicked: {
configContentDrawer.visible = true configContentDrawer.open()
} }
} }
DrawerType { Drawer2Type {
id: configContentDrawer id: configContentDrawer
parent: root
width: parent.width width: parent.width
height: parent.height * 0.9 height: parent.height
contentHeight: parent.height * 0.9
BackButtonType { BackButtonType {
id: backButton id: backButton
parent: configContentDrawer.contentParent
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.topMargin: 16 anchors.topMargin: 16
backButtonFunction: function() { backButtonFunction: function() {
configContentDrawer.visible = false configContentDrawer.close()
} }
} }
FlickableType { FlickableType {
parent: configContentDrawer.contentParent
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
+314
View File
@@ -0,0 +1,314 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Shapes
Item {
id: root
Connections {
target: PageController
function onForceCloseDrawer() {
if (root.expanded()) {
collapse()
}
}
}
signal drawerClosed
signal collapsedEntered
signal collapsedExited
signal collapsedEnter
signal collapsedPressChanged
visible: false
property bool needCloseButton: true
property string defaultColor: "#1C1D21"
property string borderColor: "#2C2D30"
property string semitransparentColor: "#90000000"
property bool needCollapsed: false
property int contentHeight: 0
property Item contentParent: contentArea
property bool dragActive: dragArea.drag.active
property int collapsedHeight: 0
property bool fullMouseAreaVisible: true
property MouseArea drawerDragArea: dragArea
state: "collapsed"
Rectangle {
id: draw2Background
anchors.fill: parent
height: parent.height
width: parent.width
radius: 16
color: "transparent"
border.color: "transparent"
border.width: 1
visible: true
MouseArea {
id: fullMouseArea
anchors.fill: parent
enabled: root.expanded()
hoverEnabled: true
visible: fullMouseAreaVisible
onClicked: {
if (root.expanded()) {
collapse()
}
}
}
Rectangle {
id: placeAreaHolder
// for apdating home drawer, normal drawer will reset it
height: 0
anchors.right: parent.right
anchors.left: parent.left
visible: true
color: "transparent"
Drag.active: dragArea.drag.active
}
Rectangle {
id: contentArea
anchors.top: placeAreaHolder.bottom
height: contentHeight
radius: 16
color: root.defaultColor
border.width: 1
border.color: root.borderColor
width: parent.width
visible: true
Rectangle {
width: parent.radius
height: parent.radius
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
color: parent.color
}
MouseArea {
id: dragArea
anchors.fill: parent
cursorShape: root.collapsed() ? Qt.PointingHandCursor : Qt.ArrowCursor
hoverEnabled: true
drag.target: placeAreaHolder
drag.axis: Drag.YAxis
drag.maximumY: root.height - root.collapsedHeight
drag.minimumY: root.collapsedHeight > 0 ? root.height - root.height * 0.9 : 0
/** If drag area is released at any point other than min or max y, transition to the other state */
onReleased: {
if (root.collapsed() && placeAreaHolder.y < drag.maximumY) {
root.state = "expanded"
return
}
if (root.expanded() && placeAreaHolder.y > drag.minimumY) {
root.state = "collapsed"
return
}
}
onClicked: {
if (root.expanded()) {
collapse()
return
}
if (root.collapsed()) {
root.state = "expanded"
}
}
onExited: {
collapsedExited()
}
onEntered: {
collapsedEnter()
}
onPressedChanged: {
collapsedPressChanged()
}
}
}
}
onStateChanged: {
if (root.collapsed()) {
var initialPageNavigationBarColor = PageController.getInitialPageNavigationBarColor()
if (initialPageNavigationBarColor !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(initialPageNavigationBarColor)
}
if (needCloseButton) {
PageController.drawerClose()
}
drawerClosed()
return
}
if (root.expanded()) {
if (PageController.getInitialPageNavigationBarColor() !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(0xFF1C1D21)
}
if (needCloseButton) {
PageController.drawerOpen()
}
return
}
}
/** Two states of buttonContent, great place to add any future animations for the drawer */
states: [
State {
name: "collapsed"
PropertyChanges {
target: placeAreaHolder
y: dragArea.drag.maximumY
}
},
State {
name: "expanded"
PropertyChanges {
target: placeAreaHolder
y: dragArea.drag.minimumY
}
}
]
transitions: [
Transition {
from: "expanded"
to: "collapsed"
PropertyAnimation {
target: placeAreaHolder
properties: "y"
duration: 200
}
onRunningChanged: {
if (!running) {
draw2Background.color = "transparent"
fullMouseArea.visible = false
}
}
},
Transition {
from: "collapsed"
to: "expanded"
PropertyAnimation {
target: placeAreaHolder
properties: "y"
duration: 200
}
onRunningChanged: {
if (!running) {
draw2Background.color = semitransparentColor
fullMouseArea.visible = true
}
}
}
]
NumberAnimation {
id: animationVisible
target: placeAreaHolder
property: "y"
from: parent.height
to: 0
duration: 200
}
// for normal drawer
function open() {
if (root.expanded()) {
return
}
draw2Background.color = semitransparentColor
fullMouseArea.visible = true
collapsedHeight = 0
root.y = 0
root.state = "expanded"
root.visible = true
root.height = parent.height
contentArea.height = contentHeight
placeAreaHolder.y = 0
placeAreaHolder.height = root.height - contentHeight
animationVisible.running = true
}
function close() {
collapse()
}
function collapse() {
draw2Background.color = "transparent"
root.state = "collapsed"
}
// for page home
function expand() {
draw2Background.color = semitransparentColor
root.state = "expanded"
}
function expanded() {
return root.state === "expanded" ? true : false
}
function collapsed() {
return root.state === "collapsed" ? true : false
}
onVisibleChanged: {
// e.g cancel, ......
if (!visible) {
if (root.expanded()) {
if (needCloseButton) {
PageController.drawerClose()
}
close()
}
}
}
}
+16 -5
View File
@@ -40,6 +40,10 @@ Item {
property alias menuVisible: menu.visible property alias menuVisible: menu.visible
property Item drawerParent: root
property Drawer2Type menu: menu
implicitWidth: rootButtonContent.implicitWidth implicitWidth: rootButtonContent.implicitWidth
implicitHeight: rootButtonContent.implicitHeight implicitHeight: rootButtonContent.implicitHeight
@@ -155,21 +159,26 @@ Item {
onClicked: { onClicked: {
if (rootButtonClickedFunction && typeof rootButtonClickedFunction === "function") { if (rootButtonClickedFunction && typeof rootButtonClickedFunction === "function") {
rootButtonClickedFunction() rootButtonClickedFunction()
} else {
menu.visible = true
} }
menu.open()
} }
} }
DrawerType { Drawer2Type {
id: menu id: menu
parent: drawerParent
width: parent.width width: parent.width
height: parent.height * drawerHeight height: parent.height
contentHeight: parent.height * drawerHeight
ColumnLayout { ColumnLayout {
id: header id: header
parent: menu.contentParent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -178,12 +187,14 @@ Item {
BackButtonType { BackButtonType {
backButtonImage: root.headerBackButtonImage backButtonImage: root.headerBackButtonImage
backButtonFunction: function() { backButtonFunction: function() {
root.menuVisible = false menu.close()
} }
} }
} }
FlickableType { FlickableType {
parent: menu.contentParent
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: 16 anchors.topMargin: 16
contentHeight: col.implicitHeight contentHeight: col.implicitHeight
+126 -188
View File
@@ -30,13 +30,13 @@ PageType {
target: PageController target: PageController
function onRestorePageHomeState(isContainerInstalled) { function onRestorePageHomeState(isContainerInstalled) {
buttonContent.state = "expanded" buttonContent.collapse()
if (isContainerInstalled) { if (isContainerInstalled) {
containersDropDown.rootButtonClickedFunction() containersDropDown.menuVisible = true
} }
} }
function onForceCloseDrawer() { function onForceCloseDrawer() {
buttonContent.state = "collapsed" buttonContent.collapse()
} }
} }
@@ -73,14 +73,8 @@ PageType {
expandedServersMenuDescription.text = description + root.defaultServerHostName expandedServersMenuDescription.text = description + root.defaultServerHostName
} }
Component.onCompleted: updateDescriptions() Component.onCompleted: {
updateDescriptions()
MouseArea {
anchors.fill: parent
enabled: buttonContent.state === "expanded"
onClicked: {
buttonContent.state = "collapsed"
}
} }
Item { Item {
@@ -92,56 +86,10 @@ PageType {
} }
} }
MouseArea {
id: dragArea
anchors.fill: buttonBackground
cursorShape: buttonContent.state === "collapsed" ? Qt.PointingHandCursor : Qt.ArrowCursor
hoverEnabled: true
drag.target: buttonContent
drag.axis: Drag.YAxis
drag.maximumY: root.height - buttonContent.collapsedHeight
drag.minimumY: root.height - root.height * 0.9
/** If drag area is released at any point other than min or max y, transition to the other state */
onReleased: {
if (buttonContent.state === "collapsed" && buttonContent.y < dragArea.drag.maximumY) {
buttonContent.state = "expanded"
return
}
if (buttonContent.state === "expanded" && buttonContent.y > dragArea.drag.minimumY) {
buttonContent.state = "collapsed"
return
}
}
onEntered: {
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.hoveredColor
collapsedButtonHeader.opacity = 0.8
}
onExited: {
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.defaultColor
collapsedButtonHeader.opacity = 1
}
onPressedChanged: {
collapsedButtonChevron.backgroundColor = pressed ? collapsedButtonChevron.pressedColor : entered ? collapsedButtonChevron.hoveredColor : collapsedButtonChevron.defaultColor
collapsedButtonHeader.opacity = 0.7
}
onClicked: {
if (buttonContent.state === "collapsed") {
buttonContent.state = "expanded"
}
}
}
Rectangle { Rectangle {
id: buttonBackground id: buttonBackground
anchors { left: buttonContent.left; right: buttonContent.right; top: buttonContent.top } anchors { left: buttonContent.left; right: buttonContent.right; top: buttonContent.top }
height: root.height
radius: 16 radius: 16
color: root.defaultColor color: root.defaultColor
border.color: root.borderColor border.color: root.borderColor
@@ -157,161 +105,126 @@ PageType {
} }
} }
ColumnLayout { Drawer2Type {
id: buttonContent id: buttonContent
visible: true
fullMouseAreaVisible: false
/** Initial height of button content */
property int collapsedHeight: 0
/** True when expanded objects should be visible */ /** True when expanded objects should be visible */
property bool expandedVisibility: buttonContent.state === "expanded" || (buttonContent.state === "collapsed" && dragArea.drag.active === true) property bool expandedVisibility: buttonContent.expanded() || (buttonContent.collapsed() && buttonContent.dragActive)
/** True when collapsed objects should be visible */ /** True when collapsed objects should be visible */
property bool collapsedVisibility: buttonContent.state === "collapsed" && dragArea.drag.active === false property bool collapsedVisibility: buttonContent.collapsed() && !buttonContent.dragActive
Drag.active: dragArea.drag.active width: parent.width
anchors.right: root.right height: parent.height
anchors.left: root.left contentHeight: parent.height * 0.9
y: root.height - buttonContent.height
Component.onCompleted: {
buttonContent.state = "collapsed"
}
/** Set once based on first implicit height change once all children are layed out */ ColumnLayout {
onImplicitHeightChanged: { id: collapsedButtonContent
if (buttonContent.state === "collapsed" && collapsedHeight == 0) {
collapsedHeight = implicitHeight
}
}
onStateChanged: { parent: buttonContent.contentParent
if (buttonContent.state === "collapsed") {
var initialPageNavigationBarColor = PageController.getInitialPageNavigationBarColor()
if (initialPageNavigationBarColor !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(initialPageNavigationBarColor)
}
PageController.drawerClose()
return
}
if (buttonContent.state === "expanded") {
if (PageController.getInitialPageNavigationBarColor() !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(0xFF1C1D21)
}
PageController.drawerOpen()
return
}
}
/** Two states of buttonContent, great place to add any future animations for the drawer */
states: [
State {
name: "collapsed"
PropertyChanges {
target: buttonContent
y: root.height - collapsedHeight
}
},
State {
name: "expanded"
PropertyChanges {
target: buttonContent
y: dragArea.drag.minimumY
}
}
]
transitions: [
Transition {
from: "collapsed"
to: "expanded"
PropertyAnimation {
target: buttonContent
properties: "y"
duration: 200
}
},
Transition {
from: "expanded"
to: "collapsed"
PropertyAnimation {
target: buttonContent
properties: "y"
duration: 200
}
}
]
DividerType {
Layout.topMargin: 10
Layout.fillWidth: false
Layout.preferredWidth: 20
Layout.preferredHeight: 2
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
visible: (buttonContent.collapsedVisibility || buttonContent.expandedVisibility)
}
RowLayout {
Layout.topMargin: 14
Layout.leftMargin: 24
Layout.rightMargin: 24
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
visible: buttonContent.collapsedVisibility visible: buttonContent.collapsedVisibility
spacing: 0 anchors.right: parent.right
anchors.left: parent.left
anchors.top: parent.top
Header1TextType { onImplicitHeightChanged: {
id: collapsedButtonHeader if (buttonContent.collapsed() && buttonContent.collapsedHeight === 0) {
Layout.maximumWidth: buttonContent.width - 48 - 18 - 12 // todo buttonContent.collapsedHeight = implicitHeight
maximumLineCount: 2
elide: Qt.ElideRight
text: root.defaultServerName
horizontalAlignment: Qt.AlignHCenter
Behavior on opacity {
PropertyAnimation { duration: 200 }
} }
} }
ImageButtonType { DividerType {
id: collapsedButtonChevron Layout.topMargin: 10
Layout.fillWidth: false
Layout.preferredWidth: 20
Layout.preferredHeight: 2
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
}
RowLayout {
Layout.topMargin: 14
Layout.leftMargin: 24
Layout.rightMargin: 24
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.leftMargin: 8 Header1TextType {
id: collapsedButtonHeader
Layout.maximumWidth: root.width - 48 - 18 - 12 // todo
hoverEnabled: false maximumLineCount: 2
image: "qrc:/images/controls/chevron-down.svg" elide: Qt.ElideRight
imageColor: "#d7d8db"
icon.width: 18 text: root.defaultServerName
icon.height: 18
backgroundRadius: 16
horizontalPadding: 4
topPadding: 4
bottomPadding: 3
onClicked: { Layout.alignment: Qt.AlignLeft
if (buttonContent.state === "collapsed") { }
buttonContent.state = "expanded"
ImageButtonType {
id: collapsedButtonChevron
hoverEnabled: false
image: "qrc:/images/controls/chevron-down.svg"
imageColor: "#d7d8db"
horizontalPadding: 0
padding: 0
spacing: 0
Rectangle {
id: rightImageBackground
anchors.fill: parent
radius: 16
color: "transparent"
Behavior on color {
PropertyAnimation { duration: 200 }
}
}
onClicked: {
if (buttonContent.collapsed()) {
buttonContent.expand()
}
} }
} }
} }
LabelTextType {
id: collapsedServerMenuDescription
Layout.bottomMargin: 44
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
visible: buttonContent.collapsedVisibility
}
} }
LabelTextType { Component.onCompleted: {
id: collapsedServerMenuDescription buttonContent.collapse()
Layout.bottomMargin: 44
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
visible: buttonContent.collapsedVisibility
} }
ColumnLayout { ColumnLayout {
id: serversMenuHeader id: serversMenuHeader
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter parent: buttonContent.contentParent
Layout.fillWidth: true
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.left
visible: buttonContent.expandedVisibility visible: buttonContent.expandedVisibility
DividerType {
Layout.topMargin: 10
Layout.fillWidth: false
Layout.preferredWidth: 20
Layout.preferredHeight: 2
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
}
Header1TextType { Header1TextType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -340,6 +253,8 @@ PageType {
DropDownType { DropDownType {
id: containersDropDown id: containersDropDown
drawerParent: root
rootButtonImageColor: "#0E0E11" rootButtonImageColor: "#0E0E11"
rootButtonBackgroundColor: "#D7D8DB" rootButtonBackgroundColor: "#D7D8DB"
rootButtonBackgroundHoveredColor: Qt.rgba(215, 216, 219, 0.8) rootButtonBackgroundHoveredColor: Qt.rgba(215, 216, 219, 0.8)
@@ -401,12 +316,18 @@ PageType {
Flickable { Flickable {
id: serversContainer id: serversContainer
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
Layout.fillWidth: true parent: buttonContent.contentParent
Layout.topMargin: 16
anchors.top: serversMenuHeader.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.topMargin: 16
contentHeight: col.implicitHeight contentHeight: col.implicitHeight
implicitHeight: root.height - (root.height * 0.1) - serversMenuHeader.implicitHeight - 52 //todo 52 is tabbar height
visible: buttonContent.expandedVisibility visible: buttonContent.expandedVisibility
clip: true clip: true
ScrollBar.vertical: ScrollBar { ScrollBar.vertical: ScrollBar {
@@ -516,7 +437,7 @@ PageType {
onClicked: function() { onClicked: function() {
ServersModel.currentlyProcessedIndex = index ServersModel.currentlyProcessedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo) PageController.goToPage(PageEnum.PageSettingsServerInfo)
buttonContent.state = "collapsed" buttonContent.collapse()
} }
} }
} }
@@ -531,5 +452,22 @@ PageType {
} }
} }
} }
onCollapsedEnter: {
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.hoveredColor
collapsedButtonHeader.opacity = 0.8
}
onCollapsedExited: {
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.defaultColor
collapsedButtonHeader.opacity = 1
}
onCollapsedPressChanged: {
collapsedButtonChevron.backgroundColor = buttonContent.drawerDragArea.pressed ?
collapsedButtonChevron.pressedColor : buttonContent.drawerDragArea.entered ?
collapsedButtonChevron.hoveredColor : collapsedButtonChevron.defaultColor
collapsedButtonHeader.opacity = 0.7
}
} }
} }
@@ -276,7 +276,7 @@ PageType {
onClicked: { onClicked: {
questionDrawer.headerText = qsTr("Remove AmneziaWG from server?") questionDrawer.headerText = qsTr("Remove AmneziaWG from server?")
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.") questionDrawer.descriptionText = qsTr("All users who you shared a connection with will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue") questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
@@ -286,9 +286,9 @@ PageType {
InstallController.removeCurrentlyProcessedContainer() InstallController.removeCurrentlyProcessedContainer()
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
@@ -324,6 +324,7 @@ PageType {
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
@@ -117,6 +117,8 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
drawerParent: root
descriptionText: qsTr("Cipher") descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher") headerText: qsTr("Cipher")
@@ -157,6 +157,8 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
drawerParent: root
enabled: !autoNegotiateEncryprionSwitcher.checked enabled: !autoNegotiateEncryprionSwitcher.checked
descriptionText: qsTr("Hash") descriptionText: qsTr("Hash")
@@ -203,6 +205,8 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
drawerParent: root
enabled: !autoNegotiateEncryprionSwitcher.checked enabled: !autoNegotiateEncryprionSwitcher.checked
descriptionText: qsTr("Cipher") descriptionText: qsTr("Cipher")
@@ -365,19 +369,19 @@ PageType {
onClicked: { onClicked: {
questionDrawer.headerText = qsTr("Remove OpenVpn from server?") questionDrawer.headerText = qsTr("Remove OpenVpn from server?")
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.") questionDrawer.descriptionText = qsTr("All users who you shared a connection with will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue") questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer() InstallController.removeCurrentlyProcessedContainer()
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
@@ -402,6 +406,7 @@ PageType {
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
+11 -6
View File
@@ -90,15 +90,19 @@ PageType {
DividerType {} DividerType {}
DrawerType { Drawer2Type {
id: configContentDrawer id: configContentDrawer
parent: root
width: parent.width width: parent.width
height: parent.height * 0.9 height: parent.height
contentHeight: parent.height * 0.9
BackButtonType { BackButtonType {
id: backButton id: backButton
parent: configContentDrawer.contentParent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -176,19 +180,19 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
questionDrawer.headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName()) questionDrawer.headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.") questionDrawer.descriptionText = qsTr("All users who you shared a connection with will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue") questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer() InstallController.removeCurrentlyProcessedContainer()
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
MouseArea { MouseArea {
@@ -203,6 +207,7 @@ PageType {
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
@@ -95,6 +95,8 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
drawerParent: root
descriptionText: qsTr("Cipher") descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher") headerText: qsTr("Cipher")
@@ -68,14 +68,14 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer() InstallController.removeCurrentlyProcessedContainer()
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
MouseArea { MouseArea {
@@ -89,6 +89,7 @@ PageType {
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
@@ -253,14 +253,14 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer() InstallController.removeCurrentlyProcessedContainer()
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
} }
@@ -270,6 +270,7 @@ PageType {
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
@@ -131,20 +131,21 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer() InstallController.removeCurrentlyProcessedContainer()
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
} }
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
@@ -119,6 +119,7 @@ PageType {
SelectLanguageDrawer { SelectLanguageDrawer {
id: selectLanguageDrawer id: selectLanguageDrawer
parent: root
} }
@@ -151,14 +152,14 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
SettingsController.clearSettings() SettingsController.clearSettings()
PageController.replaceStartPage() PageController.replaceStartPage()
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
@@ -166,6 +167,7 @@ PageType {
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
+4 -3
View File
@@ -139,18 +139,19 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
SettingsController.restoreAppConfig(filePath) SettingsController.restoreAppConfig(filePath)
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
@@ -94,7 +94,7 @@ PageType {
DividerType {} DividerType {}
LabelWithButtonType { LabelWithButtonType {
visible: true visible: GC.isDesktop()
Layout.fillWidth: true Layout.fillWidth: true
+4 -3
View File
@@ -92,7 +92,7 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
SettingsController.primaryDns = "1.1.1.1" SettingsController.primaryDns = "1.1.1.1"
primaryDns.textFieldText = SettingsController.primaryDns primaryDns.textFieldText = SettingsController.primaryDns
SettingsController.secondaryDns = "1.0.0.1" SettingsController.secondaryDns = "1.0.0.1"
@@ -100,9 +100,9 @@ PageType {
PageController.showNotificationMessage(qsTr("Settings have been reset")) PageController.showNotificationMessage(qsTr("Settings have been reset"))
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
@@ -124,6 +124,7 @@ PageType {
} }
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
+4 -3
View File
@@ -147,16 +147,16 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
SettingsController.clearLogs() SettingsController.clearLogs()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs have been cleaned up")) PageController.showNotificationMessage(qsTr("Logs have been cleaned up"))
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
@@ -172,6 +172,7 @@ PageType {
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
@@ -14,6 +14,8 @@ import "../Components"
PageType { PageType {
id: root id: root
property Item questionDrawerParent
Connections { Connections {
target: InstallController target: InstallController
@@ -94,15 +96,15 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
SettingsController.clearCachedProfiles() SettingsController.clearCachedProfiles()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
@@ -141,7 +143,7 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
ConnectionController.closeConnection() ConnectionController.closeConnection()
@@ -150,9 +152,9 @@ PageType {
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
@@ -172,7 +174,7 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
ConnectionController.closeConnection() ConnectionController.closeConnection()
@@ -180,9 +182,9 @@ PageType {
InstallController.removeAllContainers() InstallController.removeAllContainers()
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
@@ -192,6 +194,10 @@ PageType {
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
drawerHeight: 0.5
parent: questionDrawerParent
} }
} }
} }
@@ -71,15 +71,17 @@ PageType {
} }
actionButtonFunction: function() { actionButtonFunction: function() {
serverNameEditDrawer.visible = true serverNameEditDrawer.open()
} }
} }
DrawerType { Drawer2Type {
id: serverNameEditDrawer id: serverNameEditDrawer
parent: root
width: root.width width: root.width
height: root.height * 0.35 height: root.height // * 0.35
contentHeight: root.height * 0.35
onVisibleChanged: { onVisibleChanged: {
if (serverNameEditDrawer.visible) { if (serverNameEditDrawer.visible) {
@@ -88,6 +90,8 @@ PageType {
} }
ColumnLayout { ColumnLayout {
parent: serverNameEditDrawer.contentParent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -95,6 +99,7 @@ PageType {
anchors.leftMargin: 16 anchors.leftMargin: 16
anchors.rightMargin: 16 anchors.rightMargin: 16
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: serverName id: serverName
@@ -164,6 +169,7 @@ PageType {
} }
PageSettingsServerData { PageSettingsServerData {
stackView: root.stackView stackView: root.stackView
questionDrawerParent: root
} }
} }
} }
@@ -114,19 +114,19 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
questionDrawer.headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName()) questionDrawer.headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.") questionDrawer.descriptionText = qsTr("All users who you shared a connection with will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue") questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer() InstallController.removeCurrentlyProcessedContainer()
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
MouseArea { MouseArea {
@@ -141,6 +141,7 @@ PageType {
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
@@ -93,15 +93,22 @@ PageType {
SwitcherType { SwitcherType {
id: switcher id: switcher
property int lastActiveRouteMode: routeMode.onlyForwardSites
enabled: root.pageEnabled enabled: root.pageEnabled
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
checked: SitesModel.isSplitTunnelingEnabled() checked: SitesModel.routeMode !== routeMode.allSites
onToggled: { onToggled: {
SitesModel.toggleSplitTunneling(checked) if (checked) {
selector.text = root.routeModesModel[getRouteModesModelIndex()].name SitesModel.routeMode = lastActiveRouteMode
} else {
lastActiveRouteMode = SitesModel.routeMode
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
SitesModel.routeMode = routeMode.allSites
}
} }
} }
} }
@@ -109,6 +116,8 @@ PageType {
DropDownType { DropDownType {
id: selector id: selector
drawerParent: root
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16 Layout.leftMargin: 16
@@ -116,7 +125,7 @@ PageType {
drawerHeight: 0.4375 drawerHeight: 0.4375
enabled: root.pageEnabled enabled: switcher.checked && root.pageEnabled
headerText: qsTr("Mode") headerText: qsTr("Mode")
@@ -158,7 +167,7 @@ PageType {
anchors.topMargin: 16 anchors.topMargin: 16
contentHeight: col.implicitHeight + addSiteButton.implicitHeight + addSiteButton.anchors.bottomMargin + addSiteButton.anchors.topMargin contentHeight: col.implicitHeight + addSiteButton.implicitHeight + addSiteButton.anchors.bottomMargin + addSiteButton.anchors.topMargin
enabled: root.pageEnabled enabled: switcher.checked && root.pageEnabled
Column { Column {
id: col id: col
@@ -201,13 +210,13 @@ PageType {
questionDrawer.noButtonText = qsTr("Cancel") questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
SitesController.removeSite(index) SitesController.removeSite(index)
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false questionDrawer.close()
} }
questionDrawer.visible = true questionDrawer.open()
} }
} }
@@ -215,6 +224,7 @@ PageType {
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer
parent: root
} }
} }
} }
@@ -269,13 +279,18 @@ PageType {
} }
} }
DrawerType { Drawer2Type {
id: moreActionsDrawer id: moreActionsDrawer
width: parent.width width: parent.width
height: parent.height * 0.4375 height: parent.height
contentHeight: parent.height * 0.4375
parent: root
FlickableType { FlickableType {
parent: moreActionsDrawer.contentParent
anchors.fill: parent anchors.fill: parent
contentHeight: moreActionsDrawerContent.height contentHeight: moreActionsDrawerContent.height
ColumnLayout { ColumnLayout {
@@ -334,15 +349,20 @@ PageType {
} }
} }
DrawerType { Drawer2Type {
id: importSitesDrawer id: importSitesDrawer
width: parent.width width: parent.width
height: parent.height * 0.4375 height: parent.height
contentHeight: parent.height * 0.4375
parent: root
BackButtonType { BackButtonType {
id: importSitesDrawerBackButton id: importSitesDrawerBackButton
parent: importSitesDrawer.contentParent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -354,6 +374,8 @@ PageType {
} }
FlickableType { FlickableType {
parent: importSitesDrawer.contentParent
anchors.top: importSitesDrawerBackButton.bottom anchors.top: importSitesDrawerBackButton.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -25,14 +25,20 @@ PageType {
function onInstallContainerFinished(finishedMessage, isServiceInstall) { function onInstallContainerFinished(finishedMessage, isServiceInstall) {
if (!ConnectionController.isConnected && !isServiceInstall) { if (!ConnectionController.isConnected && !isServiceInstall) {
ContainersModel.setDefaultContainer(ContainersModel.getCurrentlyProcessedContainerIndex()) ContainersModel.setDefaultContainer(ContainersModel.getCurrentlyProcessedContainerIndex)
} }
PageController.closePage() // close installing page PageController.goToStartPage()
PageController.closePage() // close protocol settings page
if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageHome)) { if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageHome)) {
PageController.restorePageHomeState(true) PageController.restorePageHomeState(true)
} else if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageSettings)) {
PageController.goToPage(PageEnum.PageSettingsServersList, false)
PageController.goToPage(PageEnum.PageSettingsServerInfo, false)
if (isServiceInstall) {
PageController.goToPageSettingsServerServices()
}
} else {
PageController.goToPage(PageEnum.PageHome)
} }
PageController.showNotificationMessage(finishedMessage) PageController.showNotificationMessage(finishedMessage)
@@ -97,15 +97,20 @@ PageType {
} }
} }
DrawerType { Drawer2Type {
id: showDetailsDrawer id: showDetailsDrawer
width: parent.width width: parent.width
height: parent.height * 0.9 height: parent.height
contentHeight: parent.height * 0.9
parent: root
BackButtonType { BackButtonType {
id: showDetailsBackButton id: showDetailsBackButton
parent: showDetailsDrawer.contentParent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -117,6 +122,8 @@ PageType {
} }
FlickableType { FlickableType {
parent: showDetailsDrawer.contentParent
anchors.top: showDetailsBackButton.bottom anchors.top: showDetailsBackButton.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -115,7 +115,7 @@ PageType {
text: qsTr("I have the data to connect") text: qsTr("I have the data to connect")
onClicked: { onClicked: {
connectionTypeSelection.visible = true connectionTypeSelection.open()
} }
} }
@@ -134,12 +134,13 @@ PageType {
text: qsTr("I have nothing") text: qsTr("I have nothing")
onClicked: Qt.openUrlExternally("https://amnezia.org/instructions/0_starter-guide") onClicked: Qt.openUrlExternally("https://ru-docs.amnezia.org/guides/hosting-instructions")
} }
} }
ConnectionTypeSelectionDrawer { ConnectionTypeSelectionDrawer {
id: connectionTypeSelection id: connectionTypeSelection
parent: root
} }
} }
+10 -3
View File
@@ -118,7 +118,7 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
headerText: qsTr("Share VPN Access") headerText: qsTr("VPN Access")
} }
Rectangle { Rectangle {
@@ -171,14 +171,16 @@ PageType {
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 24 Layout.bottomMargin: 24
text: accessTypeSelector.currentIndex === 0 ? qsTr("Share VPN access without the ability to manage the server") : text: accessTypeSelector.currentIndex === 0 ? qsTr("VPN access without the ability to manage the server") :
qsTr("Share access to server management. The user with whom you share full access to the server will be able to add and remove any protocols and services to the server, as well as change settings.") qsTr("Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings.")
color: "#878B91" color: "#878B91"
} }
DropDownType { DropDownType {
id: serverSelector id: serverSelector
drawerParent: root
signal severSelectorIndexChanged signal severSelectorIndexChanged
property int currentIndex: 0 property int currentIndex: 0
@@ -241,6 +243,8 @@ PageType {
DropDownType { DropDownType {
id: protocolSelector id: protocolSelector
drawerParent: root
visible: accessTypeSelector.currentIndex === 0 visible: accessTypeSelector.currentIndex === 0
Layout.fillWidth: true Layout.fillWidth: true
@@ -330,6 +334,8 @@ PageType {
DropDownType { DropDownType {
id: exportTypeSelector id: exportTypeSelector
drawerParent: root
property int currentIndex: 0 property int currentIndex: 0
Layout.fillWidth: true Layout.fillWidth: true
@@ -371,6 +377,7 @@ PageType {
ShareConnectionDrawer { ShareConnectionDrawer {
id: shareConnectionDrawer id: shareConnectionDrawer
parent: root
} }
BasicButtonType { BasicButtonType {
+11 -1
View File
@@ -135,6 +135,8 @@ PageType {
var pagePath = PageController.getPagePath(PageEnum.PageHome) var pagePath = PageController.getPagePath(PageEnum.PageHome)
ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex
tabBarStackView.push(pagePath, { "objectName" : pagePath }) tabBarStackView.push(pagePath, { "objectName" : pagePath })
connectionTypeSelection.parent = tabBarStackView
} }
// onWidthChanged: { // onWidthChanged: {
@@ -174,6 +176,12 @@ PageType {
strokeColor: "#2C2D30" strokeColor: "#2C2D30"
fillColor: "#1C1D21" fillColor: "#1C1D21"
} }
MouseArea {
id: noPropagateMouseEvent
anchors.fill: parent
enabled: true
}
} }
TabImageButtonType { TabImageButtonType {
@@ -244,7 +252,9 @@ PageType {
ConnectionTypeSelectionDrawer { ConnectionTypeSelectionDrawer {
id: connectionTypeSelection id: connectionTypeSelection
onAboutToHide: { z: 1
onDrawerClosed: {
tabBar.setCurrentIndex(tabBar.previousIndex) tabBar.setCurrentIndex(tabBar.previousIndex)
} }
} }
+1 -23
View File
@@ -68,7 +68,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
// qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size(); // qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
} }
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString(); QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString(); QString dns2 = m_vpnConfiguration.value(config_key::dns1).toString();
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2); IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
@@ -329,8 +329,6 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
return; return;
} }
appendSplitTunnelingConfig();
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
m_vpnProtocol.reset(VpnProtocol::factory(container, m_vpnConfiguration)); m_vpnProtocol.reset(VpnProtocol::factory(container, m_vpnConfiguration));
if (!m_vpnProtocol) { if (!m_vpnProtocol) {
@@ -365,26 +363,6 @@ void VpnConnection::createProtocolConnections()
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
} }
void VpnConnection::appendSplitTunnelingConfig()
{
auto routeMode = m_settings->routeMode();
auto sites = m_settings->getVpnIps(routeMode);
QJsonArray sitesJsonArray;
for (const auto &site : sites) {
sitesJsonArray.append(site);
}
// Allow traffic to Amezia DNS
if (routeMode == Settings::VpnOnlyForwardSites){
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString());
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString());
}
m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode);
m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray);
}
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
void VpnConnection::restoreConnection() void VpnConnection::restoreConnection()
{ {
-2
View File
@@ -112,8 +112,6 @@ private:
#endif #endif
void createProtocolConnections(); void createProtocolConnections();
void appendSplitTunnelingConfig();
}; };
#endif // VPNCONNECTION_H #endif // VPNCONNECTION_H
+30 -61
View File
@@ -12,14 +12,13 @@ Usage:
Build AmneziaVPN android client. By default, a signed Android App Bundle (AAB) is built. Build AmneziaVPN android client. By default, a signed Android App Bundle (AAB) is built.
Options: Options:
-d, --debug Build debug version -d, --debug Build debug version
-a, --apk (<abi_list> | all) Build APKs for the specified ABIs or for all available ABIs -a, --apk <abi> Build APK for the specified ABI
Available ABIs: 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' Available ABIs: 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
<abi_list> - list of ABIs delimited by ';' -p, --platform <platform> The SDK platform used for building the Java code of the application
-b, --build-platform <platform> The SDK platform used for building the Java code of the application By default, the latest available platform is used
By default, the latest available platform is used -m, --move Move the build result to the root of the build directory
-m, --move Move the build result to the root of the build directory -h, --help Display this help
-h, --help Display this help
EOT EOT
} }
@@ -27,25 +26,21 @@ EOT
BUILD_TYPE="release" BUILD_TYPE="release"
AAB=1 AAB=1
opts=$(getopt -l debug,apk:,build-platform:,move,help -o "da:b:mh" -- "$@") opts=$(getopt -l debug,apk:,platform:,move,help -o "da:p:mh" -- "$@")
eval set -- "$opts" eval set -- "$opts"
while true; do while true; do
case "$1" in case "$1" in
-d | --debug) BUILD_TYPE="debug"; shift;; -d | --debug) BUILD_TYPE="debug"; shift;;
-a | --apk) ABIS=$2; unset AAB; shift 2;; -a | --apk) ABI=$2; unset AAB; shift 2;;
-b | --build-platform) ANDROID_BUILD_PLATFORM=$2; shift 2;; -p | --platform) ANDROID_PLATFORM=$2; shift 2;;
-m | --move) MOVE_RESULT=1; shift;; -m | --move) MOVE_RESULT=1; shift;;
-h | --help) usage; exit 0;; -h | --help) usage; exit 0;;
--) shift; break;; --) shift; break;;
esac esac
done done
# Validate ABIS parameter if [[ -v ABI && ! "$ABI" =~ ^(x86|x86_64|armeabi-v7a|arm64-v8a)$ ]]; then
if [[ -v ABIS && \ echo "The 'abi' option must be one of ['x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'], but is '$ABI'"
! "$ABIS" = "all" && \
! "$ABIS" =~ ^((x86|x86_64|armeabi-v7a|arm64-v8a);)*(x86|x86_64|armeabi-v7a|arm64-v8a)$ ]]; then
echo "The 'apk' option must be a list of ['x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a']" \
"delimited by ';' or 'all', but is '$ABIS'"
exit 1 exit 1
fi fi
@@ -61,19 +56,13 @@ OUT_APP_DIR=$BUILD_DIR/client
echo "Project dir: $PROJECT_DIR" echo "Project dir: $PROJECT_DIR"
echo "Build dir: $BUILD_DIR" echo "Build dir: $BUILD_DIR"
# Determine path to qt bin folder with qt-cmake if [ -v AAB ]; then
if [[ -v AAB || "$ABIS" = "all" ]]; then
qt_bin_dir_suffix="x86_64" qt_bin_dir_suffix="x86_64"
else else
if [[ $ABIS = *";"* ]]; then case $ABI in
oneOf=$(echo $ABIS | cut -d';' -f 1)
else
oneOf=$ABIS
fi
case $oneOf in
"armeabi-v7a") qt_bin_dir_suffix="armv7";; "armeabi-v7a") qt_bin_dir_suffix="armv7";;
"arm64-v8a") qt_bin_dir_suffix="arm64_v8a";; "arm64-v8a") qt_bin_dir_suffix="arm64_v8a";;
*) qt_bin_dir_suffix=$oneOf;; *) qt_bin_dir_suffix=$ABI;;
esac esac
fi fi
# get real path # get real path
@@ -90,10 +79,10 @@ echo "Using Android NDK in $ANDROID_NDK_ROOT"
# Run qt-cmake to configure build # Run qt-cmake to configure build
qt_cmake_opts=() qt_cmake_opts=()
if [[ -v AAB || "$ABIS" = "all" ]]; then if [ -v AAB ]; then
qt_cmake_opts+=(-DQT_ANDROID_BUILD_ALL_ABIS=ON) qt_cmake_opts+=(-DQT_ANDROID_BUILD_ALL_ABIS=ON)
else else
qt_cmake_opts+=(-DQT_ANDROID_ABIS="$ABIS") qt_cmake_opts+=(-DQT_ANDROID_ABIS="$ABI")
fi fi
# QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL=ON - Skip building apks as part of the default 'ALL' target # QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL=ON - Skip building apks as part of the default 'ALL' target
@@ -106,7 +95,7 @@ $QT_BIN_DIR/qt-cmake -S $PROJECT_DIR -B $BUILD_DIR \
# Build app # Build app
cmake --build $BUILD_DIR --config $BUILD_TYPE cmake --build $BUILD_DIR --config $BUILD_TYPE
# Build and package APK or AAB # Build and package APK or AAB. If this is a release, then additionally sign the result.
echo "Building APK/AAB..." echo "Building APK/AAB..."
deployqt_opts=() deployqt_opts=()
@@ -115,52 +104,32 @@ if [ -v AAB ]; then
deployqt_opts+=(--aab) deployqt_opts+=(--aab)
fi fi
if [ -v ANDROID_BUILD_PLATFORM ]; then if [ -v ANDROID_PLATFORM ]; then
deployqt_opts+=(--android-platform "$ANDROID_BUILD_PLATFORM") deployqt_opts+=(--android-platform "$ANDROID_PLATFORM")
fi fi
if [ "$BUILD_TYPE" = "release" ]; then if [ "$BUILD_TYPE" = "release" ]; then
deployqt_opts+=(--release) deployqt_opts+=(--release --sign)
fi fi
# for gradle to skip all tasks when it is executed by androiddeployqt
# gradle is started later explicitly
export ANDROIDDEPLOYQT_RUN=1
$QT_HOST_PATH/bin/androiddeployqt \ $QT_HOST_PATH/bin/androiddeployqt \
--input $OUT_APP_DIR/android-AmneziaVPN-deployment-settings.json \ --input $OUT_APP_DIR/android-AmneziaVPN-deployment-settings.json \
--output $OUT_APP_DIR/android-build \ --output $OUT_APP_DIR/android-build \
--gradle \
"${deployqt_opts[@]}" "${deployqt_opts[@]}"
# run gradle
gradle_opts=()
if [ -v AAB ]; then
gradle_opts+=(bundle"${BUILD_TYPE^}")
else
gradle_opts+=(assemble"${BUILD_TYPE^}")
fi
$OUT_APP_DIR/android-build/gradlew \
--project-dir $OUT_APP_DIR/android-build \
-DexplicitRun=1 \
"${gradle_opts[@]}"
if [[ -v CI || -v MOVE_RESULT ]]; then if [[ -v CI || -v MOVE_RESULT ]]; then
echo "Moving APK/AAB..." echo "Moving APK/AAB..."
if [ -v AAB ]; then if [ -v AAB ]; then
mv -u $OUT_APP_DIR/android-build/build/outputs/bundle/$BUILD_TYPE/AmneziaVPN-$BUILD_TYPE.aab \ mv -u $OUT_APP_DIR/android-build/build/outputs/bundle/$BUILD_TYPE/android-build-$BUILD_TYPE.aab \
$PROJECT_DIR/deploy/build/ $PROJECT_DIR/deploy/build/AmneziaVPN-$BUILD_TYPE.aab
else else
if [ "$ABIS" = "all" ]; then if [ "$BUILD_TYPE" = "release" ]; then
ABIS="x86;x86_64;armeabi-v7a;arm64-v8a" build_suffix="release-signed"
else
build_suffix=$BUILD_TYPE
fi fi
mv -u $OUT_APP_DIR/android-build/build/outputs/apk/$BUILD_TYPE/android-build-$build_suffix.apk \
IFS=';' read -r -a abi_array <<< "$ABIS" $PROJECT_DIR/deploy/build/AmneziaVPN-$ABI-$build_suffix.apk
for ABI in "${abi_array[@]}"
do
mv -u $OUT_APP_DIR/android-build/build/outputs/apk/$BUILD_TYPE/AmneziaVPN-$ABI-$BUILD_TYPE.apk \
$PROJECT_DIR/deploy/build/
done
fi fi
fi fi