Compare commits
38 commits
dev
...
fixbug/bui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0b85813c7 | ||
|
|
484d33e922 | ||
|
|
b3cd6e03a2 | ||
|
|
1cdb8b33ee | ||
|
|
e4679ff1fb | ||
|
|
6ef03f9374 | ||
|
|
e6c3ece85c | ||
|
|
f8b951a428 | ||
|
|
ea1a7e8701 | ||
|
|
4700947fcb | ||
|
|
e1d23c6738 | ||
|
|
bfc2ac6c32 | ||
|
|
9fc1df38cc | ||
|
|
336850624f | ||
|
|
4287e5aa76 | ||
|
|
c9b61f48cf | ||
|
|
4c9a249695 | ||
|
|
d0db1293b5 | ||
|
|
aaa8030348 | ||
|
|
3c3d4252f2 | ||
|
|
56d6ab2dd5 | ||
|
|
b93c5f3e57 | ||
|
|
9ddfa54352 | ||
|
|
1e893c2260 | ||
|
|
7d186c3c84 | ||
|
|
31938899a2 | ||
|
|
b338a4fb79 | ||
|
|
0500f1887c | ||
|
|
226f9b9316 | ||
|
|
ae16b19515 | ||
|
|
5569524efa | ||
|
|
e2099b1682 | ||
|
|
022444ad63 | ||
|
|
7da585fc27 | ||
|
|
b1ad874f14 | ||
|
|
f7aa82331e | ||
|
|
b6601d051a | ||
|
|
1e60792f0c |
|
|
@ -1,39 +0,0 @@
|
||||||
BasedOnStyle: WebKit
|
|
||||||
AccessModifierOffset: '-4'
|
|
||||||
AlignAfterOpenBracket: Align
|
|
||||||
AlignConsecutiveMacros: 'true'
|
|
||||||
AlignTrailingComments: 'true'
|
|
||||||
AllowAllArgumentsOnNextLine: 'true'
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: 'true'
|
|
||||||
AllowShortBlocksOnASingleLine: 'false'
|
|
||||||
AllowShortCaseLabelsOnASingleLine: 'true'
|
|
||||||
AllowShortEnumsOnASingleLine: 'false'
|
|
||||||
AllowShortFunctionsOnASingleLine: None
|
|
||||||
AlwaysBreakTemplateDeclarations: 'No'
|
|
||||||
BreakBeforeBinaryOperators: NonAssignment
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
BraceWrapping:
|
|
||||||
AfterClass: true
|
|
||||||
AfterControlStatement: false
|
|
||||||
AfterEnum: false
|
|
||||||
AfterFunction: true
|
|
||||||
AfterNamespace: true
|
|
||||||
AfterObjCDeclaration: false
|
|
||||||
AfterStruct: true
|
|
||||||
AfterUnion: false
|
|
||||||
BeforeCatch: false
|
|
||||||
BeforeElse: false
|
|
||||||
IndentBraces: false
|
|
||||||
BreakConstructorInitializers: BeforeColon
|
|
||||||
ColumnLimit: '120'
|
|
||||||
CommentPragmas: '"^!|^:"'
|
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
|
|
||||||
ConstructorInitializerIndentWidth: '4'
|
|
||||||
ContinuationIndentWidth: '8'
|
|
||||||
IndentPPDirectives: BeforeHash
|
|
||||||
NamespaceIndentation: All
|
|
||||||
PenaltyExcessCharacter: '10'
|
|
||||||
PointerAlignment: Right
|
|
||||||
SortIncludes: 'true'
|
|
||||||
SpaceAfterTemplateKeyword: 'false'
|
|
||||||
Standard: Auto
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
/client/3rd
|
|
||||||
/client/3rd-prebuild
|
|
||||||
/client/android
|
|
||||||
/client/cmake
|
|
||||||
/client/core/serialization
|
|
||||||
/client/daemon
|
|
||||||
/client/fonts
|
|
||||||
/client/images
|
|
||||||
/client/ios
|
|
||||||
/client/mozilla
|
|
||||||
/client/platforms/dummy
|
|
||||||
/client/platforms/linux
|
|
||||||
/client/platforms/macos
|
|
||||||
/client/platforms/windows
|
|
||||||
/client/server_scripts
|
|
||||||
/client/translations
|
|
||||||
/deploy
|
|
||||||
/docs
|
|
||||||
/metadata
|
|
||||||
/service/src
|
|
||||||
192
.github/workflows/deploy.yml
vendored
|
|
@ -10,18 +10,13 @@ env:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Build-Linux-Ubuntu:
|
Build-Linux-Ubuntu:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.6.2
|
QT_VERSION: 6.6.2
|
||||||
QIF_VERSION: 4.7
|
QIF_VERSION: 4.7
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
|
||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install Qt'
|
- name: 'Install Qt'
|
||||||
|
|
@ -88,12 +83,7 @@ jobs:
|
||||||
QIF_VERSION: 4.7
|
QIF_VERSION: 4.7
|
||||||
BUILD_ARCH: 64
|
BUILD_ARCH: 64
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
|
||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Get sources'
|
- name: 'Get sources'
|
||||||
|
|
@ -156,12 +146,7 @@ jobs:
|
||||||
CC: cc
|
CC: cc
|
||||||
CXX: c++
|
CXX: c++
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
|
||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
|
|
@ -196,7 +181,7 @@ jobs:
|
||||||
- name: 'Install go'
|
- name: 'Install go'
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.24'
|
go-version: '1.22.1'
|
||||||
cache: false
|
cache: false
|
||||||
|
|
||||||
- name: 'Setup gomobile'
|
- name: 'Setup gomobile'
|
||||||
|
|
@ -223,11 +208,7 @@ jobs:
|
||||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin"
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin"
|
||||||
export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos"
|
export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos"
|
||||||
export PATH=$PATH:~/go/bin
|
export PATH=$PATH:~/go/bin
|
||||||
sh deploy/build_ios.sh | \
|
sh deploy/build_ios.sh
|
||||||
sed -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/d' | \
|
|
||||||
sed -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/d' | \
|
|
||||||
sed -e '/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DPROD_AGW_PUBLIC_KEY/d' | \
|
|
||||||
sed -e '/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DDEV_AGW_PUBLIC_KEY/d'
|
|
||||||
env:
|
env:
|
||||||
IOS_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }}
|
IOS_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }}
|
||||||
IOS_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
|
IOS_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
|
||||||
|
|
@ -249,88 +230,21 @@ jobs:
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-MacOS-old:
|
Build-MacOS:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
|
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
|
||||||
QT_VERSION: 6.4.3
|
QT_VERSION: 6.4.3
|
||||||
|
QIF_VERSION: 4.6
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
|
||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
with:
|
with:
|
||||||
xcode-version: '15.4.0'
|
xcode-version: '14.3.1'
|
||||||
|
|
||||||
- name: 'Install Qt'
|
|
||||||
uses: jurplel/install-qt-action@v3
|
|
||||||
with:
|
|
||||||
version: ${{ env.QT_VERSION }}
|
|
||||||
host: 'mac'
|
|
||||||
target: 'desktop'
|
|
||||||
arch: 'clang_64'
|
|
||||||
modules: 'qtremoteobjects qt5compat qtshadertools'
|
|
||||||
dir: ${{ runner.temp }}
|
|
||||||
setup-python: 'true'
|
|
||||||
set-env: 'true'
|
|
||||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
|
||||||
|
|
||||||
|
|
||||||
- name: 'Get sources'
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
fetch-depth: 10
|
|
||||||
|
|
||||||
- name: 'Setup ccache'
|
|
||||||
uses: hendrikmuhs/ccache-action@v1.2
|
|
||||||
|
|
||||||
- name: 'Build project'
|
|
||||||
run: |
|
|
||||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
|
||||||
bash deploy/build_macos.sh
|
|
||||||
|
|
||||||
- name: 'Upload installer artifact'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: AmneziaVPN_MacOS_old_installer
|
|
||||||
path: deploy/build/pkg/AmneziaVPN.pkg
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
- name: 'Upload unpacked artifact'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: AmneziaVPN_MacOS_old_unpacked
|
|
||||||
path: deploy/build/client/AmneziaVPN.app
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
Build-MacOS:
|
|
||||||
runs-on: macos-latest
|
|
||||||
|
|
||||||
env:
|
|
||||||
QT_VERSION: 6.8.0
|
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
|
||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: 'Setup xcode'
|
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
|
||||||
with:
|
|
||||||
xcode-version: '15.4.0'
|
|
||||||
|
|
||||||
- name: 'Install Qt'
|
- name: 'Install Qt'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
|
|
@ -345,6 +259,11 @@ jobs:
|
||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
|
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
|
||||||
|
run: |
|
||||||
|
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
|
||||||
|
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
|
||||||
|
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
|
||||||
|
|
||||||
- name: 'Get sources'
|
- name: 'Get sources'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
@ -358,13 +277,14 @@ jobs:
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
run: |
|
run: |
|
||||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
|
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
|
||||||
bash deploy/build_macos.sh
|
bash deploy/build_macos.sh
|
||||||
|
|
||||||
- name: 'Upload installer artifact'
|
- name: 'Upload installer artifact'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: AmneziaVPN_MacOS_installer
|
name: AmneziaVPN_MacOS_installer
|
||||||
path: deploy/build/pkg/AmneziaVPN.pkg
|
path: AmneziaVPN.dmg
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: 'Upload unpacked artifact'
|
- name: 'Upload unpacked artifact'
|
||||||
|
|
@ -374,6 +294,70 @@ jobs:
|
||||||
path: deploy/build/client/AmneziaVPN.app
|
path: deploy/build/client/AmneziaVPN.app
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
|
# ------------------------------------------------------
|
||||||
|
Build-MacOS-NE:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
QT_VERSION: 6.4.3
|
||||||
|
QIF_VERSION: 4.6
|
||||||
|
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/
|
||||||
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 'Setup Xcode'
|
||||||
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
|
with:
|
||||||
|
xcode-version: '14.3.1'
|
||||||
|
|
||||||
|
- name: 'Install desktop Qt'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'mac'
|
||||||
|
target: 'desktop'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtshadertools qtmultimedia'
|
||||||
|
arch: 'clang_64'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--base ${{ env.QT_MIRROR }}'
|
||||||
|
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
|
||||||
|
run: |
|
||||||
|
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
|
||||||
|
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
|
||||||
|
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
|
||||||
|
- name: 'Install Go'
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22.1'
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: 'Get sources'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
fetch-depth: 10
|
||||||
|
|
||||||
|
- name: 'Install dependencies'
|
||||||
|
run: pip install jsonschema jinja2
|
||||||
|
|
||||||
|
- name: 'Set execute permissions for deploy script'
|
||||||
|
run: chmod +x deploy/build_macos_ne.sh
|
||||||
|
|
||||||
|
- name: 'Build and deploy macOS NE'
|
||||||
|
run: |
|
||||||
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
|
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
|
||||||
|
export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos"
|
||||||
|
bash deploy/build_macos_ne.sh
|
||||||
|
env:
|
||||||
|
APPSTORE_CONNECT_KEY_ID: ${{ secrets.APPSTORE_CONNECT_KEY_ID }}
|
||||||
|
APPSTORE_CONNECT_ISSUER_ID: ${{ secrets.APPSTORE_CONNECT_ISSUER_ID }}
|
||||||
|
APPSTORE_CONNECT_PRIVATE_KEY: ${{ secrets.APPSTORE_CONNECT_PRIVATE_KEY }}
|
||||||
|
MAC_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }}
|
||||||
|
MAC_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
|
||||||
|
MAC_SIGNING_CERT_PASSWORD: ${{ secrets.IOS_SIGNING_CERT_PASSWORD }}
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-Android:
|
Build-Android:
|
||||||
|
|
@ -381,15 +365,10 @@ jobs:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
ANDROID_BUILD_PLATFORM: android-34
|
ANDROID_BUILD_PLATFORM: android-34
|
||||||
QT_VERSION: 6.7.3
|
QT_VERSION: 6.7.2
|
||||||
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
|
||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install desktop Qt'
|
- name: 'Install desktop Qt'
|
||||||
|
|
@ -401,8 +380,7 @@ jobs:
|
||||||
arch: 'linux_gcc_64'
|
arch: 'linux_gcc_64'
|
||||||
modules: ${{ env.QT_MODULES }}
|
modules: ${{ env.QT_MODULES }}
|
||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
py7zrversion: '==0.22.*'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
extra: '--base ${{ env.QT_MIRROR }}'
|
|
||||||
|
|
||||||
- name: 'Install android_x86_64 Qt'
|
- name: 'Install android_x86_64 Qt'
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
|
|
@ -413,8 +391,7 @@ jobs:
|
||||||
arch: 'android_x86_64'
|
arch: 'android_x86_64'
|
||||||
modules: ${{ env.QT_MODULES }}
|
modules: ${{ env.QT_MODULES }}
|
||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
py7zrversion: '==0.22.*'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
extra: '--base ${{ env.QT_MIRROR }}'
|
|
||||||
|
|
||||||
- name: 'Install android_x86 Qt'
|
- name: 'Install android_x86 Qt'
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
|
|
@ -425,8 +402,7 @@ jobs:
|
||||||
arch: 'android_x86'
|
arch: 'android_x86'
|
||||||
modules: ${{ env.QT_MODULES }}
|
modules: ${{ env.QT_MODULES }}
|
||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
py7zrversion: '==0.22.*'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
extra: '--base ${{ env.QT_MIRROR }}'
|
|
||||||
|
|
||||||
- name: 'Install android_armv7 Qt'
|
- name: 'Install android_armv7 Qt'
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
|
|
@ -437,8 +413,7 @@ jobs:
|
||||||
arch: 'android_armv7'
|
arch: 'android_armv7'
|
||||||
modules: ${{ env.QT_MODULES }}
|
modules: ${{ env.QT_MODULES }}
|
||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
py7zrversion: '==0.22.*'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
extra: '--base ${{ env.QT_MIRROR }}'
|
|
||||||
|
|
||||||
- name: 'Install android_arm64_v8a Qt'
|
- name: 'Install android_arm64_v8a Qt'
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
|
|
@ -449,8 +424,7 @@ jobs:
|
||||||
arch: 'android_arm64_v8a'
|
arch: 'android_arm64_v8a'
|
||||||
modules: ${{ env.QT_MODULES }}
|
modules: ${{ env.QT_MODULES }}
|
||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
py7zrversion: '==0.22.*'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
extra: '--base ${{ env.QT_MIRROR }}'
|
|
||||||
|
|
||||||
- name: 'Grant execute permission for qt-cmake'
|
- name: 'Grant execute permission for qt-cmake'
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
|
||||||
5
.github/workflows/tag-deploy.yml
vendored
|
|
@ -16,12 +16,7 @@ jobs:
|
||||||
QT_VERSION: 6.4.1
|
QT_VERSION: 6.4.1
|
||||||
QIF_VERSION: 4.5
|
QIF_VERSION: 4.5
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
|
||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Install desktop Qt'
|
- name: 'Install desktop Qt'
|
||||||
|
|
|
||||||
61
.github/workflows/tag-upload.yml
vendored
|
|
@ -1,41 +1,64 @@
|
||||||
name: 'Upload a new version'
|
name: 'Upload a new version'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
push:
|
||||||
inputs:
|
tags:
|
||||||
RELEASE_VERSION:
|
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
|
||||||
description: 'Release version (e.g. 1.2.3.4)'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Upload-S3:
|
upload:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
name: upload
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout CMakeLists.txt
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.RELEASE_VERSION }}
|
ref: ${{ github.ref_name }}
|
||||||
sparse-checkout: |
|
sparse-checkout: |
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
deploy/deploy_s3.sh
|
|
||||||
sparse-checkout-cone-mode: false
|
sparse-checkout-cone-mode: false
|
||||||
|
|
||||||
- name: Verify git tag
|
- name: Verify git tag
|
||||||
run: |
|
run: |
|
||||||
TAG_NAME=${{ inputs.RELEASE_VERSION }}
|
GIT_TAG=${{ github.ref_name }}
|
||||||
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
|
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
|
||||||
if [[ "$TAG_NAME" == "$CMAKE_TAG" ]]; then
|
|
||||||
echo "Git tag ($TAG_NAME) matches CMakeLists.txt version ($CMAKE_TAG)."
|
if [[ "$GIT_TAG" == "$CMAKE_TAG" ]]; then
|
||||||
|
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are the same. Continuing..."
|
||||||
else
|
else
|
||||||
echo "::error::Mismatch: Git tag ($TAG_NAME) != CMakeLists.txt version ($CMAKE_TAG). Exiting with error..."
|
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Setup Rclone
|
- name: Download artifacts from the "${{ github.ref_name }}" tag
|
||||||
uses: AnimMouse/setup-rclone@v1
|
uses: robinraju/release-downloader@v1.8
|
||||||
with:
|
with:
|
||||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
tag: ${{ github.ref_name }}
|
||||||
|
fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*"
|
||||||
|
out-file-path: ${{ github.ref_name }}
|
||||||
|
|
||||||
- name: Send dist to S3
|
- name: Upload beta version
|
||||||
run: bash deploy/deploy_s3.sh ${{ inputs.RELEASE_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 }}
|
||||||
|
|
|
||||||
6
.gitignore
vendored
|
|
@ -133,8 +133,4 @@ client/3rd/ShadowSocks/ss_ios.xcconfig
|
||||||
out/
|
out/
|
||||||
|
|
||||||
# CMake files
|
# CMake files
|
||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
|
|
||||||
ios-ne-build.sh
|
|
||||||
macos-ne-build.sh
|
|
||||||
macos-signed-build.sh
|
|
||||||
6
.gitmodules
vendored
|
|
@ -1,3 +1,7 @@
|
||||||
|
[submodule "client/3rd/OpenVPNAdapter"]
|
||||||
|
path = client/3rd/OpenVPNAdapter
|
||||||
|
url = https://github.com/amnezia-vpn/OpenVPNAdapter.git
|
||||||
|
branch = macos-dirty-build
|
||||||
[submodule "client/3rd/qtkeychain"]
|
[submodule "client/3rd/qtkeychain"]
|
||||||
path = client/3rd/qtkeychain
|
path = client/3rd/qtkeychain
|
||||||
url = https://github.com/frankosterfeld/qtkeychain.git
|
url = https://github.com/frankosterfeld/qtkeychain.git
|
||||||
|
|
@ -7,7 +11,7 @@
|
||||||
[submodule "client/3rd-prebuilt"]
|
[submodule "client/3rd-prebuilt"]
|
||||||
path = client/3rd-prebuilt
|
path = client/3rd-prebuilt
|
||||||
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
||||||
branch = feature/special-handshake
|
branch = fixbug/mac-network-extension
|
||||||
[submodule "client/3rd/amneziawg-apple"]
|
[submodule "client/3rd/amneziawg-apple"]
|
||||||
path = client/3rd/amneziawg-apple
|
path = client/3rd/amneziawg-apple
|
||||||
url = https://github.com/amnezia-vpn/amneziawg-apple
|
url = https://github.com/amnezia-vpn/amneziawg-apple
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.8.8.1
|
project(${PROJECT} VERSION 4.8.1.9
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
HOMEPAGE_URL "https://amnezia.org/"
|
HOMEPAGE_URL "https://amnezia.org/"
|
||||||
)
|
)
|
||||||
|
|
@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 2087)
|
set(APP_ANDROID_VERSION_CODE 65)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
|
|
@ -26,18 +26,20 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
|
||||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
||||||
set(MZ_PLATFORM_NAME "wasm")
|
set(MZ_PLATFORM_NAME "wasm")
|
||||||
endif()
|
endif()
|
||||||
|
message(STATUS "Platform: ${MZ_PLATFORM_NAME}")
|
||||||
|
message(STATUS "Version: ${MACOS_NE}")
|
||||||
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if(APPLE AND NOT IOS)
|
if((APPLE AND NOT IOS) OR (DEFINED MACOS_NE AND MACOS_NE AND NOT IOS))
|
||||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(client)
|
add_subdirectory(client)
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID)
|
# Mac OSX with Network Extension don't need service
|
||||||
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
add_subdirectory(service)
|
add_subdirectory(service)
|
||||||
|
|
||||||
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
||||||
|
|
|
||||||
34
README.md
|
|
@ -1,31 +1,30 @@
|
||||||
# Amnezia VPN
|
# Amnezia VPN
|
||||||
|
## _The best client for self-hosted VPN_
|
||||||
### _The best client for self-hosted VPN_
|
|
||||||
|
|
||||||
|
|
||||||
[](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
|
[](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
|
||||||
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
||||||
|
|
||||||
### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md)
|
Amnezia is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
[Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
|
<br>
|
||||||
|
|
||||||
[](https://amnezia.org)
|
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_4.7.0.0_x64.exe"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/win.png" width="150" style="max-width: 100%;"></a>
|
||||||
|
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_4.7.0.0.dmg"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/mac.png" width="150" style="max-width: 100%;"></a>
|
||||||
|
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_Linux_4.7.0.0.tar.zip"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/lin.png" width="150" style="max-width: 100%;"></a>
|
||||||
|
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/tag/4.7.0.0"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/andr.png" width="150" style="max-width: 100%;"></a>
|
||||||
|
|
||||||
### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
|
<br>
|
||||||
|
|
||||||
> [!TIP]
|
<a href="https://play.google.com/store/search?q=amnezia+vpn&c=apps"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/play.png" width="150" style="max-width: 100%;"></a>
|
||||||
> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org ).
|
<a href="https://apps.apple.com/us/app/amneziavpn/id1600529900"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/apl.png" width="150" style="max-width: 100%;"></a>
|
||||||
|
|
||||||
<a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
|
|
||||||
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
|
|
||||||
|
|
||||||
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
|
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
|
||||||
|
|
||||||
<br/>
|
<br>
|
||||||
|
|
||||||
<a href="https://www.testiny.io"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/testiny.png" height="28px"></a>
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|
@ -38,8 +37,7 @@
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
- [https://amnezia.org](https://amnezia.org) - Project website | [Alternative link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org)
|
- [https://amnezia.org](https://amnezia.org) - project website
|
||||||
- [https://docs.amnezia.org](https://docs.amnezia.org) - Documentation
|
|
||||||
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
|
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
|
||||||
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
|
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
|
||||||
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi)
|
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi)
|
||||||
|
|
@ -185,11 +183,11 @@ GPL v3.0
|
||||||
|
|
||||||
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
|
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
|
||||||
|
|
||||||
Bitcoin: bc1qmhtgcf9637rl3kqyy22r2a8wa8laka4t9rx2mf <br>
|
Bitcoin: bc1q26eevjcg9j0wuyywd2e3uc9cs2w58lpkpjxq6p <br>
|
||||||
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
|
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
|
||||||
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
|
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
|
||||||
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br>
|
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3
|
||||||
TON: UQDpU1CyKRmg7L8mNScKk9FRc2SlESuI7N-Hby4nX-CcVmns
|
|
||||||
## Acknowledgments
|
## Acknowledgments
|
||||||
|
|
||||||
This project is tested with BrowserStack.
|
This project is tested with BrowserStack.
|
||||||
|
|
|
||||||
181
README_RU.md
|
|
@ -1,181 +0,0 @@
|
||||||
# Amnezia VPN
|
|
||||||
|
|
||||||
### _Лучший клиент для создания VPN на собственном сервере_
|
|
||||||
|
|
||||||
[](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
|
|
||||||
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
|
||||||
|
|
||||||
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
|
|
||||||
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
|
||||||
|
|
||||||
[](https://amnezia.org)
|
|
||||||
|
|
||||||
### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
|
|
||||||
|
|
||||||
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
|
|
||||||
|
|
||||||
|
|
||||||
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<a href="https://www.testiny.io"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/testiny.png" height="28px"></a>
|
|
||||||
|
|
||||||
## Особенности
|
|
||||||
|
|
||||||
- Простой в использовании — введите IP-адрес, SSH-логин и пароль, и Amnezia автоматически установит VPN-контейнеры Docker на ваш сервер и подключится к VPN.
|
|
||||||
- Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2.
|
|
||||||
- Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
|
|
||||||
- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них.
|
|
||||||
- Поддерживает платформы: Windows, macOS, Linux, Android, iOS.
|
|
||||||
- Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
|
|
||||||
|
|
||||||
## Ссылки
|
|
||||||
|
|
||||||
- [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
|
|
||||||
- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
|
|
||||||
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
|
|
||||||
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддержки в Telegram (Английский)
|
|
||||||
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддержки в Telegram (Фарси)
|
|
||||||
- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддержки в Telegram (Мьянма)
|
|
||||||
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддержки в Telegram (Русский)
|
|
||||||
- [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium | [Зеркало](https://storage.googleapis.com/kldscp/vpnpay.io/ru/amnezia-premium\)
|
|
||||||
|
|
||||||
## Технологии
|
|
||||||
|
|
||||||
AmneziaVPN использует несколько проектов с открытым исходным кодом:
|
|
||||||
|
|
||||||
- [OpenSSL](https://www.openssl.org/)
|
|
||||||
- [OpenVPN](https://openvpn.net/)
|
|
||||||
- [Shadowsocks](https://shadowsocks.org/)
|
|
||||||
- [Qt](https://www.qt.io/)
|
|
||||||
- [LibSsh](https://libssh.org)
|
|
||||||
- и другие...
|
|
||||||
|
|
||||||
## Проверка исходного кода
|
|
||||||
После клонирования репозитория обязательно загрузите все подмодули.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git submodule update --init --recursive
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Разработка
|
|
||||||
Хотите внести свой вклад? Добро пожаловать!
|
|
||||||
|
|
||||||
### Помощь с переводами
|
|
||||||
|
|
||||||
Загрузите самые актуальные файлы перевода.
|
|
||||||
|
|
||||||
Перейдите на [вкладку "Actions"](https://github.com/amnezia-vpn/amnezia-client/actions?query=is%3Asuccess+branch%3Adev), нажмите на первую строку. Затем прокрутите вниз до раздела "Artifacts" и скачайте "AmneziaVPN_translations".
|
|
||||||
|
|
||||||
Распакуйте этот файл. Каждый файл с расширением *.ts содержит строки для соответствующего языка.
|
|
||||||
|
|
||||||
Переведите или исправьте строки в одном или нескольких файлах *.ts и загрузите их обратно в этот репозиторий в папку ``client/translations``. Это можно сделать через веб-интерфейс или любым другим знакомым вам способом.
|
|
||||||
|
|
||||||
### Сборка исходного кода и деплой
|
|
||||||
Проверьте папку deploy для скриптов сборки.
|
|
||||||
|
|
||||||
### Как собрать iOS-приложение из исходного кода на MacOS
|
|
||||||
1. Убедитесь, что у вас установлен Xcode версии 14 или выше.
|
|
||||||
2. Для генерации проекта Xcode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
|
|
||||||
- MacOS
|
|
||||||
- iOS
|
|
||||||
- Модуль совместимости с Qt 5
|
|
||||||
- Qt Shader Tools
|
|
||||||
- Дополнительные библиотеки:
|
|
||||||
- Qt Image Formats
|
|
||||||
- Qt Multimedia
|
|
||||||
- Qt Remote Objects
|
|
||||||
|
|
||||||
|
|
||||||
3. Установите CMake, если это необходимо. Рекомендуемая версия — 3.25. Скачать CMake можно здесь.
|
|
||||||
4. Установите Go версии >= v1.16. Если Go ещё не установлен, скачайте его с [официального сайта](https://golang.org/dl/) или используйте Homebrew. Установите gomobile:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export PATH=$PATH:~/go/bin
|
|
||||||
go install golang.org/x/mobile/cmd/gomobile@latest
|
|
||||||
gomobile init
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Соберите проект:
|
|
||||||
```bash
|
|
||||||
export QT_BIN_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/ios/bin"
|
|
||||||
export QT_MACOS_ROOT_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/macos"
|
|
||||||
export QT_IOS_BIN=$QT_BIN_DIR
|
|
||||||
export PATH=$PATH:~/go/bin
|
|
||||||
mkdir build-ios
|
|
||||||
$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
|
|
||||||
```
|
|
||||||
Замените <PATH-TO-QT-FOLDER> и <QT-VERSION> на ваши значения.
|
|
||||||
|
|
||||||
Если появляется ошибка gomobile: command not found, убедитесь, что PATH настроен на папку bin, где установлен gomobile:
|
|
||||||
```bash
|
|
||||||
export PATH=$(PATH):/path/to/GOPATH/bin
|
|
||||||
```
|
|
||||||
|
|
||||||
6. Откройте проект в Xcode. Теперь вы можете тестировать, архивировать или публиковать приложение.
|
|
||||||
|
|
||||||
Если сборка завершится с ошибкой:
|
|
||||||
```
|
|
||||||
make: ***
|
|
||||||
[$(PROJECTDIR)/client/build/AmneziaVPN.build/Debug-iphoneos/wireguard-go-bridge/goroot/.prepared]
|
|
||||||
Error 1
|
|
||||||
```
|
|
||||||
Добавьте пользовательскую переменную PATH в настройки сборки для целей AmneziaVPN и WireGuardNetworkExtension с ключом `PATH` и значением `${PATH}/path/to/bin/folder/with/go/executable`, e.g. `${PATH}:/usr/local/go/bin`.
|
|
||||||
|
|
||||||
Если ошибка повторяется на Mac с M1, установите версию CMake для архитектуры ARM:
|
|
||||||
```
|
|
||||||
arch -arm64 brew install cmake
|
|
||||||
```
|
|
||||||
|
|
||||||
При первой попытке сборка может завершиться с ошибкой source files not found. Это происходит из-за параллельной компиляции зависимостей в XCode. Просто перезапустите сборку.
|
|
||||||
|
|
||||||
|
|
||||||
## Как собрать Android-приложение
|
|
||||||
Сборка тестировалась на MacOS. Требования:
|
|
||||||
- JDK 11
|
|
||||||
- Android SDK 33
|
|
||||||
- CMake 3.25.0
|
|
||||||
|
|
||||||
Установите QT, QT Creator и Android Studio.
|
|
||||||
Настройте QT Creator:
|
|
||||||
|
|
||||||
- В меню QT Creator перейдите в `QT Creator` -> `Preferences` -> `Devices` ->`Android`.
|
|
||||||
- Укажите путь к JDK 11.
|
|
||||||
- Укажите путь к Android SDK (`$ANDROID_HOME`)
|
|
||||||
|
|
||||||
Если вы сталкиваетесь с ошибками, связанными с отсутствием SDK или сообщением «SDK manager not running», их нельзя исправить просто корректировкой путей. Если у вас есть несколько свободных гигабайт на диске, вы можете позволить Qt Creator установить все необходимые компоненты, выбрав пустую папку для расположения Android SDK и нажав кнопку **Set Up SDK**. Учтите: это установит второй Android SDK и NDK на вашем компьютере!
|
|
||||||
|
|
||||||
Убедитесь, что настроена правильная версия CMake: перейдите в **Qt Creator -> Preferences** и в боковом меню выберите пункт **Kits**. В центральной части окна, на вкладке **Kits**, найдите запись для инструмента **CMake Tool**. Если выбранная по умолчанию версия CMake ниже 3.25.0, установите на свою систему CMake версии 3.25.0 или выше, а затем выберите опцию **System CMake at <путь>** из выпадающего списка. Если этот пункт отсутствует, это может означать, что вы еще не установили CMake, или Qt Creator не смог найти путь к нему. В таком случае в окне **Preferences** перейдите в боковое меню **CMake**, затем во вкладку **Tools** в центральной части окна и нажмите кнопку **Add**, чтобы указать путь к установленному CMake.
|
|
||||||
|
|
||||||
Убедитесь, что для вашего проекта выбрана Android Platform SDK 33: в главном окне на боковой панели выберите пункт **Projects**, и слева вы увидите раздел **Build & Run**, показывающий различные целевые Android-платформы. Вы можете выбрать любую из них, так как настройка проекта Amnezia VPN разработана таким образом, чтобы все Android-цели могли быть собраны. Перейдите в подраздел **Build** и прокрутите центральную часть окна до раздела **Build Steps**. Нажмите **Details** в заголовке **Build Android APK** (кнопка **Details** может быть скрыта, если окно Qt Creator не запущено в полноэкранном режиме!). Вот здесь выберите **android-33** в качестве Android Build Platform SDK.
|
|
||||||
|
|
||||||
### Разработка Android-компонентов
|
|
||||||
|
|
||||||
После сборки QT Creator копирует проект в отдельную папку, например, `build-amnezia-client-Android_Qt_<version>_Clang_<architecture>-<BuildType>`. Для разработки Android-компонентов откройте сгенерированный проект в Android Studio, указав папку `build-amnezia-client-Android_Qt_<version>_Clang_<architecture>-<BuildType>/client/android-build` в качестве корневой.
|
|
||||||
Изменения в сгенерированном проекте нужно вручную перенести в репозиторий. После этого можно коммитить изменения.
|
|
||||||
Если возникают проблемы со сборкой в QT Creator после работы в Android Studio, выполните команду `./gradlew clean` в корневой папке сгенерированного проекта (`<path>/client/android-build/.`).
|
|
||||||
|
|
||||||
|
|
||||||
## Лицензия
|
|
||||||
|
|
||||||
GPL v3.0
|
|
||||||
|
|
||||||
## Донаты
|
|
||||||
|
|
||||||
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
|
|
||||||
|
|
||||||
Bitcoin: bc1qmhtgcf9637rl3kqyy22r2a8wa8laka4t9rx2mf <br>
|
|
||||||
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
|
|
||||||
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
|
|
||||||
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br>
|
|
||||||
TON: UQDpU1CyKRmg7L8mNScKk9FRc2SlESuI7N-Hby4nX-CcVmns
|
|
||||||
|
|
||||||
## Благодарности
|
|
||||||
|
|
||||||
Этот проект тестируется с помощью BrowserStack.
|
|
||||||
Мы выражаем благодарность [BrowserStack](https://www.browserstack.com) за поддержку нашего проекта.
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 840b7b070e6ac8b90dda2fac6e98859b23727c0c
|
Subproject commit 435aaa793d8ce455ef4a3b2f5ff5e38f187d8efb
|
||||||
1
client/3rd/OpenVPNAdapter
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 33afba081c8592e8632128c7f9d6ebe53cae3d08
|
||||||
2
client/3rd/amneziawg-apple
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 811af0a83b3faeade89a9093a588595666d32066
|
Subproject commit 76e7db556a6d7e2582f9481df91db188a46c009c
|
||||||
|
|
@ -25,16 +25,17 @@ execute_process(
|
||||||
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
||||||
|
|
||||||
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
|
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
|
||||||
add_definitions(-DPROD_S3_ENDPOINT="$ENV{PROD_S3_ENDPOINT}")
|
add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}")
|
||||||
|
|
||||||
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
||||||
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
|
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
|
||||||
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
|
|
||||||
|
|
||||||
add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}")
|
if(IOS OR MACOS_NE)
|
||||||
add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}")
|
set(PACKAGES ${PACKAGES} Multimedia)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
#Macos Network Extension doesn't need Widgets
|
||||||
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
||||||
set(PACKAGES ${PACKAGES} Widgets)
|
set(PACKAGES ${PACKAGES} Widgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -47,14 +48,23 @@ set(LIBS ${LIBS}
|
||||||
Qt6::Core5Compat Qt6::Concurrent
|
Qt6::Core5Compat Qt6::Concurrent
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
if(IOS OR MACOS_NE)
|
||||||
|
set(LIBS ${LIBS} Qt6::Multimedia)
|
||||||
|
endif()
|
||||||
|
# message("Client desktop build ", ${MACOS_NE})
|
||||||
|
#Macos Network Extension doesn't need Widgets
|
||||||
|
|
||||||
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
||||||
|
message("Run this block when MACOS_NE is not defined or set to FALSE")
|
||||||
set(LIBS ${LIBS} Qt6::Widgets)
|
set(LIBS ${LIBS} Qt6::Widgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
qt_standard_project_setup()
|
qt_standard_project_setup()
|
||||||
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
# TODO error in there
|
||||||
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
||||||
|
message("Run this block when MACOS_NE is not defined or set to FALSE")
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
|
||||||
|
|
@ -91,6 +101,19 @@ configure_file(${CMAKE_CURRENT_LIST_DIR}/translations/translations.qrc.in ${CMAK
|
||||||
qt6_add_resources(QRC ${I18NQRC} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
|
qt6_add_resources(QRC ${I18NQRC} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
|
||||||
# -- i18n end
|
# -- i18n end
|
||||||
|
|
||||||
|
if(IOS)
|
||||||
|
message("Building for iOS")
|
||||||
|
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/ios/scripts/openvpn.sh args
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Build openvpn adapter for MacOS Network Extension
|
||||||
|
if(MACOS_NE)
|
||||||
|
message("Building for MacOS Network Extension")
|
||||||
|
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/macos/scripts/openvpn.sh args
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
set(IS_CI ${CI})
|
set(IS_CI ${CI})
|
||||||
if(IS_CI)
|
if(IS_CI)
|
||||||
message("Detected CI env")
|
message("Detected CI env")
|
||||||
|
|
@ -100,8 +123,8 @@ if(IS_CI)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake)
|
include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake)
|
||||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/sources.cmake)
|
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../ipc
|
${CMAKE_CURRENT_LIST_DIR}/../ipc
|
||||||
|
|
@ -110,22 +133,177 @@ include_directories(
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
configure_file(${CMAKE_CURRENT_LIST_DIR}/../version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
||||||
|
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/migrations.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/amnezia_application.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/defs.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/controllers/apiController.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/pages.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/version.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/serialization.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/transfer.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/enums/apiEnums.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.h
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mozilla headres
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/mozilla/controllerimpl.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.h
|
||||||
|
)
|
||||||
|
|
||||||
include_directories(mozilla)
|
include_directories(mozilla)
|
||||||
include_directories(mozilla/shared)
|
include_directories(mozilla/shared)
|
||||||
include_directories(mozilla/models)
|
include_directories(mozilla/models)
|
||||||
|
|
||||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/../version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
if(MACOS_NE)
|
||||||
|
message("MACOS_NE is ON")
|
||||||
|
add_definitions(-DQ_OS_IOS)
|
||||||
|
add_definitions(-DMACOS_NE)
|
||||||
|
message("Add macros for MacOS Network Extension")
|
||||||
|
else()
|
||||||
|
message("MACOS_NE is OFF")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if(NOT IOS AND NOT MACOS_NE)
|
||||||
|
message(" Add header for non-IOS and non-MACOS_NE")
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if(NOT ANDROID)
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/migrations.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/amnezia_application.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/controllers/apiController.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/outbound.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/inbound.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/ss.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/ssd.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vless.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/trojan.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess_new.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mozilla sources
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.cpp
|
||||||
|
)
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
|
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT IOS AND NOT MACOS_NE)
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT ANDROID)
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.h)
|
||||||
|
file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/pages_logic/*.h)
|
||||||
|
file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/pages_logic/*.cpp)
|
||||||
|
|
||||||
|
file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.h)
|
||||||
|
file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.cpp)
|
||||||
|
|
||||||
|
file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/models/*.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.h
|
||||||
|
)
|
||||||
|
file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/models/*.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h)
|
||||||
|
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp)
|
||||||
|
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${COMMON_FILES_H}
|
||||||
|
${PAGE_LOGIC_H}
|
||||||
|
${CONFIGURATORS_H}
|
||||||
|
${UI_MODELS_H}
|
||||||
|
${UI_CONTROLLERS_H}
|
||||||
|
)
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${COMMON_FILES_CPP}
|
||||||
|
${PAGE_LOGIC_CPP}
|
||||||
|
${CONFIGURATORS_CPP}
|
||||||
|
${UI_MODELS_CPP}
|
||||||
|
${UI_CONTROLLERS_CPP}
|
||||||
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
configure_file(
|
configure_file(
|
||||||
${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in
|
${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
|
${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(RESOURCES ${RESOURCES}
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
|
||||||
|
)
|
||||||
|
|
||||||
set(LIBS ${LIBS}
|
set(LIBS ${LIBS}
|
||||||
user32
|
user32
|
||||||
rasapi32
|
rasapi32
|
||||||
|
|
@ -166,9 +344,34 @@ if(LINUX AND NOT ANDROID)
|
||||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
# Macos Network Extension doesn't need
|
||||||
message("Client desktop build")
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
||||||
|
message("-----> Client desktop build")
|
||||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
add_compile_definitions(AMNEZIA_DESKTOP)
|
||||||
|
message("-----> Add AMNEZIA_DESKTOP")
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/systemtray_notificationhandler.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnprotocol.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/xrayprotocol.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/systemtray_notificationhandler.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnprotocol.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/xrayprotocol.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.cpp
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
|
|
@ -178,9 +381,12 @@ endif()
|
||||||
if(IOS)
|
if(IOS)
|
||||||
include(cmake/ios.cmake)
|
include(cmake/ios.cmake)
|
||||||
include(cmake/ios-arch-fixup.cmake)
|
include(cmake/ios-arch-fixup.cmake)
|
||||||
elseif(APPLE AND NOT IOS)
|
elseif(APPLE AND NOT IOS AND NOT DEFINED MACOS_NE)
|
||||||
include(cmake/osxtools.cmake)
|
# include(cmake/osxtools.cmake)
|
||||||
include(cmake/macos.cmake)
|
include(cmake/macos.cmake)
|
||||||
|
elseif(APPLE AND NOT IOS AND MACOS_NE)
|
||||||
|
include(cmake/osxtools.cmake)
|
||||||
|
include(cmake/macos_ne.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
||||||
|
|
@ -199,7 +405,7 @@ elseif(APPLE AND NOT IOS)
|
||||||
set(DEPLOY_PLATFORM_PATH "macos")
|
set(DEPLOY_PLATFORM_PATH "macos")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID)
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET ${PROJECT} POST_BUILD
|
TARGET ${PROJECT} POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
||||||
|
|
@ -218,4 +424,35 @@ if(NOT IOS AND NOT ANDROID)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||||
|
if(MACOS_NE)
|
||||||
|
# MacOS specific bundle operations
|
||||||
|
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||||
|
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks"
|
||||||
|
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
"$ENV{QT_HOST_PATH}/lib/QtConcurrent.framework"
|
||||||
|
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/QtConcurrent.framework"
|
||||||
|
|
||||||
|
COMMENT "Copying QtConcurrent.framework from QT_HOST_PATH to the bundle's Frameworks directory"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# MacOS specific application deployment
|
||||||
|
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
# MacOS specific code signing for Release
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
SET(SIGN_CMD codesign --deep --force --sign 'Apple Distribution: Privacy Technologies OU \(X7UJ388FXK\)' --timestamp --options runtime $<TARGET_BUNDLE_DIR:AmneziaVPN>)
|
||||||
|
message("Manual signing bundle...")
|
||||||
|
message(${SIGN_CMD})
|
||||||
|
|
||||||
|
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${SIGN_CMD}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
qt_finalize_target(${PROJECT})
|
qt_finalize_target(${PROJECT})
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
#include <QLocalServer>
|
|
||||||
#include <QLocalSocket>
|
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QQuickItem>
|
#include <QQuickItem>
|
||||||
#include <QQuickStyle>
|
#include <QQuickStyle>
|
||||||
|
|
@ -12,16 +10,26 @@
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
#include <QLocalSocket>
|
||||||
|
#include <QLocalServer>
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "ui/controllers/pageController.h"
|
|
||||||
#include "ui/models/installedAppsModel.h"
|
#include "ui/models/installedAppsModel.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include "platforms/ios/QRCodeReaderBase.h"
|
#include "platforms/ios/QRCodeReaderBase.h"
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
#include "core/installedAppsImageProvider.h"
|
||||||
|
#include "platforms/android/android_controller.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "protocols/qml_register_protocols.h"
|
#include "protocols/qml_register_protocols.h"
|
||||||
|
|
||||||
|
#if defined(Q_OS_IOS)
|
||||||
|
#include "platforms/ios/ios_controller.h"
|
||||||
|
#include <AmneziaVPN-Swift.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
|
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
|
||||||
{
|
{
|
||||||
setQuitOnLastWindowClosed(false);
|
setQuitOnLastWindowClosed(false);
|
||||||
|
|
@ -76,12 +84,78 @@ void AmneziaApplication::init()
|
||||||
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
||||||
m_vpnConnectionThread.start();
|
m_vpnConnectionThread.start();
|
||||||
|
|
||||||
m_coreController.reset(new CoreController(m_vpnConnection, m_settings, m_engine));
|
initModels();
|
||||||
|
loadTranslator();
|
||||||
|
initControllers();
|
||||||
|
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
if (!AndroidController::initLogging()) {
|
||||||
|
qFatal("Android logging initialization failed");
|
||||||
|
}
|
||||||
|
AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
|
||||||
|
connect(m_settings.get(), &Settings::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
|
||||||
|
|
||||||
|
AndroidController::instance()->setScreenshotsEnabled(m_settings->isScreenshotsEnabled());
|
||||||
|
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
|
||||||
|
|
||||||
|
connect(m_settings.get(), &Settings::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
|
||||||
|
|
||||||
|
connect(m_settings.get(), &Settings::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
|
||||||
|
|
||||||
|
connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) {
|
||||||
|
m_connectionController->onConnectionStateChanged(state);
|
||||||
|
if (m_vpnConnection)
|
||||||
|
m_vpnConnection->restoreConnection();
|
||||||
|
});
|
||||||
|
if (!AndroidController::instance()->initialize()) {
|
||||||
|
qFatal("Android controller initialization failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) {
|
||||||
|
m_pageController->goToPageHome();
|
||||||
|
m_importController->extractConfigFromData(data);
|
||||||
|
m_pageController->goToPageViewConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
IosController::Instance()->initialize();
|
||||||
|
connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) {
|
||||||
|
m_pageController->goToPageHome();
|
||||||
|
m_importController->extractConfigFromData(data);
|
||||||
|
m_pageController->goToPageViewConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) {
|
||||||
|
m_pageController->goToPageHome();
|
||||||
|
m_pageController->goToPageSettingsBackup();
|
||||||
|
m_settingsController->importBackupFromOutside(filePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
|
||||||
|
|
||||||
|
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); });
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Q_OS_ANDROID
|
||||||
|
m_notificationHandler.reset(NotificationHandler::create(nullptr));
|
||||||
|
|
||||||
|
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
|
||||||
|
&NotificationHandler::setConnectionState);
|
||||||
|
|
||||||
|
connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(), &PageController::raiseMainWindow);
|
||||||
|
connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
|
||||||
|
static_cast<void (ConnectionController::*)()>(&ConnectionController::openConnection));
|
||||||
|
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
||||||
|
&ConnectionController::closeConnection);
|
||||||
|
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
|
||||||
|
#endif
|
||||||
|
|
||||||
m_engine->addImportPath("qrc:/ui/qml/Modules/");
|
m_engine->addImportPath("qrc:/ui/qml/Modules/");
|
||||||
m_engine->load(url);
|
m_engine->load(url);
|
||||||
|
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
||||||
m_coreController->setQmlRoot();
|
|
||||||
|
|
||||||
bool enabled = m_settings->isSaveLogs();
|
bool enabled = m_settings->isSaveLogs();
|
||||||
#ifndef Q_OS_ANDROID
|
#ifndef Q_OS_ANDROID
|
||||||
|
|
@ -93,13 +167,13 @@ void AmneziaApplication::init()
|
||||||
#endif
|
#endif
|
||||||
Logger::setServiceLogsEnabled(enabled);
|
Logger::setServiceLogsEnabled(enabled);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN //TODO
|
#ifdef Q_OS_WIN
|
||||||
if (m_parser.isSet("a"))
|
if (m_parser.isSet("a"))
|
||||||
m_coreController->pageController()->showOnStartup();
|
m_pageController->showOnStartup();
|
||||||
else
|
else
|
||||||
emit m_coreController->pageController()->raiseMainWindow();
|
emit m_pageController->raiseMainWindow();
|
||||||
#else
|
#else
|
||||||
m_coreController->pageController()->showOnStartup();
|
m_pageController->showOnStartup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Android TextArea clipboard workaround
|
// Android TextArea clipboard workaround
|
||||||
|
|
@ -156,6 +230,33 @@ void AmneziaApplication::loadFonts()
|
||||||
QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf");
|
QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AmneziaApplication::loadTranslator()
|
||||||
|
{
|
||||||
|
auto locale = m_settings->getAppLanguage();
|
||||||
|
m_translator.reset(new QTranslator());
|
||||||
|
updateTranslator(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmneziaApplication::updateTranslator(const QLocale &locale)
|
||||||
|
{
|
||||||
|
if (!m_translator->isEmpty()) {
|
||||||
|
QCoreApplication::removeTranslator(m_translator.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
|
||||||
|
if (m_translator->load(strFileName)) {
|
||||||
|
if (QCoreApplication::installTranslator(m_translator.get())) {
|
||||||
|
m_settings->setAppLanguage(locale);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_settings->setAppLanguage(QLocale::English);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_engine->retranslate();
|
||||||
|
|
||||||
|
emit translationsUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
bool AmneziaApplication::parseCommands()
|
bool AmneziaApplication::parseCommands()
|
||||||
{
|
{
|
||||||
m_parser.setApplicationDescription(APPLICATION_NAME);
|
m_parser.setApplicationDescription(APPLICATION_NAME);
|
||||||
|
|
@ -180,20 +281,19 @@ bool AmneziaApplication::parseCommands()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
void AmneziaApplication::startLocalServer()
|
void AmneziaApplication::startLocalServer() {
|
||||||
{
|
|
||||||
const QString serverName("AmneziaVPNInstance");
|
const QString serverName("AmneziaVPNInstance");
|
||||||
QLocalServer::removeServer(serverName);
|
QLocalServer::removeServer(serverName);
|
||||||
|
|
||||||
QLocalServer *server = new QLocalServer(this);
|
QLocalServer* server = new QLocalServer(this);
|
||||||
server->listen(serverName);
|
server->listen(serverName);
|
||||||
|
|
||||||
QObject::connect(server, &QLocalServer::newConnection, this, [server, this]() {
|
QObject::connect(server, &QLocalServer::newConnection, this, [server, this]() {
|
||||||
if (server) {
|
if (server) {
|
||||||
QLocalSocket *clientConnection = server->nextPendingConnection();
|
QLocalSocket* clientConnection = server->nextPendingConnection();
|
||||||
clientConnection->deleteLater();
|
clientConnection->deleteLater();
|
||||||
}
|
}
|
||||||
emit m_coreController->pageController()->raiseMainWindow(); //TODO
|
emit m_pageController->raiseMainWindow();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -203,12 +303,160 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
||||||
return m_engine;
|
return m_engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkAccessManager *AmneziaApplication::networkManager()
|
void AmneziaApplication::initModels()
|
||||||
{
|
{
|
||||||
return m_nam;
|
m_containersModel.reset(new ContainersModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
|
||||||
|
|
||||||
|
m_defaultServerContainersModel.reset(new ContainersModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel.get());
|
||||||
|
|
||||||
|
m_serversModel.reset(new ServersModel(m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
|
||||||
|
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
|
||||||
|
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
|
||||||
|
&ContainersModel::updateModel);
|
||||||
|
m_serversModel->resetModel();
|
||||||
|
|
||||||
|
m_languageModel.reset(new LanguageModel(m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
|
||||||
|
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator);
|
||||||
|
connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
|
||||||
|
|
||||||
|
m_sitesModel.reset(new SitesModel(m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
||||||
|
|
||||||
|
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
||||||
|
|
||||||
|
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
|
||||||
|
|
||||||
|
m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
|
||||||
|
|
||||||
|
m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
|
||||||
|
|
||||||
|
m_cloakConfigModel.reset(new CloakConfigModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
|
||||||
|
|
||||||
|
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
|
||||||
|
|
||||||
|
m_awgConfigModel.reset(new AwgConfigModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
|
||||||
|
|
||||||
|
m_xrayConfigModel.reset(new XrayConfigModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
|
||||||
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_sftpConfigModel.reset(new SftpConfigModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
|
||||||
|
|
||||||
|
m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
|
||||||
|
|
||||||
|
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
|
||||||
|
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),
|
||||||
|
&ServersModel::clearCachedProfile);
|
||||||
|
|
||||||
|
m_apiServicesModel.reset(new ApiServicesModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ApiServicesModel", m_apiServicesModel.get());
|
||||||
|
|
||||||
|
m_apiCountryModel.reset(new ApiCountryModel(this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ApiCountryModel", m_apiCountryModel.get());
|
||||||
|
connect(m_serversModel.get(), &ServersModel::updateApiLanguageModel, this, [this]() {
|
||||||
|
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
|
||||||
|
m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
|
||||||
|
});
|
||||||
|
connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this,
|
||||||
|
[this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
QClipboard *AmneziaApplication::getClipboard()
|
void AmneziaApplication::initControllers()
|
||||||
{
|
{
|
||||||
return this->clipboard();
|
m_connectionController.reset(
|
||||||
|
new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings));
|
||||||
|
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
|
||||||
|
|
||||||
|
connect(m_connectionController.get(), qOverload<const QString &>(&ConnectionController::connectionErrorOccurred), this,
|
||||||
|
[this](const QString &errorMessage) {
|
||||||
|
emit m_pageController->showErrorMessage(errorMessage);
|
||||||
|
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_connectionController.get(), qOverload<ErrorCode>(&ConnectionController::connectionErrorOccurred), this,
|
||||||
|
[this](ErrorCode errorCode) {
|
||||||
|
emit m_pageController->showErrorMessage(errorCode);
|
||||||
|
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_connectionController.get(), &ConnectionController::connectButtonClicked, m_connectionController.get(),
|
||||||
|
&ConnectionController::toggleConnection, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
m_pageController.reset(new PageController(m_serversModel, m_settings));
|
||||||
|
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
|
||||||
|
|
||||||
|
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel,
|
||||||
|
m_apiServicesModel, m_settings));
|
||||||
|
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
|
||||||
|
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
|
||||||
|
&PageController::showPassphraseRequestDrawer);
|
||||||
|
connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
|
||||||
|
&InstallController::setEncryptedPassphrase);
|
||||||
|
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
|
||||||
|
&ConnectionController::onCurrentContainerUpdated);
|
||||||
|
|
||||||
|
connect(m_installController.get(), &InstallController::updateServerFromApiFinished, this, [this]() {
|
||||||
|
disconnect(m_reloadConfigErrorOccurredConnection);
|
||||||
|
emit m_connectionController->configFromApiUpdated();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_connectionController.get(), &ConnectionController::updateApiConfigFromGateway, this, [this]() {
|
||||||
|
m_reloadConfigErrorOccurredConnection = connect(
|
||||||
|
m_installController.get(), qOverload<ErrorCode>(&InstallController::installationErrorOccurred), this,
|
||||||
|
[this]() { emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); },
|
||||||
|
static_cast<Qt::ConnectionType>(Qt::AutoConnection || Qt::SingleShotConnection));
|
||||||
|
m_installController->updateServiceFromApi(m_serversModel->getDefaultServerIndex(), "", "");
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_connectionController.get(), &ConnectionController::updateApiConfigFromTelegram, this, [this]() {
|
||||||
|
m_reloadConfigErrorOccurredConnection = connect(
|
||||||
|
m_installController.get(), qOverload<ErrorCode>(&InstallController::installationErrorOccurred), this,
|
||||||
|
[this]() { emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); },
|
||||||
|
static_cast<Qt::ConnectionType>(Qt::AutoConnection || Qt::SingleShotConnection));
|
||||||
|
m_serversModel->removeApiConfig(m_serversModel->getDefaultServerIndex());
|
||||||
|
m_installController->updateServiceFromTelegram(m_serversModel->getDefaultServerIndex());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
|
||||||
|
|
||||||
|
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
||||||
|
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
||||||
|
|
||||||
|
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, m_settings));
|
||||||
|
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
|
||||||
|
|
||||||
|
m_settingsController.reset(
|
||||||
|
new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
|
||||||
|
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
|
||||||
|
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
|
||||||
|
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
|
||||||
|
}
|
||||||
|
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
|
||||||
|
|
||||||
|
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||||
|
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||||
|
|
||||||
|
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
||||||
|
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
||||||
|
|
||||||
|
m_systemController.reset(new SystemController(m_settings));
|
||||||
|
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,43 @@
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#endif
|
#endif
|
||||||
#include <QClipboard>
|
|
||||||
|
|
||||||
#include "core/controllers/coreController.h"
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "vpnconnection.h"
|
#include "vpnconnection.h"
|
||||||
|
|
||||||
|
#include "ui/controllers/connectionController.h"
|
||||||
|
#include "ui/controllers/exportController.h"
|
||||||
|
#include "ui/controllers/importController.h"
|
||||||
|
#include "ui/controllers/installController.h"
|
||||||
|
#include "ui/controllers/pageController.h"
|
||||||
|
#include "ui/controllers/settingsController.h"
|
||||||
|
#include "ui/controllers/sitesController.h"
|
||||||
|
#include "ui/controllers/systemController.h"
|
||||||
|
#include "ui/controllers/appSplitTunnelingController.h"
|
||||||
|
#include "ui/models/containers_model.h"
|
||||||
|
#include "ui/models/languageModel.h"
|
||||||
|
#include "ui/models/protocols/cloakConfigModel.h"
|
||||||
|
#ifndef Q_OS_ANDROID
|
||||||
|
#include "ui/notificationhandler.h"
|
||||||
|
#endif
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
#include "ui/models/protocols/ikev2ConfigModel.h"
|
||||||
|
#endif
|
||||||
|
#include "ui/models/protocols/awgConfigModel.h"
|
||||||
|
#include "ui/models/protocols/openvpnConfigModel.h"
|
||||||
|
#include "ui/models/protocols/shadowsocksConfigModel.h"
|
||||||
|
#include "ui/models/protocols/wireguardConfigModel.h"
|
||||||
|
#include "ui/models/protocols/xrayConfigModel.h"
|
||||||
|
#include "ui/models/protocols_model.h"
|
||||||
|
#include "ui/models/servers_model.h"
|
||||||
|
#include "ui/models/services/sftpConfigModel.h"
|
||||||
|
#include "ui/models/services/socks5ProxyConfigModel.h"
|
||||||
|
#include "ui/models/sites_model.h"
|
||||||
|
#include "ui/models/clientManagementModel.h"
|
||||||
|
#include "ui/models/appSplitTunnelingModel.h"
|
||||||
|
#include "ui/models/apiServicesModel.h"
|
||||||
|
#include "ui/models/apiCountryModel.h"
|
||||||
|
|
||||||
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
|
|
@ -35,6 +66,8 @@ public:
|
||||||
void init();
|
void init();
|
||||||
void registerTypes();
|
void registerTypes();
|
||||||
void loadFonts();
|
void loadFonts();
|
||||||
|
void loadTranslator();
|
||||||
|
void updateTranslator(const QLocale &locale);
|
||||||
bool parseCommands();
|
bool parseCommands();
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
|
|
@ -42,24 +75,67 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QQmlApplicationEngine *qmlEngine() const;
|
QQmlApplicationEngine *qmlEngine() const;
|
||||||
QNetworkAccessManager *networkManager();
|
QNetworkAccessManager *manager() { return m_nam; }
|
||||||
QClipboard *getClipboard();
|
|
||||||
|
signals:
|
||||||
|
void translationsUpdated();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void initModels();
|
||||||
|
void initControllers();
|
||||||
|
|
||||||
QQmlApplicationEngine *m_engine {};
|
QQmlApplicationEngine *m_engine {};
|
||||||
std::shared_ptr<Settings> m_settings;
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
|
||||||
QScopedPointer<CoreController> m_coreController;
|
|
||||||
|
|
||||||
QSharedPointer<ContainerProps> m_containerProps;
|
QSharedPointer<ContainerProps> m_containerProps;
|
||||||
QSharedPointer<ProtocolProps> m_protocolProps;
|
QSharedPointer<ProtocolProps> m_protocolProps;
|
||||||
|
|
||||||
|
QSharedPointer<QTranslator> m_translator;
|
||||||
QCommandLineParser m_parser;
|
QCommandLineParser m_parser;
|
||||||
|
|
||||||
|
QSharedPointer<ContainersModel> m_containersModel;
|
||||||
|
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
|
||||||
|
QSharedPointer<ServersModel> m_serversModel;
|
||||||
|
QSharedPointer<LanguageModel> m_languageModel;
|
||||||
|
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||||
|
QSharedPointer<SitesModel> m_sitesModel;
|
||||||
|
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
||||||
|
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||||
|
QSharedPointer<ApiServicesModel> m_apiServicesModel;
|
||||||
|
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
||||||
|
|
||||||
|
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
||||||
|
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
||||||
|
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
|
||||||
|
QScopedPointer<XrayConfigModel> m_xrayConfigModel;
|
||||||
|
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
|
||||||
|
QScopedPointer<AwgConfigModel> m_awgConfigModel;
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
|
||||||
|
QScopedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
|
||||||
|
|
||||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||||
QThread m_vpnConnectionThread;
|
QThread m_vpnConnectionThread;
|
||||||
|
#ifndef Q_OS_ANDROID
|
||||||
|
QScopedPointer<NotificationHandler> m_notificationHandler;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QScopedPointer<ConnectionController> m_connectionController;
|
||||||
|
QScopedPointer<PageController> m_pageController;
|
||||||
|
QScopedPointer<InstallController> m_installController;
|
||||||
|
QScopedPointer<ImportController> m_importController;
|
||||||
|
QScopedPointer<ExportController> m_exportController;
|
||||||
|
QScopedPointer<SettingsController> m_settingsController;
|
||||||
|
QScopedPointer<SitesController> m_sitesController;
|
||||||
|
QScopedPointer<SystemController> m_systemController;
|
||||||
|
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
||||||
|
|
||||||
QNetworkAccessManager *m_nam;
|
QNetworkAccessManager *m_nam;
|
||||||
|
|
||||||
|
QMetaObject::Connection m_reloadConfigErrorOccurredConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AMNEZIA_APPLICATION_H
|
#endif // AMNEZIA_APPLICATION_H
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<!-- To request network state -->
|
<!-- To request network state -->
|
||||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" android:maxSdkVersion="30" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
@ -91,13 +91,6 @@
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@style/Translucent" />
|
android:theme="@style/Translucent" />
|
||||||
|
|
||||||
<activity android:name=".TvFilePicker"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:taskAffinity=""
|
|
||||||
android:exported="false"
|
|
||||||
android:theme="@style/Translucent" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ImportConfigActivity"
|
android:name=".ImportConfigActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ android.library.defaults.buildfeatures.androidresources=false
|
||||||
# For development copy and set local values for these parameters in local.properties
|
# For development copy and set local values for these parameters in local.properties
|
||||||
#androidCompileSdkVersion=android-34
|
#androidCompileSdkVersion=android-34
|
||||||
#androidBuildToolsVersion=34.0.0
|
#androidBuildToolsVersion=34.0.0
|
||||||
#qtMinSdkVersion=26
|
#qtMinSdkVersion=24
|
||||||
#qtTargetSdkVersion=34
|
#qtTargetSdkVersion=34
|
||||||
#androidNdkVersion=26.1.10909125
|
#androidNdkVersion=26.1.10909125
|
||||||
#qtTargetAbiList=x86_64
|
#qtTargetAbiList=x86_64
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package org.amnezia.vpn.protocol
|
package org.amnezia.vpn.protocol
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.IpPrefix
|
import android.net.IpPrefix
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
|
|
@ -7,6 +8,9 @@ import android.net.VpnService.Builder
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.system.OsConstants
|
import android.system.OsConstants
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.util.zip.ZipFile
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import org.amnezia.vpn.util.Log
|
import org.amnezia.vpn.util.Log
|
||||||
import org.amnezia.vpn.util.net.InetNetwork
|
import org.amnezia.vpn.util.net.InetNetwork
|
||||||
|
|
|
||||||
5
client/android/res/mipmap-anydpi-v26/ic_banner.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_banner_background"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_banner_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
BIN
client/android/res/mipmap-xhdpi/ic_banner_foreground.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -23,6 +23,4 @@
|
||||||
<string name="notificationSettingsDialogTitle">Настройки уведомлений</string>
|
<string name="notificationSettingsDialogTitle">Настройки уведомлений</string>
|
||||||
<string name="notificationSettingsDialogMessage">Для показа уведомлений необходимо включить уведомления в системных настройках</string>
|
<string name="notificationSettingsDialogMessage">Для показа уведомлений необходимо включить уведомления в системных настройках</string>
|
||||||
<string name="openNotificationSettings">Открыть настройки уведомлений</string>
|
<string name="openNotificationSettings">Открыть настройки уведомлений</string>
|
||||||
|
|
||||||
<string name="tvNoFileBrowser">Пожалуйста, установите приложение для просмотра файлов</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
4
client/android/res/values/ic_banner_background.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_banner_background">#1E1E1F</color>
|
||||||
|
</resources>
|
||||||
|
|
@ -23,6 +23,4 @@
|
||||||
<string name="notificationSettingsDialogTitle">Notification settings</string>
|
<string name="notificationSettingsDialogTitle">Notification settings</string>
|
||||||
<string name="notificationSettingsDialogMessage">To show notifications, you must enable notifications in the system settings</string>
|
<string name="notificationSettingsDialogMessage">To show notifications, you must enable notifications in the system settings</string>
|
||||||
<string name="openNotificationSettings">Open notification settings</string>
|
<string name="openNotificationSettings">Open notification settings</string>
|
||||||
|
|
||||||
<string name="tvNoFileBrowser">Please install a file management utility to browse files</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -4,7 +4,6 @@ import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
|
@ -13,7 +12,6 @@ import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
|
||||||
import android.content.ServiceConnection
|
import android.content.ServiceConnection
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
|
@ -22,13 +20,7 @@ import android.os.IBinder
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.Message
|
import android.os.Message
|
||||||
import android.os.Messenger
|
import android.os.Messenger
|
||||||
import android.os.ParcelFileDescriptor
|
|
||||||
import android.os.SystemClock
|
|
||||||
import android.provider.OpenableColumns
|
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.view.WindowManager.LayoutParams
|
import android.view.WindowManager.LayoutParams
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
|
@ -37,7 +29,6 @@ import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.LazyThreadSafetyMode.NONE
|
import kotlin.LazyThreadSafetyMode.NONE
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
import kotlin.text.RegexOption.IGNORE_CASE
|
import kotlin.text.RegexOption.IGNORE_CASE
|
||||||
import AppListProvider
|
import AppListProvider
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
|
@ -79,7 +70,6 @@ class AmneziaActivity : QtActivity() {
|
||||||
private var isInBoundState = false
|
private var isInBoundState = false
|
||||||
private var notificationStateReceiver: BroadcastReceiver? = null
|
private var notificationStateReceiver: BroadcastReceiver? = null
|
||||||
private lateinit var vpnServiceMessenger: IpcMessenger
|
private lateinit var vpnServiceMessenger: IpcMessenger
|
||||||
private var pfd: ParcelFileDescriptor? = null
|
|
||||||
|
|
||||||
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
|
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
|
||||||
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
|
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
|
||||||
|
|
@ -168,7 +158,7 @@ class AmneziaActivity : QtActivity() {
|
||||||
*/
|
*/
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
Log.d(TAG, "Create Amnezia activity")
|
Log.d(TAG, "Create Amnezia activity: $intent")
|
||||||
loadLibs()
|
loadLibs()
|
||||||
window.apply {
|
window.apply {
|
||||||
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||||
|
|
@ -210,7 +200,7 @@ class AmneziaActivity : QtActivity() {
|
||||||
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
|
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Log.v(
|
Log.d(
|
||||||
TAG, "Notification state changed: ${it?.action}, blocked = " +
|
TAG, "Notification state changed: ${it?.action}, blocked = " +
|
||||||
"${it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)}"
|
"${it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)}"
|
||||||
)
|
)
|
||||||
|
|
@ -224,7 +214,7 @@ class AmneziaActivity : QtActivity() {
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
Log.v(TAG, "onNewIntent: $intent")
|
Log.d(TAG, "onNewIntent: $intent")
|
||||||
intent?.let(::processIntent)
|
intent?.let(::processIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,7 +403,7 @@ class AmneziaActivity : QtActivity() {
|
||||||
@MainThread
|
@MainThread
|
||||||
private fun startVpn(vpnConfig: String) {
|
private fun startVpn(vpnConfig: String) {
|
||||||
getVpnProto(vpnConfig)?.let { proto ->
|
getVpnProto(vpnConfig)?.let { proto ->
|
||||||
Log.v(TAG, "Proto from config: $proto, current proto: $vpnProto")
|
Log.d(TAG, "Proto from config: $proto, current proto: $vpnProto")
|
||||||
if (isServiceConnected) {
|
if (isServiceConnected) {
|
||||||
if (proto.serviceClass == vpnProto?.serviceClass) {
|
if (proto.serviceClass == vpnProto?.serviceClass) {
|
||||||
vpnProto = proto
|
vpnProto = proto
|
||||||
|
|
@ -523,25 +513,21 @@ class AmneziaActivity : QtActivity() {
|
||||||
type = "text/*"
|
type = "text/*"
|
||||||
putExtra(Intent.EXTRA_TITLE, fileName)
|
putExtra(Intent.EXTRA_TITLE, fileName)
|
||||||
}.also {
|
}.also {
|
||||||
try {
|
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
|
||||||
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
|
onSuccess = {
|
||||||
onSuccess = {
|
it?.data?.let { uri ->
|
||||||
it?.data?.let { uri ->
|
Log.d(TAG, "Save file to $uri")
|
||||||
Log.v(TAG, "Save file to $uri")
|
try {
|
||||||
try {
|
contentResolver.openOutputStream(uri)?.use { os ->
|
||||||
contentResolver.openOutputStream(uri)?.use { os ->
|
os.bufferedWriter().use { it.write(data) }
|
||||||
os.bufferedWriter().use { it.write(data) }
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.e(TAG, "Failed to save file $uri: $e")
|
|
||||||
// todo: send error to Qt
|
|
||||||
}
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, "Failed to save file $uri: $e")
|
||||||
|
// todo: send error to Qt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
))
|
}
|
||||||
} catch (_: ActivityNotFoundException) {
|
))
|
||||||
Toast.makeText(this@AmneziaActivity, "Unsupported", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -550,115 +536,46 @@ class AmneziaActivity : QtActivity() {
|
||||||
fun openFile(filter: String?) {
|
fun openFile(filter: String?) {
|
||||||
Log.v(TAG, "Open file with filter: $filter")
|
Log.v(TAG, "Open file with filter: $filter")
|
||||||
mainScope.launch {
|
mainScope.launch {
|
||||||
val intent = if (!isOnTv()) {
|
val mimeTypes = if (!filter.isNullOrEmpty()) {
|
||||||
val mimeTypes = if (!filter.isNullOrEmpty()) {
|
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
|
||||||
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
|
val mime = MimeTypeMap.getSingleton()
|
||||||
val mime = MimeTypeMap.getSingleton()
|
extensionRegex.findAll(filter).map {
|
||||||
extensionRegex.findAll(filter).map {
|
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
|
||||||
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
|
}.toSet()
|
||||||
}.toSet()
|
} else emptySet()
|
||||||
} else emptySet()
|
|
||||||
|
|
||||||
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
Log.v(TAG, "File mimyType filter: $mimeTypes")
|
Log.v(TAG, "File mimyType filter: $mimeTypes")
|
||||||
if ("*/*" in mimeTypes) {
|
if ("*/*" in mimeTypes) {
|
||||||
type = "*/*"
|
type = "*/*"
|
||||||
} else {
|
} else {
|
||||||
when (mimeTypes.size) {
|
when (mimeTypes.size) {
|
||||||
1 -> type = mimeTypes.first()
|
1 -> type = mimeTypes.first()
|
||||||
|
|
||||||
in 2..Int.MAX_VALUE -> {
|
in 2..Int.MAX_VALUE -> {
|
||||||
type = "*/*"
|
type = "*/*"
|
||||||
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
|
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
|
||||||
}
|
|
||||||
|
|
||||||
else -> type = "*/*"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else -> type = "*/*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}.also {
|
||||||
Intent(this@AmneziaActivity, TvFilePicker::class.java)
|
startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
startActivityForResult(intent, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
|
|
||||||
onAny = {
|
onAny = {
|
||||||
if (isOnTv() && it?.hasExtra("activityNotFound") == true) {
|
val uri = it?.data?.toString() ?: ""
|
||||||
showNoFileBrowserAlertDialog()
|
Log.d(TAG, "Open file: $uri")
|
||||||
}
|
|
||||||
val uri = it?.data?.apply {
|
|
||||||
grantUriPermission(packageName, this, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
}?.toString() ?: ""
|
|
||||||
Log.v(TAG, "Open file: $uri")
|
|
||||||
mainScope.launch {
|
mainScope.launch {
|
||||||
qtInitialized.await()
|
qtInitialized.await()
|
||||||
QtAndroidController.onFileOpened(uri)
|
QtAndroidController.onFileOpened(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
} catch (_: ActivityNotFoundException) {
|
|
||||||
showNoFileBrowserAlertDialog()
|
|
||||||
mainScope.launch {
|
|
||||||
qtInitialized.await()
|
|
||||||
QtAndroidController.onFileOpened("")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showNoFileBrowserAlertDialog() {
|
|
||||||
AlertDialog.Builder(this)
|
|
||||||
.setMessage(R.string.tvNoFileBrowser)
|
|
||||||
.setCancelable(false)
|
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
|
||||||
try {
|
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://webstoreredirect")))
|
|
||||||
} catch (_: Throwable) {}
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
fun getFd(fileName: String): Int {
|
|
||||||
Log.v(TAG, "Get fd for $fileName")
|
|
||||||
return blockingCall {
|
|
||||||
try {
|
|
||||||
pfd = contentResolver.openFileDescriptor(Uri.parse(fileName), "r")
|
|
||||||
pfd?.fd ?: -1
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Failed to get fd: $e")
|
|
||||||
-1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
fun closeFd() {
|
|
||||||
Log.v(TAG, "Close fd")
|
|
||||||
mainScope.launch {
|
|
||||||
pfd?.close()
|
|
||||||
pfd = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
fun getFileName(uri: String): String {
|
|
||||||
Log.v(TAG, "Get file name for uri: $uri")
|
|
||||||
return blockingCall {
|
|
||||||
try {
|
|
||||||
contentResolver.query(Uri.parse(uri), arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { cursor ->
|
|
||||||
if (cursor.moveToFirst() && !cursor.isNull(0)) {
|
|
||||||
return@blockingCall cursor.getString(0) ?: ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Failed to get file name: $e")
|
|
||||||
}
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
|
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
|
||||||
fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|
fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|
||||||
|
|
@ -803,121 +720,9 @@ class AmneziaActivity : QtActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// method to workaround Qt's problem with calling the keyboard on TVs
|
|
||||||
@Suppress("unused")
|
|
||||||
fun sendTouch(x: Float, y: Float) {
|
|
||||||
Log.v(TAG, "Send touch: $x, $y")
|
|
||||||
blockingCall {
|
|
||||||
findQtWindow(window.decorView)?.let {
|
|
||||||
Log.v(TAG, "Send touch to $it")
|
|
||||||
it.dispatchTouchEvent(createEvent(x, y, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN))
|
|
||||||
it.dispatchTouchEvent(createEvent(x, y, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findQtWindow(view: View): View? {
|
|
||||||
Log.v(TAG, "findQtWindow: process $view")
|
|
||||||
if (view::class.simpleName == "QtWindow") return view
|
|
||||||
else if (view is ViewGroup) {
|
|
||||||
for (i in 0 until view.childCount) {
|
|
||||||
val result = findQtWindow(view.getChildAt(i))
|
|
||||||
if (result != null) return result
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
} else return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createEvent(x: Float, y: Float, eventTime: Long, action: Int): MotionEvent =
|
|
||||||
MotionEvent.obtain(
|
|
||||||
eventTime,
|
|
||||||
eventTime,
|
|
||||||
action,
|
|
||||||
1,
|
|
||||||
arrayOf(MotionEvent.PointerProperties().apply {
|
|
||||||
id = 0
|
|
||||||
toolType = MotionEvent.TOOL_TYPE_FINGER
|
|
||||||
}),
|
|
||||||
arrayOf(MotionEvent.PointerCoords().apply {
|
|
||||||
this.x = x
|
|
||||||
this.y = y
|
|
||||||
pressure = 1f
|
|
||||||
size = 1f
|
|
||||||
}),
|
|
||||||
0, 0, 1.0f, 1.0f, 0, 0, 0,0
|
|
||||||
)
|
|
||||||
|
|
||||||
// workaround for a bug in Qt that causes the mouse click event not to be handled
|
|
||||||
// also disable right-click, as it causes the application to crash
|
|
||||||
private var lastButtonState = 0
|
|
||||||
private fun MotionEvent.fixCopy(): MotionEvent = MotionEvent.obtain(
|
|
||||||
downTime,
|
|
||||||
eventTime,
|
|
||||||
action,
|
|
||||||
pointerCount,
|
|
||||||
(0 until pointerCount).map { i ->
|
|
||||||
MotionEvent.PointerProperties().apply {
|
|
||||||
getPointerProperties(i, this)
|
|
||||||
}
|
|
||||||
}.toTypedArray(),
|
|
||||||
(0 until pointerCount).map { i ->
|
|
||||||
MotionEvent.PointerCoords().apply {
|
|
||||||
getPointerCoords(i, this)
|
|
||||||
}
|
|
||||||
}.toTypedArray(),
|
|
||||||
metaState,
|
|
||||||
MotionEvent.BUTTON_PRIMARY,
|
|
||||||
xPrecision,
|
|
||||||
yPrecision,
|
|
||||||
deviceId,
|
|
||||||
edgeFlags,
|
|
||||||
source,
|
|
||||||
flags
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun handleMouseEvent(ev: MotionEvent, superDispatch: (MotionEvent?) -> Boolean): Boolean {
|
|
||||||
when (ev.action) {
|
|
||||||
MotionEvent.ACTION_DOWN -> {
|
|
||||||
lastButtonState = ev.buttonState
|
|
||||||
if (ev.buttonState == MotionEvent.BUTTON_SECONDARY) return true
|
|
||||||
}
|
|
||||||
|
|
||||||
MotionEvent.ACTION_UP -> {
|
|
||||||
when (lastButtonState) {
|
|
||||||
MotionEvent.BUTTON_SECONDARY -> return true
|
|
||||||
MotionEvent.BUTTON_PRIMARY -> {
|
|
||||||
val modEvent = ev.fixCopy()
|
|
||||||
return superDispatch(modEvent).apply { modEvent.recycle() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return superDispatch(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
|
||||||
Log.v(TAG, "dispatchTouch: $ev")
|
|
||||||
if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
|
|
||||||
return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
|
|
||||||
}
|
|
||||||
return super.dispatchTouchEvent(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean {
|
|
||||||
ev?.let { return handleMouseEvent(ev) { super.dispatchTrackballEvent(it) }}
|
|
||||||
return super.dispatchTrackballEvent(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utils methods
|
* Utils methods
|
||||||
*/
|
*/
|
||||||
private fun <T> blockingCall(
|
|
||||||
context: CoroutineContext = Dispatchers.Main.immediate,
|
|
||||||
block: suspend () -> T
|
|
||||||
) = runBlocking {
|
|
||||||
mainScope.async(context) { block() }.await()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun actionCodeToString(actionCode: Int): String =
|
private fun actionCodeToString(actionCode: Int): String =
|
||||||
when (actionCode) {
|
when (actionCode) {
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ open class AmneziaVpnService : VpnService() {
|
||||||
arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED
|
arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED
|
||||||
) {
|
) {
|
||||||
it?.action?.let { action ->
|
it?.action?.let { action ->
|
||||||
Log.v(TAG, "Broadcast request received: $action")
|
Log.d(TAG, "Broadcast request received: $action")
|
||||||
when (action) {
|
when (action) {
|
||||||
ACTION_CONNECT -> connect()
|
ACTION_CONNECT -> connect()
|
||||||
ACTION_DISCONNECT -> disconnect()
|
ACTION_DISCONNECT -> disconnect()
|
||||||
|
|
@ -317,7 +317,7 @@ open class AmneziaVpnService : VpnService() {
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)
|
val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)
|
||||||
Log.v(TAG, "Notification state changed: ${it?.action}, blocked = $state")
|
Log.d(TAG, "Notification state changed: ${it?.action}, blocked = $state")
|
||||||
if (state == false) {
|
if (state == false) {
|
||||||
enableNotification()
|
enableNotification()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -450,7 +450,7 @@ open class AmneziaVpnService : VpnService() {
|
||||||
serviceNotification.isNotificationEnabled() &&
|
serviceNotification.isNotificationEnabled() &&
|
||||||
getSystemService<PowerManager>()?.isInteractive != false
|
getSystemService<PowerManager>()?.isInteractive != false
|
||||||
) {
|
) {
|
||||||
Log.v(TAG, "Launch traffic stats update")
|
Log.d(TAG, "Launch traffic stats update")
|
||||||
trafficStats.reset()
|
trafficStats.reset()
|
||||||
startTrafficStatsUpdateJob()
|
startTrafficStatsUpdateJob()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ class AuthActivity : FragmentActivity() {
|
||||||
object : BiometricPrompt.AuthenticationCallback() {
|
object : BiometricPrompt.AuthenticationCallback() {
|
||||||
override fun onAuthenticationSucceeded(result: AuthenticationResult) {
|
override fun onAuthenticationSucceeded(result: AuthenticationResult) {
|
||||||
super.onAuthenticationSucceeded(result)
|
super.onAuthenticationSucceeded(result)
|
||||||
Log.v(TAG, "Authentication succeeded")
|
Log.d(TAG, "Authentication succeeded")
|
||||||
QtAndroidController.onAuthResult(true)
|
QtAndroidController.onAuthResult(true)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,20 +29,20 @@ class ImportConfigActivity : ComponentActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
Log.v(TAG, "Create Import Config Activity: $intent")
|
Log.d(TAG, "Create Import Config Activity: $intent")
|
||||||
intent?.let(::readConfig)
|
intent?.let(::readConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
Log.v(TAG, "onNewIntent: $intent")
|
Log.d(TAG, "onNewIntent: $intent")
|
||||||
intent.let(::readConfig)
|
intent.let(::readConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readConfig(intent: Intent) {
|
private fun readConfig(intent: Intent) {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
ACTION_SEND -> {
|
ACTION_SEND -> {
|
||||||
Log.v(TAG, "Process SEND action, type: ${intent.type}")
|
Log.d(TAG, "Process SEND action, type: ${intent.type}")
|
||||||
when (intent.type) {
|
when (intent.type) {
|
||||||
"application/octet-stream" -> {
|
"application/octet-stream" -> {
|
||||||
intent.getUriCompat()?.let { uri ->
|
intent.getUriCompat()?.let { uri ->
|
||||||
|
|
@ -60,7 +60,7 @@ class ImportConfigActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTION_VIEW -> {
|
ACTION_VIEW -> {
|
||||||
Log.v(TAG, "Process VIEW action, scheme: ${intent.scheme}")
|
Log.d(TAG, "Process VIEW action, scheme: ${intent.scheme}")
|
||||||
when (intent.scheme) {
|
when (intent.scheme) {
|
||||||
"file", "content" -> {
|
"file", "content" -> {
|
||||||
intent.data?.let { uri ->
|
intent.data?.let { uri ->
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ class ServiceNotification(private val context: Context) {
|
||||||
fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification {
|
fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification {
|
||||||
val speedString = if (state == CONNECTED) zeroSpeed else null
|
val speedString = if (state == CONNECTED) zeroSpeed else null
|
||||||
|
|
||||||
Log.v(TAG, "Build notification: $serverName, $state")
|
Log.d(TAG, "Build notification: $serverName, $state")
|
||||||
|
|
||||||
return notificationBuilder
|
return notificationBuilder
|
||||||
.setSmallIcon(R.drawable.ic_amnezia_round)
|
.setSmallIcon(R.drawable.ic_amnezia_round)
|
||||||
|
|
@ -88,15 +88,17 @@ class ServiceNotification(private val context: Context) {
|
||||||
fun isNotificationEnabled(): Boolean {
|
fun isNotificationEnabled(): Boolean {
|
||||||
if (!context.isNotificationPermissionGranted()) return false
|
if (!context.isNotificationPermissionGranted()) return false
|
||||||
if (!notificationManager.areNotificationsEnabled()) return false
|
if (!notificationManager.areNotificationsEnabled()) return false
|
||||||
return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)?.let {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
it.importance != NotificationManager.IMPORTANCE_NONE
|
return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)
|
||||||
} ?: true
|
?.let { it.importance != NotificationManager.IMPORTANCE_NONE } ?: true
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
|
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
|
||||||
if (context.isNotificationPermissionGranted()) {
|
if (context.isNotificationPermissionGranted()) {
|
||||||
Log.v(TAG, "Update notification: $serverName, $state")
|
Log.d(TAG, "Update notification: $serverName, $state")
|
||||||
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
|
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
package org.amnezia.vpn
|
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import org.amnezia.vpn.util.Log
|
|
||||||
|
|
||||||
private const val TAG = "TvFilePicker"
|
|
||||||
|
|
||||||
class TvFilePicker : ComponentActivity() {
|
|
||||||
|
|
||||||
private val fileChooseResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) {
|
|
||||||
setResult(RESULT_OK, Intent().apply { data = it })
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
Log.v(TAG, "onCreate")
|
|
||||||
getFile()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
|
||||||
super.onNewIntent(intent)
|
|
||||||
Log.v(TAG, "onNewIntent")
|
|
||||||
getFile()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getFile() {
|
|
||||||
try {
|
|
||||||
Log.v(TAG, "getFile")
|
|
||||||
fileChooseResultLauncher.launch("*/*")
|
|
||||||
} catch (_: ActivityNotFoundException) {
|
|
||||||
Log.w(TAG, "Activity not found")
|
|
||||||
setResult(RESULT_CANCELED, Intent().apply { putExtra("activityNotFound", true) })
|
|
||||||
finish()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Failed to get file: $e")
|
|
||||||
setResult(RESULT_CANCELED)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -46,7 +46,7 @@ object LibraryLoader {
|
||||||
System.loadLibrary(libraryName)
|
System.loadLibrary(libraryName)
|
||||||
return
|
return
|
||||||
} catch (_: UnsatisfiedLinkError) {
|
} catch (_: UnsatisfiedLinkError) {
|
||||||
Log.w(TAG, "Failed to load library, try to extract it from apk")
|
Log.d(TAG, "Failed to load library, try to extract it from apk")
|
||||||
}
|
}
|
||||||
var tempFile: File? = null
|
var tempFile: File? = null
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package org.amnezia.vpn.util
|
package org.amnezia.vpn.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.icu.text.DateFormat
|
||||||
|
import android.icu.text.SimpleDateFormat
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
@ -10,6 +12,8 @@ import java.nio.channels.FileChannel
|
||||||
import java.nio.channels.FileLock
|
import java.nio.channels.FileLock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import org.amnezia.vpn.util.Log.Priority.D
|
import org.amnezia.vpn.util.Log.Priority.D
|
||||||
import org.amnezia.vpn.util.Log.Priority.E
|
import org.amnezia.vpn.util.Log.Priority.E
|
||||||
|
|
@ -37,7 +41,11 @@ private const val LOG_MAX_FILE_SIZE = 1024 * 1024
|
||||||
* | | | create a report and/or terminate the process |
|
* | | | create a report and/or terminate the process |
|
||||||
*/
|
*/
|
||||||
object Log {
|
object Log {
|
||||||
private val dateTimeFormat: DateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)
|
private val dateTimeFormat: Any =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)
|
||||||
|
else object : ThreadLocal<DateFormat>() {
|
||||||
|
override fun initialValue(): DateFormat = SimpleDateFormat(DATE_TIME_PATTERN, Locale.US)
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var logDir: File
|
private lateinit var logDir: File
|
||||||
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
|
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
|
||||||
|
|
@ -135,7 +143,12 @@ object Log {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
|
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
|
||||||
val date = LocalDateTime.now().format(dateTimeFormat)
|
val date = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
LocalDateTime.now().format(dateTimeFormat as DateTimeFormatter)
|
||||||
|
} else {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
(dateTimeFormat as ThreadLocal<DateFormat>).get()?.format(Date())
|
||||||
|
}
|
||||||
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
||||||
"$tag: $msg\n"
|
"$tag: $msg\n"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,18 @@ class NetworkState(
|
||||||
private val networkCallback: NetworkCallback by lazy(NONE) {
|
private val networkCallback: NetworkCallback by lazy(NONE) {
|
||||||
object : NetworkCallback() {
|
object : NetworkCallback() {
|
||||||
override fun onAvailable(network: Network) {
|
override fun onAvailable(network: Network) {
|
||||||
Log.v(TAG, "onAvailable: $network")
|
Log.d(TAG, "onAvailable: $network")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
|
Log.d(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
|
||||||
checkNetworkState(network, networkCapabilities)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
checkNetworkState(network, networkCapabilities)
|
||||||
|
} else {
|
||||||
|
handler.post {
|
||||||
|
checkNetworkState(network, networkCapabilities)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) {
|
private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
|
|
@ -67,11 +73,11 @@ class NetworkState(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
|
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
|
||||||
Log.v(TAG, "onBlockedStatusChanged: $network, $blocked")
|
Log.d(TAG, "onBlockedStatusChanged: $network, $blocked")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLost(network: Network) {
|
override fun onLost(network: Network) {
|
||||||
Log.v(TAG, "onLost: $network")
|
Log.d(TAG, "onLost: $network")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -81,7 +87,7 @@ class NetworkState(
|
||||||
Log.d(TAG, "Bind network listener")
|
Log.d(TAG, "Bind network listener")
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
|
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
|
||||||
} else {
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val numberAttempts = 300
|
val numberAttempts = 300
|
||||||
var attemptCount = 0
|
var attemptCount = 0
|
||||||
while(true) {
|
while(true) {
|
||||||
|
|
@ -102,6 +108,8 @@ class NetworkState(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
connectivityManager.requestNetwork(networkRequest, networkCallback)
|
||||||
}
|
}
|
||||||
isListenerBound = true
|
isListenerBound = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
package org.amnezia.vpn.protocol.wireguard
|
package org.amnezia.vpn.protocol.wireguard
|
||||||
|
|
||||||
import android.net.VpnService.Builder
|
import android.net.VpnService.Builder
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import java.io.IOException
|
||||||
|
import java.util.Locale
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.withContext
|
||||||
import org.amnezia.awg.GoBackend
|
import org.amnezia.awg.GoBackend
|
||||||
import org.amnezia.vpn.protocol.Protocol
|
import org.amnezia.vpn.protocol.Protocol
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||||
|
|
@ -28,8 +27,6 @@ open class Wireguard : Protocol() {
|
||||||
|
|
||||||
private var tunnelHandle: Int = -1
|
private var tunnelHandle: Int = -1
|
||||||
protected open val ifName: String = "amn0"
|
protected open val ifName: String = "amn0"
|
||||||
private lateinit var scope: CoroutineScope
|
|
||||||
private var statusJob: Job? = null
|
|
||||||
|
|
||||||
override val statistics: Statistics
|
override val statistics: Statistics
|
||||||
get() {
|
get() {
|
||||||
|
|
@ -52,17 +49,46 @@ open class Wireguard : Protocol() {
|
||||||
|
|
||||||
override fun internalInit() {
|
override fun internalInit() {
|
||||||
if (!isInitialized) loadSharedLibrary(context, "wg-go")
|
if (!isInitialized) loadSharedLibrary(context, "wg-go")
|
||||||
if (this::scope.isInitialized) {
|
|
||||||
scope.cancel()
|
|
||||||
}
|
|
||||||
scope = CoroutineScope(Dispatchers.IO)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
||||||
val wireguardConfig = parseConfig(config)
|
val wireguardConfig = parseConfig(config)
|
||||||
|
val startTime = System.currentTimeMillis()
|
||||||
start(wireguardConfig, vpnBuilder, protect)
|
start(wireguardConfig, vpnBuilder, protect)
|
||||||
|
waitForConnection(startTime)
|
||||||
|
state.value = CONNECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun waitForConnection(startTime: Long) {
|
||||||
|
Log.d(TAG, "Waiting for connection")
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val time = String.format(Locale.ROOT,"%.3f", startTime / 1000.0)
|
||||||
|
try {
|
||||||
|
delay(1000)
|
||||||
|
var log = getLogcat(time)
|
||||||
|
Log.d(TAG, "First waiting log: $log")
|
||||||
|
// check that there is a connection log,
|
||||||
|
// to avoid infinite connection
|
||||||
|
if (!log.contains("Attaching to interface")) {
|
||||||
|
Log.w(TAG, "Logs do not contain a connection log")
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
while (!log.contains("Received handshake response")) {
|
||||||
|
delay(1000)
|
||||||
|
log = getLogcat(time)
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, "Failed to get logcat: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLogcat(time: String): String =
|
||||||
|
ProcessBuilder("logcat", "--buffer=main", "--format=raw", "*:S AmneziaWG/awg0", "-t", time)
|
||||||
|
.redirectErrorStream(true)
|
||||||
|
.start()
|
||||||
|
.inputStream.reader().readText()
|
||||||
|
|
||||||
protected open fun parseConfig(config: JSONObject): WireguardConfig {
|
protected open fun parseConfig(config: JSONObject): WireguardConfig {
|
||||||
val configData = config.getJSONObject("wireguard_config_data")
|
val configData = config.getJSONObject("wireguard_config_data")
|
||||||
return WireguardConfig.build {
|
return WireguardConfig.build {
|
||||||
|
|
@ -120,21 +146,10 @@ open class Wireguard : Protocol() {
|
||||||
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
|
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
|
||||||
configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
|
configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
|
||||||
configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
|
configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
|
||||||
configData.optStringOrNull("S3")?.let { setS3(it.toInt()) }
|
|
||||||
configData.optStringOrNull("S4")?.let { setS4(it.toInt()) }
|
|
||||||
configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
|
configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
|
||||||
configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
|
configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
|
||||||
configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
|
configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
|
||||||
configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
|
configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
|
||||||
configData.optStringOrNull("I1")?.let { setI1(it) }
|
|
||||||
configData.optStringOrNull("I2")?.let { setI2(it) }
|
|
||||||
configData.optStringOrNull("I3")?.let { setI3(it) }
|
|
||||||
configData.optStringOrNull("I4")?.let { setI4(it) }
|
|
||||||
configData.optStringOrNull("I5")?.let { setI5(it) }
|
|
||||||
configData.optStringOrNull("J1")?.let { setJ1(it) }
|
|
||||||
configData.optStringOrNull("J2")?.let { setJ2(it) }
|
|
||||||
configData.optStringOrNull("J3")?.let { setJ3(it) }
|
|
||||||
configData.optStringOrNull("Itime")?.let { setItime(it.toInt()) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
||||||
|
|
@ -163,43 +178,6 @@ open class Wireguard : Protocol() {
|
||||||
tunnelHandle = -1
|
tunnelHandle = -1
|
||||||
throw VpnStartException("Protect VPN interface: permission not granted or revoked")
|
throw VpnStartException("Protect VPN interface: permission not granted or revoked")
|
||||||
}
|
}
|
||||||
launchStatusJob()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun launchStatusJob() {
|
|
||||||
Log.d(TAG, "Launch status job")
|
|
||||||
statusJob = scope.launch {
|
|
||||||
while (true) {
|
|
||||||
val lastHandshake = getLastHandshake()
|
|
||||||
Log.v(TAG, "lastHandshake=$lastHandshake")
|
|
||||||
if (lastHandshake == 0L) {
|
|
||||||
delay(1000)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (lastHandshake == -2L || lastHandshake > 0L) state.value = CONNECTED
|
|
||||||
else if (lastHandshake == -1L) state.value = DISCONNECTED
|
|
||||||
statusJob = null
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLastHandshake(): Long {
|
|
||||||
if (tunnelHandle == -1) {
|
|
||||||
Log.e(TAG, "Trying to get config of a non-existent tunnel")
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
val config = GoBackend.awgGetConfig(tunnelHandle)
|
|
||||||
if (config == null) {
|
|
||||||
Log.e(TAG, "Failed to get tunnel config")
|
|
||||||
return -2
|
|
||||||
}
|
|
||||||
val lastHandshake = config.lines().find { it.startsWith("last_handshake_time_sec=") }?.substring(24)?.toLong()
|
|
||||||
if (lastHandshake == null) {
|
|
||||||
Log.e(TAG, "Failed to get last_handshake_time_sec")
|
|
||||||
return -2
|
|
||||||
}
|
|
||||||
return lastHandshake
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopVpn() {
|
override fun stopVpn() {
|
||||||
|
|
@ -207,8 +185,6 @@ open class Wireguard : Protocol() {
|
||||||
Log.w(TAG, "Tunnel already down")
|
Log.w(TAG, "Tunnel already down")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
statusJob?.cancel()
|
|
||||||
statusJob = null
|
|
||||||
val handleToClose = tunnelHandle
|
val handleToClose = tunnelHandle
|
||||||
tunnelHandle = -1
|
tunnelHandle = -1
|
||||||
GoBackend.awgTurnOff(handleToClose)
|
GoBackend.awgTurnOff(handleToClose)
|
||||||
|
|
|
||||||
|
|
@ -20,21 +20,10 @@ open class WireguardConfig protected constructor(
|
||||||
val jmax: Int?,
|
val jmax: Int?,
|
||||||
val s1: Int?,
|
val s1: Int?,
|
||||||
val s2: Int?,
|
val s2: Int?,
|
||||||
val s3: Int?,
|
|
||||||
val s4: Int?,
|
|
||||||
val h1: Long?,
|
val h1: Long?,
|
||||||
val h2: Long?,
|
val h2: Long?,
|
||||||
val h3: Long?,
|
val h3: Long?,
|
||||||
val h4: Long?,
|
val h4: Long?
|
||||||
var i1: String?,
|
|
||||||
var i2: String?,
|
|
||||||
var i3: String?,
|
|
||||||
var i4: String?,
|
|
||||||
var i5: String?,
|
|
||||||
var j1: String?,
|
|
||||||
var j2: String?,
|
|
||||||
var j3: String?,
|
|
||||||
var itime: Int?
|
|
||||||
) : ProtocolConfig(protocolConfigBuilder) {
|
) : ProtocolConfig(protocolConfigBuilder) {
|
||||||
|
|
||||||
protected constructor(builder: Builder) : this(
|
protected constructor(builder: Builder) : this(
|
||||||
|
|
@ -50,21 +39,10 @@ open class WireguardConfig protected constructor(
|
||||||
builder.jmax,
|
builder.jmax,
|
||||||
builder.s1,
|
builder.s1,
|
||||||
builder.s2,
|
builder.s2,
|
||||||
builder.s3,
|
|
||||||
builder.s4,
|
|
||||||
builder.h1,
|
builder.h1,
|
||||||
builder.h2,
|
builder.h2,
|
||||||
builder.h3,
|
builder.h3,
|
||||||
builder.h4,
|
builder.h4
|
||||||
builder.i1,
|
|
||||||
builder.i2,
|
|
||||||
builder.i3,
|
|
||||||
builder.i4,
|
|
||||||
builder.i5,
|
|
||||||
builder.j1,
|
|
||||||
builder.j2,
|
|
||||||
builder.j3,
|
|
||||||
builder.itime
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toWgUserspaceString(): String = with(StringBuilder()) {
|
fun toWgUserspaceString(): String = with(StringBuilder()) {
|
||||||
|
|
@ -83,21 +61,10 @@ open class WireguardConfig protected constructor(
|
||||||
appendLine("jmax=$jmax")
|
appendLine("jmax=$jmax")
|
||||||
appendLine("s1=$s1")
|
appendLine("s1=$s1")
|
||||||
appendLine("s2=$s2")
|
appendLine("s2=$s2")
|
||||||
s3?.let { appendLine("s3=$it") }
|
|
||||||
s4?.let { appendLine("s4=$it") }
|
|
||||||
appendLine("h1=$h1")
|
appendLine("h1=$h1")
|
||||||
appendLine("h2=$h2")
|
appendLine("h2=$h2")
|
||||||
appendLine("h3=$h3")
|
appendLine("h3=$h3")
|
||||||
appendLine("h4=$h4")
|
appendLine("h4=$h4")
|
||||||
i1?.let { appendLine("i1=$it") }
|
|
||||||
i2?.let { appendLine("i2=$it") }
|
|
||||||
i3?.let { appendLine("i3=$it") }
|
|
||||||
i4?.let { appendLine("i4=$it") }
|
|
||||||
i5?.let { appendLine("i5=$it") }
|
|
||||||
j1?.let { appendLine("j1=$it") }
|
|
||||||
j2?.let { appendLine("j2=$it") }
|
|
||||||
j3?.let { appendLine("j3=$it") }
|
|
||||||
itime?.let { appendLine("itime=$it") }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,21 +117,10 @@ open class WireguardConfig protected constructor(
|
||||||
internal var jmax: Int? = null
|
internal var jmax: Int? = null
|
||||||
internal var s1: Int? = null
|
internal var s1: Int? = null
|
||||||
internal var s2: Int? = null
|
internal var s2: Int? = null
|
||||||
internal var s3: Int? = null
|
|
||||||
internal var s4: Int? = null
|
|
||||||
internal var h1: Long? = null
|
internal var h1: Long? = null
|
||||||
internal var h2: Long? = null
|
internal var h2: Long? = null
|
||||||
internal var h3: Long? = null
|
internal var h3: Long? = null
|
||||||
internal var h4: Long? = null
|
internal var h4: Long? = null
|
||||||
internal var i1: String? = null
|
|
||||||
internal var i2: String? = null
|
|
||||||
internal var i3: String? = null
|
|
||||||
internal var i4: String? = null
|
|
||||||
internal var i5: String? = null
|
|
||||||
internal var j1: String? = null
|
|
||||||
internal var j2: String? = null
|
|
||||||
internal var j3: String? = null
|
|
||||||
internal var itime: Int? = null
|
|
||||||
|
|
||||||
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
|
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
|
||||||
|
|
||||||
|
|
@ -183,21 +139,10 @@ open class WireguardConfig protected constructor(
|
||||||
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
|
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
|
||||||
fun setS1(s1: Int) = apply { this.s1 = s1 }
|
fun setS1(s1: Int) = apply { this.s1 = s1 }
|
||||||
fun setS2(s2: Int) = apply { this.s2 = s2 }
|
fun setS2(s2: Int) = apply { this.s2 = s2 }
|
||||||
fun setS3(s3: Int) = apply { this.s3 = s3 }
|
|
||||||
fun setS4(s4: Int) = apply { this.s4 = s4 }
|
|
||||||
fun setH1(h1: Long) = apply { this.h1 = h1 }
|
fun setH1(h1: Long) = apply { this.h1 = h1 }
|
||||||
fun setH2(h2: Long) = apply { this.h2 = h2 }
|
fun setH2(h2: Long) = apply { this.h2 = h2 }
|
||||||
fun setH3(h3: Long) = apply { this.h3 = h3 }
|
fun setH3(h3: Long) = apply { this.h3 = h3 }
|
||||||
fun setH4(h4: Long) = apply { this.h4 = h4 }
|
fun setH4(h4: Long) = apply { this.h4 = h4 }
|
||||||
fun setI1(i1: String) = apply { this.i1 = i1 }
|
|
||||||
fun setI2(i2: String) = apply { this.i2 = i2 }
|
|
||||||
fun setI3(i3: String) = apply { this.i3 = i3 }
|
|
||||||
fun setI4(i4: String) = apply { this.i4 = i4 }
|
|
||||||
fun setI5(i5: String) = apply { this.i5 = i5 }
|
|
||||||
fun setJ1(j1: String) = apply { this.j1 = j1 }
|
|
||||||
fun setJ2(j2: String) = apply { this.j2 = j2 }
|
|
||||||
fun setJ3(j3: String) = apply { this.j3 = j3 }
|
|
||||||
fun setItime(itime: Int) = apply { this.itime = itime }
|
|
||||||
|
|
||||||
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
|
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,8 +130,8 @@ class Xray : Protocol() {
|
||||||
LibXray.initXray(assetsPath)
|
LibXray.initXray(assetsPath)
|
||||||
val geoDir = File(assetsPath, "geo").absolutePath
|
val geoDir = File(assetsPath, "geo").absolutePath
|
||||||
val configPath = File(context.cacheDir, "config.json")
|
val configPath = File(context.cacheDir, "config.json")
|
||||||
Log.v(TAG, "xray.location.asset: $geoDir")
|
Log.d(TAG, "xray.location.asset: $geoDir")
|
||||||
Log.v(TAG, "config: $configPath")
|
Log.d(TAG, "config: $configPath")
|
||||||
try {
|
try {
|
||||||
configPath.writeText(configJson)
|
configPath.writeText(configJson)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
|
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
|
||||||
|
|
||||||
set(APP_ANDROID_MIN_SDK 26)
|
set(APP_ANDROID_MIN_SDK 24)
|
||||||
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
|
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
|
||||||
"The minimum API level supported by the application or library" FORCE)
|
"The minimum API level supported by the application or library" FORCE)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,22 +76,12 @@ set_target_properties(${PROJECT} PROPERTIES
|
||||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
||||||
XCODE_EMBED_APP_EXTENSIONS networkextension
|
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(DEFINED DEPLOY)
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
|
||||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN"
|
|
||||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
|
|
@ -136,9 +126,9 @@ add_subdirectory(ios/networkextension)
|
||||||
add_dependencies(${PROJECT} networkextension)
|
add_dependencies(${PROJECT} networkextension)
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework"
|
"${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/)
|
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos)
|
||||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework")
|
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework")
|
||||||
|
|
||||||
|
|
|
||||||
181
client/cmake/macos_ne.cmake
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
message("Client ==> MacOS NE build")
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||||
|
|
||||||
|
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
|
|
||||||
|
|
||||||
|
enable_language(OBJC)
|
||||||
|
# enable_language(OBJCXX)
|
||||||
|
enable_language(Swift)
|
||||||
|
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
|
||||||
|
set(LIBS ${LIBS} Qt6::ShaderTools)
|
||||||
|
|
||||||
|
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
||||||
|
#find_library(FW_UIKIT UIKit)
|
||||||
|
find_library(FW_AVFOUNDATION AVFoundation)
|
||||||
|
find_library(FW_FOUNDATION Foundation)
|
||||||
|
find_library(FW_STOREKIT StoreKit)
|
||||||
|
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
||||||
|
find_library(FW_NETWORKEXTENSION NetworkExtension)
|
||||||
|
|
||||||
|
set(LIBS ${LIBS}
|
||||||
|
${FW_AUTHENTICATIONSERVICES}
|
||||||
|
# ${FW_UIKIT}
|
||||||
|
${FW_AVFOUNDATION}
|
||||||
|
${FW_FOUNDATION}
|
||||||
|
${FW_STOREKIT}
|
||||||
|
${FW_USERNOTIFICATIONS}
|
||||||
|
${FW_NETWORKEXTENSION}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
|
||||||
|
)
|
||||||
|
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h PROPERTIES OBJECTIVE_CPP_HEADER TRUE)
|
||||||
|
|
||||||
|
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
|
||||||
|
)
|
||||||
|
|
||||||
|
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
||||||
|
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
||||||
|
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||||
|
set(SOURCES ${SOURCES} ${ICON_FILE})
|
||||||
|
|
||||||
|
# set(HEADERS ${HEADERS}
|
||||||
|
# ${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.h
|
||||||
|
# )
|
||||||
|
|
||||||
|
# set(SOURCES ${SOURCES}
|
||||||
|
# ${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
|
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Info.plist.in
|
||||||
|
#MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
||||||
|
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/app/main.entitlements"
|
||||||
|
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
||||||
|
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||||
|
XCODE_GENERATE_SCHEME TRUE
|
||||||
|
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||||
|
#XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||||
|
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||||
|
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "NO"
|
||||||
|
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES"
|
||||||
|
|
||||||
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks"
|
||||||
|
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||||
|
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
|
# XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
|
||||||
|
# XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution: Privacy Technologies OU (X7UJ388FXK)"
|
||||||
|
# XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
|
|
||||||
|
|
||||||
|
# XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "Mac AppStore AmneziaVPN"
|
||||||
|
# XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "Mac AppStore AmneziaVPN"
|
||||||
|
|
||||||
|
)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OBJC_INTEROP_MODE "objcxx"
|
||||||
|
)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||||
|
)
|
||||||
|
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
target_compile_options(${PROJECT} PRIVATE
|
||||||
|
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
|
||||||
|
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources)
|
||||||
|
|
||||||
|
target_sources(${PROJECT} PRIVATE
|
||||||
|
# ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosvpnprotocol.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/LogController.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(${PROJECT} PRIVATE
|
||||||
|
#${CMAKE_CURRENT_SOURCE_DIR}/macos_ne/app/AmneziaVPNLaunchScreen.storyboard
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Media.xcassets
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||||
|
#${CMAKE_CURRENT_SOURCE_DIR}/macos/app/AmneziaVPNLaunchScreen.storyboard
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Media.xcassets
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(macos/networkextension)
|
||||||
|
add_dependencies(${PROJECT} networkextension)
|
||||||
|
|
||||||
|
# set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||||
|
# "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos)
|
||||||
|
# target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework")
|
||||||
|
|
||||||
|
get_target_property(QtCore_location Qt6::Core LOCATION)
|
||||||
|
message("QtCore_location")
|
||||||
|
message(${QtCore_location})
|
||||||
|
|
||||||
|
get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE)
|
||||||
|
|
||||||
|
|
||||||
|
# add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
# COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
# )
|
||||||
|
|
||||||
|
# if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
# SET(SIGN_CMD codesign --deep --force --sign 'Apple Distribution: Privacy Technologies OU \(X7UJ388FXK\)' --timestamp --options runtime $<TARGET_BUNDLE_DIR:AmneziaVPN>)
|
||||||
|
# message("Manual signing bundle...")
|
||||||
|
# message(${SIGN_CMD})
|
||||||
|
|
||||||
|
|
||||||
|
# add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
# COMMAND ${SIGN_CMD}
|
||||||
|
# )
|
||||||
|
# endif()
|
||||||
|
|
@ -76,7 +76,7 @@ function(osx_bundle_assetcatalog TARGET)
|
||||||
)
|
)
|
||||||
|
|
||||||
## Patch the asset catalog into the target bundle.
|
## Patch the asset catalog into the target bundle.
|
||||||
if(NOT IOS)
|
if(NOT IOS AND NOT MACOS_NE)
|
||||||
set(XCASSETS_RESOURCE_DIR "Resources")
|
set(XCASSETS_RESOURCE_DIR "Resources")
|
||||||
endif()
|
endif()
|
||||||
add_custom_command(TARGET ${TARGET} POST_BUILD
|
add_custom_command(TARGET ${TARGET} POST_BUILD
|
||||||
|
|
@ -141,6 +141,7 @@ function(osx_codesign_target TARGET)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
foreach(FILE ${CODESIGN_FILES})
|
foreach(FILE ${CODESIGN_FILES})
|
||||||
|
message(STATUS "Signing ${TARGET}: ${FILE}")
|
||||||
add_custom_command(TARGET ${TARGET} POST_BUILD VERBATIM
|
add_custom_command(TARGET ${TARGET} POST_BUILD VERBATIM
|
||||||
COMMAND ${COMMENT_ECHO_COMMAND} "Signing ${TARGET}: ${FILE}"
|
COMMAND ${COMMENT_ECHO_COMMAND} "Signing ${TARGET}: ${FILE}"
|
||||||
COMMAND ${CODESIGN_BIN} ${CODESIGN_ARGS} ${FILE}
|
COMMAND ${CODESIGN_BIN} ${CODESIGN_ARGS} ${FILE}
|
||||||
|
|
|
||||||
|
|
@ -1,191 +0,0 @@
|
||||||
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
|
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
|
||||||
${CLIENT_ROOT_DIR}/migrations.h
|
|
||||||
${CLIENT_ROOT_DIR}/../ipc/ipc.h
|
|
||||||
${CLIENT_ROOT_DIR}/amnezia_application.h
|
|
||||||
${CLIENT_ROOT_DIR}/containers/containers_defs.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/defs.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/errorstrings.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/scripts_registry.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/server_defs.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/api/apiDefs.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/qrCodeUtils.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/coreController.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/serverController.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.h
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/protocols_defs.h
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/qml_register_protocols.h
|
|
||||||
${CLIENT_ROOT_DIR}/ui/pages.h
|
|
||||||
${CLIENT_ROOT_DIR}/ui/qautostart.h
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/vpnprotocol.h
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/version.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/sshclient.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/networkUtilities.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/serialization.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/transfer.h
|
|
||||||
${CLIENT_ROOT_DIR}/../common/logger/logger.h
|
|
||||||
${CLIENT_ROOT_DIR}/utils/qmlUtils.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/api/apiUtils.h
|
|
||||||
)
|
|
||||||
|
|
||||||
# Mozilla headres
|
|
||||||
set(HEADERS ${HEADERS}
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/models/server.h
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.h
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.h
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT IOS)
|
|
||||||
set(HEADERS ${HEADERS}
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT ANDROID)
|
|
||||||
set(HEADERS ${HEADERS}
|
|
||||||
${CLIENT_ROOT_DIR}/ui/notificationhandler.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
|
||||||
${CLIENT_ROOT_DIR}/migrations.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/amnezia_application.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/containers/containers_defs.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/errorstrings.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/scripts_registry.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/server_defs.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/qrCodeUtils.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/coreController.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/serverController.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/protocols_defs.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/ui/qautostart.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/vpnprotocol.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/sshclient.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/networkUtilities.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/outbound.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/inbound.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/ss.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/ssd.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/vless.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/trojan.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/vmess.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/vmess_new.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/utils/qmlUtils.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/api/apiUtils.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Mozilla sources
|
|
||||||
set(SOURCES ${SOURCES}
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/models/server.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT IOS)
|
|
||||||
set(SOURCES ${SOURCES}
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT ANDROID)
|
|
||||||
set(SOURCES ${SOURCES}
|
|
||||||
${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.h)
|
|
||||||
file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.cpp)
|
|
||||||
|
|
||||||
file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.h)
|
|
||||||
file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.cpp)
|
|
||||||
|
|
||||||
file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.h)
|
|
||||||
file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.cpp)
|
|
||||||
|
|
||||||
file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
|
|
||||||
${CLIENT_ROOT_DIR}/ui/models/*.h
|
|
||||||
${CLIENT_ROOT_DIR}/ui/models/protocols/*.h
|
|
||||||
${CLIENT_ROOT_DIR}/ui/models/services/*.h
|
|
||||||
${CLIENT_ROOT_DIR}/ui/models/api/*.h
|
|
||||||
)
|
|
||||||
file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
|
|
||||||
${CLIENT_ROOT_DIR}/ui/models/*.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/ui/models/protocols/*.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/ui/models/services/*.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/ui/models/api/*.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS
|
|
||||||
${CLIENT_ROOT_DIR}/ui/controllers/*.h
|
|
||||||
${CLIENT_ROOT_DIR}/ui/controllers/api/*.h
|
|
||||||
)
|
|
||||||
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS
|
|
||||||
${CLIENT_ROOT_DIR}/ui/controllers/*.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/ui/controllers/api/*.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
|
||||||
${COMMON_FILES_H}
|
|
||||||
${PAGE_LOGIC_H}
|
|
||||||
${CONFIGURATORS_H}
|
|
||||||
${UI_MODELS_H}
|
|
||||||
${UI_CONTROLLERS_H}
|
|
||||||
)
|
|
||||||
set(SOURCES ${SOURCES}
|
|
||||||
${COMMON_FILES_CPP}
|
|
||||||
${PAGE_LOGIC_CPP}
|
|
||||||
${CONFIGURATORS_CPP}
|
|
||||||
${UI_MODELS_CPP}
|
|
||||||
${UI_CONTROLLERS_CPP}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
set(HEADERS ${HEADERS}
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.h
|
|
||||||
)
|
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(RESOURCES ${RESOURCES}
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
|
||||||
message("Client desktop build")
|
|
||||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
|
||||||
${CLIENT_ROOT_DIR}/core/ipcclient.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/privileged_process.h
|
|
||||||
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.h
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.h
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.h
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/xrayprotocol.h
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/awgprotocol.h
|
|
||||||
)
|
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
|
||||||
${CLIENT_ROOT_DIR}/core/ipcclient.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/privileged_process.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/xrayprotocol.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/awgprotocol.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
#include "awg_configurator.h"
|
#include "awg_configurator.h"
|
||||||
#include "protocols/protocols_defs.h"
|
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
@ -40,20 +39,6 @@ QString AwgConfigurator::createConfig(const ServerCredentials &credentials, Dock
|
||||||
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
|
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
|
||||||
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
|
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
|
||||||
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
|
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
|
||||||
|
|
||||||
// jsonConfig[config_key::cookieReplyPacketJunkSize] = configMap.value(config_key::cookieReplyPacketJunkSize);
|
|
||||||
// jsonConfig[config_key::transportPacketJunkSize] = configMap.value(config_key::transportPacketJunkSize);
|
|
||||||
|
|
||||||
// jsonConfig[config_key::specialJunk1] = configMap.value(amnezia::config_key::specialJunk1);
|
|
||||||
// jsonConfig[config_key::specialJunk2] = configMap.value(amnezia::config_key::specialJunk2);
|
|
||||||
// jsonConfig[config_key::specialJunk3] = configMap.value(amnezia::config_key::specialJunk3);
|
|
||||||
// jsonConfig[config_key::specialJunk4] = configMap.value(amnezia::config_key::specialJunk4);
|
|
||||||
// jsonConfig[config_key::specialJunk5] = configMap.value(amnezia::config_key::specialJunk5);
|
|
||||||
// jsonConfig[config_key::controlledJunk1] = configMap.value(amnezia::config_key::controlledJunk1);
|
|
||||||
// jsonConfig[config_key::controlledJunk2] = configMap.value(amnezia::config_key::controlledJunk2);
|
|
||||||
// jsonConfig[config_key::controlledJunk3] = configMap.value(amnezia::config_key::controlledJunk3);
|
|
||||||
// jsonConfig[config_key::specialHandshakeTimeout] = configMap.value(amnezia::config_key::specialHandshakeTimeout);
|
|
||||||
|
|
||||||
jsonConfig[config_key::mtu] =
|
jsonConfig[config_key::mtu] =
|
||||||
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);
|
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "core/networkUtilities.h"
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "core/controllers/serverController.h"
|
#include "core/controllers/serverController.h"
|
||||||
#include "core/scripts_registry.h"
|
#include "core/scripts_registry.h"
|
||||||
|
#include "core/server_defs.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
|
@ -24,7 +24,6 @@
|
||||||
#include <openssl/rsa.h>
|
#include <openssl/rsa.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
|
|
||||||
|
|
||||||
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||||
QObject *parent)
|
QObject *parent)
|
||||||
: ConfiguratorBase(settings, serverController, parent)
|
: ConfiguratorBase(settings, serverController, parent)
|
||||||
|
|
@ -118,22 +117,22 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
||||||
QRegularExpression regex("redirect-gateway.*");
|
QRegularExpression regex("redirect-gateway.*");
|
||||||
config.replace(regex, "");
|
config.replace(regex, "");
|
||||||
|
|
||||||
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
|
||||||
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
|
|
||||||
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
|
|
||||||
config.replace(dnsRegex, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
||||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||||
|
|
||||||
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
|
// Prevent ipv6 leak
|
||||||
|
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||||
|
#endif
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||||
|
|
||||||
// no redirect-gateway
|
// no redirect-gateway
|
||||||
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||||
// Prevent ipv6 leak
|
// Prevent ipv6 leak
|
||||||
|
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||||
#endif
|
#endif
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
}
|
}
|
||||||
|
|
@ -167,15 +166,10 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString
|
||||||
QRegularExpression regex("redirect-gateway.*");
|
QRegularExpression regex("redirect-gateway.*");
|
||||||
config.replace(regex, "");
|
config.replace(regex, "");
|
||||||
|
|
||||||
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
|
||||||
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
|
|
||||||
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
|
|
||||||
config.replace(dnsRegex, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||||
|
|
||||||
// Prevent ipv6 leak
|
// Prevent ipv6 leak
|
||||||
|
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
|
|
||||||
// remove block-outside-dns for all exported configs
|
// remove block-outside-dns for all exported configs
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,9 @@ QProcessEnvironment SshConfigurator::prepareEnv()
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
||||||
#elif defined(Q_OS_MACX)
|
#elif defined(Q_OS_MACX)
|
||||||
|
#if !defined(MACOS_NE)
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
env.insert("PATH", pathEnvVar);
|
env.insert("PATH", pathEnvVar);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
|
@ -20,17 +19,13 @@
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
|
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||||
const QSharedPointer<ServerController> &serverController, bool isAwg,
|
bool isAwg, QObject *parent)
|
||||||
QObject *parent)
|
|
||||||
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
||||||
{
|
{
|
||||||
m_serverConfigPath =
|
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||||
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
||||||
m_serverPublicKeyPath =
|
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
||||||
m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
|
||||||
m_serverPskKeyPath =
|
|
||||||
m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
|
||||||
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
|
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
|
||||||
|
|
||||||
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
|
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
|
||||||
|
|
@ -68,31 +63,9 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QHostAddress> WireguardConfigurator::getIpsFromConf(const QString &input)
|
|
||||||
{
|
|
||||||
QRegularExpression regex("AllowedIPs = (\\d+\\.\\d+\\.\\d+\\.\\d+)");
|
|
||||||
QRegularExpressionMatchIterator matchIterator = regex.globalMatch(input);
|
|
||||||
|
|
||||||
QList<QHostAddress> ips;
|
|
||||||
|
|
||||||
while (matchIterator.hasNext()) {
|
|
||||||
QRegularExpressionMatch match = matchIterator.next();
|
|
||||||
const QString address_string { match.captured(1) };
|
|
||||||
const QHostAddress address { address_string };
|
|
||||||
if (address.isNull()) {
|
|
||||||
qWarning() << "Couldn't recognize the ip address: " << address_string;
|
|
||||||
} else {
|
|
||||||
ips << address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ips;
|
|
||||||
}
|
|
||||||
|
|
||||||
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
|
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
|
||||||
DockerContainer container,
|
DockerContainer container,
|
||||||
const QJsonObject &containerConfig,
|
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||||
ErrorCode &errorCode)
|
|
||||||
{
|
{
|
||||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||||
connData.host = credentials.hostName;
|
connData.host = credentials.hostName;
|
||||||
|
|
@ -103,45 +76,65 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString getIpsScript = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
// Get list of already created clients (only IP addresses)
|
||||||
QString stdOut;
|
QString nextIpNumber;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
{
|
||||||
stdOut += data + "\n";
|
QString script = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
||||||
return ErrorCode::NoError;
|
QString stdOut;
|
||||||
};
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
|
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
auto ips = getIpsFromConf(stdOut);
|
|
||||||
|
|
||||||
QHostAddress nextIp = [&] {
|
stdOut.replace("AllowedIPs = ", "");
|
||||||
QHostAddress result;
|
stdOut.replace("/32", "");
|
||||||
QHostAddress lastIp;
|
QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts);
|
||||||
if (ips.empty()) {
|
|
||||||
lastIp.setAddress(containerConfig.value(m_protocolName)
|
// remove extra IPs from each line for case when user manually edited the wg0.conf
|
||||||
.toObject()
|
// and added there more IPs for route his itnernal networks, like:
|
||||||
.value(config_key::subnet_address)
|
// ...
|
||||||
.toString(protocols::wireguard::defaultSubnetAddress));
|
// AllowedIPs = 10.8.1.6/32, 192.168.1.0/24, 192.168.2.0/24, ...
|
||||||
|
// ...
|
||||||
|
// without this code - next IP would be 1 if last item in 'ips' has format above
|
||||||
|
QStringList vpnIps;
|
||||||
|
for (const auto &ip : ips) {
|
||||||
|
vpnIps.append(ip.split(",", Qt::SkipEmptyParts).first().trimmed());
|
||||||
|
}
|
||||||
|
ips = vpnIps;
|
||||||
|
|
||||||
|
// Calc next IP address
|
||||||
|
if (ips.isEmpty()) {
|
||||||
|
nextIpNumber = "2";
|
||||||
} else {
|
} else {
|
||||||
lastIp = ips.last();
|
int next = ips.last().split(".").last().toInt() + 1;
|
||||||
|
if (next > 254) {
|
||||||
|
errorCode = ErrorCode::AddressPoolError;
|
||||||
|
return connData;
|
||||||
|
}
|
||||||
|
nextIpNumber = QString::number(next);
|
||||||
}
|
}
|
||||||
quint8 lastOctet = static_cast<quint8>(lastIp.toIPv4Address());
|
}
|
||||||
switch (lastOctet) {
|
|
||||||
case 254: result.setAddress(lastIp.toIPv4Address() + 3); break;
|
QString subnetIp = containerConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||||
case 255: result.setAddress(lastIp.toIPv4Address() + 2); break;
|
{
|
||||||
default: result.setAddress(lastIp.toIPv4Address() + 1); break;
|
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
|
||||||
|
if (l.isEmpty()) {
|
||||||
|
errorCode = ErrorCode::AddressPoolError;
|
||||||
|
return connData;
|
||||||
}
|
}
|
||||||
|
l.removeLast();
|
||||||
|
l.append(nextIpNumber);
|
||||||
|
|
||||||
return result;
|
connData.clientIP = l.join(".");
|
||||||
}();
|
}
|
||||||
|
|
||||||
connData.clientIP = nextIp.toString();
|
|
||||||
|
|
||||||
// Get keys
|
// Get keys
|
||||||
connData.serverPubKey =
|
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
|
||||||
connData.serverPubKey.replace("\n", "");
|
connData.serverPubKey.replace("\n", "");
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
|
|
@ -168,12 +161,10 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'")
|
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'").arg(m_serverConfigPath);
|
||||||
.arg(m_serverConfigPath);
|
|
||||||
|
|
||||||
errorCode = m_serverController->runScript(
|
errorCode = m_serverController->runScript(
|
||||||
credentials,
|
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
||||||
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
|
||||||
|
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
@ -182,8 +173,8 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
||||||
QString config = m_serverController->replaceVars(
|
QString config =
|
||||||
scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||||
|
|
||||||
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
|
@ -217,16 +208,16 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
||||||
return QJsonDocument(jConfig).toJson();
|
return QJsonDocument(jConfig).toJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
|
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||||
const bool isApiConfig, QString &protocolConfigString)
|
QString &protocolConfigString)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||||
|
|
||||||
return protocolConfigString;
|
return protocolConfigString;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
|
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||||
const bool isApiConfig, QString &protocolConfigString)
|
QString &protocolConfigString)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#ifndef WIREGUARD_CONFIGURATOR_H
|
#ifndef WIREGUARD_CONFIGURATOR_H
|
||||||
#define WIREGUARD_CONFIGURATOR_H
|
#define WIREGUARD_CONFIGURATOR_H
|
||||||
|
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
|
|
||||||
|
|
@ -13,8 +12,8 @@ class WireguardConfigurator : public ConfiguratorBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg,
|
||||||
bool isAwg, QObject *parent = nullptr);
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
struct ConnectionData
|
struct ConnectionData
|
||||||
{
|
{
|
||||||
|
|
@ -27,18 +26,15 @@ public:
|
||||||
QString port;
|
QString port;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
ErrorCode &errorCode);
|
||||||
|
|
||||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
||||||
QString &protocolConfigString);
|
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
||||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
|
||||||
QString &protocolConfigString);
|
|
||||||
|
|
||||||
static ConnectionData genClientKeys();
|
static ConnectionData genClientKeys();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<QHostAddress> getIpsFromConf(const QString &input);
|
|
||||||
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,169 +3,38 @@
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QUuid>
|
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "core/controllers/serverController.h"
|
#include "core/controllers/serverController.h"
|
||||||
#include "core/scripts_registry.h"
|
#include "core/scripts_registry.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
Logger logger("XrayConfigurator");
|
|
||||||
}
|
|
||||||
|
|
||||||
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||||
: ConfiguratorBase(settings, serverController, parent)
|
: ConfiguratorBase(settings, serverController, parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
|
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
// Generate new UUID for client
|
|
||||||
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
|
||||||
|
|
||||||
// Get current server config
|
|
||||||
QString currentConfig = m_serverController->getTextFileFromContainer(
|
|
||||||
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
|
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
logger.error() << "Failed to get server config file";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse current config as JSON
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(currentConfig.toUtf8());
|
|
||||||
if (doc.isNull() || !doc.isObject()) {
|
|
||||||
logger.error() << "Failed to parse server config JSON";
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject serverConfig = doc.object();
|
|
||||||
|
|
||||||
// Validate server config structure
|
|
||||||
if (!serverConfig.contains("inbounds")) {
|
|
||||||
logger.error() << "Server config missing 'inbounds' field";
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonArray inbounds = serverConfig["inbounds"].toArray();
|
|
||||||
if (inbounds.isEmpty()) {
|
|
||||||
logger.error() << "Server config has empty 'inbounds' array";
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject inbound = inbounds[0].toObject();
|
|
||||||
if (!inbound.contains("settings")) {
|
|
||||||
logger.error() << "Inbound missing 'settings' field";
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject settings = inbound["settings"].toObject();
|
|
||||||
if (!settings.contains("clients")) {
|
|
||||||
logger.error() << "Settings missing 'clients' field";
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonArray clients = settings["clients"].toArray();
|
|
||||||
|
|
||||||
// Create configuration for new client
|
|
||||||
QJsonObject clientConfig {
|
|
||||||
{"id", clientId},
|
|
||||||
{"flow", "xtls-rprx-vision"}
|
|
||||||
};
|
|
||||||
|
|
||||||
clients.append(clientConfig);
|
|
||||||
|
|
||||||
// Update config
|
|
||||||
settings["clients"] = clients;
|
|
||||||
inbound["settings"] = settings;
|
|
||||||
inbounds[0] = inbound;
|
|
||||||
serverConfig["inbounds"] = inbounds;
|
|
||||||
|
|
||||||
// Save updated config to server
|
|
||||||
QString updatedConfig = QJsonDocument(serverConfig).toJson();
|
|
||||||
errorCode = m_serverController->uploadTextFileToContainer(
|
|
||||||
container,
|
|
||||||
credentials,
|
|
||||||
updatedConfig,
|
|
||||||
amnezia::protocols::xray::serverConfigPath,
|
|
||||||
libssh::ScpOverwriteMode::ScpOverwriteExisting
|
|
||||||
);
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
logger.error() << "Failed to upload updated config";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restart container
|
|
||||||
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
|
|
||||||
errorCode = m_serverController->runScript(
|
|
||||||
credentials,
|
|
||||||
m_serverController->replaceVars(restartScript, m_serverController->genVarsForScript(credentials, container))
|
|
||||||
);
|
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
logger.error() << "Failed to restart container";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
|
||||||
{
|
|
||||||
// Get client ID from prepareServerConfig
|
|
||||||
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, errorCode);
|
|
||||||
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
|
|
||||||
logger.error() << "Failed to prepare server config";
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
|
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
|
||||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||||
|
|
||||||
if (config.isEmpty()) {
|
|
||||||
logger.error() << "Failed to get config template";
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString xrayPublicKey =
|
QString xrayPublicKey =
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
|
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
|
|
||||||
logger.error() << "Failed to get public key";
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
xrayPublicKey.replace("\n", "");
|
xrayPublicKey.replace("\n", "");
|
||||||
|
|
||||||
|
QString xrayUuid = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, errorCode);
|
||||||
|
xrayUuid.replace("\n", "");
|
||||||
|
|
||||||
QString xrayShortId =
|
QString xrayShortId =
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
|
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
|
|
||||||
logger.error() << "Failed to get short ID";
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
xrayShortId.replace("\n", "");
|
xrayShortId.replace("\n", "");
|
||||||
|
|
||||||
// Validate all required variables are present
|
if (errorCode != ErrorCode::NoError) {
|
||||||
if (!config.contains("$XRAY_CLIENT_ID") || !config.contains("$XRAY_PUBLIC_KEY") || !config.contains("$XRAY_SHORT_ID")) {
|
|
||||||
logger.error() << "Config template missing required variables:"
|
|
||||||
<< "XRAY_CLIENT_ID:" << !config.contains("$XRAY_CLIENT_ID")
|
|
||||||
<< "XRAY_PUBLIC_KEY:" << !config.contains("$XRAY_PUBLIC_KEY")
|
|
||||||
<< "XRAY_SHORT_ID:" << !config.contains("$XRAY_SHORT_ID");
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
config.replace("$XRAY_CLIENT_ID", xrayClientId);
|
config.replace("$XRAY_CLIENT_ID", xrayUuid);
|
||||||
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
|
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
|
||||||
config.replace("$XRAY_SHORT_ID", xrayShortId);
|
config.replace("$XRAY_SHORT_ID", xrayShortId);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,6 @@ public:
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||||
ErrorCode &errorCode);
|
ErrorCode &errorCode);
|
||||||
|
|
||||||
private:
|
|
||||||
QString prepareServerConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
|
||||||
ErrorCode &errorCode);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // XRAY_CONFIGURATOR_H
|
#endif // XRAY_CONFIGURATOR_H
|
||||||
|
|
|
||||||
|
|
@ -110,19 +110,22 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
|
||||||
QObject::tr("OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its "
|
QObject::tr("OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its "
|
||||||
"own security protocol with SSL/TLS for key exchange.") },
|
"own security protocol with SSL/TLS for key exchange.") },
|
||||||
{ DockerContainer::ShadowSocks,
|
{ DockerContainer::ShadowSocks,
|
||||||
QObject::tr("Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems.") },
|
QObject::tr("Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it "
|
||||||
|
"may be recognized by analysis systems in some highly censored regions.") },
|
||||||
{ DockerContainer::Cloak,
|
{ DockerContainer::Cloak,
|
||||||
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
|
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
|
||||||
"active-probing detection. It is very resistant to detection, but offers low speed.") },
|
"active-probing detection. Ideal for bypassing blocking in regions with the highest levels "
|
||||||
|
"of censorship.") },
|
||||||
{ DockerContainer::WireGuard,
|
{ DockerContainer::WireGuard,
|
||||||
QObject::tr("WireGuard - popular VPN protocol with high performance, high speed and low power "
|
QObject::tr("WireGuard - New popular VPN protocol with high performance, high speed and low power "
|
||||||
"consumption.") },
|
"consumption. Recommended for regions with low levels of censorship.") },
|
||||||
{ DockerContainer::Awg,
|
{ DockerContainer::Awg,
|
||||||
QObject::tr("AmneziaWG is a special protocol from Amnezia based on WireGuard. "
|
QObject::tr("AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, "
|
||||||
"It provides high connection speed and ensures stable operation even in the most challenging network conditions.") },
|
"but very resistant to blockages. "
|
||||||
|
"Recommended for regions with high levels of censorship.") },
|
||||||
{ DockerContainer::Xray,
|
{ DockerContainer::Xray,
|
||||||
QObject::tr("XRay with REALITY masks VPN traffic as web traffic and protects against active probing. "
|
QObject::tr("XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
|
||||||
"It is highly resistant to detection and offers high speed.") },
|
"Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
|
||||||
{ DockerContainer::Ipsec,
|
{ DockerContainer::Ipsec,
|
||||||
QObject::tr("IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after "
|
QObject::tr("IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after "
|
||||||
"signal loss. It has native support on the latest versions of Android and iOS.") },
|
"signal loss. It has native support on the latest versions of Android and iOS.") },
|
||||||
|
|
@ -140,83 +143,100 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
{ DockerContainer::OpenVpn,
|
{ DockerContainer::OpenVpn,
|
||||||
QObject::tr("OpenVPN is one of the most popular and reliable VPN protocols. "
|
QObject::tr(
|
||||||
"It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, "
|
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
|
||||||
"and is continuously improved by the community due to its open-source nature. "
|
"It employs its unique security protocol, "
|
||||||
"It provides a good balance between speed and security but is easily recognized by DPI systems, "
|
"leveraging the strength of SSL/TLS for encryption and key exchange. "
|
||||||
"making it susceptible to blocking.\n"
|
"Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, "
|
||||||
"\nFeatures:\n"
|
"catering to a wide range of devices and operating systems. "
|
||||||
"* Available on all AmneziaVPN platforms\n"
|
"Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, "
|
||||||
"* Normal battery consumption on mobile devices\n"
|
"which continually reinforces its security. "
|
||||||
"* Flexible customization for various devices and OS\n"
|
"With a strong balance of performance, security, and compatibility, "
|
||||||
"* Operates over both TCP and UDP protocols") },
|
"OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n"
|
||||||
|
"* Available in the AmneziaVPN across all platforms\n"
|
||||||
|
"* Normal power consumption on mobile devices\n"
|
||||||
|
"* Flexible customisation to suit user needs to work with different operating systems and devices\n"
|
||||||
|
"* Recognised by DPI analysis systems and therefore susceptible to blocking\n"
|
||||||
|
"* Can operate over both TCP and UDP network protocols.") },
|
||||||
{ DockerContainer::ShadowSocks,
|
{ DockerContainer::ShadowSocks,
|
||||||
QObject::tr("Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. "
|
QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. "
|
||||||
"Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. "
|
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection."
|
||||||
"Due to limited support in Amnezia, we recommend using the AmneziaWG protocol.\n"
|
"However, certain traffic analysis systems might still detect a Shadowsocks connection. "
|
||||||
"\nFeatures:\n"
|
"Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n"
|
||||||
"* Available in AmneziaVPN only on desktop platforms\n"
|
"* Available in the AmneziaVPN only on desktop platforms\n"
|
||||||
"* Customizable encryption protocol\n"
|
"* Configurable encryption protocol\n"
|
||||||
"* Detectable by some DPI systems\n"
|
"* Detectable by some DPI systems\n"
|
||||||
"* Operates over TCP protocol\n") },
|
"* Works over TCP network protocol.") },
|
||||||
{ DockerContainer::Cloak,
|
{ DockerContainer::Cloak,
|
||||||
QObject::tr("This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.\n"
|
QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for "
|
||||||
"\nOpenVPN securely encrypts all internet traffic between your device and the server.\n"
|
"protecting against blocking.\n\n"
|
||||||
"\nThe Cloak plugin further protects the connection from DPI detection. "
|
"OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client "
|
||||||
"It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. "
|
"and the server.\n\n"
|
||||||
"If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems.\n"
|
"Cloak protects OpenVPN from detection and blocking. \n\n"
|
||||||
"\nIn regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.\n"
|
"Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, "
|
||||||
"\nFeatures:\n"
|
"and also protects the VPN from detection by Active Probing. This makes it very resistant to "
|
||||||
"* Available on all AmneziaVPN platforms\n"
|
"being detected\n\n"
|
||||||
|
"Immediately after receiving the first data packet, Cloak authenticates the incoming connection. "
|
||||||
|
"If authentication fails, the plugin masks the server as a fake website and your VPN becomes "
|
||||||
|
"invisible to analysis systems.\n\n"
|
||||||
|
"If there is a extreme level of Internet censorship in your region, we advise you to use only "
|
||||||
|
"OpenVPN over Cloak from the first connection\n\n"
|
||||||
|
"* Available in the AmneziaVPN across all platforms\n"
|
||||||
"* High power consumption on mobile devices\n"
|
"* High power consumption on mobile devices\n"
|
||||||
"* Flexible configuration options\n"
|
"* Flexible settings\n"
|
||||||
"* Undetectable by DPI systems\n"
|
"* Not recognised by DPI analysis systems\n"
|
||||||
"* Operates over TCP protocol on port 443") },
|
"* Works over TCP network protocol, 443 port.\n") },
|
||||||
{ DockerContainer::WireGuard,
|
{ DockerContainer::WireGuard,
|
||||||
QObject::tr("WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. "
|
QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n"
|
||||||
"It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. "
|
"WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption "
|
||||||
"However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking.\n"
|
"settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n"
|
||||||
"\nFeatures:\n"
|
"WireGuard is very susceptible to blocking due to its distinct packet signatures. "
|
||||||
"* Available on all AmneziaVPN platforms\n"
|
"Unlike some other VPN protocols that employ obfuscation techniques, "
|
||||||
"* Low power consumption on mobile devices\n"
|
"the consistent signature patterns of WireGuard packets can be more easily identified and "
|
||||||
"* Minimal configuration required\n"
|
"thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n"
|
||||||
"* Easily detected by DPI systems (susceptible to blocking)\n"
|
"* Available in the AmneziaVPN across all platforms\n"
|
||||||
"* Operates over UDP protocol") },
|
"* Low power consumption\n"
|
||||||
|
"* Minimum number of settings\n"
|
||||||
|
"* Easily recognised by DPI analysis systems, susceptible to blocking\n"
|
||||||
|
"* Works over UDP network protocol.") },
|
||||||
{ DockerContainer::Awg,
|
{ DockerContainer::Awg,
|
||||||
QObject::tr("AmneziaWG is a modern VPN protocol based on WireGuard, "
|
QObject::tr("A modern iteration of the popular VPN protocol, "
|
||||||
"combining simplified architecture with high performance across all devices. "
|
"AmneziaWG builds upon the foundation set by WireGuard, "
|
||||||
"It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, "
|
"retaining its simplified architecture and high-performance capabilities across devices.\n"
|
||||||
"making VPN traffic indistinguishable from regular internet traffic.\n"
|
"While WireGuard is known for its efficiency, "
|
||||||
"\nAmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection.\n"
|
"it had issues with being easily detected due to its distinct packet signatures. "
|
||||||
"\nFeatures:\n"
|
"AmneziaWG solves this problem by using better obfuscation methods, "
|
||||||
"* Available on all AmneziaVPN platforms\n"
|
"making its traffic blend in with regular internet traffic.\n"
|
||||||
"* Low battery consumption on mobile devices\n"
|
"This means that AmneziaWG keeps the fast performance of the original "
|
||||||
"* Minimal settings required\n"
|
"while adding an extra layer of stealth, "
|
||||||
"* Undetectable by traffic analysis systems (DPI)\n"
|
"making it a great choice for those wanting a fast and discreet VPN connection.\n\n"
|
||||||
"* Operates over UDP protocol") },
|
"* Available in the AmneziaVPN across all platforms\n"
|
||||||
|
"* Low power consumption\n"
|
||||||
|
"* Minimum number of settings\n"
|
||||||
|
"* Not recognised by DPI analysis systems, resistant to blocking\n"
|
||||||
|
"* Works over UDP network protocol.") },
|
||||||
{ DockerContainer::Xray,
|
{ DockerContainer::Xray,
|
||||||
QObject::tr("REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. "
|
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
|
||||||
"REALITY identifies censorship systems during the TLS handshake, "
|
"is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion.\n"
|
||||||
"redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. "
|
"It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, "
|
||||||
"This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration."
|
"thus presenting an authentic TLS certificate and data. \n"
|
||||||
"\nUnlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in \"friend-or-foe\" detection mechanism, "
|
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
|
||||||
"effectively protecting against DPI and other traffic analysis methods.\n"
|
"legitimate sites without the need for specific configurations. \n"
|
||||||
"\nFeatures:\n"
|
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
|
||||||
"* Resistant to active probing and DPI detection\n"
|
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. "
|
||||||
"* No special configuration required to disguise traffic\n"
|
"This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.")
|
||||||
"* Highly effective in heavily censored regions\n"
|
},
|
||||||
"* Minimal battery consumption on devices\n"
|
|
||||||
"* Operates over TCP protocol") },
|
|
||||||
{ DockerContainer::Ipsec,
|
{ DockerContainer::Ipsec,
|
||||||
QObject::tr("IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. "
|
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
|
||||||
"It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. "
|
"One of its distinguishing features is its ability to swiftly switch between networks and devices, "
|
||||||
"While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking.\n"
|
"making it particularly adaptive in dynamic network environments. \n"
|
||||||
"\nFeatures:\n"
|
"While it offers a blend of security, stability, and speed, "
|
||||||
"* Available in AmneziaVPN only on Windows\n"
|
"it's essential to note that IKEv2 can be easily detected and is susceptible to blocking.\n\n"
|
||||||
"* Low battery consumption on mobile devices\n"
|
"* Available in the AmneziaVPN only on Windows\n"
|
||||||
"* Minimal configuration required\n"
|
"* Low power consumption, on mobile devices\n"
|
||||||
"* Detectable by DPI analysis systems(easily blocked)\n"
|
"* Minimal configuration\n"
|
||||||
"* Operates over UDP protocol(ports 500 and 4500)") },
|
"* Recognised by DPI analysis systems\n"
|
||||||
|
"* Works over UDP network protocol, ports 500 and 4500.") },
|
||||||
|
|
||||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||||
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
||||||
|
|
@ -312,7 +332,9 @@ QStringList ContainerProps::fixedPortsForContainer(DockerContainer c)
|
||||||
bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
|
case DockerContainer::WireGuard: return true;
|
||||||
case DockerContainer::Awg: return true;
|
case DockerContainer::Awg: return true;
|
||||||
|
// case DockerContainer::Cloak: return true;
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -320,7 +342,9 @@ bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
||||||
QString ContainerProps::easySetupHeader(DockerContainer container)
|
QString ContainerProps::easySetupHeader(DockerContainer container)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case DockerContainer::Awg: return tr("Automatic");
|
case DockerContainer::WireGuard: return tr("Low");
|
||||||
|
case DockerContainer::Awg: return tr("High");
|
||||||
|
// case DockerContainer::Cloak: return tr("Extreme");
|
||||||
default: return "";
|
default: return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -328,8 +352,10 @@ QString ContainerProps::easySetupHeader(DockerContainer container)
|
||||||
QString ContainerProps::easySetupDescription(DockerContainer container)
|
QString ContainerProps::easySetupDescription(DockerContainer container)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case DockerContainer::Awg: return tr("AmneziaWG protocol will be installed. "
|
case DockerContainer::WireGuard: return tr("I just want to increase the level of my privacy.");
|
||||||
"It provides high connection speed and ensures stable operation even in the most challenging network conditions.");
|
case DockerContainer::Awg: return tr("I want to bypass censorship. This option recommended in most cases.");
|
||||||
|
// case DockerContainer::Cloak:
|
||||||
|
// return tr("Most VPN protocols are blocked. Recommended if other options are not working.");
|
||||||
default: return "";
|
default: return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +363,9 @@ QString ContainerProps::easySetupDescription(DockerContainer container)
|
||||||
int ContainerProps::easySetupOrder(DockerContainer container)
|
int ContainerProps::easySetupOrder(DockerContainer container)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case DockerContainer::Awg: return 1;
|
case DockerContainer::WireGuard: return 3;
|
||||||
|
case DockerContainer::Awg: return 2;
|
||||||
|
// case DockerContainer::Cloak: return 1;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -356,9 +384,9 @@ bool ContainerProps::isShareable(DockerContainer container)
|
||||||
QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
|
QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
|
||||||
{
|
{
|
||||||
QString protocolConfigString = containerConfig.value(ProtocolProps::protoToString(protocol))
|
QString protocolConfigString = containerConfig.value(ProtocolProps::protoToString(protocol))
|
||||||
.toObject()
|
.toObject()
|
||||||
.value(config_key::last_config)
|
.value(config_key::last_config)
|
||||||
.toString();
|
.toString();
|
||||||
|
|
||||||
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
#ifndef APIDEFS_H
|
|
||||||
#define APIDEFS_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
namespace apiDefs
|
|
||||||
{
|
|
||||||
enum ConfigType {
|
|
||||||
AmneziaFreeV2 = 0,
|
|
||||||
AmneziaFreeV3,
|
|
||||||
AmneziaPremiumV1,
|
|
||||||
AmneziaPremiumV2,
|
|
||||||
SelfHosted,
|
|
||||||
ExternalPremium
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ConfigSource {
|
|
||||||
Telegram = 1,
|
|
||||||
AmneziaGateway
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace key
|
|
||||||
{
|
|
||||||
constexpr QLatin1String configVersion("config_version");
|
|
||||||
constexpr QLatin1String apiEndpoint("api_endpoint");
|
|
||||||
constexpr QLatin1String apiKey("api_key");
|
|
||||||
constexpr QLatin1String description("description");
|
|
||||||
constexpr QLatin1String name("name");
|
|
||||||
constexpr QLatin1String protocol("protocol");
|
|
||||||
|
|
||||||
constexpr QLatin1String apiConfig("api_config");
|
|
||||||
constexpr QLatin1String stackType("stack_type");
|
|
||||||
constexpr QLatin1String serviceType("service_type");
|
|
||||||
constexpr QLatin1String cliVersion("cli_version");
|
|
||||||
constexpr QLatin1String supportedProtocols("supported_protocols");
|
|
||||||
|
|
||||||
constexpr QLatin1String vpnKey("vpn_key");
|
|
||||||
constexpr QLatin1String config("config");
|
|
||||||
constexpr QLatin1String configs("configs");
|
|
||||||
|
|
||||||
constexpr QLatin1String installationUuid("installation_uuid");
|
|
||||||
constexpr QLatin1String workerLastUpdated("worker_last_updated");
|
|
||||||
constexpr QLatin1String lastDownloaded("last_downloaded");
|
|
||||||
constexpr QLatin1String sourceType("source_type");
|
|
||||||
|
|
||||||
constexpr QLatin1String serverCountryCode("server_country_code");
|
|
||||||
constexpr QLatin1String serverCountryName("server_country_name");
|
|
||||||
|
|
||||||
constexpr QLatin1String osVersion("os_version");
|
|
||||||
|
|
||||||
constexpr QLatin1String availableCountries("available_countries");
|
|
||||||
constexpr QLatin1String activeDeviceCount("active_device_count");
|
|
||||||
constexpr QLatin1String maxDeviceCount("max_device_count");
|
|
||||||
constexpr QLatin1String subscriptionEndDate("subscription_end_date");
|
|
||||||
constexpr QLatin1String issuedConfigs("issued_configs");
|
|
||||||
|
|
||||||
constexpr QLatin1String supportInfo("support_info");
|
|
||||||
constexpr QLatin1String email("email");
|
|
||||||
constexpr QLatin1String billingEmail("billing_email");
|
|
||||||
constexpr QLatin1String website("website");
|
|
||||||
constexpr QLatin1String websiteName("website_name");
|
|
||||||
constexpr QLatin1String telegram("telegram");
|
|
||||||
|
|
||||||
constexpr QLatin1String id("id");
|
|
||||||
constexpr QLatin1String orderId("order_id");
|
|
||||||
constexpr QLatin1String migrationCode("migration_code");
|
|
||||||
}
|
|
||||||
|
|
||||||
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // APIDEFS_H
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
||||||
#include "apiUtils.h"
|
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const QByteArray AMNEZIA_CONFIG_SIGNATURE = QByteArray::fromHex("000000ff");
|
|
||||||
|
|
||||||
QString escapeUnicode(const QString &input)
|
|
||||||
{
|
|
||||||
QString output;
|
|
||||||
for (QChar c : input) {
|
|
||||||
if (c.unicode() < 0x20 || c.unicode() > 0x7E) {
|
|
||||||
output += QString("\\u%1").arg(QString::number(c.unicode(), 16).rightJustified(4, '0'));
|
|
||||||
} else {
|
|
||||||
output += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
|
|
||||||
{
|
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
|
||||||
QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs);
|
|
||||||
return endDate < now;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
|
|
||||||
{
|
|
||||||
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
|
|
||||||
switch (configVersion) {
|
|
||||||
case apiDefs::ConfigSource::Telegram: return true;
|
|
||||||
case apiDefs::ConfigSource::AmneziaGateway: return true;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
|
|
||||||
{
|
|
||||||
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
|
|
||||||
|
|
||||||
switch (configVersion) {
|
|
||||||
case apiDefs::ConfigSource::Telegram: {
|
|
||||||
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
|
|
||||||
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
|
|
||||||
|
|
||||||
auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString();
|
|
||||||
|
|
||||||
if (apiEndpoint.contains(premiumV1Endpoint)) {
|
|
||||||
return apiDefs::ConfigType::AmneziaPremiumV1;
|
|
||||||
} else if (apiEndpoint.contains(freeV2Endpoint)) {
|
|
||||||
return apiDefs::ConfigType::AmneziaFreeV2;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
case apiDefs::ConfigSource::AmneziaGateway: {
|
|
||||||
constexpr QLatin1String servicePremium("amnezia-premium");
|
|
||||||
constexpr QLatin1String serviceFree("amnezia-free");
|
|
||||||
constexpr QLatin1String serviceExternalPremium("external-premium");
|
|
||||||
|
|
||||||
auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
|
|
||||||
auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString();
|
|
||||||
|
|
||||||
if (serviceType == servicePremium) {
|
|
||||||
return apiDefs::ConfigType::AmneziaPremiumV2;
|
|
||||||
} else if (serviceType == serviceFree) {
|
|
||||||
return apiDefs::ConfigType::AmneziaFreeV3;
|
|
||||||
} else if (serviceType == serviceExternalPremium) {
|
|
||||||
return apiDefs::ConfigType::ExternalPremium;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return apiDefs::ConfigType::SelfHosted;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
apiDefs::ConfigSource apiUtils::getConfigSource(const QJsonObject &serverConfigObject)
|
|
||||||
{
|
|
||||||
return static_cast<apiDefs::ConfigSource>(serverConfigObject.value(apiDefs::key::configVersion).toInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
|
|
||||||
{
|
|
||||||
const int httpStatusCodeConflict = 409;
|
|
||||||
const int httpStatusCodeNotFound = 404;
|
|
||||||
|
|
||||||
if (!sslErrors.empty()) {
|
|
||||||
qDebug().noquote() << sslErrors;
|
|
||||||
return amnezia::ErrorCode::ApiConfigSslError;
|
|
||||||
} else if (reply->error() == QNetworkReply::NoError) {
|
|
||||||
return amnezia::ErrorCode::NoError;
|
|
||||||
} else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
|
||||||
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
|
||||||
qDebug() << reply->error();
|
|
||||||
return amnezia::ErrorCode::ApiConfigTimeoutError;
|
|
||||||
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
|
|
||||||
qDebug() << reply->error();
|
|
||||||
return amnezia::ErrorCode::ApiUpdateRequestError;
|
|
||||||
} else {
|
|
||||||
QString err = reply->errorString();
|
|
||||||
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
||||||
qDebug() << QString::fromUtf8(reply->readAll());
|
|
||||||
qDebug() << reply->error();
|
|
||||||
qDebug() << err;
|
|
||||||
qDebug() << httpStatusCode;
|
|
||||||
if (httpStatusCode == httpStatusCodeConflict) {
|
|
||||||
return amnezia::ErrorCode::ApiConfigLimitError;
|
|
||||||
} else if (httpStatusCode == httpStatusCodeNotFound) {
|
|
||||||
return amnezia::ErrorCode::ApiNotFoundError;
|
|
||||||
}
|
|
||||||
return amnezia::ErrorCode::ApiConfigDownloadError;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "something went wrong";
|
|
||||||
return amnezia::ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
|
|
||||||
{
|
|
||||||
static const QSet<apiDefs::ConfigType> premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2,
|
|
||||||
apiDefs::ConfigType::ExternalPremium };
|
|
||||||
return premiumTypes.contains(getConfigType(serverConfigObject));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
|
|
||||||
{
|
|
||||||
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QPair<QString, QVariant>> orderedFields;
|
|
||||||
orderedFields.append(qMakePair(apiDefs::key::name, serverConfigObject[apiDefs::key::name].toString()));
|
|
||||||
orderedFields.append(qMakePair(apiDefs::key::description, serverConfigObject[apiDefs::key::description].toString()));
|
|
||||||
orderedFields.append(qMakePair(apiDefs::key::configVersion, serverConfigObject[apiDefs::key::configVersion].toDouble()));
|
|
||||||
orderedFields.append(qMakePair(apiDefs::key::protocol, serverConfigObject[apiDefs::key::protocol].toString()));
|
|
||||||
orderedFields.append(qMakePair(apiDefs::key::apiEndpoint, serverConfigObject[apiDefs::key::apiEndpoint].toString()));
|
|
||||||
orderedFields.append(qMakePair(apiDefs::key::apiKey, serverConfigObject[apiDefs::key::apiKey].toString()));
|
|
||||||
|
|
||||||
QString vpnKeyStr = "{";
|
|
||||||
for (int i = 0; i < orderedFields.size(); ++i) {
|
|
||||||
const auto &pair = orderedFields[i];
|
|
||||||
if (pair.second.typeId() == QMetaType::Type::QString) {
|
|
||||||
vpnKeyStr += "\"" + pair.first + "\": \"" + pair.second.toString() + "\"";
|
|
||||||
} else if (pair.second.typeId() == QMetaType::Type::Double || pair.second.typeId() == QMetaType::Type::Int) {
|
|
||||||
vpnKeyStr += "\"" + pair.first + "\": " + QString::number(pair.second.toDouble(), 'f', 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < orderedFields.size() - 1) {
|
|
||||||
vpnKeyStr += ", ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vpnKeyStr += "}";
|
|
||||||
|
|
||||||
QByteArray vpnKeyCompressed = escapeUnicode(vpnKeyStr).toUtf8();
|
|
||||||
vpnKeyCompressed = qCompress(vpnKeyCompressed, 6);
|
|
||||||
vpnKeyCompressed = vpnKeyCompressed.mid(4);
|
|
||||||
|
|
||||||
QByteArray signedData = AMNEZIA_CONFIG_SIGNATURE + vpnKeyCompressed;
|
|
||||||
|
|
||||||
return QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding)));
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
#ifndef APIUTILS_H
|
|
||||||
#define APIUTILS_H
|
|
||||||
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "apiDefs.h"
|
|
||||||
#include "core/defs.h"
|
|
||||||
|
|
||||||
namespace apiUtils
|
|
||||||
{
|
|
||||||
bool isServerFromApi(const QJsonObject &serverConfigObject);
|
|
||||||
|
|
||||||
bool isSubscriptionExpired(const QString &subscriptionEndDate);
|
|
||||||
|
|
||||||
bool isPremiumServer(const QJsonObject &serverConfigObject);
|
|
||||||
|
|
||||||
apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);
|
|
||||||
apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
|
|
||||||
|
|
||||||
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply);
|
|
||||||
|
|
||||||
QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // APIUTILS_H
|
|
||||||
423
client/core/controllers/apiController.cpp
Normal file
|
|
@ -0,0 +1,423 @@
|
||||||
|
#include "apiController.h"
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
#include "QBlockCipher.h"
|
||||||
|
#include "QRsa.h"
|
||||||
|
|
||||||
|
#include "amnezia_application.h"
|
||||||
|
#include "configurators/wireguard_configurator.h"
|
||||||
|
#include "core/enums/apiEnums.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
namespace configKey
|
||||||
|
{
|
||||||
|
constexpr char cloak[] = "cloak";
|
||||||
|
constexpr char awg[] = "awg";
|
||||||
|
|
||||||
|
constexpr char apiEdnpoint[] = "api_endpoint";
|
||||||
|
constexpr char accessToken[] = "api_key";
|
||||||
|
constexpr char certificate[] = "certificate";
|
||||||
|
constexpr char publicKey[] = "public_key";
|
||||||
|
constexpr char protocol[] = "protocol";
|
||||||
|
|
||||||
|
constexpr char uuid[] = "installation_uuid";
|
||||||
|
constexpr char osVersion[] = "os_version";
|
||||||
|
constexpr char appVersion[] = "app_version";
|
||||||
|
|
||||||
|
constexpr char userCountryCode[] = "user_country_code";
|
||||||
|
constexpr char serverCountryCode[] = "server_country_code";
|
||||||
|
constexpr char serviceType[] = "service_type";
|
||||||
|
|
||||||
|
constexpr char aesKey[] = "aes_key";
|
||||||
|
constexpr char aesIv[] = "aes_iv";
|
||||||
|
constexpr char aesSalt[] = "aes_salt";
|
||||||
|
|
||||||
|
constexpr char apiPayload[] = "api_payload";
|
||||||
|
constexpr char keyPayload[] = "key_payload";
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList proxyStorageUrl = { "" };
|
||||||
|
|
||||||
|
ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
if (!sslErrors.empty()) {
|
||||||
|
qDebug().noquote() << sslErrors;
|
||||||
|
return ErrorCode::ApiConfigSslError;
|
||||||
|
} else if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
} else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
||||||
|
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
||||||
|
return ErrorCode::ApiConfigTimeoutError;
|
||||||
|
} else {
|
||||||
|
QString err = reply->errorString();
|
||||||
|
qDebug() << QString::fromUtf8(reply->readAll());
|
||||||
|
qDebug() << reply->error();
|
||||||
|
qDebug() << err;
|
||||||
|
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
|
return ErrorCode::ApiConfigDownloadError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent)
|
||||||
|
: QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiController::fillServerConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData,
|
||||||
|
const QByteArray &apiResponseBody, QJsonObject &serverConfig)
|
||||||
|
{
|
||||||
|
QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString();
|
||||||
|
|
||||||
|
data.replace("vpn://", "");
|
||||||
|
QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||||
|
|
||||||
|
if (ba.isEmpty()) {
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ba_uncompressed = qUncompress(ba);
|
||||||
|
if (!ba_uncompressed.isEmpty()) {
|
||||||
|
ba = ba_uncompressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString configStr = ba;
|
||||||
|
if (protocol == configKey::cloak) {
|
||||||
|
configStr.replace("<key>", "<key>\n");
|
||||||
|
configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
|
||||||
|
} else if (protocol == configKey::awg) {
|
||||||
|
configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
|
||||||
|
auto serverConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||||
|
auto containers = serverConfig.value(config_key::containers).toArray();
|
||||||
|
if (containers.isEmpty()) {
|
||||||
|
return; // todo process error
|
||||||
|
}
|
||||||
|
auto container = containers.at(0).toObject();
|
||||||
|
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
|
||||||
|
auto containerConfig = container.value(containerName).toObject();
|
||||||
|
auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
|
||||||
|
containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount);
|
||||||
|
containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize);
|
||||||
|
containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize);
|
||||||
|
containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize);
|
||||||
|
containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize);
|
||||||
|
containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader);
|
||||||
|
containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader);
|
||||||
|
containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader);
|
||||||
|
containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
|
||||||
|
container[containerName] = containerConfig;
|
||||||
|
containers.replace(0, container);
|
||||||
|
serverConfig[config_key::containers] = containers;
|
||||||
|
configStr = QString(QJsonDocument(serverConfig).toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject apiConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||||
|
serverConfig[config_key::dns1] = apiConfig.value(config_key::dns1);
|
||||||
|
serverConfig[config_key::dns2] = apiConfig.value(config_key::dns2);
|
||||||
|
serverConfig[config_key::containers] = apiConfig.value(config_key::containers);
|
||||||
|
serverConfig[config_key::hostName] = apiConfig.value(config_key::hostName);
|
||||||
|
|
||||||
|
if (apiConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
|
||||||
|
serverConfig[config_key::configVersion] = apiConfig.value(config_key::configVersion);
|
||||||
|
serverConfig[config_key::description] = apiConfig.value(config_key::description);
|
||||||
|
serverConfig[config_key::name] = apiConfig.value(config_key::name);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString();
|
||||||
|
serverConfig[config_key::defaultContainer] = defaultContainer;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ApiController::getProxyUrls()
|
||||||
|
{
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setTransferTimeout(7000);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
QEventLoop wait;
|
||||||
|
QList<QSslError> sslErrors;
|
||||||
|
QNetworkReply *reply;
|
||||||
|
|
||||||
|
for (const auto &proxyStorageUrl : proxyStorageUrl) {
|
||||||
|
request.setUrl(proxyStorageUrl);
|
||||||
|
reply = amnApp->manager()->get(request);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto encryptedResponseBody = reply->readAll();
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
EVP_PKEY *privateKey = nullptr;
|
||||||
|
QByteArray responseBody;
|
||||||
|
try {
|
||||||
|
QByteArray key = PROD_PROXY_STORAGE_KEY;
|
||||||
|
QSimpleCrypto::QRsa rsa;
|
||||||
|
privateKey = rsa.getPrivateKeyFromByteArray(key, "");
|
||||||
|
responseBody = rsa.decrypt(encryptedResponseBody, privateKey, RSA_PKCS1_PADDING);
|
||||||
|
} catch (...) {
|
||||||
|
qCritical() << "error loading private key from environment variables or decrypting payload";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
|
||||||
|
|
||||||
|
QStringList endpoints;
|
||||||
|
for (const auto &endpoint : endpointsArray) {
|
||||||
|
endpoints.push_back(endpoint.toString());
|
||||||
|
}
|
||||||
|
return endpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiController::ApiPayloadData ApiController::generateApiPayloadData(const QString &protocol)
|
||||||
|
{
|
||||||
|
ApiController::ApiPayloadData apiPayload;
|
||||||
|
if (protocol == configKey::cloak) {
|
||||||
|
apiPayload.certRequest = OpenVpnConfigurator::createCertRequest();
|
||||||
|
} else if (protocol == configKey::awg) {
|
||||||
|
auto connData = WireguardConfigurator::genClientKeys();
|
||||||
|
apiPayload.wireGuardClientPubKey = connData.clientPubKey;
|
||||||
|
apiPayload.wireGuardClientPrivKey = connData.clientPrivKey;
|
||||||
|
}
|
||||||
|
return apiPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ApiController::fillApiPayload(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData)
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
if (protocol == configKey::cloak) {
|
||||||
|
obj[configKey::certificate] = apiPayloadData.certRequest.request;
|
||||||
|
} else if (protocol == configKey::awg) {
|
||||||
|
obj[configKey::publicKey] = apiPayloadData.wireGuardClientPubKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[configKey::osVersion] = QSysInfo::productType();
|
||||||
|
obj[configKey::appVersion] = QString(APP_VERSION);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiController::updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
IosController::Instance()->requestInetAccess();
|
||||||
|
QThread::msleep(10);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (serverConfig.value(config_key::configVersion).toInt()) {
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setTransferTimeout(7000);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
|
||||||
|
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
|
||||||
|
request.setUrl(endpoint);
|
||||||
|
|
||||||
|
QString protocol = serverConfig.value(configKey::protocol).toString();
|
||||||
|
|
||||||
|
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||||
|
|
||||||
|
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||||
|
apiPayload[configKey::uuid] = installationUuid;
|
||||||
|
|
||||||
|
QByteArray requestBody = QJsonDocument(apiPayload).toJson();
|
||||||
|
|
||||||
|
QNetworkReply *reply = amnApp->manager()->post(request, requestBody);
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, [this, reply, protocol, apiPayloadData, serverIndex, serverConfig]() mutable {
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
auto apiResponseBody = reply->readAll();
|
||||||
|
fillServerConfig(protocol, apiPayloadData, apiResponseBody, serverConfig);
|
||||||
|
emit finished(serverConfig, serverIndex);
|
||||||
|
} else {
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
||||||
|
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigTimeoutError);
|
||||||
|
} else {
|
||||||
|
QString err = reply->errorString();
|
||||||
|
qDebug() << QString::fromUtf8(reply->readAll());
|
||||||
|
qDebug() << reply->error();
|
||||||
|
qDebug() << err;
|
||||||
|
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigDownloadError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::errorOccurred,
|
||||||
|
[this, reply](QNetworkReply::NetworkError error) { qDebug() << reply->errorString() << error; });
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, reply](const QList<QSslError> &errors) {
|
||||||
|
qDebug().noquote() << errors;
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigSslError);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ApiController::getServicesList(QByteArray &responseBody)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
IosController::Instance()->requestInetAccess();
|
||||||
|
QThread::msleep(10);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setTransferTimeout(7000);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint));
|
||||||
|
|
||||||
|
QNetworkReply *reply;
|
||||||
|
reply = amnApp->manager()->get(request);
|
||||||
|
|
||||||
|
QEventLoop wait;
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
|
||||||
|
QList<QSslError> sslErrors;
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) {
|
||||||
|
m_proxyUrls = getProxyUrls();
|
||||||
|
for (const QString &proxyUrl : m_proxyUrls) {
|
||||||
|
request.setUrl(QString("%1v1/services").arg(proxyUrl));
|
||||||
|
reply = amnApp->manager()->get(request);
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
|
||||||
|
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responseBody = reply->readAll();
|
||||||
|
auto errorCode = checkErrors(sslErrors, reply);
|
||||||
|
reply->deleteLater();
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ApiController::getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &protocol, const QString &serverCountryCode, QJsonObject &serverConfig)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
IosController::Instance()->requestInetAccess();
|
||||||
|
QThread::msleep(10);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QNetworkAccessManager manager;
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setTransferTimeout(7000);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
request.setUrl(QString("%1v1/config").arg(m_gatewayEndpoint));
|
||||||
|
|
||||||
|
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||||
|
|
||||||
|
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||||
|
apiPayload[configKey::userCountryCode] = userCountryCode;
|
||||||
|
if (!serverCountryCode.isEmpty()) {
|
||||||
|
apiPayload[configKey::serverCountryCode] = serverCountryCode;
|
||||||
|
}
|
||||||
|
apiPayload[configKey::serviceType] = serviceType;
|
||||||
|
apiPayload[configKey::uuid] = installationUuid;
|
||||||
|
|
||||||
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
|
QByteArray key = blockCipher.generatePrivateSalt(32);
|
||||||
|
QByteArray iv = blockCipher.generatePrivateSalt(32);
|
||||||
|
QByteArray salt = blockCipher.generatePrivateSalt(8);
|
||||||
|
|
||||||
|
QJsonObject keyPayload;
|
||||||
|
keyPayload[configKey::aesKey] = QString(key.toBase64());
|
||||||
|
keyPayload[configKey::aesIv] = QString(iv.toBase64());
|
||||||
|
keyPayload[configKey::aesSalt] = QString(salt.toBase64());
|
||||||
|
|
||||||
|
QByteArray encryptedKeyPayload;
|
||||||
|
QByteArray encryptedApiPayload;
|
||||||
|
try {
|
||||||
|
QSimpleCrypto::QRsa rsa;
|
||||||
|
|
||||||
|
EVP_PKEY *publicKey = nullptr;
|
||||||
|
try {
|
||||||
|
QByteArray rsaKey = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
||||||
|
QSimpleCrypto::QRsa rsa;
|
||||||
|
publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
|
||||||
|
} catch (...) {
|
||||||
|
qCritical() << "error loading public key from environment variables";
|
||||||
|
return ErrorCode::ApiMissingAgwPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
|
||||||
|
EVP_PKEY_free(publicKey);
|
||||||
|
|
||||||
|
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
|
||||||
|
} catch (...) { // todo change error handling in QSimpleCrypto?
|
||||||
|
qCritical() << "error when encrypting the request body";
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject requestBody;
|
||||||
|
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
||||||
|
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
|
||||||
|
|
||||||
|
QNetworkReply *reply = manager.post(request, QJsonDocument(requestBody).toJson());
|
||||||
|
|
||||||
|
QEventLoop wait;
|
||||||
|
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
|
||||||
|
QList<QSslError> sslErrors;
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) {
|
||||||
|
if (m_proxyUrls.isEmpty()) {
|
||||||
|
m_proxyUrls = getProxyUrls();
|
||||||
|
}
|
||||||
|
for (const QString &proxyUrl : m_proxyUrls) {
|
||||||
|
request.setUrl(QString("%1v1/config").arg(proxyUrl));
|
||||||
|
reply = manager.post(request, QJsonDocument(requestBody).toJson());
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
|
||||||
|
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto errorCode = checkErrors(sslErrors, reply);
|
||||||
|
if (errorCode) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto encryptedResponseBody = reply->readAll();
|
||||||
|
reply->deleteLater();
|
||||||
|
try {
|
||||||
|
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
||||||
|
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
|
||||||
|
} catch (...) { // todo change error handling in QSimpleCrypto?
|
||||||
|
qCritical() << "error when decrypting the request body";
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
50
client/core/controllers/apiController.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef APICONTROLLER_H
|
||||||
|
#define APICONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "configurators/openvpn_configurator.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
#include "platforms/ios/ios_controller.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class ApiController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig);
|
||||||
|
|
||||||
|
ErrorCode getServicesList(QByteArray &responseBody);
|
||||||
|
ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &protocol, const QString &serverCountryCode, QJsonObject &serverConfig);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void errorOccurred(ErrorCode errorCode);
|
||||||
|
void finished(const QJsonObject &config, const int serverIndex);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ApiPayloadData
|
||||||
|
{
|
||||||
|
OpenVpnConfigurator::ConnectionData certRequest;
|
||||||
|
|
||||||
|
QString wireGuardClientPrivKey;
|
||||||
|
QString wireGuardClientPubKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiPayloadData generateApiPayloadData(const QString &protocol);
|
||||||
|
QJsonObject fillApiPayload(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData);
|
||||||
|
void fillServerConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, const QByteArray &apiResponseBody,
|
||||||
|
QJsonObject &serverConfig);
|
||||||
|
QStringList getProxyUrls();
|
||||||
|
|
||||||
|
QString m_gatewayEndpoint;
|
||||||
|
QStringList m_proxyUrls;
|
||||||
|
bool m_isDevEnvironment = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APICONTROLLER_H
|
||||||
|
|
@ -1,399 +0,0 @@
|
||||||
#include "coreController.h"
|
|
||||||
|
|
||||||
#include <QDirIterator>
|
|
||||||
#include <QTranslator>
|
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
|
||||||
#include "core/installedAppsImageProvider.h"
|
|
||||||
#include "platforms/android/android_controller.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_IOS)
|
|
||||||
#include "platforms/ios/ios_controller.h"
|
|
||||||
#include <AmneziaVPN-Swift.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnection, const std::shared_ptr<Settings> &settings,
|
|
||||||
QQmlApplicationEngine *engine, QObject *parent)
|
|
||||||
: QObject(parent), m_vpnConnection(vpnConnection), m_settings(settings), m_engine(engine)
|
|
||||||
{
|
|
||||||
initModels();
|
|
||||||
initControllers();
|
|
||||||
initSignalHandlers();
|
|
||||||
|
|
||||||
initAndroidController();
|
|
||||||
initAppleController();
|
|
||||||
|
|
||||||
initNotificationHandler();
|
|
||||||
|
|
||||||
auto locale = m_settings->getAppLanguage();
|
|
||||||
m_translator.reset(new QTranslator());
|
|
||||||
updateTranslator(locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initModels()
|
|
||||||
{
|
|
||||||
m_containersModel.reset(new ContainersModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
|
|
||||||
|
|
||||||
m_defaultServerContainersModel.reset(new ContainersModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel.get());
|
|
||||||
|
|
||||||
m_serversModel.reset(new ServersModel(m_settings, this));
|
|
||||||
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
|
|
||||||
|
|
||||||
m_languageModel.reset(new LanguageModel(m_settings, this));
|
|
||||||
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
|
|
||||||
|
|
||||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
|
||||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
|
||||||
|
|
||||||
m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this));
|
|
||||||
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
|
|
||||||
|
|
||||||
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
|
||||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
|
||||||
|
|
||||||
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
|
|
||||||
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
|
|
||||||
|
|
||||||
m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
|
|
||||||
|
|
||||||
m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
|
|
||||||
|
|
||||||
m_cloakConfigModel.reset(new CloakConfigModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
|
|
||||||
|
|
||||||
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
|
|
||||||
|
|
||||||
m_awgConfigModel.reset(new AwgConfigModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
|
|
||||||
|
|
||||||
m_xrayConfigModel.reset(new XrayConfigModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
|
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_sftpConfigModel.reset(new SftpConfigModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
|
|
||||||
|
|
||||||
m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
|
|
||||||
|
|
||||||
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
|
|
||||||
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
|
|
||||||
|
|
||||||
m_apiServicesModel.reset(new ApiServicesModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("ApiServicesModel", m_apiServicesModel.get());
|
|
||||||
|
|
||||||
m_apiCountryModel.reset(new ApiCountryModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("ApiCountryModel", m_apiCountryModel.get());
|
|
||||||
|
|
||||||
m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get());
|
|
||||||
|
|
||||||
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
|
|
||||||
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initControllers()
|
|
||||||
{
|
|
||||||
m_connectionController.reset(
|
|
||||||
new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings));
|
|
||||||
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
|
|
||||||
|
|
||||||
m_pageController.reset(new PageController(m_serversModel, m_settings));
|
|
||||||
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
|
|
||||||
|
|
||||||
m_focusController.reset(new FocusController(m_engine, this));
|
|
||||||
m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get());
|
|
||||||
|
|
||||||
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_settings));
|
|
||||||
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
|
|
||||||
|
|
||||||
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
|
|
||||||
&ConnectionController::onCurrentContainerUpdated); // TODO remove this
|
|
||||||
|
|
||||||
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
|
||||||
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
|
||||||
|
|
||||||
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, m_settings));
|
|
||||||
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
|
|
||||||
|
|
||||||
m_settingsController.reset(
|
|
||||||
new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
|
|
||||||
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
|
|
||||||
|
|
||||||
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
|
||||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
|
||||||
|
|
||||||
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
|
|
||||||
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get());
|
|
||||||
|
|
||||||
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
|
||||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
|
||||||
|
|
||||||
m_systemController.reset(new SystemController(m_settings));
|
|
||||||
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
|
||||||
|
|
||||||
m_apiSettingsController.reset(
|
|
||||||
new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_settings));
|
|
||||||
m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get());
|
|
||||||
|
|
||||||
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
|
||||||
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
|
|
||||||
|
|
||||||
m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this));
|
|
||||||
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initAndroidController()
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_ANDROID
|
|
||||||
if (!AndroidController::initLogging()) {
|
|
||||||
qFatal("Android logging initialization failed");
|
|
||||||
}
|
|
||||||
AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
|
|
||||||
connect(m_settings.get(), &Settings::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
|
|
||||||
|
|
||||||
AndroidController::instance()->setScreenshotsEnabled(m_settings->isScreenshotsEnabled());
|
|
||||||
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
|
|
||||||
|
|
||||||
connect(m_settings.get(), &Settings::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
|
|
||||||
|
|
||||||
connect(m_settings.get(), &Settings::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
|
|
||||||
|
|
||||||
connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) {
|
|
||||||
m_connectionController->onConnectionStateChanged(state);
|
|
||||||
if (m_vpnConnection)
|
|
||||||
m_vpnConnection->restoreConnection();
|
|
||||||
});
|
|
||||||
if (!AndroidController::instance()->initialize()) {
|
|
||||||
qFatal("Android controller initialization failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) {
|
|
||||||
emit m_pageController->goToPageHome();
|
|
||||||
m_importController->extractConfigFromData(data);
|
|
||||||
data.clear();
|
|
||||||
emit m_pageController->goToPageViewConfig();
|
|
||||||
});
|
|
||||||
|
|
||||||
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initAppleController()
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_IOS
|
|
||||||
IosController::Instance()->initialize();
|
|
||||||
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
|
|
||||||
emit m_pageController->goToPageHome();
|
|
||||||
m_importController->extractConfigFromData(data);
|
|
||||||
emit m_pageController->goToPageViewConfig();
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) {
|
|
||||||
emit m_pageController->goToPageHome();
|
|
||||||
m_pageController->goToPageSettingsBackup();
|
|
||||||
emit m_settingsController->importBackupFromOutside(filePath);
|
|
||||||
});
|
|
||||||
|
|
||||||
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
|
|
||||||
|
|
||||||
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); });
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initSignalHandlers()
|
|
||||||
{
|
|
||||||
initErrorMessagesHandler();
|
|
||||||
|
|
||||||
initApiCountryModelUpdateHandler();
|
|
||||||
initContainerModelUpdateHandler();
|
|
||||||
initAdminConfigRevokedHandler();
|
|
||||||
initPassphraseRequestHandler();
|
|
||||||
initTranslationsUpdatedHandler();
|
|
||||||
initAutoConnectHandler();
|
|
||||||
initAmneziaDnsToggledHandler();
|
|
||||||
initPrepareConfigHandler();
|
|
||||||
initImportPremiumV2VpnKeyHandler();
|
|
||||||
initShowMigrationDrawerHandler();
|
|
||||||
initStrictKillSwitchHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initNotificationHandler()
|
|
||||||
{
|
|
||||||
#ifndef Q_OS_ANDROID
|
|
||||||
m_notificationHandler.reset(NotificationHandler::create(nullptr));
|
|
||||||
|
|
||||||
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
|
|
||||||
&NotificationHandler::setConnectionState);
|
|
||||||
|
|
||||||
connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(), &PageController::raiseMainWindow);
|
|
||||||
connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
|
|
||||||
static_cast<void (ConnectionController::*)()>(&ConnectionController::openConnection));
|
|
||||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
|
||||||
&ConnectionController::closeConnection);
|
|
||||||
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::updateTranslator(const QLocale &locale)
|
|
||||||
{
|
|
||||||
if (!m_translator->isEmpty()) {
|
|
||||||
QCoreApplication::removeTranslator(m_translator.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList availableTranslations;
|
|
||||||
QDirIterator it(":/translations", QStringList("amneziavpn_*.qm"), QDir::Files);
|
|
||||||
while (it.hasNext()) {
|
|
||||||
availableTranslations << it.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This code allow to load translation for the language only, without country code
|
|
||||||
const QString lang = locale.name().split("_").first();
|
|
||||||
const QString translationFilePrefix = QString(":/translations/amneziavpn_") + lang;
|
|
||||||
QString strFileName = QString(":/translations/amneziavpn_%1.qm").arg(locale.name());
|
|
||||||
for (const QString &translation : availableTranslations) {
|
|
||||||
if (translation.contains(translationFilePrefix)) {
|
|
||||||
strFileName = translation;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_translator->load(strFileName)) {
|
|
||||||
if (QCoreApplication::installTranslator(m_translator.get())) {
|
|
||||||
m_settings->setAppLanguage(locale);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_settings->setAppLanguage(QLocale::English);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_engine->retranslate();
|
|
||||||
|
|
||||||
emit translationsUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initErrorMessagesHandler()
|
|
||||||
{
|
|
||||||
connect(m_connectionController.get(), &ConnectionController::connectionErrorOccurred, this, [this](ErrorCode errorCode) {
|
|
||||||
emit m_pageController->showErrorMessage(errorCode);
|
|
||||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_apiConfigsController.get(), &ApiConfigsController::errorOccurred, m_pageController.get(),
|
|
||||||
qOverload<ErrorCode>(&PageController::showErrorMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::setQmlRoot()
|
|
||||||
{
|
|
||||||
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initApiCountryModelUpdateHandler()
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
|
|
||||||
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
|
|
||||||
m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
|
|
||||||
});
|
|
||||||
connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this,
|
|
||||||
[this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initContainerModelUpdateHandler()
|
|
||||||
{
|
|
||||||
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
|
|
||||||
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
|
|
||||||
&ContainersModel::updateModel);
|
|
||||||
m_serversModel->resetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initAdminConfigRevokedHandler()
|
|
||||||
{
|
|
||||||
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),
|
|
||||||
&ServersModel::clearCachedProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initPassphraseRequestHandler()
|
|
||||||
{
|
|
||||||
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
|
|
||||||
&PageController::showPassphraseRequestDrawer);
|
|
||||||
connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
|
|
||||||
&InstallController::setEncryptedPassphrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initTranslationsUpdatedHandler()
|
|
||||||
{
|
|
||||||
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &CoreController::updateTranslator);
|
|
||||||
connect(this, &CoreController::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
|
|
||||||
connect(this, &CoreController::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initAutoConnectHandler()
|
|
||||||
{
|
|
||||||
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
|
|
||||||
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initAmneziaDnsToggledHandler()
|
|
||||||
{
|
|
||||||
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initPrepareConfigHandler()
|
|
||||||
{
|
|
||||||
connect(m_connectionController.get(), &ConnectionController::prepareConfig, this, [this]() {
|
|
||||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
|
|
||||||
|
|
||||||
if (!m_apiConfigsController->isConfigValid()) {
|
|
||||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_installController->isConfigValid()) {
|
|
||||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_connectionController->openConnection();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initImportPremiumV2VpnKeyHandler()
|
|
||||||
{
|
|
||||||
connect(m_apiPremV1MigrationController.get(), &ApiPremV1MigrationController::importPremiumV2VpnKey, this, [this](const QString &vpnKey) {
|
|
||||||
m_importController->extractConfigFromData(vpnKey);
|
|
||||||
m_importController->importConfig();
|
|
||||||
|
|
||||||
emit m_apiPremV1MigrationController->migrationFinished();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initShowMigrationDrawerHandler()
|
|
||||||
{
|
|
||||||
QTimer::singleShot(1000, this, [this]() {
|
|
||||||
if (m_apiPremV1MigrationController->isPremV1MigrationReminderActive() && m_apiPremV1MigrationController->hasConfigsToMigration()) {
|
|
||||||
m_apiPremV1MigrationController->showMigrationDrawer();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initStrictKillSwitchHandler()
|
|
||||||
{
|
|
||||||
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(),
|
|
||||||
&VpnConnection::onKillSwitchModeChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<PageController> CoreController::pageController() const
|
|
||||||
{
|
|
||||||
return m_pageController;
|
|
||||||
}
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
#ifndef CORECONTROLLER_H
|
|
||||||
#define CORECONTROLLER_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QQmlContext>
|
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
#include "ui/controllers/api/apiConfigsController.h"
|
|
||||||
#include "ui/controllers/api/apiSettingsController.h"
|
|
||||||
#include "ui/controllers/api/apiPremV1MigrationController.h"
|
|
||||||
#include "ui/controllers/appSplitTunnelingController.h"
|
|
||||||
#include "ui/controllers/allowedDnsController.h"
|
|
||||||
#include "ui/controllers/connectionController.h"
|
|
||||||
#include "ui/controllers/exportController.h"
|
|
||||||
#include "ui/controllers/focusController.h"
|
|
||||||
#include "ui/controllers/importController.h"
|
|
||||||
#include "ui/controllers/installController.h"
|
|
||||||
#include "ui/controllers/pageController.h"
|
|
||||||
#include "ui/controllers/settingsController.h"
|
|
||||||
#include "ui/controllers/sitesController.h"
|
|
||||||
#include "ui/controllers/systemController.h"
|
|
||||||
|
|
||||||
#include "ui/models/allowed_dns_model.h"
|
|
||||||
#include "ui/models/containers_model.h"
|
|
||||||
#include "ui/models/languageModel.h"
|
|
||||||
#include "ui/models/protocols/cloakConfigModel.h"
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
#include "ui/models/protocols/ikev2ConfigModel.h"
|
|
||||||
#endif
|
|
||||||
#include "ui/models/api/apiAccountInfoModel.h"
|
|
||||||
#include "ui/models/api/apiCountryModel.h"
|
|
||||||
#include "ui/models/api/apiDevicesModel.h"
|
|
||||||
#include "ui/models/api/apiServicesModel.h"
|
|
||||||
#include "ui/models/appSplitTunnelingModel.h"
|
|
||||||
#include "ui/models/clientManagementModel.h"
|
|
||||||
#include "ui/models/protocols/awgConfigModel.h"
|
|
||||||
#include "ui/models/protocols/openvpnConfigModel.h"
|
|
||||||
#include "ui/models/protocols/shadowsocksConfigModel.h"
|
|
||||||
#include "ui/models/protocols/wireguardConfigModel.h"
|
|
||||||
#include "ui/models/protocols/xrayConfigModel.h"
|
|
||||||
#include "ui/models/protocols_model.h"
|
|
||||||
#include "ui/models/servers_model.h"
|
|
||||||
#include "ui/models/services/sftpConfigModel.h"
|
|
||||||
#include "ui/models/services/socks5ProxyConfigModel.h"
|
|
||||||
#include "ui/models/sites_model.h"
|
|
||||||
|
|
||||||
#ifndef Q_OS_ANDROID
|
|
||||||
#include "ui/notificationhandler.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CoreController : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit CoreController(const QSharedPointer<VpnConnection> &vpnConnection, const std::shared_ptr<Settings> &settings,
|
|
||||||
QQmlApplicationEngine *engine, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QSharedPointer<PageController> pageController() const;
|
|
||||||
void setQmlRoot();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void translationsUpdated();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initModels();
|
|
||||||
void initControllers();
|
|
||||||
void initAndroidController();
|
|
||||||
void initAppleController();
|
|
||||||
void initSignalHandlers();
|
|
||||||
|
|
||||||
void initNotificationHandler();
|
|
||||||
|
|
||||||
void updateTranslator(const QLocale &locale);
|
|
||||||
|
|
||||||
void initErrorMessagesHandler();
|
|
||||||
|
|
||||||
void initApiCountryModelUpdateHandler();
|
|
||||||
void initContainerModelUpdateHandler();
|
|
||||||
void initAdminConfigRevokedHandler();
|
|
||||||
void initPassphraseRequestHandler();
|
|
||||||
void initTranslationsUpdatedHandler();
|
|
||||||
void initAutoConnectHandler();
|
|
||||||
void initAmneziaDnsToggledHandler();
|
|
||||||
void initPrepareConfigHandler();
|
|
||||||
void initImportPremiumV2VpnKeyHandler();
|
|
||||||
void initShowMigrationDrawerHandler();
|
|
||||||
void initStrictKillSwitchHandler();
|
|
||||||
|
|
||||||
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
|
||||||
std::shared_ptr<Settings> m_settings;
|
|
||||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
|
||||||
QSharedPointer<QTranslator> m_translator;
|
|
||||||
|
|
||||||
#ifndef Q_OS_ANDROID
|
|
||||||
QScopedPointer<NotificationHandler> m_notificationHandler;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QMetaObject::Connection m_reloadConfigErrorOccurredConnection;
|
|
||||||
|
|
||||||
QScopedPointer<ConnectionController> m_connectionController;
|
|
||||||
QScopedPointer<FocusController> m_focusController;
|
|
||||||
QSharedPointer<PageController> m_pageController; // TODO
|
|
||||||
QScopedPointer<InstallController> m_installController;
|
|
||||||
QScopedPointer<ImportController> m_importController;
|
|
||||||
QScopedPointer<ExportController> m_exportController;
|
|
||||||
QScopedPointer<SettingsController> m_settingsController;
|
|
||||||
QScopedPointer<SitesController> m_sitesController;
|
|
||||||
QScopedPointer<SystemController> m_systemController;
|
|
||||||
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
|
||||||
QScopedPointer<AllowedDnsController> m_allowedDnsController;
|
|
||||||
|
|
||||||
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
|
||||||
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
|
||||||
QScopedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationController;
|
|
||||||
|
|
||||||
QSharedPointer<ContainersModel> m_containersModel;
|
|
||||||
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
|
|
||||||
QSharedPointer<ServersModel> m_serversModel;
|
|
||||||
QSharedPointer<LanguageModel> m_languageModel;
|
|
||||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
|
||||||
QSharedPointer<SitesModel> m_sitesModel;
|
|
||||||
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
|
||||||
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
|
||||||
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
|
||||||
|
|
||||||
QSharedPointer<ApiServicesModel> m_apiServicesModel;
|
|
||||||
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
|
||||||
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
|
|
||||||
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
|
|
||||||
|
|
||||||
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
|
||||||
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
|
||||||
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
|
|
||||||
QScopedPointer<XrayConfigModel> m_xrayConfigModel;
|
|
||||||
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
|
|
||||||
QScopedPointer<AwgConfigModel> m_awgConfigModel;
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
|
|
||||||
#endif
|
|
||||||
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
|
|
||||||
QScopedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CORECONTROLLER_H
|
|
||||||
|
|
@ -1,364 +0,0 @@
|
||||||
#include "gatewayController.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QUrl>
|
|
||||||
|
|
||||||
#include "QBlockCipher.h"
|
|
||||||
#include "QRsa.h"
|
|
||||||
|
|
||||||
#include "amnezia_application.h"
|
|
||||||
#include "core/api/apiUtils.h"
|
|
||||||
#include "core/networkUtilities.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
|
||||||
#include "core/ipcclient.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
namespace configKey
|
|
||||||
{
|
|
||||||
constexpr char aesKey[] = "aes_key";
|
|
||||||
constexpr char aesIv[] = "aes_iv";
|
|
||||||
constexpr char aesSalt[] = "aes_salt";
|
|
||||||
|
|
||||||
constexpr char apiPayload[] = "api_payload";
|
|
||||||
constexpr char keyPayload[] = "key_payload";
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
|
|
||||||
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
|
|
||||||
constexpr QLatin1String errorResponsePattern3("Account not found.");
|
|
||||||
|
|
||||||
constexpr QLatin1String updateRequestResponsePattern("client version update is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
|
|
||||||
const bool isStrictKillSwitchEnabled, QObject *parent)
|
|
||||||
: QObject(parent),
|
|
||||||
m_gatewayEndpoint(gatewayEndpoint),
|
|
||||||
m_isDevEnvironment(isDevEnvironment),
|
|
||||||
m_requestTimeoutMsecs(requestTimeoutMsecs),
|
|
||||||
m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBody)
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_IOS
|
|
||||||
IosController::Instance()->requestInetAccess();
|
|
||||||
QThread::msleep(10);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QNetworkRequest request;
|
|
||||||
request.setTransferTimeout(m_requestTimeoutMsecs);
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
||||||
|
|
||||||
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
|
|
||||||
|
|
||||||
// bypass killSwitch exceptions for API-gateway
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
|
||||||
if (m_isStrictKillSwitchEnabled) {
|
|
||||||
QString host = QUrl(request.url()).host();
|
|
||||||
QString ip = NetworkUtilities::getIPAddress(host);
|
|
||||||
if (!ip.isEmpty()) {
|
|
||||||
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QNetworkReply *reply;
|
|
||||||
reply = amnApp->networkManager()->get(request);
|
|
||||||
|
|
||||||
QEventLoop wait;
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
|
||||||
|
|
||||||
QList<QSslError> sslErrors;
|
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
|
||||||
wait.exec();
|
|
||||||
|
|
||||||
responseBody = reply->readAll();
|
|
||||||
|
|
||||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
|
|
||||||
auto requestFunction = [&request, &responseBody](const QString &url) {
|
|
||||||
request.setUrl(url);
|
|
||||||
return amnApp->networkManager()->get(request);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto replyProcessingFunction = [&responseBody, &reply, &sslErrors, this](QNetworkReply *nestedReply,
|
|
||||||
const QList<QSslError> &nestedSslErrors) {
|
|
||||||
responseBody = nestedReply->readAll();
|
|
||||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, responseBody, false)) {
|
|
||||||
sslErrors = nestedSslErrors;
|
|
||||||
reply = nestedReply;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_IOS
|
|
||||||
IosController::Instance()->requestInetAccess();
|
|
||||||
QThread::msleep(10);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QNetworkRequest request;
|
|
||||||
request.setTransferTimeout(m_requestTimeoutMsecs);
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
||||||
|
|
||||||
request.setUrl(endpoint.arg(m_gatewayEndpoint));
|
|
||||||
|
|
||||||
// bypass killSwitch exceptions for API-gateway
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
|
||||||
if (m_isStrictKillSwitchEnabled) {
|
|
||||||
QString host = QUrl(request.url()).host();
|
|
||||||
QString ip = NetworkUtilities::getIPAddress(host);
|
|
||||||
if (!ip.isEmpty()) {
|
|
||||||
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
|
||||||
QByteArray key = blockCipher.generatePrivateSalt(32);
|
|
||||||
QByteArray iv = blockCipher.generatePrivateSalt(32);
|
|
||||||
QByteArray salt = blockCipher.generatePrivateSalt(8);
|
|
||||||
|
|
||||||
QJsonObject keyPayload;
|
|
||||||
keyPayload[configKey::aesKey] = QString(key.toBase64());
|
|
||||||
keyPayload[configKey::aesIv] = QString(iv.toBase64());
|
|
||||||
keyPayload[configKey::aesSalt] = QString(salt.toBase64());
|
|
||||||
|
|
||||||
QByteArray encryptedKeyPayload;
|
|
||||||
QByteArray encryptedApiPayload;
|
|
||||||
try {
|
|
||||||
QSimpleCrypto::QRsa rsa;
|
|
||||||
|
|
||||||
EVP_PKEY *publicKey = nullptr;
|
|
||||||
try {
|
|
||||||
QByteArray rsaKey = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
|
||||||
QSimpleCrypto::QRsa rsa;
|
|
||||||
publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
|
|
||||||
} catch (...) {
|
|
||||||
Utils::logException();
|
|
||||||
qCritical() << "error loading public key from environment variables";
|
|
||||||
return ErrorCode::ApiMissingAgwPublicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
|
|
||||||
EVP_PKEY_free(publicKey);
|
|
||||||
|
|
||||||
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
|
|
||||||
} catch (...) { // todo change error handling in QSimpleCrypto?
|
|
||||||
Utils::logException();
|
|
||||||
qCritical() << "error when encrypting the request body";
|
|
||||||
return ErrorCode::ApiConfigDecryptionError;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject requestBody;
|
|
||||||
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
|
||||||
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
|
|
||||||
|
|
||||||
QNetworkReply *reply = amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
|
||||||
|
|
||||||
QEventLoop wait;
|
|
||||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
|
||||||
|
|
||||||
QList<QSslError> sslErrors;
|
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
|
||||||
wait.exec();
|
|
||||||
|
|
||||||
QByteArray encryptedResponseBody = reply->readAll();
|
|
||||||
|
|
||||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
|
|
||||||
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
|
|
||||||
request.setUrl(url);
|
|
||||||
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
|
||||||
};
|
|
||||||
|
|
||||||
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
|
|
||||||
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
|
|
||||||
encryptedResponseBody = nestedReply->readAll();
|
|
||||||
reply = nestedReply;
|
|
||||||
if (!sslErrors.isEmpty() || shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
|
|
||||||
sslErrors = nestedSslErrors;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
|
|
||||||
reply->deleteLater();
|
|
||||||
if (errorCode) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
} catch (...) { // todo change error handling in QSimpleCrypto?
|
|
||||||
Utils::logException();
|
|
||||||
qCritical() << "error when decrypting the request body";
|
|
||||||
return ErrorCode::ApiConfigDecryptionError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList GatewayController::getProxyUrls()
|
|
||||||
{
|
|
||||||
QNetworkRequest request;
|
|
||||||
request.setTransferTimeout(m_requestTimeoutMsecs);
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
||||||
|
|
||||||
QEventLoop wait;
|
|
||||||
QList<QSslError> sslErrors;
|
|
||||||
QNetworkReply *reply;
|
|
||||||
|
|
||||||
QStringList proxyStorageUrls;
|
|
||||||
if (m_isDevEnvironment) {
|
|
||||||
proxyStorageUrls = QString(DEV_S3_ENDPOINT).split(", ");
|
|
||||||
} else {
|
|
||||||
proxyStorageUrls = QString(PROD_S3_ENDPOINT).split(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
|
||||||
|
|
||||||
for (const auto &proxyStorageUrl : proxyStorageUrls) {
|
|
||||||
request.setUrl(proxyStorageUrl);
|
|
||||||
reply = amnApp->networkManager()->get(request);
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
|
||||||
wait.exec();
|
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
|
||||||
auto encryptedResponseBody = reply->readAll();
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
EVP_PKEY *privateKey = nullptr;
|
|
||||||
QByteArray responseBody;
|
|
||||||
try {
|
|
||||||
if (!m_isDevEnvironment) {
|
|
||||||
QCryptographicHash hash(QCryptographicHash::Sha512);
|
|
||||||
hash.addData(key);
|
|
||||||
QByteArray hashResult = hash.result().toHex();
|
|
||||||
|
|
||||||
QByteArray key = QByteArray::fromHex(hashResult.left(64));
|
|
||||||
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
|
|
||||||
|
|
||||||
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
|
|
||||||
|
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
|
||||||
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
|
|
||||||
} else {
|
|
||||||
responseBody = encryptedResponseBody;
|
|
||||||
}
|
|
||||||
} catch (...) {
|
|
||||||
Utils::logException();
|
|
||||||
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
|
|
||||||
|
|
||||||
QStringList endpoints;
|
|
||||||
for (const auto &endpoint : endpointsArray) {
|
|
||||||
endpoints.push_back(endpoint.toString());
|
|
||||||
}
|
|
||||||
return endpoints;
|
|
||||||
} else {
|
|
||||||
apiUtils::checkNetworkReplyErrors(sslErrors, reply);
|
|
||||||
qDebug() << "go to the next storage endpoint";
|
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
|
|
||||||
const QByteArray &iv, const QByteArray &salt)
|
|
||||||
{
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
|
||||||
qDebug() << "timeout occurred";
|
|
||||||
qDebug() << reply->error();
|
|
||||||
return true;
|
|
||||||
} else if (responseBody.contains("html")) {
|
|
||||||
qDebug() << "the response contains an html tag";
|
|
||||||
return true;
|
|
||||||
} else if (reply->error() == QNetworkReply::NetworkError::ContentNotFoundError) {
|
|
||||||
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|
|
||||||
|| responseBody.contains(errorResponsePattern3)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
qDebug() << reply->error();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
|
|
||||||
if (responseBody.contains(updateRequestResponsePattern)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
qDebug() << reply->error();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (reply->error() != QNetworkReply::NetworkError::NoError) {
|
|
||||||
qDebug() << reply->error();
|
|
||||||
return true;
|
|
||||||
} else if (checkEncryption) {
|
|
||||||
try {
|
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
|
||||||
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
|
|
||||||
} catch (...) {
|
|
||||||
qDebug() << "failed to decrypt the data";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *reply,
|
|
||||||
std::function<QNetworkReply *(const QString &url)> requestFunction,
|
|
||||||
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction)
|
|
||||||
{
|
|
||||||
QStringList proxyUrls = getProxyUrls();
|
|
||||||
std::random_device randomDevice;
|
|
||||||
std::mt19937 generator(randomDevice());
|
|
||||||
std::shuffle(proxyUrls.begin(), proxyUrls.end(), generator);
|
|
||||||
|
|
||||||
QEventLoop wait;
|
|
||||||
QList<QSslError> sslErrors;
|
|
||||||
QByteArray responseBody;
|
|
||||||
|
|
||||||
for (const QString &proxyUrl : proxyUrls) {
|
|
||||||
qDebug() << "go to the next proxy endpoint";
|
|
||||||
reply->deleteLater(); // delete the previous reply
|
|
||||||
reply = requestFunction(endpoint.arg(proxyUrl));
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
|
||||||
wait.exec();
|
|
||||||
|
|
||||||
if (replyProcessingFunction(reply, sslErrors)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
#ifndef GATEWAYCONTROLLER_H
|
|
||||||
#define GATEWAYCONTROLLER_H
|
|
||||||
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "core/defs.h"
|
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
|
||||||
#include "platforms/ios/ios_controller.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class GatewayController : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
|
|
||||||
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody);
|
|
||||||
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QStringList getProxyUrls();
|
|
||||||
bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
|
|
||||||
const QByteArray &iv = "", const QByteArray &salt = "");
|
|
||||||
void bypassProxy(const QString &endpoint, QNetworkReply *reply, std::function<QNetworkReply *(const QString &url)> requestFunction,
|
|
||||||
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction);
|
|
||||||
|
|
||||||
int m_requestTimeoutMsecs;
|
|
||||||
QString m_gatewayEndpoint;
|
|
||||||
bool m_isDevEnvironment = false;
|
|
||||||
bool m_isStrictKillSwitchEnabled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // GATEWAYCONTROLLER_H
|
|
||||||
|
|
@ -138,7 +138,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||||
|
|
||||||
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
|
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path),
|
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
|
||||||
genVarsForScript(credentials, container)),
|
genVarsForScript(credentials, container)),
|
||||||
cbReadStd, cbReadStd);
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
|
|
@ -146,7 +146,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||||
return e;
|
return e;
|
||||||
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
|
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName),
|
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
|
||||||
genVarsForScript(credentials, container)),
|
genVarsForScript(credentials, container)),
|
||||||
cbReadStd, cbReadStd);
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
|
|
@ -154,7 +154,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path),
|
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
||||||
genVarsForScript(credentials, container)),
|
genVarsForScript(credentials, container)),
|
||||||
cbReadStd, cbReadStd);
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
|
|
@ -177,7 +177,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
||||||
|
|
||||||
errorCode = ErrorCode::NoError;
|
errorCode = ErrorCode::NoError;
|
||||||
|
|
||||||
QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path);
|
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").arg(ContainerProps::containerToString(container)).arg(path);
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
|
@ -346,10 +346,8 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container == DockerContainer::Awg) {
|
if (container == DockerContainer::Awg) {
|
||||||
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
|
if ((oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)
|
||||||
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|
!= newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort))
|
||||||
|| (oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)
|
|
||||||
!= newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort))
|
|
||||||
|| (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)
|
|| (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)
|
||||||
!= newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount))
|
!= newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount))
|
||||||
|| (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)
|
|| (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)
|
||||||
|
|
@ -366,21 +364,14 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
||||||
!= newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader))
|
!= newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader))
|
||||||
|| (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)
|
|| (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)
|
||||||
!= newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader))
|
!= newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader))
|
||||||
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))
|
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)
|
||||||
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))
|
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)))
|
||||||
// || (oldProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize)
|
|
||||||
// != newProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize))
|
|
||||||
// || (oldProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize)
|
|
||||||
// != newProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize))
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container == DockerContainer::WireGuard) {
|
if (container == DockerContainer::WireGuard) {
|
||||||
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
|
if (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
|
||||||
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort))
|
||||||
|| (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
|
|
||||||
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -388,13 +379,6 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container == DockerContainer::Xray) {
|
|
||||||
if (oldProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)
|
|
||||||
!= newProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,24 +435,15 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
ErrorCode error =
|
errorCode =
|
||||||
runScript(credentials,
|
runScript(credentials,
|
||||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
|
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
|
||||||
cbReadStdOut, cbReadStdErr);
|
cbReadStdOut);
|
||||||
|
if (errorCode)
|
||||||
|
return errorCode;
|
||||||
|
|
||||||
if (stdOut.contains("doesn't work on cgroups v2"))
|
return errorCode;
|
||||||
return ErrorCode::ServerDockerOnCgroupsV2;
|
|
||||||
if (stdOut.contains("cgroup mountpoint does not exist"))
|
|
||||||
return ErrorCode::ServerCgroupMountpoint;
|
|
||||||
if (stdOut.contains("have reached") && stdOut.contains("pull rate limit"))
|
|
||||||
return ErrorCode::DockerPullRateLimit;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
||||||
|
|
@ -632,8 +607,6 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||||
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
|
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
|
||||||
|
|
||||||
// Amnezia wireguard vars
|
// Amnezia wireguard vars
|
||||||
vars.append({ { "$AWG_SUBNET_IP",
|
|
||||||
amneziaWireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
|
|
||||||
vars.append({ { "$AWG_SERVER_PORT", amneziaWireguarConfig.value(config_key::port).toString(protocols::awg::defaultPort) } });
|
vars.append({ { "$AWG_SERVER_PORT", amneziaWireguarConfig.value(config_key::port).toString(protocols::awg::defaultPort) } });
|
||||||
|
|
||||||
vars.append({ { "$JUNK_PACKET_COUNT", amneziaWireguarConfig.value(config_key::junkPacketCount).toString() } });
|
vars.append({ { "$JUNK_PACKET_COUNT", amneziaWireguarConfig.value(config_key::junkPacketCount).toString() } });
|
||||||
|
|
@ -646,9 +619,6 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||||
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
|
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
|
||||||
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
|
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
|
||||||
|
|
||||||
vars.append({ { "$COOKIE_REPLY_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::cookieReplyPacketJunkSize).toString() } });
|
|
||||||
vars.append({ { "$TRANSPORT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::transportPacketJunkSize).toString() } });
|
|
||||||
|
|
||||||
// Socks5 proxy vars
|
// Socks5 proxy vars
|
||||||
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
|
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
|
||||||
auto username = socks5ProxyConfig.value(config_key::userName).toString();
|
auto username = socks5ProxyConfig.value(config_key::userName).toString();
|
||||||
|
|
@ -733,7 +703,7 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||||
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
||||||
|
|
||||||
// TODO reimplement with netstat
|
// TODO reimplement with netstat
|
||||||
QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
QString script = QString("which lsof &>/dev/null || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||||
for (auto &port : fixedPorts) {
|
for (auto &port : fixedPorts) {
|
||||||
script = script.append("|:%1").arg(port);
|
script = script.append("|:%1").arg(port);
|
||||||
}
|
}
|
||||||
|
|
@ -781,6 +751,10 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||||
|
|
||||||
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
|
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
|
||||||
{
|
{
|
||||||
|
if (credentials.userName == "root") {
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
|
@ -794,16 +768,8 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
||||||
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
||||||
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||||
|
|
||||||
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
|
if (!stdOut.contains("sudo"))
|
||||||
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
|
|
||||||
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
|
|
||||||
return ErrorCode::ServerUserNotInSudo;
|
return ErrorCode::ServerUserNotInSudo;
|
||||||
if (stdOut.contains("can't cd to") || stdOut.contains("Permission denied") || stdOut.contains("No such file or directory"))
|
|
||||||
return ErrorCode::ServerUserDirectoryNotAccessible;
|
|
||||||
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
|
|
||||||
return ErrorCode::ServerUserNotAllowedInSudoers;
|
|
||||||
if (stdOut.contains("password is required"))
|
|
||||||
return ErrorCode::ServerUserPasswordRequired;
|
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
@ -835,7 +801,7 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
||||||
|
|
||||||
if (stdOut.contains("Packet manager not found"))
|
if (stdOut.contains("Packet manager not found"))
|
||||||
return ErrorCode::ServerPacketManagerError;
|
return ErrorCode::ServerPacketManagerError;
|
||||||
if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed"))
|
if (stdOut.contains("fuser not installed"))
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
|
|
||||||
if (stdOut.isEmpty()) {
|
if (stdOut.isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,8 @@ ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isA
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
|
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
|
||||||
const QJsonObject &containerConfig, const DockerContainer container)
|
const QJsonObject &containerConfig, const DockerContainer container,
|
||||||
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
QJsonObject vpnConfiguration {};
|
QJsonObject vpnConfiguration {};
|
||||||
|
|
||||||
|
|
@ -102,8 +103,7 @@ QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QStr
|
||||||
if (container == DockerContainer::Awg || container == DockerContainer::WireGuard) {
|
if (container == DockerContainer::Awg || container == DockerContainer::WireGuard) {
|
||||||
// add mtu for old configs
|
// add mtu for old configs
|
||||||
if (vpnConfigData[config_key::mtu].toString().isEmpty()) {
|
if (vpnConfigData[config_key::mtu].toString().isEmpty()) {
|
||||||
vpnConfigData[config_key::mtu] =
|
vpnConfigData[config_key::mtu] = container == DockerContainer::Awg ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
|
||||||
container == DockerContainer::Awg ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@ class VpnConfigurationsController : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit VpnConfigurationsController(const std::shared_ptr<Settings> &settings, QSharedPointer<ServerController> serverController,
|
explicit VpnConfigurationsController(const std::shared_ptr<Settings> &settings, QSharedPointer<ServerController> serverController, QObject *parent = nullptr);
|
||||||
QObject *parent = nullptr);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container,
|
ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container,
|
||||||
|
|
@ -22,7 +21,7 @@ public slots:
|
||||||
const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol,
|
const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol,
|
||||||
QString &protocolConfigString);
|
QString &protocolConfigString);
|
||||||
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
|
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
|
||||||
const QJsonObject &containerConfig, const DockerContainer container);
|
const QJsonObject &containerConfig, const DockerContainer container, ErrorCode &errorCode);
|
||||||
|
|
||||||
static void updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig, const QString &stdOut);
|
static void updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig, const QString &stdOut);
|
||||||
signals:
|
signals:
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@
|
||||||
|
|
||||||
namespace amnezia
|
namespace amnezia
|
||||||
{
|
{
|
||||||
|
|
||||||
|
constexpr const qint16 qrMagicCode = 1984;
|
||||||
|
|
||||||
struct ServerCredentials
|
struct ServerCredentials
|
||||||
{
|
{
|
||||||
QString hostName;
|
QString hostName;
|
||||||
|
|
@ -44,7 +47,6 @@ namespace amnezia
|
||||||
InternalError = 101,
|
InternalError = 101,
|
||||||
NotImplementedError = 102,
|
NotImplementedError = 102,
|
||||||
AmneziaServiceNotRunning = 103,
|
AmneziaServiceNotRunning = 103,
|
||||||
NotSupportedOnThisPlatform = 104,
|
|
||||||
|
|
||||||
// Server errors
|
// Server errors
|
||||||
ServerCheckFailed = 200,
|
ServerCheckFailed = 200,
|
||||||
|
|
@ -54,13 +56,6 @@ namespace amnezia
|
||||||
ServerCancelInstallation = 204,
|
ServerCancelInstallation = 204,
|
||||||
ServerUserNotInSudo = 205,
|
ServerUserNotInSudo = 205,
|
||||||
ServerPacketManagerError = 206,
|
ServerPacketManagerError = 206,
|
||||||
ServerSudoPackageIsNotPreinstalled = 207,
|
|
||||||
ServerUserDirectoryNotAccessible = 208,
|
|
||||||
ServerUserNotAllowedInSudoers = 209,
|
|
||||||
ServerUserPasswordRequired = 210,
|
|
||||||
ServerDockerOnCgroupsV2 = 211,
|
|
||||||
ServerCgroupMountpoint = 212,
|
|
||||||
DockerPullRateLimit = 213,
|
|
||||||
|
|
||||||
// Ssh connection errors
|
// Ssh connection errors
|
||||||
SshRequestDeniedError = 300,
|
SshRequestDeniedError = 300,
|
||||||
|
|
@ -101,8 +96,6 @@ namespace amnezia
|
||||||
|
|
||||||
// import and install errors
|
// import and install errors
|
||||||
ImportInvalidConfigError = 900,
|
ImportInvalidConfigError = 900,
|
||||||
ImportOpenConfigError = 901,
|
|
||||||
NoInstalledContainersError = 902,
|
|
||||||
|
|
||||||
// Android errors
|
// Android errors
|
||||||
AndroidError = 1000,
|
AndroidError = 1000,
|
||||||
|
|
@ -114,12 +107,6 @@ namespace amnezia
|
||||||
ApiConfigTimeoutError = 1103,
|
ApiConfigTimeoutError = 1103,
|
||||||
ApiConfigSslError = 1104,
|
ApiConfigSslError = 1104,
|
||||||
ApiMissingAgwPublicKey = 1105,
|
ApiMissingAgwPublicKey = 1105,
|
||||||
ApiConfigDecryptionError = 1106,
|
|
||||||
ApiServicesMissingError = 1107,
|
|
||||||
ApiConfigLimitError = 1108,
|
|
||||||
ApiNotFoundError = 1109,
|
|
||||||
ApiMigrationError = 1110,
|
|
||||||
ApiUpdateRequestError = 1111,
|
|
||||||
|
|
||||||
// QFile errors
|
// QFile errors
|
||||||
OpenError = 1200,
|
OpenError = 1200,
|
||||||
|
|
|
||||||
9
client/core/enums/apiEnums.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef APIENUMS_H
|
||||||
|
#define APIENUMS_H
|
||||||
|
|
||||||
|
enum ApiConfigSources {
|
||||||
|
Telegram = 1,
|
||||||
|
AmneziaGateway
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APIENUMS_H
|
||||||
|
|
@ -12,7 +12,6 @@ QString errorString(ErrorCode code) {
|
||||||
case(ErrorCode::UnknownError): errorMessage = QObject::tr("Unknown error"); break;
|
case(ErrorCode::UnknownError): errorMessage = QObject::tr("Unknown error"); break;
|
||||||
case(ErrorCode::NotImplementedError): errorMessage = QObject::tr("Function not implemented"); break;
|
case(ErrorCode::NotImplementedError): errorMessage = QObject::tr("Function not implemented"); break;
|
||||||
case(ErrorCode::AmneziaServiceNotRunning): errorMessage = QObject::tr("Background service is not running"); break;
|
case(ErrorCode::AmneziaServiceNotRunning): errorMessage = QObject::tr("Background service is not running"); break;
|
||||||
case(ErrorCode::NotSupportedOnThisPlatform): errorMessage = QObject::tr("The selected protocol is not supported on the current platform"); break;
|
|
||||||
|
|
||||||
// Server errors
|
// Server errors
|
||||||
case(ErrorCode::ServerCheckFailed): errorMessage = QObject::tr("Server check failed"); break;
|
case(ErrorCode::ServerCheckFailed): errorMessage = QObject::tr("Server check failed"); break;
|
||||||
|
|
@ -20,15 +19,8 @@ QString errorString(ErrorCode code) {
|
||||||
case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break;
|
case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break;
|
||||||
case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
|
case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
|
||||||
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
|
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
|
||||||
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user is not a member of the sudo group"); break;
|
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break;
|
||||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Package manager error"); break;
|
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
|
||||||
case(ErrorCode::ServerSudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed on the server"); break;
|
|
||||||
case(ErrorCode::ServerUserDirectoryNotAccessible): errorMessage = QObject::tr("The server user's home directory is not accessible"); break;
|
|
||||||
case(ErrorCode::ServerUserNotAllowedInSudoers): errorMessage = QObject::tr("Action not allowed in sudoers"); break;
|
|
||||||
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
|
|
||||||
case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break;
|
|
||||||
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
|
|
||||||
case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); break;
|
|
||||||
|
|
||||||
// Libssh errors
|
// Libssh errors
|
||||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
||||||
|
|
@ -58,8 +50,6 @@ QString errorString(ErrorCode code) {
|
||||||
case (ErrorCode::AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break;
|
case (ErrorCode::AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break;
|
||||||
|
|
||||||
case (ErrorCode::ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break;
|
case (ErrorCode::ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break;
|
||||||
case (ErrorCode::ImportOpenConfigError): errorMessage = QObject::tr("Unable to open config file"); break;
|
|
||||||
case(ErrorCode::NoInstalledContainersError): errorMessage = QObject::tr("VPN Protocols is not installed.\n Please install VPN container at first"); break;
|
|
||||||
|
|
||||||
// Android errors
|
// Android errors
|
||||||
case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break;
|
case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break;
|
||||||
|
|
@ -71,13 +61,7 @@ QString errorString(ErrorCode code) {
|
||||||
case (ErrorCode::ApiConfigSslError): errorMessage = QObject::tr("SSL error occurred"); break;
|
case (ErrorCode::ApiConfigSslError): errorMessage = QObject::tr("SSL error occurred"); break;
|
||||||
case (ErrorCode::ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); break;
|
case (ErrorCode::ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); break;
|
||||||
case (ErrorCode::ApiMissingAgwPublicKey): errorMessage = QObject::tr("Missing AGW public key"); break;
|
case (ErrorCode::ApiMissingAgwPublicKey): errorMessage = QObject::tr("Missing AGW public key"); break;
|
||||||
case (ErrorCode::ApiConfigDecryptionError): errorMessage = QObject::tr("Failed to decrypt response payload"); break;
|
|
||||||
case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break;
|
|
||||||
case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break;
|
|
||||||
case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
|
|
||||||
case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break;
|
|
||||||
case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break;
|
|
||||||
|
|
||||||
// QFile errors
|
// QFile errors
|
||||||
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
|
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
|
||||||
case(ErrorCode::ReadError): errorMessage = QObject::tr("QFile error: An error occurred when reading from the file"); break;
|
case(ErrorCode::ReadError): errorMessage = QObject::tr("QFile error: An error occurred when reading from the file"); break;
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ IpcClient *IpcClient::m_instance = nullptr;
|
||||||
|
|
||||||
IpcClient::IpcClient(QObject *parent) : QObject(parent)
|
IpcClient::IpcClient(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcClient::~IpcClient()
|
IpcClient::~IpcClient()
|
||||||
{
|
{
|
||||||
if (m_localSocket)
|
if (m_localSocket) m_localSocket->close();
|
||||||
m_localSocket->close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IpcClient::isSocketConnected() const
|
bool IpcClient::isSocketConnected() const
|
||||||
|
|
@ -25,15 +25,13 @@ IpcClient *IpcClient::Instance()
|
||||||
|
|
||||||
QSharedPointer<IpcInterfaceReplica> IpcClient::Interface()
|
QSharedPointer<IpcInterfaceReplica> IpcClient::Interface()
|
||||||
{
|
{
|
||||||
if (!Instance())
|
if (!Instance()) return nullptr;
|
||||||
return nullptr;
|
|
||||||
return Instance()->m_ipcClient;
|
return Instance()->m_ipcClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<IpcProcessTun2SocksReplica> IpcClient::InterfaceTun2Socks()
|
QSharedPointer<IpcProcessTun2SocksReplica> IpcClient::InterfaceTun2Socks()
|
||||||
{
|
{
|
||||||
if (!Instance())
|
if (!Instance()) return nullptr;
|
||||||
return nullptr;
|
|
||||||
return Instance()->m_Tun2SocksClient;
|
return Instance()->m_Tun2SocksClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,28 +42,15 @@ bool IpcClient::init(IpcClient *instance)
|
||||||
Instance()->m_localSocket = new QLocalSocket(Instance());
|
Instance()->m_localSocket = new QLocalSocket(Instance());
|
||||||
connect(Instance()->m_localSocket.data(), &QLocalSocket::connected, &Instance()->m_ClientNode, []() {
|
connect(Instance()->m_localSocket.data(), &QLocalSocket::connected, &Instance()->m_ClientNode, []() {
|
||||||
Instance()->m_ClientNode.addClientSideConnection(Instance()->m_localSocket.data());
|
Instance()->m_ClientNode.addClientSideConnection(Instance()->m_localSocket.data());
|
||||||
auto cliNode = Instance()->m_ClientNode.acquire<IpcInterfaceReplica>();
|
|
||||||
cliNode->waitForSource(5000);
|
|
||||||
Instance()->m_ipcClient.reset(cliNode);
|
|
||||||
|
|
||||||
if (!Instance()->m_ipcClient) {
|
|
||||||
qWarning() << "IpcClient is not ready!";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Instance()->m_ipcClient.reset(Instance()->m_ClientNode.acquire<IpcInterfaceReplica>());
|
||||||
Instance()->m_ipcClient->waitForSource(1000);
|
Instance()->m_ipcClient->waitForSource(1000);
|
||||||
|
|
||||||
if (!Instance()->m_ipcClient->isReplicaValid()) {
|
if (!Instance()->m_ipcClient->isReplicaValid()) {
|
||||||
qWarning() << "IpcClient replica is not connected!";
|
qWarning() << "IpcClient replica is not connected!";
|
||||||
}
|
}
|
||||||
|
|
||||||
auto t2sNode = Instance()->m_ClientNode.acquire<IpcProcessTun2SocksReplica>();
|
Instance()->m_Tun2SocksClient.reset(Instance()->m_ClientNode.acquire<IpcProcessTun2SocksReplica>());
|
||||||
t2sNode->waitForSource(5000);
|
|
||||||
Instance()->m_Tun2SocksClient.reset(t2sNode);
|
|
||||||
|
|
||||||
if (!Instance()->m_Tun2SocksClient) {
|
|
||||||
qWarning() << "IpcClient::m_Tun2SocksClient is not ready!";
|
|
||||||
}
|
|
||||||
|
|
||||||
Instance()->m_Tun2SocksClient->waitForSource(1000);
|
Instance()->m_Tun2SocksClient->waitForSource(1000);
|
||||||
|
|
||||||
if (!Instance()->m_Tun2SocksClient->isReplicaValid()) {
|
if (!Instance()->m_Tun2SocksClient->isReplicaValid()) {
|
||||||
|
|
@ -73,8 +58,9 @@ bool IpcClient::init(IpcClient *instance)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(Instance()->m_localSocket, &QLocalSocket::disconnected,
|
connect(Instance()->m_localSocket, &QLocalSocket::disconnected, [instance](){
|
||||||
[instance]() { instance->m_isSocketConnected = false; });
|
instance->m_isSocketConnected = false;
|
||||||
|
});
|
||||||
|
|
||||||
Instance()->m_localSocket->connectToServer(amnezia::getIpcServiceUrl());
|
Instance()->m_localSocket->connectToServer(amnezia::getIpcServiceUrl());
|
||||||
Instance()->m_localSocket->waitForConnected();
|
Instance()->m_localSocket->waitForConnected();
|
||||||
|
|
@ -91,7 +77,7 @@ bool IpcClient::init(IpcClient *instance)
|
||||||
|
|
||||||
QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
|
QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
|
||||||
{
|
{
|
||||||
if (!Instance()->m_ipcClient || !Instance()->m_ipcClient->isReplicaValid()) {
|
if (! Instance()->m_ipcClient || ! Instance()->m_ipcClient->isReplicaValid()) {
|
||||||
qWarning() << "IpcClient::createPrivilegedProcess : IpcClient IpcClient replica is not valid";
|
qWarning() << "IpcClient::createPrivilegedProcess : IpcClient IpcClient replica is not valid";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -114,15 +100,18 @@ QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
|
||||||
pd->ipcProcess.reset(priv);
|
pd->ipcProcess.reset(priv);
|
||||||
if (!pd->ipcProcess) {
|
if (!pd->ipcProcess) {
|
||||||
qWarning() << "Acquire PrivilegedProcess failed";
|
qWarning() << "Acquire PrivilegedProcess failed";
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
pd->ipcProcess->waitForSource(1000);
|
pd->ipcProcess->waitForSource(1000);
|
||||||
if (!pd->ipcProcess->isReplicaValid()) {
|
if (!pd->ipcProcess->isReplicaValid()) {
|
||||||
qWarning() << "PrivilegedProcess replica is not connected!";
|
qWarning() << "PrivilegedProcess replica is not connected!";
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject::connect(pd->ipcProcess.data(), &PrivilegedProcess::destroyed, pd->ipcProcess.data(),
|
QObject::connect(pd->ipcProcess.data(), &PrivilegedProcess::destroyed, pd->ipcProcess.data(), [pd](){
|
||||||
[pd]() { pd->replicaNode->deleteLater(); });
|
pd->replicaNode->deleteLater();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
pd->localSocket->connectToServer(amnezia::getIpcProcessUrl(pid));
|
pd->localSocket->connectToServer(amnezia::getIpcProcessUrl(pid));
|
||||||
pd->localSocket->waitForConnected();
|
pd->localSocket->waitForConnected();
|
||||||
|
|
@ -130,3 +119,5 @@ QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
|
||||||
auto processReplica = QSharedPointer<PrivilegedProcess>(pd->ipcProcess);
|
auto processReplica = QSharedPointer<PrivilegedProcess>(pd->ipcProcess);
|
||||||
return processReplica;
|
return processReplica;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
#include <winsock.h>
|
#include <winsock.h>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
#include "qendian.h"
|
#include "qendian.h"
|
||||||
#include <QSettings>
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
@ -186,17 +185,6 @@ int NetworkUtilities::AdapterIndexTo(const QHostAddress& dst) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkUtilities::checkIpv6Enabled() {
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
QSettings RegHLM("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters",
|
|
||||||
QSettings::NativeFormat);
|
|
||||||
int ret = RegHLM.value("DisabledComponents", 0).toInt();
|
|
||||||
qDebug() << "Check for Windows disabled IPv6 return " << ret;
|
|
||||||
return (ret != 255);
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
DWORD GetAdaptersAddressesWrapper(const ULONG Family,
|
DWORD GetAdaptersAddressesWrapper(const ULONG Family,
|
||||||
const ULONG Flags,
|
const ULONG Flags,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QNetworkReply>
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkUtilities : public QObject
|
class NetworkUtilities : public QObject
|
||||||
|
|
@ -16,7 +15,6 @@ public:
|
||||||
static QString getStringBetween(const QString &s, const QString &a, const QString &b);
|
static QString getStringBetween(const QString &s, const QString &a, const QString &b);
|
||||||
static bool checkIPv4Format(const QString &ip);
|
static bool checkIPv4Format(const QString &ip);
|
||||||
static bool checkIpSubnetFormat(const QString &ip);
|
static bool checkIpSubnetFormat(const QString &ip);
|
||||||
static bool checkIpv6Enabled();
|
|
||||||
static QString getGatewayAndIface();
|
static QString getGatewayAndIface();
|
||||||
// Returns the Interface Index that could Route to dst
|
// Returns the Interface Index that could Route to dst
|
||||||
static int AdapterIndexTo(const QHostAddress& dst);
|
static int AdapterIndexTo(const QHostAddress& dst);
|
||||||
|
|
@ -30,7 +28,9 @@ public:
|
||||||
|
|
||||||
static QString netMaskFromIpWithSubnet(const QString ip);
|
static QString netMaskFromIpWithSubnet(const QString ip);
|
||||||
static QString ipAddressFromIpWithSubnet(const QString ip);
|
static QString ipAddressFromIpWithSubnet(const QString ip);
|
||||||
|
|
||||||
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
|
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NETWORKUTILITIES_H
|
#endif // NETWORKUTILITIES_H
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
#include "qrCodeUtils.h"
|
|
||||||
|
|
||||||
#include <QIODevice>
|
|
||||||
#include <QList>
|
|
||||||
|
|
||||||
QList<QString> qrCodeUtils::generateQrCodeImageSeries(const QByteArray &data)
|
|
||||||
{
|
|
||||||
double k = 850;
|
|
||||||
|
|
||||||
quint8 chunksCount = std::ceil(data.size() / k);
|
|
||||||
QList<QString> chunks;
|
|
||||||
for (int i = 0; i < data.size(); i = i + k) {
|
|
||||||
QByteArray chunk;
|
|
||||||
QDataStream s(&chunk, QIODevice::WriteOnly);
|
|
||||||
s << qrCodeUtils::qrMagicCode << chunksCount << (quint8)std::round(i / k) << data.mid(i, k);
|
|
||||||
|
|
||||||
QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
|
||||||
|
|
||||||
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW);
|
|
||||||
QString svg = QString::fromStdString(toSvgString(qr, 1));
|
|
||||||
chunks.append(svgToBase64(svg));
|
|
||||||
}
|
|
||||||
|
|
||||||
return chunks;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString qrCodeUtils::svgToBase64(const QString &image)
|
|
||||||
{
|
|
||||||
return "data:image/svg;base64," + QString::fromLatin1(image.toUtf8().toBase64().data());
|
|
||||||
}
|
|
||||||
|
|
||||||
qrcodegen::QrCode qrCodeUtils::generateQrCode(const QByteArray &data)
|
|
||||||
{
|
|
||||||
return qrcodegen::QrCode::encodeText(data, qrcodegen::QrCode::Ecc::LOW);
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#ifndef QRCODEUTILS_H
|
|
||||||
#define QRCODEUTILS_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
#include "qrcodegen.hpp"
|
|
||||||
|
|
||||||
namespace qrCodeUtils
|
|
||||||
{
|
|
||||||
constexpr const qint16 qrMagicCode = 1984;
|
|
||||||
|
|
||||||
QList<QString> generateQrCodeImageSeries(const QByteArray &data);
|
|
||||||
qrcodegen::QrCode generateQrCode(const QByteArray &data);
|
|
||||||
QString svgToBase64(const QString &image);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // QRCODEUTILS_H
|
|
||||||
|
|
@ -104,7 +104,7 @@ QJsonObject Deserialize(const QString &vmessStr, QString *alias, QString *errMes
|
||||||
server.users.first().security = "auto";
|
server.users.first().security = "auto";
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto getQueryValue = [&query](const QString &key, const QString &defaultValue) {
|
const static auto getQueryValue = [&query](const QString &key, const QString &defaultValue) {
|
||||||
if (query.hasQueryItem(key))
|
if (query.hasQueryItem(key))
|
||||||
return query.queryItemValue(key, QUrl::FullyDecoded);
|
return query.queryItemValue(key, QUrl::FullyDecoded);
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dnsutils()->restoreResolvers()) {
|
if (supportDnsUtils() && !dnsutils()->restoreResolvers()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,23 +114,12 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||||
|
|
||||||
// Bring up the wireguard interface if not already done.
|
// Bring up the wireguard interface if not already done.
|
||||||
if (!wgutils()->interfaceExists()) {
|
if (!wgutils()->interfaceExists()) {
|
||||||
// Create the interface.
|
|
||||||
if (!wgutils()->addInterface(config)) {
|
if (!wgutils()->addInterface(config)) {
|
||||||
logger.error() << "Interface creation failed.";
|
logger.error() << "Interface creation failed.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bring the interface up.
|
|
||||||
if (supportIPUtils()) {
|
|
||||||
if (!iputils()->addInterfaceIPs(config)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!iputils()->setMTUAndUp(config)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure routing for excluded addresses.
|
// Configure routing for excluded addresses.
|
||||||
for (const QString& i : config.m_excludedAddresses) {
|
for (const QString& i : config.m_excludedAddresses) {
|
||||||
addExclusionRoute(IPAddress(i));
|
addExclusionRoute(IPAddress(i));
|
||||||
|
|
@ -146,10 +135,20 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (supportIPUtils()) {
|
||||||
|
if (!iputils()->addInterfaceIPs(config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!iputils()->setMTUAndUp(config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set routing
|
// set routing
|
||||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||||
if (!wgutils()->updateRoutePrefix(ip)) {
|
if (!wgutils()->updateRoutePrefix(ip)) {
|
||||||
logger.debug() << "Routing configuration failed for" << ip.toString();
|
logger.debug() << "Routing configuration failed for"
|
||||||
|
<< logger.sensitive(ip.toString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -166,17 +165,18 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
|
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
|
||||||
|
if (!supportDnsUtils()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
|
if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
|
||||||
(config.m_hopType == InterfaceConfig::SingleHop)) {
|
(config.m_hopType == InterfaceConfig::SingleHop)) {
|
||||||
QList<QHostAddress> resolvers;
|
QList<QHostAddress> resolvers;
|
||||||
resolvers.append(QHostAddress(config.m_primaryDnsServer));
|
resolvers.append(QHostAddress(config.m_dnsServer));
|
||||||
if (!config.m_secondaryDnsServer.isEmpty()) {
|
|
||||||
resolvers.append(QHostAddress(config.m_secondaryDnsServer));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the DNS is not the Gateway, it's a user defined DNS
|
// If the DNS is not the Gateway, it's a user defined DNS
|
||||||
// thus, not add any other :)
|
// thus, not add any other :)
|
||||||
if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) {
|
if (config.m_dnsServer == config.m_serverIpv4Gateway) {
|
||||||
resolvers.append(QHostAddress(config.m_serverIpv6Gateway));
|
resolvers.append(QHostAddress(config.m_serverIpv6Gateway));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,26 +282,15 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||||
config.m_serverIpv4Gateway = obj.value("serverIpv4Gateway").toString();
|
config.m_serverIpv4Gateway = obj.value("serverIpv4Gateway").toString();
|
||||||
config.m_serverIpv6Gateway = obj.value("serverIpv6Gateway").toString();
|
config.m_serverIpv6Gateway = obj.value("serverIpv6Gateway").toString();
|
||||||
|
|
||||||
if (!obj.contains("primaryDnsServer")) {
|
if (!obj.contains("dnsServer")) {
|
||||||
config.m_primaryDnsServer = QString();
|
config.m_dnsServer = QString();
|
||||||
} else {
|
} else {
|
||||||
QJsonValue value = obj.value("primaryDnsServer");
|
QJsonValue value = obj.value("dnsServer");
|
||||||
if (!value.isString()) {
|
if (!value.isString()) {
|
||||||
logger.error() << "dnsServer is not a string";
|
logger.error() << "dnsServer is not a string";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
config.m_primaryDnsServer = value.toString();
|
config.m_dnsServer = value.toString();
|
||||||
}
|
|
||||||
|
|
||||||
if (!obj.contains("secondaryDnsServer")) {
|
|
||||||
config.m_secondaryDnsServer = QString();
|
|
||||||
} else {
|
|
||||||
QJsonValue value = obj.value("secondaryDnsServer");
|
|
||||||
if (!value.isString()) {
|
|
||||||
logger.error() << "dnsServer is not a string";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
config.m_secondaryDnsServer = value.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!obj.contains("hopType")) {
|
if (!obj.contains("hopType")) {
|
||||||
|
|
@ -384,9 +373,6 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||||
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!parseStringList(obj, "allowedDnsServers", config.m_allowedDnsServers)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
|
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
|
||||||
|
|
||||||
|
|
@ -405,13 +391,6 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||||
if (!obj.value("S2").isNull()) {
|
if (!obj.value("S2").isNull()) {
|
||||||
config.m_responsePacketJunkSize = obj.value("S2").toString();
|
config.m_responsePacketJunkSize = obj.value("S2").toString();
|
||||||
}
|
}
|
||||||
if (!obj.value("S3").isNull()) {
|
|
||||||
config.m_cookieReplyPacketJunkSize = obj.value("S3").toString();
|
|
||||||
}
|
|
||||||
if (!obj.value("S4").isNull()) {
|
|
||||||
config.m_transportPacketJunkSize = obj.value("S4").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!obj.value("H1").isNull()) {
|
if (!obj.value("H1").isNull()) {
|
||||||
config.m_initPacketMagicHeader = obj.value("H1").toString();
|
config.m_initPacketMagicHeader = obj.value("H1").toString();
|
||||||
}
|
}
|
||||||
|
|
@ -425,34 +404,6 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||||
config.m_transportPacketMagicHeader = obj.value("H4").toString();
|
config.m_transportPacketMagicHeader = obj.value("H4").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!obj.value("I1").isNull()) {
|
|
||||||
config.m_specialJunk["I1"] = obj.value("I1").toString();
|
|
||||||
}
|
|
||||||
if (!obj.value("I2").isNull()) {
|
|
||||||
config.m_specialJunk["I2"] = obj.value("I2").toString();
|
|
||||||
}
|
|
||||||
if (!obj.value("I3").isNull()) {
|
|
||||||
config.m_specialJunk["I3"] = obj.value("I3").toString();
|
|
||||||
}
|
|
||||||
if (!obj.value("I4").isNull()) {
|
|
||||||
config.m_specialJunk["I4"] = obj.value("I4").toString();
|
|
||||||
}
|
|
||||||
if (!obj.value("I5").isNull()) {
|
|
||||||
config.m_specialJunk["I5"] = obj.value("I5").toString();
|
|
||||||
}
|
|
||||||
if (!obj.value("J1").isNull()) {
|
|
||||||
config.m_controlledJunk["J1"] = obj.value("J1").toString();
|
|
||||||
}
|
|
||||||
if (!obj.value("J2").isNull()) {
|
|
||||||
config.m_controlledJunk["J2"] = obj.value("J2").toString();
|
|
||||||
}
|
|
||||||
if (!obj.value("J3").isNull()) {
|
|
||||||
config.m_controlledJunk["J3"] = obj.value("J3").toString();
|
|
||||||
}
|
|
||||||
if (!obj.value("Itime").isNull()) {
|
|
||||||
config.m_specialHandshakeTimeout = obj.value("Itime").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -472,8 +423,13 @@ bool Daemon::deactivate(bool emitSignals) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup DNS
|
// Cleanup DNS
|
||||||
if (!dnsutils()->restoreResolvers()) {
|
if (supportDnsUtils() && !dnsutils()->restoreResolvers()) {
|
||||||
logger.warning() << "Failed to restore DNS resolvers.";
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wgutils()->interfaceExists()) {
|
||||||
|
logger.warning() << "Wireguard interface does not exist.";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup peers and routing
|
// Cleanup peers and routing
|
||||||
|
|
@ -493,9 +449,13 @@ bool Daemon::deactivate(bool emitSignals) {
|
||||||
}
|
}
|
||||||
m_excludedAddrSet.clear();
|
m_excludedAddrSet.clear();
|
||||||
|
|
||||||
m_connections.clear();
|
|
||||||
// Delete the interface
|
// Delete the interface
|
||||||
return wgutils()->deleteInterface();
|
if (!wgutils()->deleteInterface()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_connections.clear();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Daemon::logs() {
|
QString Daemon::logs() {
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "daemon/daemonerrors.h"
|
|
||||||
#include "daemonerrors.h"
|
|
||||||
#include "dnsutils.h"
|
#include "dnsutils.h"
|
||||||
#include "interfaceconfig.h"
|
#include "interfaceconfig.h"
|
||||||
#include "iputils.h"
|
#include "iputils.h"
|
||||||
|
|
@ -53,7 +51,7 @@ class Daemon : public QObject {
|
||||||
*/
|
*/
|
||||||
void activationFailure();
|
void activationFailure();
|
||||||
void disconnected();
|
void disconnected();
|
||||||
void backendFailure(DaemonError reason = DaemonError::ERROR_FATAL);
|
void backendFailure();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool maybeUpdateResolvers(const InterfaceConfig& config);
|
bool maybeUpdateResolvers(const InterfaceConfig& config);
|
||||||
|
|
@ -71,6 +69,7 @@ class Daemon : public QObject {
|
||||||
virtual WireguardUtils* wgutils() const = 0;
|
virtual WireguardUtils* wgutils() const = 0;
|
||||||
virtual bool supportIPUtils() const { return false; }
|
virtual bool supportIPUtils() const { return false; }
|
||||||
virtual IPUtils* iputils() { return nullptr; }
|
virtual IPUtils* iputils() { return nullptr; }
|
||||||
|
virtual bool supportDnsUtils() const { return false; }
|
||||||
virtual DnsUtils* dnsutils() { return nullptr; }
|
virtual DnsUtils* dnsutils() { return nullptr; }
|
||||||
|
|
||||||
static bool parseStringList(const QJsonObject& obj, const QString& name,
|
static bool parseStringList(const QJsonObject& obj, const QString& name,
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
enum class DaemonError : uint8_t {
|
|
||||||
ERROR_NONE = 0u,
|
|
||||||
ERROR_FATAL = 1u,
|
|
||||||
ERROR_SPLIT_TUNNEL_INIT_FAILURE = 2u,
|
|
||||||
ERROR_SPLIT_TUNNEL_START_FAILURE = 3u,
|
|
||||||
ERROR_SPLIT_TUNNEL_EXCLUDE_FAILURE = 4u,
|
|
||||||
|
|
||||||
DAEMON_ERROR_MAX = 5u,
|
|
||||||
};
|
|
||||||
|
|
@ -92,17 +92,6 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
|
||||||
|
|
||||||
logger.debug() << "Command received:" << type;
|
logger.debug() << "Command received:" << type;
|
||||||
|
|
||||||
// It is expected that sometimes the client will request backend logs
|
|
||||||
// before the first authentication. In these cases we just return empty
|
|
||||||
// logs.
|
|
||||||
if (type == "logs") {
|
|
||||||
QJsonObject obj;
|
|
||||||
obj.insert("type", "logs");
|
|
||||||
obj.insert("logs", "");
|
|
||||||
write(obj);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == "activate") {
|
if (type == "activate") {
|
||||||
InterfaceConfig config;
|
InterfaceConfig config;
|
||||||
if (!Daemon::parseConfig(obj, config)) {
|
if (!Daemon::parseConfig(obj, config)) {
|
||||||
|
|
@ -126,7 +115,8 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
|
||||||
if (type == "status") {
|
if (type == "status") {
|
||||||
QJsonObject obj = Daemon::instance()->getStatus();
|
QJsonObject obj = Daemon::instance()->getStatus();
|
||||||
obj.insert("type", "status");
|
obj.insert("type", "status");
|
||||||
write(obj);
|
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||||
|
m_socket->write("\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,7 +124,8 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
obj.insert("type", "logs");
|
obj.insert("type", "logs");
|
||||||
obj.insert("logs", Daemon::instance()->logs().replace("\n", "|"));
|
obj.insert("logs", Daemon::instance()->logs().replace("\n", "|"));
|
||||||
write(obj);
|
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||||
|
m_socket->write("\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,10 +150,9 @@ void DaemonLocalServerConnection::disconnected() {
|
||||||
write(obj);
|
write(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonLocalServerConnection::backendFailure(DaemonError err) {
|
void DaemonLocalServerConnection::backendFailure() {
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
obj.insert("type", "backendFailure");
|
obj.insert("type", "backendFailure");
|
||||||
obj.insert("errorCode", static_cast<int>(err));
|
|
||||||
write(obj);
|
write(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "daemonerrors.h"
|
|
||||||
|
|
||||||
class QLocalSocket;
|
class QLocalSocket;
|
||||||
|
|
||||||
class DaemonLocalServerConnection final : public QObject {
|
class DaemonLocalServerConnection final : public QObject {
|
||||||
|
|
@ -25,7 +23,7 @@ class DaemonLocalServerConnection final : public QObject {
|
||||||
|
|
||||||
void connected(const QString& pubkey);
|
void connected(const QString& pubkey);
|
||||||
void disconnected();
|
void disconnected();
|
||||||
void backendFailure(DaemonError err);
|
void backendFailure();
|
||||||
|
|
||||||
void write(const QJsonObject& obj);
|
void write(const QJsonObject& obj);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,7 @@ QJsonObject InterfaceConfig::toJson() const {
|
||||||
(m_hopType == InterfaceConfig::SingleHop)) {
|
(m_hopType == InterfaceConfig::SingleHop)) {
|
||||||
json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway));
|
json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway));
|
||||||
json.insert("serverIpv6Gateway", QJsonValue(m_serverIpv6Gateway));
|
json.insert("serverIpv6Gateway", QJsonValue(m_serverIpv6Gateway));
|
||||||
json.insert("primaryDnsServer", QJsonValue(m_primaryDnsServer));
|
json.insert("dnsServer", QJsonValue(m_dnsServer));
|
||||||
json.insert("secondaryDnsServer", QJsonValue(m_secondaryDnsServer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonArray allowedIPAddesses;
|
QJsonArray allowedIPAddesses;
|
||||||
|
|
@ -49,13 +48,6 @@ QJsonObject InterfaceConfig::toJson() const {
|
||||||
}
|
}
|
||||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||||
|
|
||||||
|
|
||||||
QJsonArray jsAllowedDnsServers;
|
|
||||||
for (const QString& i : m_allowedDnsServers) {
|
|
||||||
jsAllowedDnsServers.append(QJsonValue(i));
|
|
||||||
}
|
|
||||||
json.insert("allowedDnsServers", jsAllowedDnsServers);
|
|
||||||
|
|
||||||
QJsonArray disabledApps;
|
QJsonArray disabledApps;
|
||||||
for (const QString& i : m_vpnDisabledApps) {
|
for (const QString& i : m_vpnDisabledApps) {
|
||||||
disabledApps.append(QJsonValue(i));
|
disabledApps.append(QJsonValue(i));
|
||||||
|
|
@ -101,15 +93,11 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
||||||
out << "MTU = " << m_deviceMTU << "\n";
|
out << "MTU = " << m_deviceMTU << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_primaryDnsServer.isNull()) {
|
if (!m_dnsServer.isNull()) {
|
||||||
QStringList dnsServers;
|
QStringList dnsServers(m_dnsServer);
|
||||||
dnsServers.append(m_primaryDnsServer);
|
|
||||||
if (!m_secondaryDnsServer.isNull()) {
|
|
||||||
dnsServers.append(m_secondaryDnsServer);
|
|
||||||
}
|
|
||||||
// If the DNS is not the Gateway, it's a user defined DNS
|
// If the DNS is not the Gateway, it's a user defined DNS
|
||||||
// thus, not add any other :)
|
// thus, not add any other :)
|
||||||
if (m_primaryDnsServer == m_serverIpv4Gateway) {
|
if (m_dnsServer == m_serverIpv4Gateway) {
|
||||||
dnsServers.append(m_serverIpv6Gateway);
|
dnsServers.append(m_serverIpv6Gateway);
|
||||||
}
|
}
|
||||||
out << "DNS = " << dnsServers.join(", ") << "\n";
|
out << "DNS = " << dnsServers.join(", ") << "\n";
|
||||||
|
|
@ -130,12 +118,6 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
||||||
if (!m_responsePacketJunkSize.isNull()) {
|
if (!m_responsePacketJunkSize.isNull()) {
|
||||||
out << "S2 = " << m_responsePacketJunkSize << "\n";
|
out << "S2 = " << m_responsePacketJunkSize << "\n";
|
||||||
}
|
}
|
||||||
if (!m_cookieReplyPacketJunkSize.isNull()) {
|
|
||||||
out << "S3 = " << m_cookieReplyPacketJunkSize << "\n";
|
|
||||||
}
|
|
||||||
if (!m_transportPacketJunkSize.isNull()) {
|
|
||||||
out << "S4 = " << m_transportPacketJunkSize << "\n";
|
|
||||||
}
|
|
||||||
if (!m_initPacketMagicHeader.isNull()) {
|
if (!m_initPacketMagicHeader.isNull()) {
|
||||||
out << "H1 = " << m_initPacketMagicHeader << "\n";
|
out << "H1 = " << m_initPacketMagicHeader << "\n";
|
||||||
}
|
}
|
||||||
|
|
@ -149,16 +131,6 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
||||||
out << "H4 = " << m_transportPacketMagicHeader << "\n";
|
out << "H4 = " << m_transportPacketMagicHeader << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const QString& key : m_specialJunk.keys()) {
|
|
||||||
out << key << " = " << m_specialJunk[key] << "\n";
|
|
||||||
}
|
|
||||||
for (const QString& key : m_controlledJunk.keys()) {
|
|
||||||
out << key << " = " << m_controlledJunk[key] << "\n";
|
|
||||||
}
|
|
||||||
if (!m_specialHandshakeTimeout.isNull()) {
|
|
||||||
out << "Itime = " << m_specialHandshakeTimeout << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// If any extra config was provided, append it now.
|
// If any extra config was provided, append it now.
|
||||||
for (const QString& key : extra.keys()) {
|
for (const QString& key : extra.keys()) {
|
||||||
out << key << " = " << extra[key] << "\n";
|
out << key << " = " << extra[key] << "\n";
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
#define INTERFACECONFIG_H
|
#define INTERFACECONFIG_H
|
||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMap>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "ipaddress.h"
|
#include "ipaddress.h"
|
||||||
|
|
@ -32,14 +31,12 @@ class InterfaceConfig {
|
||||||
QString m_serverIpv4AddrIn;
|
QString m_serverIpv4AddrIn;
|
||||||
QString m_serverPskKey;
|
QString m_serverPskKey;
|
||||||
QString m_serverIpv6AddrIn;
|
QString m_serverIpv6AddrIn;
|
||||||
QString m_primaryDnsServer;
|
QString m_dnsServer;
|
||||||
QString m_secondaryDnsServer;
|
|
||||||
int m_serverPort = 0;
|
int m_serverPort = 0;
|
||||||
int m_deviceMTU = 1420;
|
int m_deviceMTU = 1420;
|
||||||
QList<IPAddress> m_allowedIPAddressRanges;
|
QList<IPAddress> m_allowedIPAddressRanges;
|
||||||
QStringList m_excludedAddresses;
|
QStringList m_excludedAddresses;
|
||||||
QStringList m_vpnDisabledApps;
|
QStringList m_vpnDisabledApps;
|
||||||
QStringList m_allowedDnsServers;
|
|
||||||
bool m_killSwitchEnabled;
|
bool m_killSwitchEnabled;
|
||||||
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
||||||
QString m_installationId;
|
QString m_installationId;
|
||||||
|
|
@ -50,15 +47,10 @@ class InterfaceConfig {
|
||||||
QString m_junkPacketMaxSize;
|
QString m_junkPacketMaxSize;
|
||||||
QString m_initPacketJunkSize;
|
QString m_initPacketJunkSize;
|
||||||
QString m_responsePacketJunkSize;
|
QString m_responsePacketJunkSize;
|
||||||
QString m_cookieReplyPacketJunkSize;
|
|
||||||
QString m_transportPacketJunkSize;
|
|
||||||
QString m_initPacketMagicHeader;
|
QString m_initPacketMagicHeader;
|
||||||
QString m_responsePacketMagicHeader;
|
QString m_responsePacketMagicHeader;
|
||||||
QString m_underloadPacketMagicHeader;
|
QString m_underloadPacketMagicHeader;
|
||||||
QString m_transportPacketMagicHeader;
|
QString m_transportPacketMagicHeader;
|
||||||
QMap<QString, QString> m_specialJunk;
|
|
||||||
QMap<QString, QString> m_controlledJunk;
|
|
||||||
QString m_specialHandshakeTimeout;
|
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
QString toWgConf(
|
QString toWgConf(
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,9 @@ class WireguardUtils : public QObject {
|
||||||
|
|
||||||
virtual bool updateRoutePrefix(const IPAddress& prefix) = 0;
|
virtual bool updateRoutePrefix(const IPAddress& prefix) = 0;
|
||||||
virtual bool deleteRoutePrefix(const IPAddress& prefix) = 0;
|
virtual bool deleteRoutePrefix(const IPAddress& prefix) = 0;
|
||||||
|
|
||||||
virtual bool addExclusionRoute(const IPAddress& prefix) = 0;
|
virtual bool addExclusionRoute(const IPAddress& prefix) = 0;
|
||||||
virtual bool deleteExclusionRoute(const IPAddress& prefix) = 0;
|
virtual bool deleteExclusionRoute(const IPAddress& prefix) = 0;
|
||||||
|
|
||||||
virtual bool excludeLocalNetworks(const QList<IPAddress>& addresses) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WIREGUARDUTILS_H
|
#endif // WIREGUARDUTILS_H
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M18 13V19C18 19.5304 17.7893 20.0391 17.4142 20.4142C17.0391 20.7893 16.5304 21 16 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V8C3 7.46957 3.21071 6.96086 3.58579 6.58579C3.96086 6.21071 4.46957 6 5 6H11" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M15 3H21V9" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M10 14L21 3" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 644 B |
|
|
@ -1,5 +0,0 @@
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M20 3H4C2.89543 3 2 3.89543 2 5V15C2 16.1046 2.89543 17 4 17H20C21.1046 17 22 16.1046 22 15V5C22 3.89543 21.1046 3 20 3Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M8 21H16" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M12 17V21" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 522 B |
|
|
@ -26,21 +26,14 @@ set_target_properties(networkextension PROPERTIES
|
||||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
||||||
)
|
|
||||||
|
|
||||||
if(DEPLOY)
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
set_target_properties(networkextension PROPERTIES
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN.network-extension"
|
||||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN"
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN.network-extension"
|
||||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN"
|
)
|
||||||
)
|
|
||||||
else()
|
|
||||||
set_target_properties(networkextension PROPERTIES
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set_target_properties(networkextension PROPERTIES
|
set_target_properties(networkextension PROPERTIES
|
||||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
|
|
|
||||||
19
client/ios/scripts/openvpn.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
XCODEBUILD="/usr/bin/xcodebuild"
|
||||||
|
WORKINGDIR=`pwd`
|
||||||
|
PATCH="/usr/bin/patch"
|
||||||
|
|
||||||
|
cat $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/Project.xcconfig > $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
||||||
|
cat << EOF >> $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
||||||
|
PROJECT_TEMP_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/OpenVPNAdapter.build
|
||||||
|
CONFIGURATION_BUILD_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-iphoneos
|
||||||
|
BUILT_PRODUCTS_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-iphoneos
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
cd 3rd/OpenVPNAdapter
|
||||||
|
if $XCODEBUILD -scheme OpenVPNAdapter -configuration Release -xcconfig Configuration/amnezia.xcconfig -sdk iphoneos -destination 'generic/platform=iOS' -project OpenVPNAdapter.xcodeproj ; then
|
||||||
|
echo "OpenVPNAdapter built successfully"
|
||||||
|
else
|
||||||
|
echo "OpenVPNAdapter build failed"
|
||||||
|
fi
|
||||||
|
cd ../../
|
||||||
43
client/macos/app/AmneziaVPNLaunchScreen.storyboard
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||||
|
<device id="ipad12_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--View Controller-->
|
||||||
|
<scene sceneID="EHf-IW-A2E">
|
||||||
|
<objects>
|
||||||
|
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="gZ9-gc-3t5">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="1024" height="1366"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch.png" translatesAutoresizingMaskIntoConstraints="NO" id="q5g-aV-39U">
|
||||||
|
<rect key="frame" x="467" y="638" width="90" height="90"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="90" id="VFp-nz-h8O"/>
|
||||||
|
<constraint firstAttribute="height" constant="90" id="ZUg-Ud-mgE"/>
|
||||||
|
</constraints>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<viewLayoutGuide key="safeArea" id="Whf-X3-AA4"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="q5g-aV-39U" firstAttribute="centerX" secondItem="gZ9-gc-3t5" secondAttribute="centerX" id="Ayw-bo-LVF"/>
|
||||||
|
<constraint firstItem="q5g-aV-39U" firstAttribute="centerY" secondItem="gZ9-gc-3t5" secondAttribute="centerY" id="YHd-Kc-J0u"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="53" y="375"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="launch.png" width="1024" height="1024"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
178
client/macos/app/Info.plist.in
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleAllowMixedLocalizations</key>
|
||||||
|
<true/>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||||
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
<false/>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
|
<true/>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>AmneziaVPNLaunchScreen</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array/>
|
||||||
|
<key>UIRequiresFullScreen</key>
|
||||||
|
<true/>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array/>
|
||||||
|
<key>UIUserInterfaceStyle</key>
|
||||||
|
<string>Light</string>
|
||||||
|
<key>com.wireguard.ios.app_group_id</key>
|
||||||
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Amnezia VPN needs access to the camera for reading QR-codes.</string>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSAllowsLocalNetworking</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>CFBundleIcons</key>
|
||||||
|
<dict/>
|
||||||
|
<key>CFBundleIcons~ipad</key>
|
||||||
|
<dict/>
|
||||||
|
<key>UTImportedTypeDeclarations</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>Amnezia VPN config</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>vpn</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>WireGuard config</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>conf</string>
|
||||||
|
<string>cfg</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>OpenVPN config</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>ovpn</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>AmneziaVPN backup file</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>backup</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Amnezia VPN config</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Alternate</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
||||||
|
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
||||||
|
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
||||||
|
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/100.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/1024.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/114.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/120.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/144.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/152.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/167.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/180.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/20.png
Normal file
|
After Width: | Height: | Size: 767 B |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/29.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/40.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/50.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/57.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
client/macos/app/Media.xcassets/AppIcon.appiconset/58.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |