mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-23 02:00:20 +07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e6ecfaf171 |
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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")
|
||||||
|
|||||||
+1
-1
Submodule client/3rd-prebuilt updated: fcf3022a27...ac32d33555
Vendored
+1
-1
Submodule client/3rd/awg-apple updated: 233eda6760...fab07138db
@@ -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());
|
||||||
|
|||||||
@@ -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
@@ -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
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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.
|
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Vendored
+115
-192
@@ -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" "$@"
|
||||||
|
|||||||
Vendored
+24
-32
@@ -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
|
||||||
|
|||||||
@@ -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'
|
||||||
@@ -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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|||||||
-1
@@ -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() {
|
||||||
|
|||||||
+2
-3
@@ -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) {
|
||||||
|
|||||||
+1
-2
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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" не реализовано,опция отключена</translation>
|
<translation>Раздельное туннелирование для "Wireguard" не реализовано,опция отключена</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
|
||||||
|
|
||||||
|
|||||||
@@ -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'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'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()
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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"))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,8 +112,6 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void createProtocolConnections();
|
void createProtocolConnections();
|
||||||
|
|
||||||
void appendSplitTunnelingConfig();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VPNCONNECTION_H
|
#endif // VPNCONNECTION_H
|
||||||
|
|||||||
+30
-61
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user