mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 22:43:24 +00:00
Compare commits
208 Commits
chore/clan
...
refactorin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f68c8ed80 | ||
|
|
153f96fc61 | ||
|
|
0dbab0e84b | ||
|
|
1b6442079a | ||
|
|
6b0c543fd2 | ||
|
|
76413ec2df | ||
|
|
9f0d4aaba1 | ||
|
|
abb9ffe1b7 | ||
|
|
5012cd90e2 | ||
|
|
39374075c5 | ||
|
|
c0fcb23b66 | ||
|
|
ac0ce8a6f6 | ||
|
|
9f9da885b7 | ||
|
|
f51fd2bf3e | ||
|
|
c8378fd32d | ||
|
|
d767214f10 | ||
|
|
e027c504ae | ||
|
|
669a95d975 | ||
|
|
a96df5d518 | ||
|
|
c5c81735a0 | ||
|
|
c933745707 | ||
|
|
6710fd18b3 | ||
|
|
1b78a71529 | ||
|
|
1909d3c94e | ||
|
|
10a107716c | ||
|
|
5445e6637b | ||
|
|
2380cd5cfb | ||
|
|
42661618dc | ||
|
|
8a7e901d7a | ||
|
|
f8bea71716 | ||
|
|
d766a001e3 | ||
|
|
efcc0b7efc | ||
|
|
4d17e913b5 | ||
|
|
b341934863 | ||
|
|
127f8ed3bb | ||
|
|
9dca80de18 | ||
|
|
b0a6bcc055 | ||
|
|
f0626e2eca | ||
|
|
65f60ab922 | ||
|
|
979ab42c5a | ||
|
|
e152e84ddc | ||
|
|
2d22a74b22 | ||
|
|
2605978889 | ||
|
|
a2d30efaab | ||
|
|
d3715d00ae | ||
|
|
c37662dbe2 | ||
|
|
768ca1e73d | ||
|
|
a20516850c | ||
|
|
7a203868ec | ||
|
|
43c3ce9a6e | ||
|
|
369e08844f | ||
|
|
48a5452a65 | ||
|
|
c2f9340db6 | ||
|
|
a6508e642a | ||
|
|
a3e73797c2 | ||
|
|
df7bf204ea | ||
|
|
e16243ff55 | ||
|
|
e23cbe67ad | ||
|
|
7702f2f74c | ||
|
|
b457ef9a3f | ||
|
|
a28ed6a977 | ||
|
|
0c73682cfc | ||
|
|
7e380b6cfb | ||
|
|
63b5257986 | ||
|
|
acc4485e81 | ||
|
|
2c44999a31 | ||
|
|
e59a48f9f4 | ||
|
|
b86356b0cc | ||
|
|
f6d7552b58 | ||
|
|
5bd88ac2e9 | ||
|
|
94fa5b59f3 | ||
|
|
7169480999 | ||
|
|
c44ce0d77c | ||
|
|
7fd71a8408 | ||
|
|
68db721089 | ||
|
|
a180e12bdf | ||
|
|
f3a4a1b1be | ||
|
|
6977a8ecbc | ||
|
|
d00f64e6ad | ||
|
|
d5b3da6ba3 | ||
|
|
c245318339 | ||
|
|
b3b0fec2e1 | ||
|
|
9d571a4c71 | ||
|
|
f283858490 | ||
|
|
76fe203767 | ||
|
|
b9a47f2f50 | ||
|
|
27cb17c640 | ||
|
|
ef8fb89eb3 | ||
|
|
f1b045f8a8 | ||
|
|
050066132b | ||
|
|
2a6e6a1e24 | ||
|
|
92689d084c | ||
|
|
00f314039d | ||
|
|
fcb75e837d | ||
|
|
9fbea76b74 | ||
|
|
b3ff120bcf | ||
|
|
9dea98f020 | ||
|
|
c4701d4e7a | ||
|
|
48903ca3a1 | ||
|
|
0c9fd4aef4 | ||
|
|
b2af2e46ac | ||
|
|
efc76a0683 | ||
|
|
c4a553c166 | ||
|
|
69a00b0252 | ||
|
|
4257c08b43 | ||
|
|
c9e5b92f79 | ||
|
|
99818c2ad8 | ||
|
|
99e3afabad | ||
|
|
d3339a7f3a | ||
|
|
678bfffe49 | ||
|
|
728b48044c | ||
|
|
7ccbfa48bc | ||
|
|
83460bc29b | ||
|
|
c28e1b468a | ||
|
|
abd7fdd19c | ||
|
|
2b1ec9c693 | ||
|
|
19fcddfdaf | ||
|
|
0bca78eca9 | ||
|
|
68046a0b7c | ||
|
|
d19017f87b | ||
|
|
46536bc60a | ||
|
|
6a424e9858 | ||
|
|
8afe50cd87 | ||
|
|
48980c486e | ||
|
|
5f6cd282d3 | ||
|
|
95121c06e2 | ||
|
|
c2b17c128d | ||
|
|
eda24765e7 | ||
|
|
35e0e146e6 | ||
|
|
a5254ac238 | ||
|
|
517b5e5ca6 | ||
|
|
cfeb6cbffd | ||
|
|
c128ba981c | ||
|
|
a1ca994c8b | ||
|
|
52c12940c4 | ||
|
|
25d759374c | ||
|
|
e9250afd2b | ||
|
|
eb83086d5c | ||
|
|
9398e0e695 | ||
|
|
915c8f46c5 | ||
|
|
ec132ac96c | ||
|
|
101838404e | ||
|
|
db3164223a | ||
|
|
5a7b5d34fb | ||
|
|
9420333c76 | ||
|
|
f6403fe82e | ||
|
|
c55b025eee | ||
|
|
fc6fc26148 | ||
|
|
48b43ee102 | ||
|
|
e091020692 | ||
|
|
07baf0ed65 | ||
|
|
42d3d9b98a | ||
|
|
389c1f5327 | ||
|
|
703b9137e0 | ||
|
|
b183a3b232 | ||
|
|
f163f0fc1d | ||
|
|
3b49d5ca59 | ||
|
|
236e5ca2e3 | ||
|
|
2f6e28b980 | ||
|
|
46d96a8887 | ||
|
|
56221881da | ||
|
|
3f55f6a629 | ||
|
|
7c8ae9c311 | ||
|
|
b173dcaa17 | ||
|
|
da5fe1d766 | ||
|
|
a15ea0e8a1 | ||
|
|
fbbba648c4 | ||
|
|
f79bfa9d2e | ||
|
|
3011a0e306 | ||
|
|
76640311ab | ||
|
|
e707471b04 | ||
|
|
6425700d1c | ||
|
|
36045c6694 | ||
|
|
52ecd6899b | ||
|
|
49a6a9ed76 | ||
|
|
4869429eb6 | ||
|
|
956dd6e37a | ||
|
|
665a2911be | ||
|
|
1cfa4e0630 | ||
|
|
5bda624576 | ||
|
|
d1f0560595 | ||
|
|
df07fc1b1f | ||
|
|
8ca31e0c90 | ||
|
|
f1c6067485 | ||
|
|
ca04c63f5e | ||
|
|
89cdd2bece | ||
|
|
73d7dfa54f | ||
|
|
0a5b54a2e4 | ||
|
|
e43aa02a5b | ||
|
|
c3fb62a6ab | ||
|
|
62f3a339b7 | ||
|
|
767b14b37a | ||
|
|
e7fa160c9c | ||
|
|
7350d79c50 | ||
|
|
86f08554cd | ||
|
|
a741186c21 | ||
|
|
6acaab0ffa | ||
|
|
212e9b3a91 | ||
|
|
2bff37efae | ||
|
|
b88ab8e432 | ||
|
|
48f6cf904e | ||
|
|
367789bda2 | ||
|
|
d06924c59d | ||
|
|
2db99715b1 | ||
|
|
9688a8e52d | ||
|
|
b2c429f74d | ||
|
|
c5aa070bf4 | ||
|
|
d67201ede9 |
69
.cursorrules
Normal file
69
.cursorrules
Normal file
@@ -0,0 +1,69 @@
|
||||
# Amnezia VPN Client - MVVM Refactoring Rules
|
||||
|
||||
## Architecture Requirements
|
||||
|
||||
### MVVM Pattern
|
||||
- Follow Model-View-ViewModel architecture strictly
|
||||
- Maintain clear separation of concerns between layers
|
||||
- UI controllers should only handle user input and delegate to core controllers
|
||||
- Core controllers contain all business logic and data manipulation
|
||||
|
||||
### Dependencies Direction
|
||||
- UI layer depends on Core layer (unidirectional dependency)
|
||||
- UI controllers must not depend on each other
|
||||
- Core controllers must not depend on each other (exceptions: ServerController, VpnConfigurationsController, GatewayController can be created "on the fly")
|
||||
|
||||
### Settings Usage
|
||||
- UI classes must NOT use Settings class directly
|
||||
- Create wrapper methods in core controllers for Settings operations
|
||||
|
||||
### Server Controller Creation
|
||||
- ServerController creation must happen in Core controllers, never in UI
|
||||
- Remove direct instantiation: `new ServerController(m_settings)` from UI code
|
||||
|
||||
## Code Organization
|
||||
|
||||
### Folder Structure
|
||||
- Core controllers: `client/core/controllers/`
|
||||
- Core models: `client/core/models/`
|
||||
- UI controllers: `client/ui/controllers/`
|
||||
- UI models: `client/ui/models/`
|
||||
- Common models and controllers in root of respective folders
|
||||
|
||||
### Controller Management
|
||||
- All controllers must be instantiated in central `coreController`
|
||||
- Dependencies between core controllers resolved in `coreController`
|
||||
- UI controllers accept core controllers via constructor injection
|
||||
- UI controllers should be renamed with "UI" suffix (e.g., `exportUIController`)
|
||||
|
||||
## Code Style
|
||||
|
||||
### Comments
|
||||
- Do NOT include comments in code
|
||||
- Code should be self-documenting
|
||||
|
||||
### Models Behavior
|
||||
- Models should only display data
|
||||
- Keep core models as pure data structures without business logic
|
||||
|
||||
## Refactoring Process
|
||||
|
||||
### UI Controller Refactoring
|
||||
- Remove all business logic from UI controllers
|
||||
- Replace direct method calls with delegation to core controllers
|
||||
- Maintain only UI-specific logic (signals, model updates)
|
||||
- Remove Settings dependencies
|
||||
|
||||
### Core Controller Pattern
|
||||
- Create corresponding methods in core controllers for UI operations
|
||||
- Core controllers handle all complex logic, network operations, file I/O
|
||||
- Return structured results with error codes and data
|
||||
- Manage ServerController lifecycle internally
|
||||
|
||||
## Git Workflow
|
||||
- Use `git mv` for renaming controllers to maintain history
|
||||
|
||||
## Error Handling
|
||||
- Core controllers return structured error results
|
||||
- UI controllers only handle error presentation
|
||||
- Maintain consistent error propagation patterns
|
||||
133
.github/workflows/deploy.yml
vendored
133
.github/workflows/deploy.yml
vendored
@@ -10,7 +10,7 @@ env:
|
||||
|
||||
jobs:
|
||||
Build-Linux-Ubuntu:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.6.2
|
||||
@@ -20,6 +20,8 @@ jobs:
|
||||
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: 'Install Qt'
|
||||
@@ -90,6 +92,8 @@ jobs:
|
||||
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: 'Get sources'
|
||||
@@ -156,6 +160,8 @@ jobs:
|
||||
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'
|
||||
@@ -190,7 +196,7 @@ jobs:
|
||||
- name: 'Install go'
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22.1'
|
||||
go-version: '1.24'
|
||||
cache: false
|
||||
|
||||
- name: 'Setup gomobile'
|
||||
@@ -243,18 +249,33 @@ jobs:
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
Build-MacOS:
|
||||
Build-MacOS-old:
|
||||
runs-on: macos-latest
|
||||
|
||||
env:
|
||||
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
|
||||
QT_VERSION: 6.4.3
|
||||
QIF_VERSION: 4.6
|
||||
|
||||
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
||||
|
||||
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
|
||||
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
|
||||
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
|
||||
|
||||
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
|
||||
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
|
||||
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
|
||||
|
||||
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
|
||||
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
|
||||
|
||||
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'
|
||||
@@ -275,11 +296,6 @@ jobs:
|
||||
set-env: 'true'
|
||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||
|
||||
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
|
||||
run: |
|
||||
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
|
||||
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
|
||||
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
|
||||
|
||||
- name: 'Get sources'
|
||||
uses: actions/checkout@v4
|
||||
@@ -293,14 +309,90 @@ jobs:
|
||||
- name: 'Build project'
|
||||
run: |
|
||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
|
||||
bash deploy/build_macos.sh
|
||||
bash deploy/build_macos.sh -n
|
||||
|
||||
- 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
|
||||
|
||||
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
||||
|
||||
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
|
||||
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
|
||||
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
|
||||
|
||||
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
|
||||
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
|
||||
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
|
||||
|
||||
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
|
||||
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
|
||||
|
||||
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'
|
||||
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 -n
|
||||
|
||||
- name: 'Upload installer artifact'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_MacOS_installer
|
||||
path: AmneziaVPN.dmg
|
||||
path: deploy/build/pkg/AmneziaVPN.pkg
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload unpacked artifact'
|
||||
@@ -324,6 +416,8 @@ jobs:
|
||||
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: 'Install desktop Qt'
|
||||
@@ -335,7 +429,8 @@ jobs:
|
||||
arch: 'linux_gcc_64'
|
||||
modules: ${{ env.QT_MODULES }}
|
||||
dir: ${{ runner.temp }}
|
||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||
py7zrversion: '==0.22.*'
|
||||
extra: '--base ${{ env.QT_MIRROR }}'
|
||||
|
||||
- name: 'Install android_x86_64 Qt'
|
||||
uses: jurplel/install-qt-action@v4
|
||||
@@ -346,7 +441,8 @@ jobs:
|
||||
arch: 'android_x86_64'
|
||||
modules: ${{ env.QT_MODULES }}
|
||||
dir: ${{ runner.temp }}
|
||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||
py7zrversion: '==0.22.*'
|
||||
extra: '--base ${{ env.QT_MIRROR }}'
|
||||
|
||||
- name: 'Install android_x86 Qt'
|
||||
uses: jurplel/install-qt-action@v4
|
||||
@@ -357,7 +453,8 @@ jobs:
|
||||
arch: 'android_x86'
|
||||
modules: ${{ env.QT_MODULES }}
|
||||
dir: ${{ runner.temp }}
|
||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||
py7zrversion: '==0.22.*'
|
||||
extra: '--base ${{ env.QT_MIRROR }}'
|
||||
|
||||
- name: 'Install android_armv7 Qt'
|
||||
uses: jurplel/install-qt-action@v4
|
||||
@@ -368,7 +465,8 @@ jobs:
|
||||
arch: 'android_armv7'
|
||||
modules: ${{ env.QT_MODULES }}
|
||||
dir: ${{ runner.temp }}
|
||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||
py7zrversion: '==0.22.*'
|
||||
extra: '--base ${{ env.QT_MIRROR }}'
|
||||
|
||||
- name: 'Install android_arm64_v8a Qt'
|
||||
uses: jurplel/install-qt-action@v4
|
||||
@@ -379,7 +477,8 @@ jobs:
|
||||
arch: 'android_arm64_v8a'
|
||||
modules: ${{ env.QT_MODULES }}
|
||||
dir: ${{ runner.temp }}
|
||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||
py7zrversion: '==0.22.*'
|
||||
extra: '--base ${{ env.QT_MIRROR }}'
|
||||
|
||||
- name: 'Grant execute permission for qt-cmake'
|
||||
shell: bash
|
||||
|
||||
2
.github/workflows/tag-deploy.yml
vendored
2
.github/workflows/tag-deploy.yml
vendored
@@ -20,6 +20,8 @@ jobs:
|
||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||
DEV_AGW_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: 'Install desktop Qt'
|
||||
|
||||
61
.github/workflows/tag-upload.yml
vendored
61
.github/workflows/tag-upload.yml
vendored
@@ -1,64 +1,41 @@
|
||||
name: 'Upload a new version'
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
RELEASE_VERSION:
|
||||
description: 'Release version (e.g. 1.2.3.4)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
Upload-S3:
|
||||
runs-on: ubuntu-latest
|
||||
name: upload
|
||||
steps:
|
||||
- name: Checkout CMakeLists.txt
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref_name }}
|
||||
ref: ${{ inputs.RELEASE_VERSION }}
|
||||
sparse-checkout: |
|
||||
CMakeLists.txt
|
||||
deploy/deploy_s3.sh
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Verify git tag
|
||||
run: |
|
||||
GIT_TAG=${{ github.ref_name }}
|
||||
TAG_NAME=${{ inputs.RELEASE_VERSION }}
|
||||
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
|
||||
|
||||
if [[ "$GIT_TAG" == "$CMAKE_TAG" ]]; then
|
||||
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are the same. Continuing..."
|
||||
if [[ "$TAG_NAME" == "$CMAKE_TAG" ]]; then
|
||||
echo "Git tag ($TAG_NAME) matches CMakeLists.txt version ($CMAKE_TAG)."
|
||||
else
|
||||
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..."
|
||||
echo "::error::Mismatch: Git tag ($TAG_NAME) != CMakeLists.txt version ($CMAKE_TAG). Exiting with error..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Download artifacts from the "${{ github.ref_name }}" tag
|
||||
uses: robinraju/release-downloader@v1.8
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
tag: ${{ github.ref_name }}
|
||||
fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*"
|
||||
out-file-path: ${{ github.ref_name }}
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
|
||||
- name: Upload beta version
|
||||
uses: jakejarvis/s3-sync-action@master
|
||||
if: contains(github.event.base_ref, 'dev')
|
||||
with:
|
||||
args: --include "AmneziaVPN*" --delete
|
||||
env:
|
||||
AWS_S3_BUCKET: updates
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
|
||||
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
|
||||
SOURCE_DIR: ${{ github.ref_name }}
|
||||
DEST_DIR: beta/${{ github.ref_name }}
|
||||
|
||||
- name: Upload stable version
|
||||
uses: jakejarvis/s3-sync-action@master
|
||||
if: contains(github.event.base_ref, 'master')
|
||||
with:
|
||||
args: --include "AmneziaVPN*" --delete
|
||||
env:
|
||||
AWS_S3_BUCKET: updates
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
|
||||
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
|
||||
SOURCE_DIR: ${{ github.ref_name }}
|
||||
DEST_DIR: stable/${{ github.ref_name }}
|
||||
- name: Send dist to S3
|
||||
run: bash deploy/deploy_s3.sh ${{ inputs.RELEASE_VERSION }}
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -133,4 +133,9 @@ client/3rd/ShadowSocks/ss_ios.xcconfig
|
||||
out/
|
||||
|
||||
# CMake files
|
||||
CMakeFiles/
|
||||
CMakeFiles/
|
||||
|
||||
ios-ne-build.sh
|
||||
macos-ne-build.sh
|
||||
macos-signed-build.sh
|
||||
macos-with-sign-build.sh
|
||||
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,6 +1,3 @@
|
||||
[submodule "client/3rd/OpenVPNAdapter"]
|
||||
path = client/3rd/OpenVPNAdapter
|
||||
url = https://github.com/amnezia-vpn/OpenVPNAdapter.git
|
||||
[submodule "client/3rd/qtkeychain"]
|
||||
path = client/3rd/qtkeychain
|
||||
url = https://github.com/frankosterfeld/qtkeychain.git
|
||||
@@ -10,6 +7,7 @@
|
||||
[submodule "client/3rd-prebuilt"]
|
||||
path = client/3rd-prebuilt
|
||||
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
||||
branch = feature/special-handshake
|
||||
[submodule "client/3rd/amneziawg-apple"]
|
||||
path = client/3rd/amneziawg-apple
|
||||
url = https://github.com/amnezia-vpn/amneziawg-apple
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
set(AMNEZIAVPN_VERSION 4.8.9.1)
|
||||
|
||||
project(${PROJECT} VERSION 4.8.2.4
|
||||
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
HOMEPAGE_URL "https://amnezia.org/"
|
||||
)
|
||||
@@ -11,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
set(APP_ANDROID_VERSION_CODE 2071)
|
||||
set(APP_ANDROID_VERSION_CODE 2091)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
@@ -28,7 +29,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
||||
endif()
|
||||
|
||||
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(APPLE AND NOT IOS)
|
||||
|
||||
15
README.md
15
README.md
@@ -1,20 +1,25 @@
|
||||
# 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://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](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.
|
||||
|
||||
[](https://amnezia.org)
|
||||
|
||||
### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/kldscp/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
|
||||
### [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)
|
||||
|
||||
> [!TIP]
|
||||
> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/kldscp/amnezia.org).
|
||||
> 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://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/kldscp/amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></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)
|
||||
|
||||
@@ -180,7 +185,7 @@ GPL v3.0
|
||||
|
||||
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
|
||||
|
||||
Bitcoin: bc1q26eevjcg9j0wuyywd2e3uc9cs2w58lpkpjxq6p <br>
|
||||
Bitcoin: bc1qmhtgcf9637rl3kqyy22r2a8wa8laka4t9rx2mf <br>
|
||||
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
|
||||
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
|
||||
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br>
|
||||
|
||||
135
README_RU.md
135
README_RU.md
@@ -1,17 +1,22 @@
|
||||
# Amnezia VPN
|
||||
## _Лучший клиент для создания VPN на собственном сервере_
|
||||
|
||||
[AmneziaVPN](https://amnezia.org) — это open sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный 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/kldscp/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
||||
### [Сайт](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/kldscp/amnezia.org).
|
||||
> Если [сайт 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>
|
||||
|
||||
<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/kldscp/amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
|
||||
|
||||
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
|
||||
|
||||
@@ -25,7 +30,7 @@
|
||||
- Классические 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.
|
||||
- Поддерживает платформы: 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).
|
||||
|
||||
## Ссылки
|
||||
@@ -33,10 +38,10 @@
|
||||
- [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://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\)
|
||||
|
||||
## Технологии
|
||||
@@ -50,6 +55,112 @@ AmneziaVPN использует несколько проектов с откр
|
||||
- [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
|
||||
@@ -58,7 +169,7 @@ GPL v3.0
|
||||
|
||||
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
|
||||
|
||||
Bitcoin: bc1q26eevjcg9j0wuyywd2e3uc9cs2w58lpkpjxq6p <br>
|
||||
Bitcoin: bc1qmhtgcf9637rl3kqyy22r2a8wa8laka4t9rx2mf <br>
|
||||
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
|
||||
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
|
||||
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br>
|
||||
|
||||
Submodule client/3rd-prebuilt updated: ba580dc5bd...63d3c17adb
1
client/3rd/OpenVPNAdapter
vendored
1
client/3rd/OpenVPNAdapter
vendored
Submodule client/3rd/OpenVPNAdapter deleted from 7c821a8d5c
2
client/3rd/amneziawg-apple
vendored
2
client/3rd/amneziawg-apple
vendored
Submodule client/3rd/amneziawg-apple updated: 76e7db556a...811af0a83b
@@ -31,9 +31,8 @@ add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
||||
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
|
||||
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
|
||||
|
||||
if(IOS)
|
||||
set(PACKAGES ${PACKAGES} Multimedia)
|
||||
endif()
|
||||
add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}")
|
||||
add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}")
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
set(PACKAGES ${PACKAGES} Widgets)
|
||||
@@ -48,10 +47,6 @@ set(LIBS ${LIBS}
|
||||
Qt6::Core5Compat Qt6::Concurrent
|
||||
)
|
||||
|
||||
if(IOS)
|
||||
set(LIBS ${LIBS} Qt6::Multimedia)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
set(LIBS ${LIBS} Qt6::Widgets)
|
||||
endif()
|
||||
@@ -96,11 +91,6 @@ configure_file(${CMAKE_CURRENT_LIST_DIR}/translations/translations.qrc.in ${CMAK
|
||||
qt6_add_resources(QRC ${I18NQRC} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
|
||||
# -- i18n end
|
||||
|
||||
if(IOS)
|
||||
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/ios/scripts/openvpn.sh args
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||
endif()
|
||||
|
||||
set(IS_CI ${CI})
|
||||
if(IS_CI)
|
||||
message("Detected CI env")
|
||||
@@ -110,8 +100,8 @@ if(IS_CI)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/sources.cmake)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_LIST_DIR}/../ipc
|
||||
@@ -120,165 +110,22 @@ include_directories(
|
||||
${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/shared)
|
||||
include_directories(mozilla/models)
|
||||
|
||||
if(NOT IOS)
|
||||
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
|
||||
)
|
||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/../version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
|
||||
endif()
|
||||
|
||||
if(NOT IOS)
|
||||
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)
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in
|
||||
${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}
|
||||
user32
|
||||
rasapi32
|
||||
@@ -322,30 +169,6 @@ 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}
|
||||
${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()
|
||||
|
||||
if(ANDROID)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QFontDatabase>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QMimeData>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickStyle>
|
||||
@@ -10,26 +12,16 @@
|
||||
#include <QTextDocument>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
#include <QLocalSocket>
|
||||
#include <QLocalServer>
|
||||
|
||||
#include "logger.h"
|
||||
#include "ui/controllers/pageController.h"
|
||||
#include "ui/models/installedAppsModel.h"
|
||||
#include "version.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"
|
||||
|
||||
#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)
|
||||
{
|
||||
setQuitOnLastWindowClosed(false);
|
||||
@@ -84,79 +76,12 @@ void AmneziaApplication::init()
|
||||
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
||||
m_vpnConnectionThread.start();
|
||||
|
||||
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, [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
|
||||
|
||||
#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
|
||||
|
||||
#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_coreController.reset(new CoreController(m_vpnConnection, m_settings, m_engine));
|
||||
|
||||
m_engine->addImportPath("qrc:/ui/qml/Modules/");
|
||||
m_engine->load(url);
|
||||
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
||||
|
||||
m_coreController->setQmlRoot();
|
||||
|
||||
bool enabled = m_settings->isSaveLogs();
|
||||
#ifndef Q_OS_ANDROID
|
||||
@@ -168,13 +93,13 @@ void AmneziaApplication::init()
|
||||
#endif
|
||||
Logger::setServiceLogsEnabled(enabled);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef Q_OS_WIN //TODO
|
||||
if (m_parser.isSet("a"))
|
||||
m_pageController->showOnStartup();
|
||||
m_coreController->pageController()->showOnStartup();
|
||||
else
|
||||
emit m_pageController->raiseMainWindow();
|
||||
emit m_coreController->pageController()->raiseMainWindow();
|
||||
#else
|
||||
m_pageController->showOnStartup();
|
||||
m_coreController->pageController()->showOnStartup();
|
||||
#endif
|
||||
|
||||
// Android TextArea clipboard workaround
|
||||
@@ -231,33 +156,6 @@ void AmneziaApplication::loadFonts()
|
||||
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()
|
||||
{
|
||||
m_parser.setApplicationDescription(APPLICATION_NAME);
|
||||
@@ -282,19 +180,20 @@ bool AmneziaApplication::parseCommands()
|
||||
}
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
void AmneziaApplication::startLocalServer() {
|
||||
void AmneziaApplication::startLocalServer()
|
||||
{
|
||||
const QString serverName("AmneziaVPNInstance");
|
||||
QLocalServer::removeServer(serverName);
|
||||
|
||||
QLocalServer* server = new QLocalServer(this);
|
||||
QLocalServer *server = new QLocalServer(this);
|
||||
server->listen(serverName);
|
||||
|
||||
QObject::connect(server, &QLocalServer::newConnection, this, [server, this]() {
|
||||
if (server) {
|
||||
QLocalSocket* clientConnection = server->nextPendingConnection();
|
||||
QLocalSocket *clientConnection = server->nextPendingConnection();
|
||||
clientConnection->deleteLater();
|
||||
}
|
||||
emit m_pageController->raiseMainWindow();
|
||||
emit m_coreController->pageController()->raiseMainWindow(); //TODO
|
||||
});
|
||||
}
|
||||
#endif
|
||||
@@ -304,160 +203,12 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
||||
return m_engine;
|
||||
}
|
||||
|
||||
void AmneziaApplication::initModels()
|
||||
QNetworkAccessManager *AmneziaApplication::networkManager()
|
||||
{
|
||||
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()); });
|
||||
return m_nam;
|
||||
}
|
||||
|
||||
void AmneziaApplication::initControllers()
|
||||
QClipboard *AmneziaApplication::getClipboard()
|
||||
{
|
||||
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());
|
||||
return this->clipboard();
|
||||
}
|
||||
|
||||
@@ -11,43 +11,12 @@
|
||||
#else
|
||||
#include <QApplication>
|
||||
#endif
|
||||
#include <QClipboard>
|
||||
|
||||
#include "core/controllers/coreController.h"
|
||||
#include "settings.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()))
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
@@ -66,8 +35,6 @@ public:
|
||||
void init();
|
||||
void registerTypes();
|
||||
void loadFonts();
|
||||
void loadTranslator();
|
||||
void updateTranslator(const QLocale &locale);
|
||||
bool parseCommands();
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
@@ -75,67 +42,24 @@ public:
|
||||
#endif
|
||||
|
||||
QQmlApplicationEngine *qmlEngine() const;
|
||||
QNetworkAccessManager *manager() { return m_nam; }
|
||||
|
||||
signals:
|
||||
void translationsUpdated();
|
||||
QNetworkAccessManager *networkManager();
|
||||
QClipboard *getClipboard();
|
||||
|
||||
private:
|
||||
void initModels();
|
||||
void initControllers();
|
||||
|
||||
QQmlApplicationEngine *m_engine {};
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
|
||||
QScopedPointer<CoreController> m_coreController;
|
||||
|
||||
QSharedPointer<ContainerProps> m_containerProps;
|
||||
QSharedPointer<ProtocolProps> m_protocolProps;
|
||||
|
||||
QSharedPointer<QTranslator> m_translator;
|
||||
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;
|
||||
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;
|
||||
|
||||
QMetaObject::Connection m_reloadConfigErrorOccurredConnection;
|
||||
};
|
||||
|
||||
#endif // AMNEZIA_APPLICATION_H
|
||||
|
||||
@@ -91,6 +91,13 @@
|
||||
android:exported="false"
|
||||
android:theme="@style/Translucent" />
|
||||
|
||||
<activity android:name=".TvFilePicker"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity=""
|
||||
android:exported="false"
|
||||
android:theme="@style/Translucent" />
|
||||
|
||||
<activity
|
||||
android:name=".ImportConfigActivity"
|
||||
android:excludeFromRecents="true"
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?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>
|
||||
BIN
client/android/res/mipmap-hdpi/ic_banner.png
Normal file
BIN
client/android/res/mipmap-hdpi/ic_banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
client/android/res/mipmap-mdpi/ic_banner.png
Normal file
BIN
client/android/res/mipmap-mdpi/ic_banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@@ -23,4 +23,6 @@
|
||||
<string name="notificationSettingsDialogTitle">Настройки уведомлений</string>
|
||||
<string name="notificationSettingsDialogMessage">Для показа уведомлений необходимо включить уведомления в системных настройках</string>
|
||||
<string name="openNotificationSettings">Открыть настройки уведомлений</string>
|
||||
|
||||
<string name="tvNoFileBrowser">Пожалуйста, установите приложение для просмотра файлов</string>
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_banner_background">#1E1E1F</color>
|
||||
</resources>
|
||||
@@ -23,4 +23,6 @@
|
||||
<string name="notificationSettingsDialogTitle">Notification 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="tvNoFileBrowser">Please install a file management utility to browse files</string>
|
||||
</resources>
|
||||
@@ -4,6 +4,7 @@ import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.app.NotificationManager
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
@@ -12,6 +13,7 @@ import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.net.VpnService
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -20,8 +22,13 @@ import android.os.IBinder
|
||||
import android.os.Looper
|
||||
import android.os.Message
|
||||
import android.os.Messenger
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.os.SystemClock
|
||||
import android.provider.OpenableColumns
|
||||
import android.provider.Settings
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager.LayoutParams
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.Toast
|
||||
@@ -30,6 +37,7 @@ import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.ContextCompat
|
||||
import java.io.IOException
|
||||
import kotlin.LazyThreadSafetyMode.NONE
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.text.RegexOption.IGNORE_CASE
|
||||
import AppListProvider
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
@@ -71,6 +79,7 @@ class AmneziaActivity : QtActivity() {
|
||||
private var isInBoundState = false
|
||||
private var notificationStateReceiver: BroadcastReceiver? = null
|
||||
private lateinit var vpnServiceMessenger: IpcMessenger
|
||||
private var pfd: ParcelFileDescriptor? = null
|
||||
|
||||
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
|
||||
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
|
||||
@@ -514,21 +523,25 @@ class AmneziaActivity : QtActivity() {
|
||||
type = "text/*"
|
||||
putExtra(Intent.EXTRA_TITLE, fileName)
|
||||
}.also {
|
||||
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
|
||||
onSuccess = {
|
||||
it?.data?.let { uri ->
|
||||
Log.v(TAG, "Save file to $uri")
|
||||
try {
|
||||
contentResolver.openOutputStream(uri)?.use { os ->
|
||||
os.bufferedWriter().use { it.write(data) }
|
||||
try {
|
||||
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
|
||||
onSuccess = {
|
||||
it?.data?.let { uri ->
|
||||
Log.v(TAG, "Save file to $uri")
|
||||
try {
|
||||
contentResolver.openOutputStream(uri)?.use { os ->
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -537,35 +550,46 @@ class AmneziaActivity : QtActivity() {
|
||||
fun openFile(filter: String?) {
|
||||
Log.v(TAG, "Open file with filter: $filter")
|
||||
mainScope.launch {
|
||||
val mimeTypes = if (!filter.isNullOrEmpty()) {
|
||||
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
|
||||
val mime = MimeTypeMap.getSingleton()
|
||||
extensionRegex.findAll(filter).map {
|
||||
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
|
||||
}.toSet()
|
||||
} else emptySet()
|
||||
val intent = if (!isOnTv()) {
|
||||
val mimeTypes = if (!filter.isNullOrEmpty()) {
|
||||
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
|
||||
val mime = MimeTypeMap.getSingleton()
|
||||
extensionRegex.findAll(filter).map {
|
||||
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
|
||||
}.toSet()
|
||||
} else emptySet()
|
||||
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
Log.v(TAG, "File mimyType filter: $mimeTypes")
|
||||
if ("*/*" in mimeTypes) {
|
||||
type = "*/*"
|
||||
} else {
|
||||
when (mimeTypes.size) {
|
||||
1 -> type = mimeTypes.first()
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
Log.v(TAG, "File mimyType filter: $mimeTypes")
|
||||
if ("*/*" in mimeTypes) {
|
||||
type = "*/*"
|
||||
} else {
|
||||
when (mimeTypes.size) {
|
||||
1 -> type = mimeTypes.first()
|
||||
|
||||
in 2..Int.MAX_VALUE -> {
|
||||
type = "*/*"
|
||||
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
|
||||
in 2..Int.MAX_VALUE -> {
|
||||
type = "*/*"
|
||||
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
|
||||
}
|
||||
|
||||
else -> type = "*/*"
|
||||
}
|
||||
|
||||
else -> type = "*/*"
|
||||
}
|
||||
}
|
||||
}.also {
|
||||
startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
|
||||
} else {
|
||||
Intent(this@AmneziaActivity, TvFilePicker::class.java)
|
||||
}
|
||||
|
||||
try {
|
||||
startActivityForResult(intent, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
|
||||
onAny = {
|
||||
val uri = it?.data?.toString() ?: ""
|
||||
if (isOnTv() && it?.hasExtra("activityNotFound") == true) {
|
||||
showNoFileBrowserAlertDialog()
|
||||
}
|
||||
val uri = it?.data?.apply {
|
||||
grantUriPermission(packageName, this, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}?.toString() ?: ""
|
||||
Log.v(TAG, "Open file: $uri")
|
||||
mainScope.launch {
|
||||
qtInitialized.await()
|
||||
@@ -573,10 +597,68 @@ class AmneziaActivity : QtActivity() {
|
||||
}
|
||||
}
|
||||
))
|
||||
} 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")
|
||||
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
|
||||
fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|
||||
@@ -721,6 +803,50 @@ 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
|
||||
@@ -770,6 +896,7 @@ class AmneziaActivity : QtActivity() {
|
||||
}
|
||||
|
||||
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) }
|
||||
}
|
||||
@@ -784,6 +911,13 @@ class AmneziaActivity : QtActivity() {
|
||||
/**
|
||||
* Utils methods
|
||||
*/
|
||||
private fun <T> blockingCall(
|
||||
context: CoroutineContext = Dispatchers.Main.immediate,
|
||||
block: suspend () -> T
|
||||
) = runBlocking {
|
||||
mainScope.async(context) { block() }.await()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun actionCodeToString(actionCode: Int): String =
|
||||
when (actionCode) {
|
||||
|
||||
45
client/android/src/org/amnezia/vpn/TvFilePicker.kt
Normal file
45
client/android/src/org/amnezia/vpn/TvFilePicker.kt
Normal file
@@ -0,0 +1,45 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,10 +120,21 @@ open class Wireguard : Protocol() {
|
||||
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
|
||||
configData.optStringOrNull("S1")?.let { setS1(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("H2")?.let { setH2(it.toLong()) }
|
||||
configData.optStringOrNull("H3")?.let { setH3(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) {
|
||||
|
||||
@@ -20,10 +20,21 @@ open class WireguardConfig protected constructor(
|
||||
val jmax: Int?,
|
||||
val s1: Int?,
|
||||
val s2: Int?,
|
||||
val s3: Int?,
|
||||
val s4: Int?,
|
||||
val h1: Long?,
|
||||
val h2: 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) {
|
||||
|
||||
protected constructor(builder: Builder) : this(
|
||||
@@ -39,10 +50,21 @@ open class WireguardConfig protected constructor(
|
||||
builder.jmax,
|
||||
builder.s1,
|
||||
builder.s2,
|
||||
builder.s3,
|
||||
builder.s4,
|
||||
builder.h1,
|
||||
builder.h2,
|
||||
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()) {
|
||||
@@ -61,10 +83,21 @@ open class WireguardConfig protected constructor(
|
||||
appendLine("jmax=$jmax")
|
||||
appendLine("s1=$s1")
|
||||
appendLine("s2=$s2")
|
||||
s3?.let { appendLine("s3=$it") }
|
||||
s4?.let { appendLine("s4=$it") }
|
||||
appendLine("h1=$h1")
|
||||
appendLine("h2=$h2")
|
||||
appendLine("h3=$h3")
|
||||
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") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,10 +150,21 @@ open class WireguardConfig protected constructor(
|
||||
internal var jmax: Int? = null
|
||||
internal var s1: Int? = null
|
||||
internal var s2: Int? = null
|
||||
internal var s3: Int? = null
|
||||
internal var s4: Int? = null
|
||||
internal var h1: Long? = null
|
||||
internal var h2: Long? = null
|
||||
internal var h3: 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 }
|
||||
|
||||
@@ -139,10 +183,21 @@ open class WireguardConfig protected constructor(
|
||||
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
|
||||
fun setS1(s1: Int) = apply { this.s1 = s1 }
|
||||
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 setH2(h2: Long) = apply { this.h2 = h2 }
|
||||
fun setH3(h3: Long) = apply { this.h3 = h3 }
|
||||
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) }
|
||||
}
|
||||
|
||||
@@ -76,12 +76,22 @@ set_target_properties(${PROJECT} PROPERTIES
|
||||
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_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
|
||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||
@@ -126,9 +136,9 @@ add_subdirectory(ios/networkextension)
|
||||
add_dependencies(${PROJECT} networkextension)
|
||||
|
||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/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")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/)
|
||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework")
|
||||
|
||||
|
||||
@@ -18,7 +18,11 @@ set(LIBS ${LIBS}
|
||||
${FW_NETWORK_EXTENSION}
|
||||
)
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
|
||||
|
||||
235
client/cmake/sources.cmake
Normal file
235
client/cmake/sources.cmake
Normal file
@@ -0,0 +1,235 @@
|
||||
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}/core/models/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/api/gatewayController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/api/apiConfigController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/api/apiSettingsController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/api/apiPremV1MigrationController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/serverController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/selfhostedConfigController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/clientManagementController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/configController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/dnsController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/splitTunnelingController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/settingsController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/connectionController.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
|
||||
${CLIENT_ROOT_DIR}/core/utils/fileUtils.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}/core/models/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/api/gatewayController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/api/apiConfigController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/api/apiSettingsController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/api/apiPremV1MigrationController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/serverController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/selfhostedConfigController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/clientManagementController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/configController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/dnsController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/splitTunnelingController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/settingsController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/connectionController.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
|
||||
${CLIENT_ROOT_DIR}/core/utils/fileUtils.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
|
||||
${CLIENT_ROOT_DIR}/ui/models/selfhosted/*.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
|
||||
${CLIENT_ROOT_DIR}/ui/models/selfhosted/*.cpp
|
||||
)
|
||||
|
||||
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS
|
||||
${CLIENT_ROOT_DIR}/ui/controllers/*.h
|
||||
${CLIENT_ROOT_DIR}/ui/controllers/api/*.h
|
||||
${CLIENT_ROOT_DIR}/ui/controllers/selfhosted/*.h
|
||||
)
|
||||
|
||||
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS
|
||||
${CLIENT_ROOT_DIR}/ui/controllers/*.cpp
|
||||
${CLIENT_ROOT_DIR}/ui/controllers/api/*.cpp
|
||||
${CLIENT_ROOT_DIR}/ui/controllers/selfhosted/*.cpp
|
||||
)
|
||||
|
||||
file(GLOB CORE_MODELS_H CONFIGURE_DEPENDS
|
||||
${CLIENT_ROOT_DIR}/core/models/*.h
|
||||
${CLIENT_ROOT_DIR}/core/models/containers/*.h
|
||||
${CLIENT_ROOT_DIR}/core/models/protocols/*.h
|
||||
${CLIENT_ROOT_DIR}/core/models/servers/*.h
|
||||
)
|
||||
|
||||
file(GLOB CORE_MODELS_CPP CONFIGURE_DEPENDS
|
||||
${CLIENT_ROOT_DIR}/core/models/*.cpp
|
||||
${CLIENT_ROOT_DIR}/core/models/containers/*.cpp
|
||||
${CLIENT_ROOT_DIR}/core/models/protocols/*.cpp
|
||||
${CLIENT_ROOT_DIR}/core/models/servers/*.cpp
|
||||
)
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${COMMON_FILES_H}
|
||||
${PAGE_LOGIC_H}
|
||||
${CONFIGURATORS_H}
|
||||
${UI_MODELS_H}
|
||||
${UI_CONTROLLERS_H}
|
||||
${CORE_MODELS_H}
|
||||
)
|
||||
set(SOURCES ${SOURCES}
|
||||
${COMMON_FILES_CPP}
|
||||
${PAGE_LOGIC_CPP}
|
||||
${CONFIGURATORS_CPP}
|
||||
${UI_MODELS_CPP}
|
||||
${UI_CONTROLLERS_CPP}
|
||||
${CORE_MODELS_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,23 +1,29 @@
|
||||
#include "awg_configurator.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||
: WireguardConfigurator(settings, serverController, true, parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode &errorCode)
|
||||
QSharedPointer<ProtocolConfig> AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
|
||||
{
|
||||
QString config = WireguardConfigurator::createConfig(credentials, container, containerConfig, errorCode);
|
||||
|
||||
QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||
QString awgConfig = jsonConfig.value(config_key::config).toString();
|
||||
auto result = WireguardConfigurator::createConfig(credentials, container, protocolConfig, errorCode);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(result);
|
||||
if (!awgConfig) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString config = awgConfig->clientProtocolConfig.nativeConfig;
|
||||
|
||||
QMap<QString, QString> configMap;
|
||||
auto configLines = awgConfig.split("\n");
|
||||
auto configLines = config.split("\n");
|
||||
for (auto &line : configLines) {
|
||||
auto trimmedLine = line.trimmed();
|
||||
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
|
||||
@@ -30,17 +36,17 @@ QString AwgConfigurator::createConfig(const ServerCredentials &credentials, Dock
|
||||
}
|
||||
}
|
||||
|
||||
jsonConfig[config_key::junkPacketCount] = configMap.value(config_key::junkPacketCount);
|
||||
jsonConfig[config_key::junkPacketMinSize] = configMap.value(config_key::junkPacketMinSize);
|
||||
jsonConfig[config_key::junkPacketMaxSize] = configMap.value(config_key::junkPacketMaxSize);
|
||||
jsonConfig[config_key::initPacketJunkSize] = configMap.value(config_key::initPacketJunkSize);
|
||||
jsonConfig[config_key::responsePacketJunkSize] = configMap.value(config_key::responsePacketJunkSize);
|
||||
jsonConfig[config_key::initPacketMagicHeader] = configMap.value(config_key::initPacketMagicHeader);
|
||||
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
|
||||
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
|
||||
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
|
||||
jsonConfig[config_key::mtu] =
|
||||
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);
|
||||
awgConfig->clientProtocolConfig.awgData.junkPacketCount = configMap.value(config_key::junkPacketCount);
|
||||
awgConfig->clientProtocolConfig.awgData.junkPacketMinSize = configMap.value(config_key::junkPacketMinSize);
|
||||
awgConfig->clientProtocolConfig.awgData.junkPacketMaxSize = configMap.value(config_key::junkPacketMaxSize);
|
||||
awgConfig->clientProtocolConfig.awgData.initPacketJunkSize = configMap.value(config_key::initPacketJunkSize);
|
||||
awgConfig->clientProtocolConfig.awgData.responsePacketJunkSize = configMap.value(config_key::responsePacketJunkSize);
|
||||
awgConfig->clientProtocolConfig.awgData.initPacketMagicHeader = configMap.value(config_key::initPacketMagicHeader);
|
||||
awgConfig->clientProtocolConfig.awgData.responsePacketMagicHeader = configMap.value(config_key::responsePacketMagicHeader);
|
||||
awgConfig->clientProtocolConfig.awgData.underloadPacketMagicHeader = configMap.value(config_key::underloadPacketMagicHeader);
|
||||
awgConfig->clientProtocolConfig.awgData.transportPacketMagicHeader = configMap.value(config_key::transportPacketMagicHeader);
|
||||
|
||||
return QJsonDocument(jsonConfig).toJson();
|
||||
awgConfig->clientProtocolConfig.wireGuardData.mtu = awgConfig->serverProtocolConfig.mtu;
|
||||
|
||||
return awgConfig;
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ class AwgConfigurator : public WireguardConfigurator
|
||||
public:
|
||||
AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
|
||||
};
|
||||
|
||||
#endif // AWGCONFIGURATOR_H
|
||||
|
||||
@@ -4,23 +4,47 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
#include "core/models/protocols/cloakProtocolConfig.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString CloakConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode &errorCode)
|
||||
ConfiguratorBase::Vars CloakConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const
|
||||
{
|
||||
Vars vars = generateCommonVars(credentials, container);
|
||||
|
||||
auto cloakConfig = qSharedPointerCast<CloakProtocolConfig>(protocolConfig);
|
||||
if (!cloakConfig) {
|
||||
return vars;
|
||||
}
|
||||
|
||||
vars.append({{"$CLOAK_SERVER_PORT", cloakConfig->serverProtocolConfig.port}});
|
||||
vars.append({{"$FAKE_WEB_SITE_ADDRESS", cloakConfig->serverProtocolConfig.site}});
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
QSharedPointer<ProtocolConfig> CloakConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
|
||||
{
|
||||
auto cloakConfig = qSharedPointerCast<CloakProtocolConfig>(protocolConfig);
|
||||
if (!cloakConfig) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString cloakPublicKey =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckPublicKeyPath, errorCode);
|
||||
cloakPublicKey.replace("\n", "");
|
||||
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString cloakBypassUid =
|
||||
@@ -28,24 +52,38 @@ QString CloakConfigurator::createConfig(const ServerCredentials &credentials, Do
|
||||
cloakBypassUid.replace("\n", "");
|
||||
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cloakConfig->clientProtocolConfig.transport = "direct";
|
||||
cloakConfig->clientProtocolConfig.proxyMethod = "openvpn";
|
||||
cloakConfig->clientProtocolConfig.encryptionMethod = "aes-gcm";
|
||||
cloakConfig->clientProtocolConfig.uid = cloakBypassUid;
|
||||
cloakConfig->clientProtocolConfig.publicKey = cloakPublicKey;
|
||||
cloakConfig->clientProtocolConfig.serverName = cloakConfig->serverProtocolConfig.site;
|
||||
cloakConfig->clientProtocolConfig.numConn = 1;
|
||||
cloakConfig->clientProtocolConfig.browserSig = "chrome";
|
||||
cloakConfig->clientProtocolConfig.streamTimeout = 300;
|
||||
cloakConfig->clientProtocolConfig.remoteHost = credentials.hostName;
|
||||
cloakConfig->clientProtocolConfig.remotePort = cloakConfig->serverProtocolConfig.port;
|
||||
|
||||
QJsonObject config;
|
||||
config.insert("Transport", "direct");
|
||||
config.insert("ProxyMethod", "openvpn");
|
||||
config.insert("EncryptionMethod", "aes-gcm");
|
||||
config.insert("UID", cloakBypassUid);
|
||||
config.insert("PublicKey", cloakPublicKey);
|
||||
config.insert("ServerName", "$FAKE_WEB_SITE_ADDRESS");
|
||||
config.insert("NumConn", 1);
|
||||
config.insert("BrowserSig", "chrome");
|
||||
config.insert("StreamTimeout", 300);
|
||||
config.insert("RemoteHost", credentials.hostName);
|
||||
config.insert("RemotePort", "$CLOAK_SERVER_PORT");
|
||||
config.insert("Transport", cloakConfig->clientProtocolConfig.transport);
|
||||
config.insert("ProxyMethod", cloakConfig->clientProtocolConfig.proxyMethod);
|
||||
config.insert("EncryptionMethod", cloakConfig->clientProtocolConfig.encryptionMethod);
|
||||
config.insert("UID", cloakConfig->clientProtocolConfig.uid);
|
||||
config.insert("PublicKey", cloakConfig->clientProtocolConfig.publicKey);
|
||||
config.insert("ServerName", cloakConfig->clientProtocolConfig.serverName);
|
||||
config.insert("NumConn", cloakConfig->clientProtocolConfig.numConn);
|
||||
config.insert("BrowserSig", cloakConfig->clientProtocolConfig.browserSig);
|
||||
config.insert("StreamTimeout", cloakConfig->clientProtocolConfig.streamTimeout);
|
||||
config.insert("RemoteHost", cloakConfig->clientProtocolConfig.remoteHost);
|
||||
config.insert("RemotePort", cloakConfig->clientProtocolConfig.remotePort);
|
||||
|
||||
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
|
||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
QString textCfg = QJsonDocument(config).toJson();
|
||||
|
||||
return textCfg;
|
||||
cloakConfig->clientProtocolConfig.isEmpty = false;
|
||||
cloakConfig->clientProtocolConfig.nativeConfig = textCfg;
|
||||
|
||||
return cloakConfig;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "configurator_base.h"
|
||||
#include "core/models/protocols/cloakProtocolConfig.h"
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
@@ -13,8 +14,11 @@ class CloakConfigurator : public ConfiguratorBase
|
||||
public:
|
||||
CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
|
||||
|
||||
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
|
||||
};
|
||||
|
||||
#endif // CLOAK_CONFIGURATOR_H
|
||||
|
||||
@@ -1,26 +1,58 @@
|
||||
#include "configurator_base.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
#include <variant>
|
||||
|
||||
ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||
: QObject { parent }, m_settings(settings), m_serverController(serverController)
|
||||
{
|
||||
}
|
||||
|
||||
QString ConfiguratorBase::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
void ConfiguratorBase::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QSharedPointer<ProtocolConfig> &protocolConfig)
|
||||
{
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
return protocolConfigString;
|
||||
processConfigWithDnsSettings(dns, protocolConfig);
|
||||
}
|
||||
|
||||
QString ConfiguratorBase::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
void ConfiguratorBase::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QSharedPointer<ProtocolConfig> &protocolConfig)
|
||||
{
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
return protocolConfigString;
|
||||
processConfigWithDnsSettings(dns, protocolConfig);
|
||||
}
|
||||
|
||||
void ConfiguratorBase::processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString)
|
||||
void ConfiguratorBase::processConfigWithDnsSettings(const QPair<QString, QString> &dns, QSharedPointer<ProtocolConfig> &protocolConfig)
|
||||
{
|
||||
protocolConfigString.replace("$PRIMARY_DNS", dns.first);
|
||||
protocolConfigString.replace("$SECONDARY_DNS", dns.second);
|
||||
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
|
||||
std::visit([&dns](const auto &config) -> void {
|
||||
config->clientProtocolConfig.nativeConfig.replace("$PRIMARY_DNS", dns.first);
|
||||
config->clientProtocolConfig.nativeConfig.replace("$SECONDARY_DNS", dns.second);
|
||||
}, variant);
|
||||
}
|
||||
|
||||
ConfiguratorBase::Vars ConfiguratorBase::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const
|
||||
{
|
||||
return generateCommonVars(credentials, container);
|
||||
}
|
||||
|
||||
ConfiguratorBase::Vars ConfiguratorBase::generateCommonVars(const ServerCredentials &credentials, DockerContainer container) const
|
||||
{
|
||||
Vars vars;
|
||||
|
||||
vars.append({{"$REMOTE_HOST", credentials.hostName}});
|
||||
|
||||
QString serverIp = (container != DockerContainer::Awg && container != DockerContainer::WireGuard && container != DockerContainer::Xray)
|
||||
? NetworkUtilities::getIPAddress(credentials.hostName)
|
||||
: credentials.hostName;
|
||||
if (!serverIp.isEmpty()) {
|
||||
vars.append({{"$SERVER_IP_ADDRESS", serverIp}});
|
||||
}
|
||||
|
||||
vars.append({{"$CONTAINER_NAME", ContainerProps::containerToString(container)}});
|
||||
vars.append({{"$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container)}});
|
||||
|
||||
vars.append({{"$PRIMARY_SERVER_DNS", m_settings->primaryDns()}});
|
||||
vars.append({{"$SECONDARY_SERVER_DNS", m_settings->secondaryDns()}});
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
@@ -2,28 +2,39 @@
|
||||
#define CONFIGURATORBASE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
#include "settings.h"
|
||||
|
||||
class ConfiguratorBase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using Vars = QList<QPair<QString, QString>>;
|
||||
|
||||
explicit ConfiguratorBase(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
virtual QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode) = 0;
|
||||
virtual QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) = 0;
|
||||
|
||||
virtual QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString);
|
||||
virtual QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString);
|
||||
virtual void processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QSharedPointer<ProtocolConfig> &protocolConfig);
|
||||
virtual void processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QSharedPointer<ProtocolConfig> &protocolConfig);
|
||||
|
||||
virtual Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const;
|
||||
|
||||
protected:
|
||||
void processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString);
|
||||
void processConfigWithDnsSettings(const QPair<QString, QString> &dns, QSharedPointer<ProtocolConfig> &protocolConfig);
|
||||
|
||||
Vars generateCommonVars(const ServerCredentials &credentials, DockerContainer container) const;
|
||||
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
QSharedPointer<ServerController> m_serverController;
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
#include <QTemporaryFile>
|
||||
#include <QUuid>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
#include "utilities.h"
|
||||
|
||||
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||
@@ -19,6 +20,28 @@ Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, const Q
|
||||
{
|
||||
}
|
||||
|
||||
ConfiguratorBase::Vars Ikev2Configurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const
|
||||
{
|
||||
Vars vars = generateCommonVars(credentials, container);
|
||||
|
||||
vars.append({{"$IPSEC_VPN_L2TP_NET", "192.168.42.0/24"}});
|
||||
vars.append({{"$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250"}});
|
||||
vars.append({{"$IPSEC_VPN_L2TP_LOCAL", "192.168.42.1"}});
|
||||
|
||||
vars.append({{"$IPSEC_VPN_XAUTH_NET", "192.168.43.0/24"}});
|
||||
vars.append({{"$IPSEC_VPN_XAUTH_POOL", "192.168.43.10-192.168.43.250"}});
|
||||
|
||||
vars.append({{"$IPSEC_VPN_SHA2_TRUNCBUG", "yes"}});
|
||||
vars.append({{"$IPSEC_VPN_VPN_ANDROID_MTU_FIX", "yes"}});
|
||||
vars.append({{"$IPSEC_VPN_DISABLE_IKEV2", "no"}});
|
||||
vars.append({{"$IPSEC_VPN_DISABLE_L2TP", "no"}});
|
||||
vars.append({{"$IPSEC_VPN_DISABLE_XAUTH", "no"}});
|
||||
vars.append({{"$IPSEC_VPN_C2C_TRAFFIC", "no"}});
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const ServerCredentials &credentials, DockerContainer container,
|
||||
ErrorCode &errorCode)
|
||||
{
|
||||
@@ -54,21 +77,58 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode &errorCode)
|
||||
QSharedPointer<ProtocolConfig> Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
|
||||
{
|
||||
Q_UNUSED(containerConfig)
|
||||
Q_UNUSED(protocolConfig)
|
||||
|
||||
// IKEv2 uses a generic ProtocolConfig - no specific subclass needed
|
||||
if (!protocolConfig) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ConnectionData connData = prepareIkev2Config(credentials, container, errorCode);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return genIkev2Config(connData);
|
||||
auto ikev2Config = qSharedPointerCast<Ikev2ProtocolConfig>(protocolConfig);
|
||||
if (!ikev2Config) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ikev2Config->clientProtocolConfig.isEmpty = false;
|
||||
ikev2Config->clientProtocolConfig.hostName = connData.host;
|
||||
ikev2Config->clientProtocolConfig.userName = connData.clientId;
|
||||
ikev2Config->clientProtocolConfig.cert = QString(connData.clientCert.toBase64());
|
||||
ikev2Config->clientProtocolConfig.password = connData.password;
|
||||
|
||||
// Generate the appropriate native config based on platform
|
||||
QString nativeConfigStr;
|
||||
switch (Utils::systemType()) {
|
||||
case SystemType::iOS:
|
||||
[[fallthrough]];
|
||||
case SystemType::macOS:
|
||||
nativeConfigStr = genMobileConfig(connData);
|
||||
break;
|
||||
case SystemType::Android:
|
||||
nativeConfigStr = genIkev2Config(connData);
|
||||
break;
|
||||
default:
|
||||
nativeConfigStr = genStrongSwanConfig(connData);
|
||||
break;
|
||||
}
|
||||
|
||||
ikev2Config->clientProtocolConfig.nativeConfig = nativeConfigStr;
|
||||
|
||||
return ikev2Config;
|
||||
}
|
||||
|
||||
QString Ikev2Configurator::genIkev2Config(const ConnectionData &connData)
|
||||
{
|
||||
// Create temporary JSON for Android platform (will be eliminated when android protocols are updated)
|
||||
QJsonObject config;
|
||||
config[config_key::hostName] = connData.host;
|
||||
config[config_key::userName] = connData.clientId;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include "configurator_base.h"
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
||||
#include "core/defs.h"
|
||||
|
||||
class Ikev2Configurator : public ConfiguratorBase
|
||||
@@ -21,13 +23,16 @@ public:
|
||||
QString host; // host ip
|
||||
};
|
||||
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
|
||||
|
||||
QString genIkev2Config(const ConnectionData &connData);
|
||||
QString genMobileConfig(const ConnectionData &connData);
|
||||
QString genStrongSwanConfig(const ConnectionData &connData);
|
||||
|
||||
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
|
||||
|
||||
ConnectionData prepareIkev2Config(const ServerCredentials &credentials,
|
||||
DockerContainer container, ErrorCode &errorCode);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "openvpn_configurator.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
@@ -13,10 +12,14 @@
|
||||
#include <QApplication>
|
||||
#endif
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
#include <variant>
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "core/models/protocols/openvpnProtocolConfig.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
|
||||
@@ -24,12 +27,46 @@
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
|
||||
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent)
|
||||
{
|
||||
}
|
||||
|
||||
ConfiguratorBase::Vars OpenVpnConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const
|
||||
{
|
||||
Vars vars = generateCommonVars(credentials, container);
|
||||
|
||||
auto openVpnConfig = qSharedPointerCast<OpenVpnProtocolConfig>(protocolConfig);
|
||||
if (!openVpnConfig) {
|
||||
return vars;
|
||||
}
|
||||
|
||||
vars.append({{"$OPENVPN_SUBNET_IP", openVpnConfig->serverProtocolConfig.subnetAddress}});
|
||||
vars.append({{"$OPENVPN_SUBNET_CIDR", protocols::openvpn::defaultSubnetCidr}});
|
||||
vars.append({{"$OPENVPN_SUBNET_MASK", protocols::openvpn::defaultSubnetMask}});
|
||||
|
||||
vars.append({{"$OPENVPN_PORT", openVpnConfig->serverProtocolConfig.port}});
|
||||
vars.append({{"$OPENVPN_TRANSPORT_PROTO", openVpnConfig->serverProtocolConfig.transportProto}});
|
||||
|
||||
vars.append({{"$OPENVPN_NCP_DISABLE", openVpnConfig->serverProtocolConfig.ncpDisable ? protocols::openvpn::ncpDisableString : ""}});
|
||||
|
||||
vars.append({{"$OPENVPN_CIPHER", openVpnConfig->serverProtocolConfig.cipher}});
|
||||
vars.append({{"$OPENVPN_HASH", openVpnConfig->serverProtocolConfig.hash}});
|
||||
|
||||
vars.append({{"$OPENVPN_TLS_AUTH", openVpnConfig->serverProtocolConfig.tlsAuth ? protocols::openvpn::tlsAuthString : ""}});
|
||||
if (!openVpnConfig->serverProtocolConfig.tlsAuth) {
|
||||
vars.append({{"$OPENVPN_TA_KEY", ""}});
|
||||
}
|
||||
|
||||
vars.append({{"$OPENVPN_ADDITIONAL_CLIENT_CONFIG", openVpnConfig->serverProtocolConfig.additionalClientConfig}});
|
||||
vars.append({{"$OPENVPN_ADDITIONAL_SERVER_CONFIG", openVpnConfig->serverProtocolConfig.additionalServerConfig}});
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials,
|
||||
DockerContainer container, ErrorCode &errorCode)
|
||||
{
|
||||
@@ -71,15 +108,21 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||
QSharedPointer<ProtocolConfig> OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
|
||||
{
|
||||
auto openVpnConfig = qSharedPointerCast<OpenVpnProtocolConfig>(protocolConfig);
|
||||
if (!openVpnConfig) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
|
||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
generateProtocolVars(credentials, container, protocolConfig));
|
||||
|
||||
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
config.replace("$OPENVPN_CA_CERT", connData.caCert);
|
||||
@@ -97,86 +140,100 @@ QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials,
|
||||
config.replace("block-outside-dns", "");
|
||||
#endif
|
||||
|
||||
QJsonObject jConfig;
|
||||
jConfig[config_key::config] = config;
|
||||
openVpnConfig->clientProtocolConfig.isEmpty = false;
|
||||
openVpnConfig->clientProtocolConfig.clientId = connData.clientId;
|
||||
openVpnConfig->clientProtocolConfig.nativeConfig = config;
|
||||
|
||||
jConfig[config_key::clientId] = connData.clientId;
|
||||
|
||||
return QJsonDocument(jConfig).toJson();
|
||||
return openVpnConfig;
|
||||
}
|
||||
|
||||
QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
void OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QSharedPointer<ProtocolConfig> &protocolConfig)
|
||||
{
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
processConfigWithDnsSettings(dns, protocolConfig);
|
||||
|
||||
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||
QString config = json[config_key::config].toString();
|
||||
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
|
||||
std::visit([this, &dns, isApiConfig](const auto &config) -> void {
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(config)>, QSharedPointer<OpenVpnProtocolConfig>>) {
|
||||
QString &nativeConfig = config->clientProtocolConfig.nativeConfig;
|
||||
|
||||
if (!isApiConfig) {
|
||||
QRegularExpression regex("redirect-gateway.*");
|
||||
config.replace(regex, "");
|
||||
|
||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||
if (!isApiConfig) {
|
||||
QRegularExpression regex("redirect-gateway.*");
|
||||
nativeConfig.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);
|
||||
nativeConfig.replace(dnsRegex, "");
|
||||
}
|
||||
|
||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
||||
nativeConfig.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||
nativeConfig.append("block-ipv6\n");
|
||||
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||
|
||||
// no redirect-gateway
|
||||
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||
#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");
|
||||
nativeConfig.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||
// Prevent ipv6 leak
|
||||
#endif
|
||||
config.append("block-ipv6\n");
|
||||
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||
nativeConfig.append("block-ipv6\n");
|
||||
}
|
||||
|
||||
// no redirect-gateway
|
||||
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||
// Prevent ipv6 leak
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
#endif
|
||||
config.append("block-ipv6\n");
|
||||
}
|
||||
}
|
||||
QStringList routeList = m_settings->vpnRoutes();
|
||||
|
||||
if (!routeList.isEmpty()) {
|
||||
for (auto route : routeList) {
|
||||
nativeConfig.append("\nroute " + route + " 255.255.255.255 vpn_gateway\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MZ_WINDOWS
|
||||
config.replace("block-outside-dns", "");
|
||||
nativeConfig.replace("block-outside-dns", "");
|
||||
#endif
|
||||
|
||||
#if (defined(MZ_MACOS) || defined(MZ_LINUX))
|
||||
QString dnsConf = QString("\nscript-security 2\n"
|
||||
"up %1/update-resolv-conf.sh\n"
|
||||
"down %1/update-resolv-conf.sh\n")
|
||||
.arg(qApp->applicationDirPath());
|
||||
QString dnsConf = QString("\nscript-security 2\n"
|
||||
"up %1/update-resolv-conf.sh\n"
|
||||
"down %1/update-resolv-conf.sh\n")
|
||||
.arg(qApp->applicationDirPath());
|
||||
|
||||
config.append(dnsConf);
|
||||
nativeConfig.append(dnsConf);
|
||||
#endif
|
||||
|
||||
json[config_key::config] = config;
|
||||
return QJsonDocument(json).toJson();
|
||||
}
|
||||
}, variant);
|
||||
}
|
||||
|
||||
QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
void OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QSharedPointer<ProtocolConfig> &protocolConfig)
|
||||
{
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
processConfigWithDnsSettings(dns, protocolConfig);
|
||||
|
||||
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||
QString config = json[config_key::config].toString();
|
||||
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
|
||||
std::visit([&dns](const auto &config) -> void {
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(config)>, QSharedPointer<OpenVpnProtocolConfig>>) {
|
||||
QString &nativeConfig = config->clientProtocolConfig.nativeConfig;
|
||||
|
||||
QRegularExpression regex("redirect-gateway.*");
|
||||
config.replace(regex, "");
|
||||
QRegularExpression regex("redirect-gateway.*");
|
||||
nativeConfig.replace(regex, "");
|
||||
|
||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||
// 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);
|
||||
nativeConfig.replace(dnsRegex, "");
|
||||
}
|
||||
|
||||
// Prevent ipv6 leak
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
config.append("block-ipv6\n");
|
||||
nativeConfig.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||
|
||||
// remove block-outside-dns for all exported configs
|
||||
config.replace("block-outside-dns", "");
|
||||
// Prevent ipv6 leak
|
||||
nativeConfig.append("block-ipv6\n");
|
||||
|
||||
json[config_key::config] = config;
|
||||
return QJsonDocument(json).toJson();
|
||||
// remove block-outside-dns for all exported configs
|
||||
nativeConfig.replace("block-outside-dns", "");
|
||||
}
|
||||
}, variant);
|
||||
}
|
||||
|
||||
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId)
|
||||
@@ -193,7 +250,7 @@ ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerC
|
||||
.arg(clientId);
|
||||
|
||||
QStringList scriptList { script_import, script_sign };
|
||||
QString script = m_serverController->replaceVars(scriptList.join("\n"), m_serverController->genVarsForScript(credentials, container));
|
||||
QString script = m_serverController->replaceVars(scriptList.join("\n"), generateProtocolVars(credentials, container));
|
||||
|
||||
return m_serverController->runScript(credentials, script);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "configurator_base.h"
|
||||
#include "core/defs.h"
|
||||
#include "core/models/protocols/openvpnProtocolConfig.h"
|
||||
|
||||
class OpenVpnConfigurator : public ConfiguratorBase
|
||||
{
|
||||
@@ -24,13 +25,16 @@ public:
|
||||
QString host; // host ip
|
||||
};
|
||||
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
|
||||
|
||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString);
|
||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString);
|
||||
void processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QSharedPointer<ProtocolConfig> &protocolConfig) override;
|
||||
void processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QSharedPointer<ProtocolConfig> &protocolConfig) override;
|
||||
|
||||
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
|
||||
|
||||
static ConnectionData createCertRequest();
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
#include "core/models/protocols/shadowsocksProtocolConfig.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
QObject *parent)
|
||||
@@ -13,28 +15,59 @@ ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> setti
|
||||
{
|
||||
}
|
||||
|
||||
QString ShadowSocksConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||
ConfiguratorBase::Vars ShadowSocksConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const
|
||||
{
|
||||
Vars vars = generateCommonVars(credentials, container);
|
||||
|
||||
auto shadowsocksConfig = qSharedPointerCast<ShadowsocksProtocolConfig>(protocolConfig);
|
||||
if (!shadowsocksConfig) {
|
||||
return vars;
|
||||
}
|
||||
|
||||
vars.append({{"$SHADOWSOCKS_SERVER_PORT", shadowsocksConfig->serverProtocolConfig.port}});
|
||||
vars.append({{"$SHADOWSOCKS_LOCAL_PORT", protocols::shadowsocks::defaultLocalProxyPort}});
|
||||
vars.append({{"$SHADOWSOCKS_CIPHER", shadowsocksConfig->serverProtocolConfig.cipher}});
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
QSharedPointer<ProtocolConfig> ShadowSocksConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
|
||||
{
|
||||
auto shadowsocksConfig = qSharedPointerCast<ShadowsocksProtocolConfig>(protocolConfig);
|
||||
if (!shadowsocksConfig) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString ssKey =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::shadowsocks::ssKeyPath, errorCode);
|
||||
ssKey.replace("\n", "");
|
||||
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shadowsocksConfig->clientProtocolConfig.server = credentials.hostName;
|
||||
shadowsocksConfig->clientProtocolConfig.serverPort = shadowsocksConfig->serverProtocolConfig.port;
|
||||
shadowsocksConfig->clientProtocolConfig.localPort = protocols::shadowsocks::defaultLocalProxyPort;
|
||||
shadowsocksConfig->clientProtocolConfig.password = ssKey;
|
||||
shadowsocksConfig->clientProtocolConfig.timeout = 60;
|
||||
shadowsocksConfig->clientProtocolConfig.method = shadowsocksConfig->serverProtocolConfig.cipher;
|
||||
|
||||
QJsonObject config;
|
||||
config.insert("server", credentials.hostName);
|
||||
config.insert("server_port", "$SHADOWSOCKS_SERVER_PORT");
|
||||
config.insert("local_port", "$SHADOWSOCKS_LOCAL_PORT");
|
||||
config.insert("password", ssKey);
|
||||
config.insert("timeout", 60);
|
||||
config.insert("method", "$SHADOWSOCKS_CIPHER");
|
||||
config.insert("server", shadowsocksConfig->clientProtocolConfig.server);
|
||||
config.insert("server_port", shadowsocksConfig->clientProtocolConfig.serverPort);
|
||||
config.insert("local_port", shadowsocksConfig->clientProtocolConfig.localPort);
|
||||
config.insert("password", shadowsocksConfig->clientProtocolConfig.password);
|
||||
config.insert("timeout", shadowsocksConfig->clientProtocolConfig.timeout);
|
||||
config.insert("method", shadowsocksConfig->clientProtocolConfig.method);
|
||||
|
||||
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
|
||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
QString textCfg = QJsonDocument(config).toJson();
|
||||
|
||||
// qDebug().noquote() << textCfg;
|
||||
return textCfg;
|
||||
shadowsocksConfig->clientProtocolConfig.isEmpty = false;
|
||||
shadowsocksConfig->clientProtocolConfig.nativeConfig = textCfg;
|
||||
|
||||
return shadowsocksConfig;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "configurator_base.h"
|
||||
#include "core/models/protocols/shadowsocksProtocolConfig.h"
|
||||
#include "core/defs.h"
|
||||
|
||||
class ShadowSocksConfigurator : public ConfiguratorBase
|
||||
@@ -12,8 +13,11 @@ class ShadowSocksConfigurator : public ConfiguratorBase
|
||||
public:
|
||||
ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
|
||||
|
||||
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
|
||||
};
|
||||
|
||||
#endif // SHADOWSOCKS_CONFIGURATOR_H
|
||||
|
||||
@@ -3,35 +3,86 @@
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTemporaryFile>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "core/models/protocols/wireguardProtocolConfig.h"
|
||||
#include "core/models/protocols/awgProtocolConfig.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
|
||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
bool isAwg, QObject *parent)
|
||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
|
||||
const QSharedPointer<ServerController> &serverController, bool isAwg,
|
||||
QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
||||
{
|
||||
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
||||
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
||||
m_serverConfigPath =
|
||||
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||
m_serverPublicKeyPath =
|
||||
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_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
|
||||
m_defaultPort = m_isAwg ? protocols::wireguard::defaultPort : protocols::awg::defaultPort;
|
||||
}
|
||||
|
||||
ConfiguratorBase::Vars WireguardConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const
|
||||
{
|
||||
Vars vars = generateCommonVars(credentials, container);
|
||||
|
||||
if (m_isAwg) {
|
||||
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig);
|
||||
if (!awgConfig) {
|
||||
return vars;
|
||||
}
|
||||
|
||||
vars.append({{"$AWG_SUBNET_IP", awgConfig->serverProtocolConfig.subnetAddress}});
|
||||
vars.append({{"$AWG_SERVER_PORT", awgConfig->serverProtocolConfig.port}});
|
||||
|
||||
const auto &awgData = awgConfig->serverProtocolConfig.awgData;
|
||||
vars.append({{"$JUNK_PACKET_COUNT", awgData.junkPacketCount}});
|
||||
vars.append({{"$JUNK_PACKET_MIN_SIZE", awgData.junkPacketMinSize}});
|
||||
vars.append({{"$JUNK_PACKET_MAX_SIZE", awgData.junkPacketMaxSize}});
|
||||
vars.append({{"$INIT_PACKET_JUNK_SIZE", awgData.initPacketJunkSize}});
|
||||
vars.append({{"$RESPONSE_PACKET_JUNK_SIZE", awgData.responsePacketJunkSize}});
|
||||
vars.append({{"$INIT_PACKET_MAGIC_HEADER", awgData.initPacketMagicHeader}});
|
||||
vars.append({{"$RESPONSE_PACKET_MAGIC_HEADER", awgData.responsePacketMagicHeader}});
|
||||
vars.append({{"$UNDERLOAD_PACKET_MAGIC_HEADER", awgData.underloadPacketMagicHeader}});
|
||||
vars.append({{"$TRANSPORT_PACKET_MAGIC_HEADER", awgData.transportPacketMagicHeader}});
|
||||
vars.append({{"$COOKIE_REPLY_PACKET_JUNK_SIZE", awgData.cookieReplyPacketJunkSize}});
|
||||
vars.append({{"$TRANSPORT_PACKET_JUNK_SIZE", awgData.transportPacketJunkSize}});
|
||||
} else {
|
||||
auto wgConfig = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig);
|
||||
if (!wgConfig) {
|
||||
return vars;
|
||||
}
|
||||
|
||||
vars.append({{"$WIREGUARD_SUBNET_IP", wgConfig->serverProtocolConfig.subnetAddress}});
|
||||
vars.append({{"$WIREGUARD_SUBNET_CIDR", protocols::wireguard::defaultSubnetCidr}});
|
||||
vars.append({{"$WIREGUARD_SUBNET_MASK", protocols::wireguard::defaultSubnetMask}});
|
||||
vars.append({{"$WIREGUARD_SERVER_PORT", wgConfig->serverProtocolConfig.port}});
|
||||
}
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
||||
{
|
||||
// TODO review
|
||||
@@ -63,78 +114,94 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
||||
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,
|
||||
DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig,
|
||||
ErrorCode &errorCode)
|
||||
{
|
||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||
connData.host = credentials.hostName;
|
||||
connData.port = containerConfig.value(m_protocolName).toObject().value(config_key::port).toString(m_defaultPort);
|
||||
|
||||
// Extract port from appropriate protocol config
|
||||
if (m_isAwg) {
|
||||
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig);
|
||||
connData.port = awgConfig ? awgConfig->serverProtocolConfig.port : m_defaultPort;
|
||||
} else {
|
||||
auto wgConfig = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig);
|
||||
connData.port = wgConfig ? wgConfig->serverProtocolConfig.port : m_defaultPort;
|
||||
}
|
||||
|
||||
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return connData;
|
||||
}
|
||||
|
||||
// Get list of already created clients (only IP addresses)
|
||||
QString nextIpNumber;
|
||||
{
|
||||
QString script = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
QString getIpsScript = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
auto ips = getIpsFromConf(stdOut);
|
||||
|
||||
stdOut.replace("AllowedIPs = ", "");
|
||||
stdOut.replace("/32", "");
|
||||
QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts);
|
||||
|
||||
// remove extra IPs from each line for case when user manually edited the wg0.conf
|
||||
// and added there more IPs for route his itnernal networks, like:
|
||||
// ...
|
||||
// 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 {
|
||||
int next = ips.last().split(".").last().toInt() + 1;
|
||||
if (next > 254) {
|
||||
errorCode = ErrorCode::AddressPoolError;
|
||||
return connData;
|
||||
QHostAddress nextIp = [&] {
|
||||
QHostAddress result;
|
||||
QHostAddress lastIp;
|
||||
if (ips.empty()) {
|
||||
// Get subnet from protocol config
|
||||
QString subnetAddress;
|
||||
if (m_isAwg) {
|
||||
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig);
|
||||
subnetAddress = awgConfig ? awgConfig->serverProtocolConfig.subnetAddress : protocols::wireguard::defaultSubnetAddress;
|
||||
} else {
|
||||
auto wgConfig = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig);
|
||||
subnetAddress = wgConfig ? wgConfig->serverProtocolConfig.subnetAddress : protocols::wireguard::defaultSubnetAddress;
|
||||
}
|
||||
nextIpNumber = QString::number(next);
|
||||
lastIp.setAddress(subnetAddress);
|
||||
} else {
|
||||
lastIp = ips.last();
|
||||
}
|
||||
}
|
||||
|
||||
QString subnetIp = containerConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
{
|
||||
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
|
||||
if (l.isEmpty()) {
|
||||
errorCode = ErrorCode::AddressPoolError;
|
||||
return connData;
|
||||
quint8 lastOctet = static_cast<quint8>(lastIp.toIPv4Address());
|
||||
switch (lastOctet) {
|
||||
case 254: result.setAddress(lastIp.toIPv4Address() + 3); break;
|
||||
case 255: result.setAddress(lastIp.toIPv4Address() + 2); break;
|
||||
default: result.setAddress(lastIp.toIPv4Address() + 1); break;
|
||||
}
|
||||
l.removeLast();
|
||||
l.append(nextIpNumber);
|
||||
|
||||
connData.clientIP = l.join(".");
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
|
||||
connData.clientIP = nextIp.toString();
|
||||
|
||||
// Get keys
|
||||
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||
connData.serverPubKey =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||
connData.serverPubKey.replace("\n", "");
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
@@ -161,24 +228,44 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'").arg(m_serverConfigPath);
|
||||
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'")
|
||||
.arg(m_serverConfigPath);
|
||||
|
||||
errorCode = m_serverController->runScript(
|
||||
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
||||
credentials,
|
||||
m_serverController->replaceVars(script, generateProtocolVars(credentials, container)));
|
||||
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||
QSharedPointer<ProtocolConfig> WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
|
||||
{
|
||||
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
||||
QString config =
|
||||
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
QSharedPointer<ProtocolConfig> result;
|
||||
|
||||
if (m_isAwg) {
|
||||
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig);
|
||||
if (!awgConfig) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
result = awgConfig;
|
||||
} else {
|
||||
auto wgConfig = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig);
|
||||
if (!wgConfig) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
result = wgConfig;
|
||||
}
|
||||
|
||||
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
||||
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
||||
QString config = m_serverController->replaceVars(
|
||||
scriptData, generateProtocolVars(credentials, container, protocolConfig));
|
||||
|
||||
ConnectionData connData = prepareWireguardConfig(credentials, container, protocolConfig, errorCode);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return "";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", connData.clientPrivKey);
|
||||
@@ -186,40 +273,37 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
||||
config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey);
|
||||
config.replace("$WIREGUARD_PSK", connData.pskKey);
|
||||
|
||||
const QJsonObject &wireguarConfig = containerConfig.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
|
||||
QJsonObject jConfig;
|
||||
jConfig[config_key::config] = config;
|
||||
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(result);
|
||||
std::visit([&connData, &config](const auto &protocolConfig) -> void {
|
||||
using ConfigType = std::decay_t<decltype(*protocolConfig)>;
|
||||
if constexpr (std::is_same_v<ConfigType, AwgProtocolConfig> || std::is_same_v<ConfigType, WireGuardProtocolConfig>) {
|
||||
protocolConfig->clientProtocolConfig.isEmpty = false;
|
||||
protocolConfig->clientProtocolConfig.clientId = connData.clientPubKey;
|
||||
protocolConfig->clientProtocolConfig.hostname = connData.host;
|
||||
protocolConfig->clientProtocolConfig.port = connData.port.toInt();
|
||||
protocolConfig->clientProtocolConfig.wireGuardData.clientPrivateKey = connData.clientPrivKey;
|
||||
protocolConfig->clientProtocolConfig.wireGuardData.clientIp = connData.clientIP;
|
||||
protocolConfig->clientProtocolConfig.wireGuardData.clientPublicKey = connData.clientPubKey;
|
||||
protocolConfig->clientProtocolConfig.wireGuardData.pskKey = connData.pskKey;
|
||||
protocolConfig->clientProtocolConfig.wireGuardData.serverPubKey = connData.serverPubKey;
|
||||
protocolConfig->clientProtocolConfig.wireGuardData.mtu = protocolConfig->serverProtocolConfig.mtu;
|
||||
protocolConfig->clientProtocolConfig.wireGuardData.persistentKeepAlive = "25";
|
||||
protocolConfig->clientProtocolConfig.wireGuardData.allowedIps = QStringList{"0.0.0.0/0", "::/0"};
|
||||
protocolConfig->clientProtocolConfig.nativeConfig = config;
|
||||
}
|
||||
}, variant);
|
||||
|
||||
jConfig[config_key::hostName] = connData.host;
|
||||
jConfig[config_key::port] = connData.port.toInt();
|
||||
jConfig[config_key::client_priv_key] = connData.clientPrivKey;
|
||||
jConfig[config_key::client_ip] = connData.clientIP;
|
||||
jConfig[config_key::client_pub_key] = connData.clientPubKey;
|
||||
jConfig[config_key::psk_key] = connData.pskKey;
|
||||
jConfig[config_key::server_pub_key] = connData.serverPubKey;
|
||||
jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu);
|
||||
|
||||
jConfig[config_key::persistent_keep_alive] = "25";
|
||||
QJsonArray allowedIps { "0.0.0.0/0", "::/0" };
|
||||
jConfig[config_key::allowed_ips] = allowedIps;
|
||||
|
||||
jConfig[config_key::clientId] = connData.clientPubKey;
|
||||
|
||||
return QJsonDocument(jConfig).toJson();
|
||||
return result;
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
void WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
|
||||
const bool isApiConfig, QSharedPointer<ProtocolConfig> &protocolConfig)
|
||||
{
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
|
||||
return protocolConfigString;
|
||||
processConfigWithDnsSettings(dns, protocolConfig);
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
void WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
|
||||
const bool isApiConfig, QSharedPointer<ProtocolConfig> &protocolConfig)
|
||||
{
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
|
||||
return protocolConfigString;
|
||||
processConfigWithDnsSettings(dns, protocolConfig);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#ifndef WIREGUARD_CONFIGURATOR_H
|
||||
#define WIREGUARD_CONFIGURATOR_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QObject>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include "configurator_base.h"
|
||||
#include "core/models/protocols/wireguardProtocolConfig.h"
|
||||
#include "core/models/protocols/awgProtocolConfig.h"
|
||||
#include "core/defs.h"
|
||||
#include "core/scripts_registry.h"
|
||||
|
||||
@@ -12,8 +15,8 @@ class WireguardConfigurator : public ConfiguratorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg,
|
||||
QObject *parent = nullptr);
|
||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
bool isAwg, QObject *parent = nullptr);
|
||||
|
||||
struct ConnectionData
|
||||
{
|
||||
@@ -26,17 +29,23 @@ public:
|
||||
QString port;
|
||||
};
|
||||
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode &errorCode);
|
||||
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
|
||||
|
||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
||||
void processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QSharedPointer<ProtocolConfig> &protocolConfig) override;
|
||||
void processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QSharedPointer<ProtocolConfig> &protocolConfig) override;
|
||||
|
||||
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
|
||||
|
||||
static ConnectionData genClientKeys();
|
||||
|
||||
private:
|
||||
QList<QHostAddress> getIpsFromConf(const QString &input);
|
||||
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode);
|
||||
|
||||
bool m_isAwg;
|
||||
QString m_serverConfigPath;
|
||||
|
||||
@@ -3,40 +3,198 @@
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QUuid>
|
||||
#include "logger.h"
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("XrayConfigurator");
|
||||
}
|
||||
|
||||
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode &errorCode)
|
||||
ConfiguratorBase::Vars XrayConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const
|
||||
{
|
||||
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
|
||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
Vars vars = generateCommonVars(credentials, container);
|
||||
|
||||
QString xrayPublicKey =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
|
||||
xrayPublicKey.replace("\n", "");
|
||||
auto xrayConfig = qSharedPointerCast<XrayProtocolConfig>(protocolConfig);
|
||||
if (!xrayConfig) {
|
||||
return vars;
|
||||
}
|
||||
|
||||
QString xrayUuid = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, errorCode);
|
||||
xrayUuid.replace("\n", "");
|
||||
vars.append({{"$XRAY_SITE_NAME", xrayConfig->serverProtocolConfig.site}});
|
||||
vars.append({{"$XRAY_SERVER_PORT", xrayConfig->serverProtocolConfig.port}});
|
||||
|
||||
QString xrayShortId =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
|
||||
xrayShortId.replace("\n", "");
|
||||
return vars;
|
||||
}
|
||||
|
||||
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, 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 "";
|
||||
}
|
||||
|
||||
config.replace("$XRAY_CLIENT_ID", xrayUuid);
|
||||
// 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();
|
||||
|
||||
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, generateProtocolVars(credentials, container))
|
||||
);
|
||||
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to restart container";
|
||||
return "";
|
||||
}
|
||||
|
||||
return clientId;
|
||||
}
|
||||
|
||||
QSharedPointer<ProtocolConfig> XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
|
||||
{
|
||||
auto xrayConfig = qSharedPointerCast<XrayProtocolConfig>(protocolConfig);
|
||||
if (!xrayConfig) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get client ID from prepareServerConfig
|
||||
QString xrayClientId = prepareServerConfig(credentials, container, protocolConfig, errorCode);
|
||||
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
|
||||
logger.error() << "Failed to prepare server config";
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
|
||||
generateProtocolVars(credentials, container, protocolConfig));
|
||||
|
||||
if (config.isEmpty()) {
|
||||
logger.error() << "Failed to get config template";
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString xrayPublicKey =
|
||||
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 nullptr;
|
||||
}
|
||||
xrayPublicKey.replace("\n", "");
|
||||
|
||||
QString xrayShortId =
|
||||
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 nullptr;
|
||||
}
|
||||
xrayShortId.replace("\n", "");
|
||||
|
||||
// Validate all required variables are present
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
config.replace("$XRAY_CLIENT_ID", xrayClientId);
|
||||
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
|
||||
config.replace("$XRAY_SHORT_ID", xrayShortId);
|
||||
|
||||
return config;
|
||||
xrayConfig->clientProtocolConfig.isEmpty = false;
|
||||
xrayConfig->clientProtocolConfig.clientId = xrayClientId;
|
||||
xrayConfig->clientProtocolConfig.nativeConfig = config;
|
||||
|
||||
return xrayConfig;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "configurator_base.h"
|
||||
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||
#include "core/defs.h"
|
||||
|
||||
class XrayConfigurator : public ConfiguratorBase
|
||||
@@ -12,8 +13,15 @@ class XrayConfigurator : public ConfiguratorBase
|
||||
public:
|
||||
XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
||||
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode &errorCode);
|
||||
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
|
||||
|
||||
Vars generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig) const override;
|
||||
|
||||
private:
|
||||
QString prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode);
|
||||
};
|
||||
|
||||
#endif // XRAY_CONFIGURATOR_H
|
||||
|
||||
67
client/core/api/apiDefs.h
Normal file
67
client/core/api/apiDefs.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef APIDEFS_H
|
||||
#define APIDEFS_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace apiDefs
|
||||
{
|
||||
enum ConfigType {
|
||||
AmneziaFreeV2 = 0,
|
||||
AmneziaFreeV3,
|
||||
AmneziaPremiumV1,
|
||||
AmneziaPremiumV2,
|
||||
SelfHosted,
|
||||
ExternalPremium
|
||||
};
|
||||
|
||||
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
|
||||
164
client/core/api/apiUtils.cpp
Normal file
164
client/core/api/apiUtils.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
#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 amnezia::ServerConfigType::ApiV1: return true;
|
||||
case amnezia::ServerConfigType::ApiV2: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
|
||||
{
|
||||
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
|
||||
|
||||
switch (configVersion) {
|
||||
case amnezia::ServerConfigType::ApiV1: {
|
||||
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 amnezia::ServerConfigType::ApiV2: {
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
amnezia::ServerConfigType apiUtils::getConfigSource(const QJsonObject &serverConfigObject)
|
||||
{
|
||||
return static_cast<amnezia::ServerConfigType>(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)));
|
||||
}
|
||||
26
client/core/api/apiUtils.h
Normal file
26
client/core/api/apiUtils.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#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);
|
||||
amnezia::ServerConfigType getConfigSource(const QJsonObject &serverConfigObject);
|
||||
|
||||
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply);
|
||||
|
||||
QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject);
|
||||
}
|
||||
|
||||
#endif // APIUTILS_H
|
||||
684
client/core/controllers/api/apiConfigController.cpp
Normal file
684
client/core/controllers/api/apiConfigController.cpp
Normal file
@@ -0,0 +1,684 @@
|
||||
#include "apiConfigController.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QEventLoop>
|
||||
|
||||
#include "amnezia_application.h"
|
||||
#include "configurators/wireguard_configurator.h"
|
||||
#include "core/api/apiDefs.h"
|
||||
#include "core/api/apiUtils.h"
|
||||
#include "core/controllers/api/gatewayController.h"
|
||||
#include "core/qrCodeUtils.h"
|
||||
#include "core/utils/fileUtils.h"
|
||||
#include "core/models/servers/serverConfig.h"
|
||||
#include "core/models/servers/apiV2ServerConfig.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace configKey
|
||||
{
|
||||
constexpr char cloak[] = "cloak";
|
||||
constexpr char awg[] = "awg";
|
||||
constexpr char vless[] = "vless";
|
||||
|
||||
constexpr char apiEndpoint[] = "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 serviceInfo[] = "service_info";
|
||||
constexpr char serviceProtocol[] = "service_protocol";
|
||||
|
||||
constexpr char apiPayload[] = "api_payload";
|
||||
constexpr char keyPayload[] = "key_payload";
|
||||
|
||||
constexpr char apiConfig[] = "api_config";
|
||||
constexpr char authData[] = "auth_data";
|
||||
|
||||
constexpr char config[] = "config";
|
||||
}
|
||||
|
||||
struct ProtocolData
|
||||
{
|
||||
OpenVpnConfigurator::ConnectionData certRequest;
|
||||
|
||||
QString wireGuardClientPrivKey;
|
||||
QString wireGuardClientPubKey;
|
||||
|
||||
QString xrayUuid;
|
||||
};
|
||||
|
||||
struct GatewayRequestData
|
||||
{
|
||||
QString osVersion;
|
||||
QString appVersion;
|
||||
|
||||
QString installationUuid;
|
||||
|
||||
QString userCountryCode;
|
||||
QString serverCountryCode;
|
||||
QString serviceType;
|
||||
QString serviceProtocol;
|
||||
|
||||
QJsonObject authData;
|
||||
|
||||
QJsonObject toJsonObject() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
if (!osVersion.isEmpty()) {
|
||||
obj[configKey::osVersion] = osVersion;
|
||||
}
|
||||
if (!appVersion.isEmpty()) {
|
||||
obj[configKey::appVersion] = appVersion;
|
||||
}
|
||||
if (!installationUuid.isEmpty()) {
|
||||
obj[configKey::uuid] = installationUuid;
|
||||
}
|
||||
if (!userCountryCode.isEmpty()) {
|
||||
obj[configKey::userCountryCode] = userCountryCode;
|
||||
}
|
||||
if (!serverCountryCode.isEmpty()) {
|
||||
obj[configKey::serverCountryCode] = serverCountryCode;
|
||||
}
|
||||
if (!serviceType.isEmpty()) {
|
||||
obj[configKey::serviceType] = serviceType;
|
||||
}
|
||||
if (!serviceProtocol.isEmpty()) {
|
||||
obj[configKey::serviceProtocol] = serviceProtocol;
|
||||
}
|
||||
if (!authData.isEmpty()) {
|
||||
obj[configKey::authData] = authData;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
ProtocolData generateProtocolData(const QString &protocol)
|
||||
{
|
||||
ProtocolData protocolData;
|
||||
if (protocol == configKey::cloak) {
|
||||
protocolData.certRequest = OpenVpnConfigurator::createCertRequest();
|
||||
} else if (protocol == configKey::awg) {
|
||||
auto connData = WireguardConfigurator::genClientKeys();
|
||||
protocolData.wireGuardClientPubKey = connData.clientPubKey;
|
||||
protocolData.wireGuardClientPrivKey = connData.clientPrivKey;
|
||||
} else if (protocol == configKey::vless) {
|
||||
protocolData.xrayUuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||
}
|
||||
|
||||
return protocolData;
|
||||
}
|
||||
|
||||
void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload)
|
||||
{
|
||||
if (protocol == configKey::cloak) {
|
||||
apiPayload[configKey::certificate] = protocolData.certRequest.request;
|
||||
} else if (protocol == configKey::awg) {
|
||||
apiPayload[configKey::publicKey] = protocolData.wireGuardClientPubKey;
|
||||
} else if (protocol == configKey::vless) {
|
||||
apiPayload[configKey::publicKey] = protocolData.xrayUuid;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCode fillServerConfig(const QString &protocol, const ProtocolData &apiPayloadData, const QByteArray &apiResponseBody,
|
||||
QSharedPointer<ServerConfig> &serverConfigPtr)
|
||||
{
|
||||
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()) {
|
||||
qDebug() << "empty vpn key";
|
||||
return ErrorCode::ApiConfigEmptyError;
|
||||
}
|
||||
|
||||
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 newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||
auto containers = newServerConfig.value(config_key::containers).toArray();
|
||||
if (containers.isEmpty()) {
|
||||
qDebug() << "missing containers field";
|
||||
return ErrorCode::ApiConfigEmptyError;
|
||||
}
|
||||
auto container = containers.at(0).toObject();
|
||||
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
|
||||
auto serverProtocolConfig = container.value(containerName).toObject();
|
||||
auto clientProtocolConfig =
|
||||
QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object();
|
||||
|
||||
//TODO looks like this block can be removed after v1 configs EOL
|
||||
|
||||
serverProtocolConfig[config_key::junkPacketCount] = clientProtocolConfig.value(config_key::junkPacketCount);
|
||||
serverProtocolConfig[config_key::junkPacketMinSize] = clientProtocolConfig.value(config_key::junkPacketMinSize);
|
||||
serverProtocolConfig[config_key::junkPacketMaxSize] = clientProtocolConfig.value(config_key::junkPacketMaxSize);
|
||||
serverProtocolConfig[config_key::initPacketJunkSize] = clientProtocolConfig.value(config_key::initPacketJunkSize);
|
||||
serverProtocolConfig[config_key::responsePacketJunkSize] = clientProtocolConfig.value(config_key::responsePacketJunkSize);
|
||||
serverProtocolConfig[config_key::initPacketMagicHeader] = clientProtocolConfig.value(config_key::initPacketMagicHeader);
|
||||
serverProtocolConfig[config_key::responsePacketMagicHeader] = clientProtocolConfig.value(config_key::responsePacketMagicHeader);
|
||||
serverProtocolConfig[config_key::underloadPacketMagicHeader] = clientProtocolConfig.value(config_key::underloadPacketMagicHeader);
|
||||
serverProtocolConfig[config_key::transportPacketMagicHeader] = clientProtocolConfig.value(config_key::transportPacketMagicHeader);
|
||||
|
||||
serverProtocolConfig[config_key::cookieReplyPacketJunkSize] = clientProtocolConfig.value(config_key::cookieReplyPacketJunkSize);
|
||||
serverProtocolConfig[config_key::transportPacketJunkSize] = clientProtocolConfig.value(config_key::transportPacketJunkSize);
|
||||
serverProtocolConfig[config_key::specialJunk1] = clientProtocolConfig.value(config_key::specialJunk1);
|
||||
serverProtocolConfig[config_key::specialJunk2] = clientProtocolConfig.value(config_key::specialJunk2);
|
||||
serverProtocolConfig[config_key::specialJunk3] = clientProtocolConfig.value(config_key::specialJunk3);
|
||||
serverProtocolConfig[config_key::specialJunk4] = clientProtocolConfig.value(config_key::specialJunk4);
|
||||
serverProtocolConfig[config_key::specialJunk5] = clientProtocolConfig.value(config_key::specialJunk5);
|
||||
serverProtocolConfig[config_key::controlledJunk1] = clientProtocolConfig.value(config_key::controlledJunk1);
|
||||
serverProtocolConfig[config_key::controlledJunk2] = clientProtocolConfig.value(config_key::controlledJunk2);
|
||||
serverProtocolConfig[config_key::controlledJunk3] = clientProtocolConfig.value(config_key::controlledJunk3);
|
||||
serverProtocolConfig[config_key::specialHandshakeTimeout] = clientProtocolConfig.value(config_key::specialHandshakeTimeout);
|
||||
|
||||
//
|
||||
|
||||
container[containerName] = serverProtocolConfig;
|
||||
containers.replace(0, container);
|
||||
newServerConfig[config_key::containers] = containers;
|
||||
configStr = QString(QJsonDocument(newServerConfig).toJson());
|
||||
}
|
||||
|
||||
QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||
serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
|
||||
serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
|
||||
serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
|
||||
serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
|
||||
|
||||
if (newServerConfig.value(config_key::configVersion).toInt() == static_cast<int>(amnezia::ServerConfigType::ApiV2)) {
|
||||
serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
|
||||
serverConfig[config_key::description] = newServerConfig.value(config_key::description);
|
||||
serverConfig[config_key::name] = newServerConfig.value(config_key::name);
|
||||
}
|
||||
|
||||
auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
|
||||
serverConfig[config_key::defaultContainer] = defaultContainer;
|
||||
|
||||
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
|
||||
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
|
||||
auto apiConfig = QJsonObject::fromVariantMap(map);
|
||||
|
||||
if (newServerConfig.value(config_key::configVersion).toInt() == static_cast<int>(amnezia::ServerConfigType::ApiV2)) {
|
||||
apiConfig.insert(apiDefs::key::supportedProtocols,
|
||||
QJsonDocument::fromJson(apiResponseBody).object().value(apiDefs::key::supportedProtocols).toArray());
|
||||
}
|
||||
|
||||
serverConfig[configKey::apiConfig] = apiConfig;
|
||||
|
||||
serverConfigPtr = ServerConfig::createServerConfig(serverConfig);
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
}
|
||||
|
||||
ApiConfigsController::ApiConfigsController(const QSharedPointer<ServersModel> &serversModel,
|
||||
const QSharedPointer<ApiServicesModel> &apiServicesModel,
|
||||
const std::shared_ptr<Settings> &settings, QObject *parent)
|
||||
: QObject(parent), m_serversModel(serversModel), m_apiServicesModel(apiServicesModel), m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorCode ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, const QString &fileName)
|
||||
{
|
||||
if (fileName.isEmpty()) {
|
||||
return ErrorCode::PermissionsError;
|
||||
}
|
||||
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
||||
auto serverConfigObject = serverConfigPtr->toJson();
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
serverCountryCode,
|
||||
apiConfigObject.value(configKey::serviceType).toString(),
|
||||
configKey::awg, // apiConfigObject.value(configKey::serviceProtocol).toString(),
|
||||
serverConfigObject.value(configKey::authData).toObject() };
|
||||
|
||||
QString protocol = gatewayRequestData.serviceProtocol;
|
||||
ProtocolData protocolData = generateProtocolData(protocol);
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/native_config"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QJsonObject jsonConfig = QJsonDocument::fromJson(responseBody).object();
|
||||
QString nativeConfig = jsonConfig.value(configKey::config).toString();
|
||||
nativeConfig.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", protocolData.wireGuardClientPrivKey);
|
||||
|
||||
FileUtils::saveFile(fileName, nativeConfig);
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
|
||||
{
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
||||
auto serverConfigObject = serverConfigPtr->toJson();
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
serverCountryCode,
|
||||
apiConfigObject.value(configKey::serviceType).toString(),
|
||||
configKey::awg, // apiConfigObject.value(configKey::serviceProtocol).toString(),
|
||||
serverConfigObject.value(configKey::authData).toObject() };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_native_config"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||
return errorCode;
|
||||
}
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
void ApiConfigsController::prepareVpnKeyExport()
|
||||
{
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
|
||||
auto serverConfigObject = serverConfigPtr->toJson();
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
auto vpnKey = apiConfigObject.value(apiDefs::key::vpnKey).toString();
|
||||
m_vpnKey = vpnKey;
|
||||
|
||||
vpnKey.replace("vpn://", "");
|
||||
|
||||
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(vpnKey.toUtf8());
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ApiConfigsController::copyVpnKeyToClipboard()
|
||||
{
|
||||
auto clipboard = amnApp->getClipboard();
|
||||
clipboard->setText(m_vpnKey);
|
||||
}
|
||||
|
||||
ErrorCode ApiConfigsController::fillAvailableServices()
|
||||
{
|
||||
QJsonObject apiPayload;
|
||||
apiPayload[configKey::osVersion] = QSysInfo::productType();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody);
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
if (!responseBody.contains("services")) {
|
||||
errorCode = ErrorCode::ApiServicesMissingError;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QJsonObject data = QJsonDocument::fromJson(responseBody).object();
|
||||
m_apiServicesModel->updateModel(data);
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
bool ApiConfigsController::isServerFromApiAlreadyExists(const QString &userCountryCode,
|
||||
const QString &serviceType,
|
||||
const QString &serviceProtocol) const
|
||||
{
|
||||
auto servers = m_settings->serversArray();
|
||||
for (const auto &server : servers) {
|
||||
auto serverConfig = ServerConfig::createServerConfig(server.toObject());
|
||||
if (serverConfig->type != amnezia::ServerConfigType::ApiV2) continue;
|
||||
auto apiV2Config = qSharedPointerCast<ApiV2ServerConfig>(serverConfig);
|
||||
if (!apiV2Config) continue;
|
||||
|
||||
if (apiV2Config->apiConfig.userCountryCode == userCountryCode &&
|
||||
apiV2Config->apiConfig.serviceType == serviceType &&
|
||||
apiV2Config->apiConfig.serviceProtocol == serviceProtocol) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ApiConfigsController::isApiKeyExpired(int serverIndex) const
|
||||
{
|
||||
auto servers = m_settings->serversArray();
|
||||
if (serverIndex >= servers.size()) return false;
|
||||
|
||||
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
|
||||
if (serverConfig->type != amnezia::ServerConfigType::ApiV2) return false;
|
||||
|
||||
auto apiV2Config = qSharedPointerCast<ApiV2ServerConfig>(serverConfig);
|
||||
if (!apiV2Config) return false;
|
||||
|
||||
QString expiresIso = apiV2Config->apiConfig.publicKey.expiresAt;
|
||||
if (expiresIso.isEmpty()) {
|
||||
expiresIso = apiV2Config->apiConfig.subscription.end_date;
|
||||
}
|
||||
if (expiresIso.isEmpty()) return false;
|
||||
|
||||
auto expiresAt = QDateTime::fromString(expiresIso, Qt::ISODate);
|
||||
return QDateTime::currentDateTime() > expiresAt;
|
||||
}
|
||||
|
||||
void ApiConfigsController::removeApiConfig(int serverIndex)
|
||||
{
|
||||
auto servers = m_settings->serversArray();
|
||||
if (serverIndex >= servers.size()) return;
|
||||
|
||||
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
|
||||
if (serverConfig->type != amnezia::ServerConfigType::ApiV2) return;
|
||||
|
||||
auto apiV2 = qSharedPointerCast<ApiV2ServerConfig>(serverConfig);
|
||||
if (!apiV2) return;
|
||||
|
||||
apiV2->containerConfigs.clear();
|
||||
apiV2->apiConfig.publicKey.expiresAt.clear();
|
||||
apiV2->apiConfig.vpnKey.clear();
|
||||
apiV2->defaultContainer = ContainerProps::containerToString(DockerContainer::None);
|
||||
|
||||
m_serversModel->editServer(apiV2, serverIndex);
|
||||
}
|
||||
|
||||
ErrorCode ApiConfigsController::importServiceFromGateway()
|
||||
{
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getInstallationUuid(true),
|
||||
m_apiServicesModel->getCountryCode(),
|
||||
"",
|
||||
m_apiServicesModel->getSelectedServiceType(),
|
||||
m_apiServicesModel->getSelectedServiceProtocol(),
|
||||
QJsonObject() };
|
||||
|
||||
if (isServerFromApiAlreadyExists(gatewayRequestData.userCountryCode, gatewayRequestData.serviceType,
|
||||
gatewayRequestData.serviceProtocol)) {
|
||||
return ErrorCode::ApiConfigAlreadyAdded;
|
||||
}
|
||||
|
||||
ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol);
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
|
||||
|
||||
QSharedPointer<ServerConfig> serverConfigPtr;
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
errorCode = fillServerConfig(gatewayRequestData.serviceProtocol, protocolData, responseBody, serverConfigPtr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
if (serverConfigPtr->type == amnezia::ServerConfigType::ApiV2) {
|
||||
auto apiV2ServerConfig = qSharedPointerCast<ApiV2ServerConfig>(serverConfigPtr);
|
||||
apiV2ServerConfig->apiConfig.userCountryCode = m_apiServicesModel->getCountryCode();
|
||||
apiV2ServerConfig->apiConfig.serviceType = m_apiServicesModel->getSelectedServiceType();
|
||||
apiV2ServerConfig->apiConfig.serviceProtocol = m_apiServicesModel->getSelectedServiceProtocol();
|
||||
}
|
||||
m_serversModel->addServer(serverConfigPtr);
|
||||
return ErrorCode::NoError;
|
||||
} else {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCode ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
|
||||
bool reloadServiceConfig)
|
||||
{
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(serverIndex);
|
||||
auto serverConfigJson = serverConfigPtr->toJson();
|
||||
auto apiConfig = serverConfigJson.value(configKey::apiConfig).toObject();
|
||||
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfig.value(configKey::userCountryCode).toString(),
|
||||
newCountryCode,
|
||||
apiConfig.value(configKey::serviceType).toString(),
|
||||
apiConfig.value(configKey::serviceProtocol).toString(),
|
||||
serverConfigJson.value(configKey::authData).toObject() };
|
||||
|
||||
ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol);
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
|
||||
|
||||
QSharedPointer<ServerConfig> newServerConfigPtr;
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
errorCode = fillServerConfig(gatewayRequestData.serviceProtocol, protocolData, responseBody, newServerConfigPtr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
if (newServerConfigPtr->type == amnezia::ServerConfigType::ApiV2 && serverConfigPtr->type == amnezia::ServerConfigType::ApiV2) {
|
||||
auto newApiV2 = qSharedPointerCast<ApiV2ServerConfig>(newServerConfigPtr);
|
||||
auto oldApiV2 = qSharedPointerCast<ApiV2ServerConfig>(serverConfigPtr);
|
||||
newApiV2->apiConfig.userCountryCode = oldApiV2->apiConfig.userCountryCode;
|
||||
newApiV2->apiConfig.serviceType = oldApiV2->apiConfig.serviceType;
|
||||
newApiV2->apiConfig.serviceProtocol = oldApiV2->apiConfig.serviceProtocol;
|
||||
newApiV2->apiConfig.vpnKey = oldApiV2->apiConfig.vpnKey;
|
||||
newApiV2->apiConfig.authData.apiKey = gatewayRequestData.authData.value("api_key").toString();
|
||||
if (serverConfigPtr->nameOverriddenByUser) {
|
||||
newApiV2->name = oldApiV2->name;
|
||||
newApiV2->nameOverriddenByUser = true;
|
||||
}
|
||||
}
|
||||
m_serversModel->editServer(newServerConfigPtr, serverIndex);
|
||||
return ErrorCode::NoError;
|
||||
} else {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCode ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
|
||||
{
|
||||
#ifdef Q_OS_IOS
|
||||
IosController::Instance()->requestInetAccess();
|
||||
QThread::msleep(10);
|
||||
#endif
|
||||
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(serverIndex);
|
||||
auto installationUuid = m_settings->getInstallationUuid(true);
|
||||
|
||||
auto serverConfigJson2 = serverConfigPtr->toJson();
|
||||
QString serviceProtocol = serverConfigJson2.value(configKey::protocol).toString();
|
||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||
|
||||
QJsonObject apiPayload;
|
||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
apiPayload[configKey::osVersion] = QSysInfo::productType();
|
||||
apiPayload[configKey::appVersion] = QString(APP_VERSION);
|
||||
apiPayload[configKey::accessToken] = serverConfigJson2.value(configKey::accessToken).toString();
|
||||
apiPayload[configKey::apiEndpoint] = serverConfigJson2.value(configKey::apiEndpoint).toString();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody);
|
||||
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
QSharedPointer<ServerConfig> updatedConfigPtr;
|
||||
errorCode = fillServerConfig(serviceProtocol, protocolData, responseBody, updatedConfigPtr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
m_serversModel->editServer(updatedConfigPtr, serverIndex);
|
||||
return ErrorCode::NoError;
|
||||
} else {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCode ApiConfigsController::deactivateDevice()
|
||||
{
|
||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(serverIndex);
|
||||
auto serverConfigObject = serverConfigPtr->toJson();
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
if (!apiUtils::isPremiumServer(serverConfigObject)) {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_settings->getInstallationUuid(true),
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
apiConfigObject.value(configKey::serverCountryCode).toString(),
|
||||
apiConfigObject.value(configKey::serviceType).toString(),
|
||||
"",
|
||||
serverConfigObject.value(configKey::authData).toObject() };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
serverConfigPtr->containerConfigs.clear();
|
||||
m_serversModel->editServer(serverConfigPtr, serverIndex);
|
||||
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode)
|
||||
{
|
||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(serverIndex);
|
||||
auto serverConfigObject = serverConfigPtr->toJson();
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
if (!apiUtils::isPremiumServer(serverConfigObject)) {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
uuid,
|
||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||
serverCountryCode,
|
||||
apiConfigObject.value(configKey::serviceType).toString(),
|
||||
"",
|
||||
serverConfigObject.value(configKey::authData).toObject() };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
if (uuid == m_settings->getInstallationUuid(true)) {
|
||||
serverConfigPtr->containerConfigs.clear();
|
||||
m_serversModel->editServer(serverConfigPtr, serverIndex);
|
||||
}
|
||||
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
bool ApiConfigsController::isConfigValid()
|
||||
{
|
||||
int serverIndex = m_serversModel->getDefaultServerIndex();
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(serverIndex);
|
||||
QJsonObject serverConfigObject = serverConfigPtr->toJson();
|
||||
auto configSource = apiUtils::getConfigSource(serverConfigObject);
|
||||
|
||||
if (configSource == amnezia::ServerConfigType::ApiV1
|
||||
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
|
||||
removeApiConfig(serverIndex);
|
||||
return updateServiceFromTelegram(serverIndex);
|
||||
} else if (configSource == amnezia::ServerConfigType::ApiV2
|
||||
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
|
||||
return updateServiceFromGateway(serverIndex, "", "");
|
||||
} else if (configSource && isApiKeyExpired(serverIndex)) {
|
||||
qDebug() << "attempt to update api config by expires_at event";
|
||||
if (configSource == amnezia::ServerConfigType::ApiV2) {
|
||||
return updateServiceFromGateway(serverIndex, "", "");
|
||||
} else {
|
||||
removeApiConfig(serverIndex);
|
||||
return updateServiceFromTelegram(serverIndex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ApiConfigsController::setCurrentProtocol(const QString &protocolName)
|
||||
{
|
||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(serverIndex);
|
||||
auto serverConfigObject = serverConfigPtr->toJson();
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
apiConfigObject[configKey::serviceProtocol] = protocolName;
|
||||
|
||||
serverConfigObject.insert(configKey::apiConfig, apiConfigObject);
|
||||
|
||||
auto updatedPtr = ServerConfig::createServerConfig(serverConfigObject);
|
||||
m_serversModel->editServer(updatedPtr, serverIndex);
|
||||
}
|
||||
|
||||
bool ApiConfigsController::isVlessProtocol()
|
||||
{
|
||||
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
||||
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||
|
||||
if (apiConfigObject[configKey::serviceProtocol].toString() == "vless") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<QString> ApiConfigsController::getQrCodes()
|
||||
{
|
||||
return m_qrCodes;
|
||||
}
|
||||
|
||||
int ApiConfigsController::getQrCodesCount()
|
||||
{
|
||||
return m_qrCodes.size();
|
||||
}
|
||||
|
||||
QString ApiConfigsController::getVpnKey()
|
||||
{
|
||||
return m_vpnKey;
|
||||
}
|
||||
|
||||
ErrorCode ApiConfigsController::executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
return gatewayController.post(endpoint, apiPayload, responseBody);
|
||||
}
|
||||
57
client/core/controllers/api/apiConfigController.h
Normal file
57
client/core/controllers/api/apiConfigController.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef APICONFIGSCONTROLLER_H
|
||||
#define APICONFIGSCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "configurators/openvpn_configurator.h"
|
||||
#include "ui/models/api/apiServicesModel.h"
|
||||
#include "ui/models/servers_model.h"
|
||||
|
||||
class ApiConfigsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ApiConfigsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiServicesModel> &apiServicesModel,
|
||||
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
|
||||
|
||||
QList<QString> getQrCodes();
|
||||
int getQrCodesCount();
|
||||
QString getVpnKey();
|
||||
|
||||
public slots:
|
||||
ErrorCode exportNativeConfig(const QString &serverCountryCode, const QString &fileName);
|
||||
ErrorCode revokeNativeConfig(const QString &serverCountryCode);
|
||||
void prepareVpnKeyExport();
|
||||
void copyVpnKeyToClipboard();
|
||||
|
||||
ErrorCode fillAvailableServices();
|
||||
ErrorCode importServiceFromGateway();
|
||||
ErrorCode updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
|
||||
bool reloadServiceConfig = false);
|
||||
ErrorCode updateServiceFromTelegram(const int serverIndex);
|
||||
ErrorCode deactivateDevice();
|
||||
ErrorCode deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode);
|
||||
|
||||
bool isConfigValid();
|
||||
|
||||
void setCurrentProtocol(const QString &protocolName);
|
||||
bool isVlessProtocol();
|
||||
|
||||
private:
|
||||
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody);
|
||||
|
||||
bool isServerFromApiAlreadyExists(const QString &userCountryCode,
|
||||
const QString &serviceType,
|
||||
const QString &serviceProtocol) const;
|
||||
bool isApiKeyExpired(int serverIndex) const;
|
||||
void removeApiConfig(int serverIndex);
|
||||
|
||||
QList<QString> m_qrCodes;
|
||||
QString m_vpnKey;
|
||||
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
QSharedPointer<ApiServicesModel> m_apiServicesModel;
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
};
|
||||
|
||||
#endif // APICONFIGSCONTROLLER_H
|
||||
134
client/core/controllers/api/apiPremV1MigrationController.cpp
Normal file
134
client/core/controllers/api/apiPremV1MigrationController.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "apiPremV1MigrationController.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
|
||||
#include "core/api/apiDefs.h"
|
||||
#include "core/api/apiUtils.h"
|
||||
#include "core/controllers/api/gatewayController.h"
|
||||
|
||||
ApiPremV1MigrationController::ApiPremV1MigrationController(const QSharedPointer<ServersModel> &serversModel,
|
||||
const std::shared_ptr<Settings> &settings, QObject *parent)
|
||||
: QObject(parent), m_serversModel(serversModel), m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
bool ApiPremV1MigrationController::hasConfigsToMigration()
|
||||
{
|
||||
QJsonArray vpnKeys;
|
||||
|
||||
auto serversCount = m_serversModel->getServersCount();
|
||||
for (size_t i = 0; i < serversCount; i++) {
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(i);
|
||||
auto serverConfigObject = serverConfigPtr->toJson();
|
||||
|
||||
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString vpnKey = apiUtils::getPremiumV1VpnKey(serverConfigObject);
|
||||
vpnKeys.append(vpnKey);
|
||||
}
|
||||
|
||||
if (!vpnKeys.isEmpty()) {
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
QJsonObject apiPayload;
|
||||
|
||||
apiPayload["configs"] = vpnKeys;
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/is-active-subscription"), apiPayload, responseBody);
|
||||
|
||||
auto migrationsStatus = QJsonDocument::fromJson(responseBody).object();
|
||||
for (const auto &migrationStatus : migrationsStatus) {
|
||||
if (migrationStatus == "not_found") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApiPremV1MigrationController::getSubscriptionList(const QString &email)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
QJsonObject apiPayload;
|
||||
|
||||
apiPayload[apiDefs::key::email] = email;
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/subscription-list"), apiPayload, responseBody);
|
||||
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
m_email = email;
|
||||
m_subscriptionsModel = QJsonDocument::fromJson(responseBody).array();
|
||||
if (m_subscriptionsModel.isEmpty()) {
|
||||
emit noSubscriptionToMigrate();
|
||||
return;
|
||||
}
|
||||
|
||||
emit subscriptionsModelChanged();
|
||||
} else {
|
||||
emit errorOccurred(ErrorCode::ApiMigrationError);
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray ApiPremV1MigrationController::getSubscriptionModel()
|
||||
{
|
||||
return m_subscriptionsModel;
|
||||
}
|
||||
|
||||
void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex)
|
||||
{
|
||||
QEventLoop wait;
|
||||
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
QJsonObject apiPayload;
|
||||
|
||||
apiPayload[apiDefs::key::email] = m_email;
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migration-code"), apiPayload, responseBody);
|
||||
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
m_subscriptionIndex = subscriptionIndex;
|
||||
emit otpSuccessfullySent();
|
||||
} else {
|
||||
emit errorOccurred(ErrorCode::ApiMigrationError);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiPremV1MigrationController::migrate(const QString &migrationCode)
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
QJsonObject apiPayload;
|
||||
|
||||
apiPayload[apiDefs::key::email] = m_email;
|
||||
apiPayload[apiDefs::key::orderId] = m_subscriptionsModel.at(m_subscriptionIndex).toObject().value(apiDefs::key::id).toString();
|
||||
apiPayload[apiDefs::key::migrationCode] = migrationCode;
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migrate"), apiPayload, responseBody);
|
||||
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
auto responseObject = QJsonDocument::fromJson(responseBody).object();
|
||||
QString premiumV2VpnKey = responseObject.value(apiDefs::key::config).toString();
|
||||
|
||||
emit importPremiumV2VpnKey(premiumV2VpnKey);
|
||||
} else {
|
||||
emit errorOccurred(ErrorCode::ApiMigrationError);
|
||||
}
|
||||
}
|
||||
|
||||
bool ApiPremV1MigrationController::isPremV1MigrationReminderActive()
|
||||
{
|
||||
return m_settings->isPremV1MigrationReminderActive();
|
||||
}
|
||||
|
||||
void ApiPremV1MigrationController::disablePremV1MigrationReminder()
|
||||
{
|
||||
m_settings->disablePremV1MigrationReminder();
|
||||
}
|
||||
50
client/core/controllers/api/apiPremV1MigrationController.h
Normal file
50
client/core/controllers/api/apiPremV1MigrationController.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef APIPREMV1MIGRATIONCONTROLLER_H
|
||||
#define APIPREMV1MIGRATIONCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "ui/models/servers_model.h"
|
||||
|
||||
class ApiPremV1MigrationController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ApiPremV1MigrationController(const QSharedPointer<ServersModel> &serversModel, const std::shared_ptr<Settings> &settings,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
Q_PROPERTY(QJsonArray subscriptionsModel READ getSubscriptionModel NOTIFY subscriptionsModelChanged)
|
||||
|
||||
public slots:
|
||||
bool hasConfigsToMigration();
|
||||
void getSubscriptionList(const QString &email);
|
||||
QJsonArray getSubscriptionModel();
|
||||
void sendMigrationCode(const int subscriptionIndex);
|
||||
void migrate(const QString &migrationCode);
|
||||
|
||||
bool isPremV1MigrationReminderActive();
|
||||
void disablePremV1MigrationReminder();
|
||||
|
||||
signals:
|
||||
void subscriptionsModelChanged();
|
||||
|
||||
void otpSuccessfullySent();
|
||||
|
||||
void importPremiumV2VpnKey(const QString &vpnKey);
|
||||
|
||||
void errorOccurred(ErrorCode errorCode);
|
||||
|
||||
void showMigrationDrawer();
|
||||
void migrationFinished();
|
||||
|
||||
void noSubscriptionToMigrate();
|
||||
|
||||
private:
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
|
||||
QJsonArray m_subscriptionsModel;
|
||||
int m_subscriptionIndex;
|
||||
QString m_email;
|
||||
};
|
||||
|
||||
#endif // APIPREMV1MIGRATIONCONTROLLER_H
|
||||
94
client/core/controllers/api/apiSettingsController.cpp
Normal file
94
client/core/controllers/api/apiSettingsController.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "apiSettingsController.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
|
||||
#include "core/api/apiUtils.h"
|
||||
#include "core/controllers/api/gatewayController.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace configKey
|
||||
{
|
||||
constexpr char userCountryCode[] = "user_country_code";
|
||||
constexpr char serverCountryCode[] = "server_country_code";
|
||||
constexpr char serviceType[] = "service_type";
|
||||
constexpr char serviceInfo[] = "service_info";
|
||||
|
||||
constexpr char apiConfig[] = "api_config";
|
||||
constexpr char authData[] = "auth_data";
|
||||
}
|
||||
|
||||
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
||||
}
|
||||
|
||||
ApiSettingsController::ApiSettingsController(const QSharedPointer<ServersModel> &serversModel,
|
||||
const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
|
||||
const QSharedPointer<ApiCountryModel> &apiCountryModel,
|
||||
const QSharedPointer<ApiDevicesModel> &apiDevicesModel,
|
||||
const std::shared_ptr<Settings> &settings, QObject *parent)
|
||||
: QObject(parent),
|
||||
m_serversModel(serversModel),
|
||||
m_apiAccountInfoModel(apiAccountInfoModel),
|
||||
m_apiCountryModel(apiCountryModel),
|
||||
m_apiDevicesModel(apiDevicesModel),
|
||||
m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
ApiSettingsController::~ApiSettingsController()
|
||||
{
|
||||
}
|
||||
|
||||
ErrorCode ApiSettingsController::getAccountInfo(bool reload)
|
||||
{
|
||||
if (reload) {
|
||||
QEventLoop wait;
|
||||
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
}
|
||||
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
auto processedIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfigPtr = m_serversModel->getServerConfig(processedIndex);
|
||||
auto serverConfig = serverConfigPtr->toJson();
|
||||
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
|
||||
auto authData = serverConfig.value(configKey::authData).toObject();
|
||||
|
||||
QJsonObject apiPayload;
|
||||
apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString();
|
||||
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
|
||||
apiPayload[configKey::authData] = authData;
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
|
||||
QByteArray responseBody;
|
||||
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object();
|
||||
m_apiAccountInfoModel->updateModel(accountInfo, serverConfig);
|
||||
|
||||
if (reload) {
|
||||
updateApiCountryModel();
|
||||
updateApiDevicesModel();
|
||||
}
|
||||
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
void ApiSettingsController::updateApiCountryModel()
|
||||
{
|
||||
m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
|
||||
m_apiCountryModel->updateIssuedConfigsInfo(m_apiAccountInfoModel->getIssuedConfigsInfo());
|
||||
}
|
||||
|
||||
void ApiSettingsController::updateApiDevicesModel()
|
||||
{
|
||||
m_apiDevicesModel->updateModel(m_apiAccountInfoModel->getIssuedConfigsInfo());
|
||||
}
|
||||
34
client/core/controllers/api/apiSettingsController.h
Normal file
34
client/core/controllers/api/apiSettingsController.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef APISETTINGSCONTROLLER_H
|
||||
#define APISETTINGSCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "ui/models/api/apiAccountInfoModel.h"
|
||||
#include "ui/models/api/apiCountryModel.h"
|
||||
#include "ui/models/api/apiDevicesModel.h"
|
||||
#include "ui/models/servers_model.h"
|
||||
|
||||
class ApiSettingsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ApiSettingsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
|
||||
const QSharedPointer<ApiCountryModel> &apiCountryModel, const QSharedPointer<ApiDevicesModel> &apiDevicesModel,
|
||||
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
|
||||
~ApiSettingsController();
|
||||
|
||||
public slots:
|
||||
ErrorCode getAccountInfo(bool reload);
|
||||
void updateApiCountryModel();
|
||||
void updateApiDevicesModel();
|
||||
|
||||
private:
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
|
||||
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
||||
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
|
||||
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
};
|
||||
|
||||
#endif // APISETTINGSCONTROLLER_H
|
||||
364
client/core/controllers/api/gatewayController.cpp
Normal file
364
client/core/controllers/api/gatewayController.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
client/core/controllers/api/gatewayController.h
Normal file
37
client/core/controllers/api/gatewayController.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#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
|
||||
@@ -1,502 +0,0 @@
|
||||
#include "apiController.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
#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 "utilities.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 serviceInfo[] = "service_info";
|
||||
|
||||
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 char apiConfig[] = "api_config";
|
||||
constexpr char authData[] = "auth_data";
|
||||
}
|
||||
|
||||
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool 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";
|
||||
return true;
|
||||
} else if (responseBody.contains("html")) {
|
||||
qDebug() << "The response contains an html tag";
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||
auto containers = newServerConfig.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);
|
||||
newServerConfig[config_key::containers] = containers;
|
||||
configStr = QString(QJsonDocument(newServerConfig).toJson());
|
||||
}
|
||||
|
||||
QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||
serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
|
||||
serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
|
||||
serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
|
||||
serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
|
||||
|
||||
if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
|
||||
serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
|
||||
serverConfig[config_key::description] = newServerConfig.value(config_key::description);
|
||||
serverConfig[config_key::name] = newServerConfig.value(config_key::name);
|
||||
}
|
||||
|
||||
auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
|
||||
serverConfig[config_key::defaultContainer] = defaultContainer;
|
||||
|
||||
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
|
||||
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
|
||||
auto apiConfig = QJsonObject::fromVariantMap(map);
|
||||
|
||||
if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
|
||||
apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject());
|
||||
}
|
||||
|
||||
serverConfig[configKey::apiConfig] = apiConfig;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList ApiController::getProxyUrls()
|
||||
{
|
||||
QNetworkRequest request;
|
||||
request.setTransferTimeout(requestTimeoutMsecs);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
QEventLoop wait;
|
||||
QList<QSslError> sslErrors;
|
||||
QNetworkReply *reply;
|
||||
|
||||
QStringList proxyStorageUrl;
|
||||
if (m_isDevEnvironment) {
|
||||
proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
|
||||
} else {
|
||||
proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
|
||||
}
|
||||
|
||||
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
||||
|
||||
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 {
|
||||
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";
|
||||
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(requestTimeoutMsecs);
|
||||
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(requestTimeoutMsecs);
|
||||
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();
|
||||
|
||||
responseBody = reply->readAll();
|
||||
|
||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
|
||||
m_proxyUrls = getProxyUrls();
|
||||
std::random_device randomDevice;
|
||||
std::mt19937 generator(randomDevice());
|
||||
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
|
||||
for (const QString &proxyUrl : m_proxyUrls) {
|
||||
qDebug() << "Go to the next endpoint";
|
||||
request.setUrl(QString("%1v1/services").arg(proxyUrl));
|
||||
reply->deleteLater(); // delete the previous reply
|
||||
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();
|
||||
|
||||
responseBody = reply->readAll();
|
||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, responseBody, false)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, const QJsonObject &authData,
|
||||
QJsonObject &serverConfig)
|
||||
{
|
||||
#ifdef Q_OS_IOS
|
||||
IosController::Instance()->requestInetAccess();
|
||||
QThread::msleep(10);
|
||||
#endif
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setTransferTimeout(requestTimeoutMsecs);
|
||||
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;
|
||||
if (!authData.isEmpty()) {
|
||||
apiPayload[configKey::authData] = authData;
|
||||
}
|
||||
|
||||
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->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();
|
||||
|
||||
auto encryptedResponseBody = reply->readAll();
|
||||
|
||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
|
||||
m_proxyUrls = getProxyUrls();
|
||||
std::random_device randomDevice;
|
||||
std::mt19937 generator(randomDevice());
|
||||
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
|
||||
for (const QString &proxyUrl : m_proxyUrls) {
|
||||
qDebug() << "Go to the next endpoint";
|
||||
request.setUrl(QString("%1v1/config").arg(proxyUrl));
|
||||
reply->deleteLater(); // delete the previous reply
|
||||
reply = amnApp->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();
|
||||
|
||||
encryptedResponseBody = reply->readAll();
|
||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto errorCode = checkErrors(sslErrors, reply);
|
||||
reply->deleteLater();
|
||||
if (errorCode) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
try {
|
||||
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
||||
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
|
||||
} catch (...) { // todo change error handling in QSimpleCrypto?
|
||||
Utils::logException();
|
||||
qCritical() << "error when decrypting the request body";
|
||||
return ErrorCode::ApiConfigDecryptionError;
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#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, const QJsonObject &authData, 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
|
||||
172
client/core/controllers/configController.cpp
Normal file
172
client/core/controllers/configController.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "configController.h"
|
||||
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/models/servers/apiV1ServerConfig.h"
|
||||
#include "core/models/servers/apiV2ServerConfig.h"
|
||||
#include "core/models/servers/selfHostedServerConfig.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
#include "settings.h"
|
||||
#include "core/models/protocols/awgProtocolConfig.h"
|
||||
#include "core/models/protocols/wireguardProtocolConfig.h"
|
||||
#include "core/models/protocols/openvpnProtocolConfig.h"
|
||||
#include "core/models/protocols/shadowsocksProtocolConfig.h"
|
||||
#include "core/models/protocols/cloakProtocolConfig.h"
|
||||
|
||||
ConfigController::ConfigController(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QObject(parent), m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
void ConfigController::addServer(const QSharedPointer<ServerConfig> &serverConfig)
|
||||
{
|
||||
m_settings->addServer(serverConfig->toJson());
|
||||
emit serverAdded(m_settings->serversCount() - 1);
|
||||
}
|
||||
|
||||
void ConfigController::editServer(const QSharedPointer<ServerConfig> &serverConfig, int serverIndex)
|
||||
{
|
||||
updateServerInSettings(serverConfig, serverIndex);
|
||||
emit serverEdited(serverIndex);
|
||||
}
|
||||
|
||||
void ConfigController::removeServer(int serverIndex)
|
||||
{
|
||||
m_settings->removeServer(serverIndex);
|
||||
emit serverRemoved(serverIndex);
|
||||
}
|
||||
|
||||
void ConfigController::setDefaultServer(int serverIndex)
|
||||
{
|
||||
m_settings->setDefaultServer(serverIndex);
|
||||
emit defaultServerChanged(serverIndex);
|
||||
}
|
||||
|
||||
void ConfigController::setDefaultContainer(int serverIndex, int containerIndex)
|
||||
{
|
||||
auto servers = m_settings->serversArray();
|
||||
if (serverIndex >= servers.size()) return;
|
||||
|
||||
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
|
||||
auto container = static_cast<DockerContainer>(containerIndex);
|
||||
serverConfig->defaultContainer = ContainerProps::containerToString(container);
|
||||
|
||||
updateServerInSettings(serverConfig, serverIndex);
|
||||
emit defaultContainerChanged(serverIndex, container);
|
||||
}
|
||||
|
||||
bool ConfigController::isServerFromApiAlreadyExists(quint16 crc) const
|
||||
{
|
||||
auto servers = m_settings->serversArray();
|
||||
for (const auto &server : servers) {
|
||||
auto serverConfig = ServerConfig::createServerConfig(server.toObject());
|
||||
if (static_cast<quint16>(serverConfig->crc) == crc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Removed API-specific helpers; moved to ApiConfigsController
|
||||
|
||||
QStringList ConfigController::getAllInstalledServicesName(int serverIndex) const
|
||||
{
|
||||
auto servers = m_settings->serversArray();
|
||||
if (serverIndex >= servers.size()) return {};
|
||||
|
||||
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
|
||||
|
||||
QStringList serviceNames;
|
||||
for (auto it = serverConfig->containerConfigs.constBegin();
|
||||
it != serverConfig->containerConfigs.constEnd(); ++it) {
|
||||
const QString &containerName = it.key();
|
||||
serviceNames.append(containerName);
|
||||
}
|
||||
return serviceNames;
|
||||
}
|
||||
|
||||
void ConfigController::clearCachedProfile(int serverIndex, DockerContainer container)
|
||||
{
|
||||
auto servers = m_settings->serversArray();
|
||||
if (serverIndex >= servers.size()) return;
|
||||
|
||||
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
|
||||
|
||||
QString containerName = ContainerProps::containerToString(container);
|
||||
if (serverConfig->containerConfigs.contains(containerName)) {
|
||||
auto &containerConfig = serverConfig->containerConfigs[containerName];
|
||||
containerConfig.clearProfile();
|
||||
updateServerInSettings(serverConfig, serverIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigController::updateServerInSettings(const QSharedPointer<ServerConfig> &serverConfig, int serverIndex)
|
||||
{
|
||||
m_settings->editServer(serverIndex, serverConfig->toJson());
|
||||
}
|
||||
|
||||
bool ConfigController::isDefaultServerDefaultContainerHasSplitTunneling() const
|
||||
{
|
||||
int defaultServerIndex = m_settings->defaultServerIndex();
|
||||
auto servers = m_settings->serversArray();
|
||||
if (defaultServerIndex >= servers.size()) return false;
|
||||
|
||||
auto serverConfig = ServerConfig::createServerConfig(servers.at(defaultServerIndex).toObject());
|
||||
if (!serverConfig->containerConfigs.contains(serverConfig->defaultContainer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &containerConfig = serverConfig->containerConfigs[serverConfig->defaultContainer];
|
||||
return checkSplitTunnelingInContainer(containerConfig, serverConfig->defaultContainer);
|
||||
}
|
||||
|
||||
bool ConfigController::checkSplitTunnelingInContainer(const ContainerConfig &containerConfig, const QString &defaultContainer) const
|
||||
{
|
||||
const DockerContainer containerType = ContainerProps::containerFromString(defaultContainer);
|
||||
|
||||
auto isWireguardHasSplit = [](const QString &nativeConfig, const QStringList &allowedIps) -> bool {
|
||||
if (nativeConfig.contains("AllowedIPs") && !nativeConfig.contains("AllowedIPs = 0.0.0.0/0, ::/0")) {
|
||||
return true;
|
||||
}
|
||||
if (!allowedIps.isEmpty() && !allowedIps.contains("0.0.0.0/0")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (containerType == DockerContainer::Awg || containerType == DockerContainer::WireGuard) {
|
||||
const auto protocolConfig = containerConfig.protocolConfigs.value(defaultContainer);
|
||||
if (!protocolConfig) return false;
|
||||
auto variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
|
||||
return std::visit(
|
||||
[&](const auto &ptr) -> bool {
|
||||
using T = std::decay_t<decltype(ptr)>;
|
||||
if constexpr (std::is_same_v<T, QSharedPointer<AwgProtocolConfig>> || std::is_same_v<T, QSharedPointer<WireGuardProtocolConfig>>) {
|
||||
return isWireguardHasSplit(ptr->clientProtocolConfig.nativeConfig,
|
||||
ptr->clientProtocolConfig.wireGuardData.allowedIps);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
variant);
|
||||
}
|
||||
|
||||
if (containerType == DockerContainer::Cloak || containerType == DockerContainer::OpenVpn || containerType == DockerContainer::ShadowSocks) {
|
||||
const auto &protocolConfig = containerConfig.protocolConfigs.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn));
|
||||
if (!protocolConfig) return false;
|
||||
auto variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
|
||||
return std::visit(
|
||||
[&](const auto &ptr) -> bool {
|
||||
using T = std::decay_t<decltype(ptr)>;
|
||||
if constexpr (std::is_same_v<T, QSharedPointer<OpenVpnProtocolConfig>> ||
|
||||
std::is_same_v<T, QSharedPointer<ShadowsocksProtocolConfig>> ||
|
||||
std::is_same_v<T, QSharedPointer<CloakProtocolConfig>>) {
|
||||
const auto nativeConfig = ptr->clientProtocolConfig.nativeConfig;
|
||||
return (!nativeConfig.isEmpty() && !nativeConfig.contains("redirect-gateway"));
|
||||
}
|
||||
return false;
|
||||
},
|
||||
variant);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
59
client/core/controllers/configController.h
Normal file
59
client/core/controllers/configController.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef CONFIGCONTROLLER_H
|
||||
#define CONFIGCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "core/defs.h"
|
||||
#include "core/models/servers/serverConfig.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
|
||||
class Settings;
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
class ConfigController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
virtual ~ConfigController() = default;
|
||||
|
||||
// Basic server management
|
||||
virtual void addServer(const QSharedPointer<ServerConfig> &serverConfig);
|
||||
virtual void editServer(const QSharedPointer<ServerConfig> &serverConfig, int serverIndex);
|
||||
virtual void removeServer(int serverIndex);
|
||||
|
||||
// Default settings management
|
||||
void setDefaultServer(int serverIndex);
|
||||
void setDefaultContainer(int serverIndex, int containerIndex);
|
||||
|
||||
// API server utilities
|
||||
bool isServerFromApiAlreadyExists(quint16 crc) const;
|
||||
|
||||
// General utilities
|
||||
QStringList getAllInstalledServicesName(int serverIndex) const;
|
||||
void clearCachedProfile(int serverIndex, DockerContainer container);
|
||||
|
||||
// Split tunneling detection
|
||||
bool isDefaultServerDefaultContainerHasSplitTunneling() const;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
|
||||
// Protected helper methods for derived classes
|
||||
void updateServerInSettings(const QSharedPointer<ServerConfig> &serverConfig, int serverIndex);
|
||||
|
||||
bool checkSplitTunnelingInContainer(const ContainerConfig &containerConfig, const QString &defaultContainer) const;
|
||||
|
||||
signals:
|
||||
// Common server management signals
|
||||
void serverAdded(int serverIndex);
|
||||
void serverEdited(int serverIndex);
|
||||
void serverRemoved(int serverIndex);
|
||||
void defaultServerChanged(int serverIndex);
|
||||
void defaultContainerChanged(int serverIndex, DockerContainer container);
|
||||
};
|
||||
|
||||
#endif // CONFIGCONTROLLER_H
|
||||
93
client/core/controllers/connectionController.cpp
Normal file
93
client/core/controllers/connectionController.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "connectionController.h"
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "core/controllers/vpnConfigurationController.h"
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/models/containers/containerConfig.h"
|
||||
#include "settings.h"
|
||||
#include "logger.h"
|
||||
#include "utilities.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
Logger logger("ConnectionController");
|
||||
}
|
||||
|
||||
ConnectionController::ConnectionController(const QSharedPointer<VpnConnection> &vpnConnection,
|
||||
std::shared_ptr<Settings> settings,
|
||||
QObject *parent)
|
||||
: QObject(parent), m_vpnConnection(vpnConnection), m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
QFuture<QJsonObject> ConnectionController::prepareVpnConfiguration(const QSharedPointer<ServerConfig> &serverConfig,
|
||||
DockerContainer container,
|
||||
const QPair<QString, QString> &dns) const
|
||||
{
|
||||
return QtConcurrent::run([this, serverConfig, container, dns]() -> QJsonObject {
|
||||
logger.info() << "Preparing VPN configuration for container" << ContainerProps::containerToString(container);
|
||||
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
||||
|
||||
QString containerName = ContainerProps::containerToString(container);
|
||||
const ContainerConfig &containerConfig = serverConfig->containerConfigs.value(containerName);
|
||||
|
||||
auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns,
|
||||
serverConfig,
|
||||
containerConfig,
|
||||
container);
|
||||
|
||||
emit configurationPrepared(vpnConfiguration);
|
||||
return vpnConfiguration;
|
||||
});
|
||||
}
|
||||
|
||||
QFuture<ErrorCode> ConnectionController::openConnection(const int serverIndex,
|
||||
const QSharedPointer<ServerConfig> &serverConfig,
|
||||
const DockerContainer container,
|
||||
const ServerCredentials &credentials,
|
||||
const QPair<QString, QString> &dns)
|
||||
{
|
||||
return QtConcurrent::run([this, serverIndex, serverConfig, container, credentials, dns]() -> ErrorCode {
|
||||
if (!isServerSupported(container)) {
|
||||
emit connectionError(ErrorCode::NotSupportedOnThisPlatform);
|
||||
return ErrorCode::NotSupportedOnThisPlatform;
|
||||
}
|
||||
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
||||
|
||||
const QString containerName = ContainerProps::containerToString(container);
|
||||
const ContainerConfig &containerConfig = serverConfig->containerConfigs.value(containerName);
|
||||
auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container);
|
||||
emit connectionProgress(QString("Connecting to %1...").arg(ContainerProps::containerToString(container)));
|
||||
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "connectToVpn", Qt::QueuedConnection,
|
||||
Q_ARG(int, serverIndex),
|
||||
Q_ARG(ServerCredentials, credentials),
|
||||
Q_ARG(DockerContainer, container),
|
||||
Q_ARG(QJsonObject, vpnConfiguration));
|
||||
return ErrorCode::NoError;
|
||||
});
|
||||
}
|
||||
|
||||
QFuture<ErrorCode> ConnectionController::closeConnection()
|
||||
{
|
||||
return QtConcurrent::run([this]() -> ErrorCode {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "disconnectFromVpn", Qt::QueuedConnection);
|
||||
return ErrorCode::NoError;
|
||||
});
|
||||
}
|
||||
|
||||
bool ConnectionController::isServerSupported(DockerContainer container) const
|
||||
{
|
||||
return ContainerProps::isSupportedByCurrentPlatform(container);
|
||||
}
|
||||
|
||||
ErrorCode ConnectionController::getLastConnectionError() const
|
||||
{
|
||||
return m_vpnConnection ? m_vpnConnection->lastError() : ErrorCode::NoError;
|
||||
}
|
||||
57
client/core/controllers/connectionController.h
Normal file
57
client/core/controllers/connectionController.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef CONNECTIONCONTROLLER_H
|
||||
#define CONNECTIONCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QFuture>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "core/defs.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/models/servers/serverConfig.h"
|
||||
#include "protocols/vpnprotocol.h"
|
||||
#include "vpnconnection.h"
|
||||
|
||||
class Settings;
|
||||
class ServerController;
|
||||
class VpnConfigurationsController;
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
class ConnectionController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConnectionController(const QSharedPointer<VpnConnection> &vpnConnection,
|
||||
std::shared_ptr<Settings> settings,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
QFuture<QJsonObject> prepareVpnConfiguration(const QSharedPointer<ServerConfig> &serverConfig,
|
||||
DockerContainer container,
|
||||
const QPair<QString, QString> &dns) const;
|
||||
|
||||
QFuture<ErrorCode> openConnection(const int serverIndex,
|
||||
const QSharedPointer<ServerConfig> &serverConfig,
|
||||
const DockerContainer container,
|
||||
const ServerCredentials &credentials,
|
||||
const QPair<QString, QString> &dns);
|
||||
|
||||
QFuture<ErrorCode> closeConnection();
|
||||
|
||||
ErrorCode getLastConnectionError() const;
|
||||
|
||||
signals:
|
||||
void configurationPrepared(const QJsonObject &vpnConfiguration);
|
||||
void connectionEstablished();
|
||||
void connectionTerminated();
|
||||
void connectionError(ErrorCode errorCode);
|
||||
void connectionProgress(const QString &message);
|
||||
|
||||
private:
|
||||
bool isServerSupported(DockerContainer container) const;
|
||||
|
||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
};
|
||||
|
||||
#endif // CONNECTIONCONTROLLER_H
|
||||
464
client/core/controllers/coreController.cpp
Normal file
464
client/core/controllers/coreController.cpp
Normal file
@@ -0,0 +1,464 @@
|
||||
#include "coreController.h"
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QTranslator>
|
||||
|
||||
#include "core/models/clientInfo.h"
|
||||
|
||||
#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)
|
||||
{
|
||||
initCoreControllers();
|
||||
initModels();
|
||||
initUIControllers();
|
||||
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_openVpnConfigModel = QSharedPointer<OpenVpnConfigModel>::create(this);
|
||||
m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
|
||||
|
||||
m_shadowSocksConfigModel = QSharedPointer<ShadowSocksConfigModel>::create(this);
|
||||
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
|
||||
|
||||
m_cloakConfigModel = QSharedPointer<CloakConfigModel>::create(this);
|
||||
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
|
||||
|
||||
m_wireGuardConfigModel = QSharedPointer<WireGuardConfigModel>::create(this);
|
||||
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
|
||||
|
||||
m_awgConfigModel = QSharedPointer<AwgConfigModel>::create(this);
|
||||
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
|
||||
|
||||
m_xrayConfigModel = QSharedPointer<XrayConfigModel>::create(this);
|
||||
m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
m_ikev2ConfigModel = QSharedPointer<Ikev2ConfigModel>::create(this);
|
||||
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
|
||||
#endif
|
||||
|
||||
m_sftpConfigModel = QSharedPointer<SftpConfigModel>::create(this);
|
||||
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
|
||||
|
||||
m_socks5ConfigModel = QSharedPointer<Socks5ProxyConfigModel>::create(this);
|
||||
m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
|
||||
|
||||
m_protocolsModel.reset(new ProtocolsModel(m_openVpnConfigModel, m_shadowSocksConfigModel, m_cloakConfigModel, m_wireGuardConfigModel,
|
||||
m_awgConfigModel, m_xrayConfigModel,
|
||||
#ifdef Q_OS_WINDOWS
|
||||
m_ikev2ConfigModel,
|
||||
#endif
|
||||
m_sftpConfigModel, m_socks5ConfigModel, this));
|
||||
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
|
||||
|
||||
auto clientManagementController = QSharedPointer<ClientManagementController>::create(m_settings, this);
|
||||
m_clientManagementModel.reset(new ClientManagementModel(clientManagementController, this));
|
||||
|
||||
m_clientManagementUIController.reset(new ClientManagementUIController(clientManagementController, this));
|
||||
m_engine->rootContext()->setContextProperty("ClientManagementUIController", m_clientManagementUIController.get());
|
||||
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());
|
||||
|
||||
m_sitesModel.reset(new SitesModel(m_splitTunnelingController, this));
|
||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
||||
|
||||
m_allowedDnsModel.reset(new AllowedDnsModel(m_dnsController, this));
|
||||
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
|
||||
|
||||
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_splitTunnelingController, this));
|
||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
||||
|
||||
m_languageModel.reset(new LanguageModel(m_settingsController, this));
|
||||
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
|
||||
}
|
||||
|
||||
void CoreController::initCoreControllers()
|
||||
{
|
||||
m_settingsController = QSharedPointer<SettingsController>::create(m_settings, this);
|
||||
m_dnsController = QSharedPointer<DnsController>::create(m_settings, this);
|
||||
m_splitTunnelingController = QSharedPointer<SplitTunnelingController>::create(m_settings, m_vpnConnection, this);
|
||||
m_exportController = QSharedPointer<ExportController>::create(m_settings, this);
|
||||
m_installController = QSharedPointer<InstallController>::create(m_settings, this);
|
||||
}
|
||||
|
||||
void CoreController::initUIControllers()
|
||||
{
|
||||
auto coreConnectionController = QSharedPointer<ConnectionController>::create(m_vpnConnection, m_settings, this);
|
||||
m_connectionController.reset(
|
||||
new ConnectionUIController(m_serversModel, m_containersModel, m_clientManagementModel, coreConnectionController));
|
||||
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
|
||||
|
||||
m_pageController.reset(new PageController(m_serversModel, m_settingsController));
|
||||
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
|
||||
|
||||
m_focusController.reset(new FocusController(m_engine, this));
|
||||
m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get());
|
||||
|
||||
auto clientManagementController = m_clientManagementUIController->getClientManagementController();
|
||||
m_exportUIController.reset(new ExportUIController(m_serversModel, m_containersModel, m_clientManagementModel, m_exportController, clientManagementController));
|
||||
m_engine->rootContext()->setContextProperty("ExportController", m_exportUIController.get());
|
||||
|
||||
m_installUIController.reset(new InstallUIController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_installController, m_apiConfigsCoreController, clientManagementController));
|
||||
m_engine->rootContext()->setContextProperty("InstallController", m_installUIController.get());
|
||||
|
||||
connect(m_installUIController.get(), &InstallUIController::currentContainerUpdated, m_connectionController.get(),
|
||||
&ConnectionUIController::onCurrentContainerUpdated);
|
||||
|
||||
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settingsController));
|
||||
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
||||
|
||||
m_settingsUIController.reset(
|
||||
new SettingsUIController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settingsController));
|
||||
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsUIController.get());
|
||||
|
||||
m_siteSplitUIController.reset(new SiteSplitUIController(m_splitTunnelingController, m_sitesModel));
|
||||
m_engine->rootContext()->setContextProperty("SitesController", m_siteSplitUIController.get());
|
||||
|
||||
m_allowedDnsUIController.reset(new AllowedDnsUIController(m_dnsController, m_allowedDnsModel));
|
||||
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsUIController.get());
|
||||
|
||||
m_appSplitUIController.reset(new AppSplitUIController(m_splitTunnelingController, m_appSplitTunnelingModel));
|
||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitUIController.get());
|
||||
|
||||
m_systemController.reset(new SystemController());
|
||||
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
||||
|
||||
m_apiSettingsCoreController = QSharedPointer<ApiSettingsController>::create(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_settings);
|
||||
m_apiConfigsCoreController = QSharedPointer<ApiConfigsController>::create(m_serversModel, m_apiServicesModel, m_settings);
|
||||
m_apiPremV1MigrationCoreController = QSharedPointer<ApiPremV1MigrationController>::create(m_serversModel, m_settings, this);
|
||||
|
||||
m_apiSettingsUIController.reset(new ApiSettingsUIController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_apiSettingsCoreController));
|
||||
m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsUIController.get());
|
||||
|
||||
m_apiConfigUIController.reset(new ApiConfigUIController(m_serversModel, m_apiServicesModel, m_apiConfigsCoreController));
|
||||
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigUIController.get());
|
||||
|
||||
m_apiPremV1MigrationUIController.reset(new ApiPremV1MigrationUIController(m_serversModel, m_apiPremV1MigrationCoreController));
|
||||
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationUIController.get());
|
||||
|
||||
setupControllerSignalConnections();
|
||||
}
|
||||
|
||||
void CoreController::setupControllerSignalConnections()
|
||||
{
|
||||
auto clientManagementController = m_clientManagementUIController->getClientManagementController();
|
||||
|
||||
connect(m_exportController.data(), &ExportController::clientAppendRequested,
|
||||
clientManagementController.data(),
|
||||
[clientManagementController](const DockerContainer container, const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig, const QString &clientName,
|
||||
const QSharedPointer<ServerController> &serverController) {
|
||||
QList<ClientInfo> clientsList;
|
||||
ErrorCode result = clientManagementController->appendClient(container, credentials, containerConfig,
|
||||
clientName, serverController, clientsList);
|
||||
emit clientManagementController->clientAppendCompleted(result);
|
||||
});
|
||||
|
||||
connect(m_exportController.data(), &ExportController::nativeConfigClientAppendRequested,
|
||||
clientManagementController.data(),
|
||||
[clientManagementController](const QSharedPointer<ProtocolConfig> &protocolConfig, const QString &clientName,
|
||||
const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController) {
|
||||
QList<ClientInfo> clientsList;
|
||||
auto nonConstProtocolConfig = QSharedPointer<ProtocolConfig>(protocolConfig);
|
||||
ErrorCode result = clientManagementController->appendClient(nonConstProtocolConfig, clientName, container,
|
||||
credentials, serverController, clientsList);
|
||||
emit clientManagementController->nativeConfigClientAppendCompleted(result);
|
||||
});
|
||||
|
||||
connect(clientManagementController.data(), &ClientManagementController::clientAppendCompleted,
|
||||
m_exportController.data(), &ExportController::onClientAppendCompleted);
|
||||
|
||||
connect(clientManagementController.data(), &ClientManagementController::nativeConfigClientAppendCompleted,
|
||||
m_exportController.data(), &ExportController::onNativeConfigClientAppendCompleted);
|
||||
|
||||
connect(m_installController.data(), &InstallController::clientAppendRequested,
|
||||
clientManagementController.data(),
|
||||
[clientManagementController](const DockerContainer container, const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig, const QString &clientName,
|
||||
const QSharedPointer<ServerController> &serverController) {
|
||||
QList<ClientInfo> clientsList;
|
||||
clientManagementController->appendClient(container, credentials, containerConfig,
|
||||
clientName, serverController, clientsList);
|
||||
});
|
||||
}
|
||||
|
||||
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_settingsUIController->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 (ConnectionUIController::*)()>(&ConnectionUIController::openConnection));
|
||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
||||
&ConnectionUIController::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();
|
||||
}
|
||||
|
||||
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(), &ConnectionUIController::connectionErrorOccurred, this, [this](ErrorCode errorCode) {
|
||||
emit m_pageController->showErrorMessage(errorCode);
|
||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
});
|
||||
|
||||
connect(m_apiConfigUIController.get(), &ApiConfigUIController::errorOccurred, m_pageController.get(),
|
||||
qOverload<ErrorCode>(&PageController::showErrorMessage));
|
||||
}
|
||||
|
||||
void CoreController::setQmlRoot()
|
||||
{
|
||||
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
||||
}
|
||||
|
||||
void CoreController::initApiCountryModelUpdateHandler()
|
||||
{
|
||||
connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
|
||||
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
|
||||
m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
|
||||
});
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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(), &ConnectionUIController::onTranslationsUpdated);
|
||||
}
|
||||
|
||||
void CoreController::initAutoConnectHandler()
|
||||
{
|
||||
if (m_settingsUIController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
|
||||
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
|
||||
}
|
||||
}
|
||||
|
||||
void CoreController::initAmneziaDnsToggledHandler()
|
||||
{
|
||||
connect(m_settingsUIController.get(), &SettingsUIController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
|
||||
}
|
||||
|
||||
void CoreController::initPrepareConfigHandler()
|
||||
{
|
||||
connect(m_connectionController.get(), &ConnectionUIController::prepareConfig, this, [this]() {
|
||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
|
||||
|
||||
if (!m_apiConfigUIController->isConfigValid()) {
|
||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_installController->isConfigValid(m_serversModel->getProcessedServerCredentials())) {
|
||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
return;
|
||||
}
|
||||
|
||||
m_connectionController->openConnection();
|
||||
});
|
||||
}
|
||||
|
||||
void CoreController::initImportPremiumV2VpnKeyHandler()
|
||||
{
|
||||
connect(m_apiPremV1MigrationUIController.get(), &ApiPremV1MigrationUIController::importPremiumV2VpnKey, this, [this](const QString &vpnKey) {
|
||||
m_importController->extractConfigFromData(vpnKey);
|
||||
m_importController->importConfig();
|
||||
|
||||
emit m_apiPremV1MigrationUIController->migrationFinished();
|
||||
});
|
||||
}
|
||||
|
||||
void CoreController::initShowMigrationDrawerHandler()
|
||||
{
|
||||
QTimer::singleShot(1000, this, [this]() {
|
||||
if (m_apiPremV1MigrationUIController->isPremV1MigrationReminderActive() && m_apiPremV1MigrationUIController->hasConfigsToMigration()) {
|
||||
m_apiPremV1MigrationUIController->showMigrationDrawer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CoreController::initStrictKillSwitchHandler()
|
||||
{
|
||||
connect(m_settingsUIController.get(), &SettingsUIController::strictKillSwitchEnabledChanged, m_vpnConnection.get(),
|
||||
&VpnConnection::onKillSwitchModeChanged);
|
||||
}
|
||||
|
||||
QSharedPointer<PageController> CoreController::pageController() const
|
||||
{
|
||||
return m_pageController;
|
||||
}
|
||||
170
client/core/controllers/coreController.h
Normal file
170
client/core/controllers/coreController.h
Normal file
@@ -0,0 +1,170 @@
|
||||
#ifndef CORECONTROLLER_H
|
||||
#define CORECONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlContext>
|
||||
#include <QThread>
|
||||
|
||||
#include "ui/controllers/api/apiConfigUIController.h"
|
||||
#include "ui/controllers/api/apiSettingsUIController.h"
|
||||
#include "ui/controllers/api/apiPremV1MigrationUIController.h"
|
||||
#include "core/controllers/api/apiConfigController.h"
|
||||
#include "core/controllers/api/apiSettingsController.h"
|
||||
#include "core/controllers/api/apiPremV1MigrationController.h"
|
||||
#include "core/controllers/selfhosted/clientManagementController.h"
|
||||
#include "ui/controllers/appSplitUIController.h"
|
||||
#include "ui/controllers/allowedDnsUIController.h"
|
||||
#include "ui/controllers/connectionUIController.h"
|
||||
#include "core/controllers/selfhosted/exportController.h"
|
||||
#include "core/controllers/selfhosted/installController.h"
|
||||
#include "ui/controllers/selfhosted/exportUIController.h"
|
||||
#include "ui/controllers/focusController.h"
|
||||
#include "ui/controllers/importController.h"
|
||||
#include "ui/controllers/selfhosted/installUIController.h"
|
||||
#include "ui/controllers/pageController.h"
|
||||
#include "ui/controllers/settingsUIController.h"
|
||||
#include "ui/controllers/siteSplitUIController.h"
|
||||
#include "ui/controllers/systemController.h"
|
||||
#include "core/utils/fileUtils.h"
|
||||
#include "core/controllers/settingsController.h"
|
||||
#include "core/controllers/dnsController.h"
|
||||
#include "core/controllers/connectionController.h"
|
||||
#include "core/controllers/splitTunnelingController.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/selfhosted/clientManagementModel.h"
|
||||
#include "ui/controllers/selfhosted/clientManagementUIController.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 initCoreControllers();
|
||||
void initUIControllers();
|
||||
void initSignalHandlers();
|
||||
void initAndroidController();
|
||||
void initAppleController();
|
||||
void setupControllerSignalConnections();
|
||||
|
||||
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 {};
|
||||
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<ConnectionUIController> m_connectionController;
|
||||
QScopedPointer<FocusController> m_focusController;
|
||||
QSharedPointer<PageController> m_pageController;
|
||||
QSharedPointer<ExportController> m_exportController;
|
||||
QSharedPointer<InstallController> m_installController;
|
||||
QScopedPointer<ExportUIController> m_exportUIController;
|
||||
QScopedPointer<InstallUIController> m_installUIController;
|
||||
QScopedPointer<ImportController> m_importController;
|
||||
QScopedPointer<SettingsUIController> m_settingsUIController;
|
||||
QScopedPointer<SiteSplitUIController> m_siteSplitUIController;
|
||||
QScopedPointer<SystemController> m_systemController;
|
||||
QScopedPointer<AppSplitUIController> m_appSplitUIController;
|
||||
QScopedPointer<AllowedDnsUIController> m_allowedDnsUIController;
|
||||
|
||||
QSharedPointer<ApiSettingsController> m_apiSettingsCoreController;
|
||||
QSharedPointer<ApiConfigsController> m_apiConfigsCoreController;
|
||||
QSharedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationCoreController;
|
||||
|
||||
QScopedPointer<ApiSettingsUIController> m_apiSettingsUIController;
|
||||
QScopedPointer<ApiConfigUIController> m_apiConfigUIController;
|
||||
QScopedPointer<ApiPremV1MigrationUIController> m_apiPremV1MigrationUIController;
|
||||
|
||||
QSharedPointer<SettingsController> m_settingsController;
|
||||
QSharedPointer<DnsController> m_dnsController;
|
||||
QSharedPointer<SplitTunnelingController> m_splitTunnelingController;
|
||||
|
||||
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<ClientManagementUIController> m_clientManagementUIController;
|
||||
|
||||
QSharedPointer<ApiServicesModel> m_apiServicesModel;
|
||||
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
||||
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
|
||||
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
|
||||
|
||||
QSharedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
||||
QSharedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
||||
QSharedPointer<CloakConfigModel> m_cloakConfigModel;
|
||||
QSharedPointer<XrayConfigModel> m_xrayConfigModel;
|
||||
QSharedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
|
||||
QSharedPointer<AwgConfigModel> m_awgConfigModel;
|
||||
#ifdef Q_OS_WINDOWS
|
||||
QSharedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
|
||||
#endif
|
||||
QSharedPointer<SftpConfigModel> m_sftpConfigModel;
|
||||
QSharedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
|
||||
};
|
||||
|
||||
#endif // CORECONTROLLER_H
|
||||
66
client/core/controllers/dnsController.cpp
Normal file
66
client/core/controllers/dnsController.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "dnsController.h"
|
||||
#include "core/networkUtilities.h"
|
||||
|
||||
DnsController::DnsController(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QObject(parent), m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
bool DnsController::addDns(const QString &ip)
|
||||
{
|
||||
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList currentDnsServers = m_settings->allowedDnsServers();
|
||||
|
||||
if (currentDnsServers.contains(ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentDnsServers.append(ip);
|
||||
m_settings->setAllowedDnsServers(currentDnsServers);
|
||||
|
||||
emit dnsAdded(ip);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DnsController::addDnsList(const QStringList &dnsServers, bool replaceExisting)
|
||||
{
|
||||
QStringList currentDnsServers;
|
||||
|
||||
if (!replaceExisting) {
|
||||
currentDnsServers = m_settings->allowedDnsServers();
|
||||
}
|
||||
|
||||
for (const QString &ip : dnsServers) {
|
||||
if (!currentDnsServers.contains(ip)) {
|
||||
currentDnsServers.append(ip);
|
||||
}
|
||||
}
|
||||
|
||||
m_settings->setAllowedDnsServers(currentDnsServers);
|
||||
|
||||
emit dnsListAdded(dnsServers);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DnsController::removeDns(const QString &ip)
|
||||
{
|
||||
QStringList currentDnsServers = m_settings->allowedDnsServers();
|
||||
|
||||
if (!currentDnsServers.contains(ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentDnsServers.removeAll(ip);
|
||||
m_settings->setAllowedDnsServers(currentDnsServers);
|
||||
|
||||
emit dnsRemoved(ip);
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList DnsController::getAllowedDnsServers() const
|
||||
{
|
||||
return m_settings->allowedDnsServers();
|
||||
}
|
||||
32
client/core/controllers/dnsController.h
Normal file
32
client/core/controllers/dnsController.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef DNSCONTROLLER_H
|
||||
#define DNSCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
class DnsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DnsController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
|
||||
// DNS management
|
||||
bool addDns(const QString &ip);
|
||||
bool addDnsList(const QStringList &dnsServers, bool replaceExisting = false);
|
||||
bool removeDns(const QString &ip);
|
||||
QStringList getAllowedDnsServers() const;
|
||||
|
||||
signals:
|
||||
void dnsAdded(const QString &ip);
|
||||
void dnsListAdded(const QStringList &dnsServers);
|
||||
void dnsRemoved(const QString &ip);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
};
|
||||
|
||||
#endif // DNSCONTROLLER_H
|
||||
@@ -0,0 +1,825 @@
|
||||
#include "clientManagementController.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
#include "core/models/protocols/openvpnProtocolConfig.h"
|
||||
#include "core/models/protocols/wireguardProtocolConfig.h"
|
||||
#include "core/models/protocols/awgProtocolConfig.h"
|
||||
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||
#include "core/models/protocols/shadowsocksProtocolConfig.h"
|
||||
#include "core/models/protocols/cloakProtocolConfig.h"
|
||||
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
||||
#include "core/models/protocols/sftpProtocolConfig.h"
|
||||
#include "core/models/protocols/socks5ProtocolConfig.h"
|
||||
#include "core/models/protocols/torWebsiteProtocolConfig.h"
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
#include "core/models/clientInfo.h"
|
||||
#include <variant>
|
||||
#include "settings.h"
|
||||
#include "logger.h"
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
using namespace amnezia;
|
||||
using namespace amnezia::config_key;
|
||||
|
||||
namespace
|
||||
{
|
||||
Logger logger("ClientManagementController");
|
||||
|
||||
namespace configKey
|
||||
{
|
||||
constexpr char clientId[] = "clientId";
|
||||
constexpr char clientName[] = "clientName";
|
||||
constexpr char container[] = "container";
|
||||
constexpr char userData[] = "userData";
|
||||
constexpr char creationDate[] = "creationDate";
|
||||
constexpr char latestHandshake[] = "latestHandshake";
|
||||
constexpr char dataReceived[] = "dataReceived";
|
||||
constexpr char dataSent[] = "dataSent";
|
||||
constexpr char allowedIps[] = "allowedIps";
|
||||
}
|
||||
}
|
||||
|
||||
ClientManagementController::ClientManagementController(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QObject(parent), m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
QString ClientManagementController::getClientsTableFilePath(const DockerContainer container)
|
||||
{
|
||||
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
|
||||
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) {
|
||||
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn));
|
||||
} else {
|
||||
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container));
|
||||
}
|
||||
return clientsTableFile;
|
||||
}
|
||||
|
||||
void ClientManagementController::migration(const QByteArray &clientsTableString, QList<ClientInfo> &clientsList)
|
||||
{
|
||||
QJsonObject clientsTableObj = QJsonDocument::fromJson(clientsTableString).object();
|
||||
|
||||
for (auto &clientId : clientsTableObj.keys()) {
|
||||
ClientInfo client;
|
||||
client.clientId = clientId;
|
||||
client.clientName = clientsTableObj.value(clientId).toObject().value(configKey::clientName).toString();
|
||||
client.creationDate = QDateTime::currentDateTime();
|
||||
|
||||
clientsList.append(client);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ErrorCode ClientManagementController::updateClientsData(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList)
|
||||
{
|
||||
clientsList.clear();
|
||||
ErrorCode error = ErrorCode::NoError;
|
||||
|
||||
QString clientsTableFile = getClientsTableFilePath(container);
|
||||
const QByteArray clientsTableString = serverController->getTextFileFromContainer(container, credentials, clientsTableFile, error);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to get the clientsTable file from the server";
|
||||
return error;
|
||||
}
|
||||
|
||||
QJsonArray clientsTable = QJsonDocument::fromJson(clientsTableString).array();
|
||||
|
||||
if (clientsTable.isEmpty()) {
|
||||
migration(clientsTableString, clientsList);
|
||||
const QByteArray newClientsTableString = QJsonDocument(clientsToJsonArray(clientsList)).toJson();
|
||||
error = serverController->uploadTextFileToContainer(container, credentials, newClientsTableString, clientsTableFile);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||
return error;
|
||||
}
|
||||
} else {
|
||||
clientsList = clientsFromJsonArray(clientsTable);
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
switch (container) {
|
||||
case DockerContainer::OpenVpn:
|
||||
case DockerContainer::ShadowSocks:
|
||||
case DockerContainer::Cloak:
|
||||
error = getOpenVpnClients(container, credentials, serverController, count, clientsList);
|
||||
break;
|
||||
case DockerContainer::WireGuard:
|
||||
case DockerContainer::Awg:
|
||||
error = getWireGuardClients(container, credentials, serverController, count, clientsList);
|
||||
break;
|
||||
case DockerContainer::Xray:
|
||||
error = getXrayClients(container, credentials, serverController, count, clientsList);
|
||||
break;
|
||||
default:
|
||||
error = ErrorCode::NoError;
|
||||
break;
|
||||
}
|
||||
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to get clients for container" << ContainerProps::containerTypeToString(container);
|
||||
return error;
|
||||
}
|
||||
|
||||
emit clientsDataUpdated(clientsList);
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
// UI-facing methods - create ServerController internally
|
||||
ErrorCode ClientManagementController::updateClientsData(const DockerContainer container, const ServerCredentials &credentials)
|
||||
{
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
QList<ClientInfo> clientsList;
|
||||
return updateClientsData(container, credentials, serverController, clientsList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ErrorCode ClientManagementController::appendClient(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig, const QString &clientName,
|
||||
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList)
|
||||
{
|
||||
Proto protocol;
|
||||
switch (container) {
|
||||
case DockerContainer::ShadowSocks:
|
||||
case DockerContainer::Cloak:
|
||||
protocol = Proto::OpenVpn;
|
||||
break;
|
||||
case DockerContainer::OpenVpn:
|
||||
case DockerContainer::WireGuard:
|
||||
case DockerContainer::Awg:
|
||||
case DockerContainer::Xray:
|
||||
protocol = ContainerProps::defaultProtocol(container);
|
||||
break;
|
||||
default:
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
QString protocolName = ProtocolProps::protoToString(protocol);
|
||||
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
|
||||
return appendClient(protocolConfig, clientName, container, credentials, serverController, clientsList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ErrorCode ClientManagementController::appendClient(QSharedPointer<ProtocolConfig> &protocolConfig, const QString &clientName, const DockerContainer container,
|
||||
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
|
||||
QList<ClientInfo> &clientsList)
|
||||
{
|
||||
QString clientId;
|
||||
if (container == DockerContainer::Xray) {
|
||||
if (!protocolConfig) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonObject protocolConfigJson = protocolConfig->toJson();
|
||||
if (!protocolConfigJson.contains("outbounds")) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonArray outbounds = protocolConfigJson.value("outbounds").toArray();
|
||||
if (outbounds.isEmpty()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonObject outbound = outbounds[0].toObject();
|
||||
if (!outbound.contains("settings")) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonObject settings = outbound["settings"].toObject();
|
||||
if (!settings.contains("vnext")) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonArray vnext = settings["vnext"].toArray();
|
||||
if (vnext.isEmpty()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonObject vnextObj = vnext[0].toObject();
|
||||
if (!vnextObj.contains("users")) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonArray users = vnextObj["users"].toArray();
|
||||
if (users.isEmpty()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonObject user = users[0].toObject();
|
||||
if (!user.contains("id")) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
clientId = user["id"].toString();
|
||||
} else {
|
||||
if (!protocolConfig) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
|
||||
std::visit([&clientId](const auto &config) -> void {
|
||||
clientId = config->clientProtocolConfig.clientId;
|
||||
}, variant);
|
||||
}
|
||||
|
||||
return appendClient(clientId, clientName, container, credentials, serverController, clientsList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ErrorCode ClientManagementController::appendClient(const QString &clientId, const QString &clientName, const DockerContainer container,
|
||||
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
|
||||
QList<ClientInfo> &clientsList)
|
||||
{
|
||||
ErrorCode error = ErrorCode::NoError;
|
||||
|
||||
QList<ClientInfo> currentClients;
|
||||
error = updateClientsData(container, credentials, serverController, currentClients);
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
for (int i = 0; i < currentClients.size(); ++i) {
|
||||
if (currentClients[i].clientId == clientId) {
|
||||
return renameClient(i, clientName, container, credentials, serverController, currentClients, true);
|
||||
}
|
||||
}
|
||||
|
||||
ClientInfo newClient(clientId, clientName);
|
||||
newClient.container = container;
|
||||
currentClients.append(newClient);
|
||||
|
||||
const QByteArray clientsTableString = QJsonDocument(clientsToJsonArray(currentClients)).toJson();
|
||||
QString clientsTableFile = getClientsTableFilePath(container);
|
||||
|
||||
error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||
return error;
|
||||
}
|
||||
|
||||
clientsList = currentClients;
|
||||
emit clientAdded(clientId, clientName);
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::renameClient(const int row, const QString &clientName, const DockerContainer container,
|
||||
const ServerCredentials &credentials, bool addTimeStamp)
|
||||
{
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
QList<ClientInfo> clientsList;
|
||||
ErrorCode errorCode = updateClientsData(container, credentials, serverController, clientsList);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
return renameClient(row, clientName, container, credentials, serverController, clientsList, addTimeStamp);
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::renameClient(const int row, const QString &clientName, const DockerContainer container,
|
||||
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
|
||||
QList<ClientInfo> &clientsList, bool addTimeStamp)
|
||||
{
|
||||
if (row < 0 || row >= clientsList.size()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
clientsList[row].clientName = clientName;
|
||||
if (addTimeStamp) {
|
||||
clientsList[row].creationDate = QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
const QByteArray clientsTableString = QJsonDocument(clientsToJsonArray(clientsList)).toJson();
|
||||
QString clientsTableFile = getClientsTableFilePath(container);
|
||||
|
||||
ErrorCode error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||
return error;
|
||||
}
|
||||
|
||||
emit clientRenamed(row, clientName);
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ErrorCode ClientManagementController::getWireGuardClients(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList)
|
||||
{
|
||||
ErrorCode error = ErrorCode::NoError;
|
||||
|
||||
const QString wireGuardConfigFile = QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg");
|
||||
const QString wireguardConfigString = serverController->getTextFileFromContainer(container, credentials, wireGuardConfigFile, error);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to get the wg conf file from the server";
|
||||
return error;
|
||||
}
|
||||
|
||||
auto configLines = wireguardConfigString.split("\n", Qt::SkipEmptyParts);
|
||||
QStringList wireguardKeys;
|
||||
for (const auto &line : configLines) {
|
||||
auto configPair = line.split(" = ", Qt::SkipEmptyParts);
|
||||
if (configPair.front() == "PublicKey") {
|
||||
wireguardKeys.push_back(configPair.back());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &wireguardKey : wireguardKeys) {
|
||||
if (!isClientExists(wireguardKey, clientsList)) {
|
||||
ClientInfo client(wireguardKey, QString("Client %1").arg(count));
|
||||
client.container = container;
|
||||
clientsList.append(client);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
|
||||
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList)
|
||||
{
|
||||
ErrorCode error = ErrorCode::NoError;
|
||||
|
||||
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
|
||||
const QString configString = serverController->getTextFileFromContainer(container, credentials, serverConfigPath, error);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to get the xray server config file from the server";
|
||||
return error;
|
||||
}
|
||||
|
||||
QJsonDocument serverConfig = QJsonDocument::fromJson(configString.toUtf8());
|
||||
if (serverConfig.isNull()) {
|
||||
logger.error() << "Failed to parse xray server config JSON";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
if (!serverConfig.object().contains("inbounds") || serverConfig.object()["inbounds"].toArray().isEmpty()) {
|
||||
logger.error() << "Invalid xray server config structure";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
const QJsonObject inbound = serverConfig.object()["inbounds"].toArray()[0].toObject();
|
||||
if (!inbound.contains("settings")) {
|
||||
logger.error() << "Missing settings in xray inbound config";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
const QJsonObject settings = inbound["settings"].toObject();
|
||||
if (!settings.contains("clients")) {
|
||||
logger.error() << "Missing clients in xray settings config";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
const QJsonArray clients = settings["clients"].toArray();
|
||||
for (const auto &clientValue : clients) {
|
||||
const QJsonObject clientObj = clientValue.toObject();
|
||||
if (!clientObj.contains("id")) {
|
||||
logger.error() << "Missing id in xray client config";
|
||||
continue;
|
||||
}
|
||||
QString clientId = clientObj["id"].toString();
|
||||
|
||||
QString xrayDefaultUuid = serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, error);
|
||||
xrayDefaultUuid.replace("\n", "");
|
||||
|
||||
if (!isClientExists(clientId, clientsList) && clientId != xrayDefaultUuid) {
|
||||
ClientInfo client(clientId, QString("Client %1").arg(count));
|
||||
client.container = container;
|
||||
clientsList.append(client);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::getOpenVpnClients(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList)
|
||||
{
|
||||
Q_UNUSED(container);
|
||||
Q_UNUSED(credentials);
|
||||
Q_UNUSED(serverController);
|
||||
count = clientsList.size();
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::wgShow(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, std::vector<WgShowData> &data)
|
||||
{
|
||||
if (container != DockerContainer::WireGuard && container != DockerContainer::Awg) {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode error = ErrorCode::NoError;
|
||||
QString stdOut;
|
||||
std::function<ErrorCode(const QString &, libssh::Client &)> cbReadStdOut = [&](const QString &data, libssh::Client &){
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
const QString command = QString("sudo docker exec -i $CONTAINER_NAME bash -c '%1'").arg("wg show all");
|
||||
|
||||
QString script = serverController->replaceVars(command, serverController->generateVarsForContainer(credentials, container));
|
||||
error = serverController->runScript(credentials, script, cbReadStdOut);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to execute wg show command";
|
||||
return error;
|
||||
}
|
||||
|
||||
if (stdOut.isEmpty()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
const auto getStrValue = [](const auto str) { return str.mid(str.indexOf(":") + 1).trimmed(); };
|
||||
|
||||
const auto parts = stdOut.split('\n');
|
||||
const auto peerList = parts.filter("peer:");
|
||||
const auto latestHandshakeList = parts.filter("latest handshake:");
|
||||
const auto transferredDataList = parts.filter("transfer:");
|
||||
const auto allowedIpsList = parts.filter("allowed ips:");
|
||||
|
||||
if (allowedIpsList.isEmpty() || latestHandshakeList.isEmpty() || transferredDataList.isEmpty() || peerList.isEmpty()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
const auto changeHandshakeFormat = [](QString &latestHandshake) {
|
||||
const std::vector<std::pair<QString, QString>> replaceMap = { { " days", "d" }, { " hours", "h" }, { " minutes", "m" },
|
||||
{ " seconds", "s" }, { " day", "d" }, { " hour", "h" },
|
||||
{ " minute", "m" }, { " second", "s" } };
|
||||
|
||||
for (const auto &item : replaceMap) {
|
||||
latestHandshake.replace(item.first, item.second);
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < peerList.size() && i < transferredDataList.size() && i < latestHandshakeList.size() && i < allowedIpsList.size(); ++i) {
|
||||
|
||||
const auto transferredData = getStrValue(transferredDataList[i]).split(",");
|
||||
auto latestHandshake = getStrValue(latestHandshakeList[i]);
|
||||
auto serverBytesReceived = transferredData.front().trimmed();
|
||||
auto serverBytesSent = transferredData.back().trimmed();
|
||||
auto allowedIps = getStrValue(allowedIpsList[i]);
|
||||
|
||||
changeHandshakeFormat(latestHandshake);
|
||||
|
||||
serverBytesReceived.chop(QStringLiteral(" received").length());
|
||||
serverBytesSent.chop(QStringLiteral(" sent").length());
|
||||
|
||||
data.push_back({ getStrValue(peerList[i]), latestHandshake, serverBytesSent, serverBytesReceived, allowedIps });
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ErrorCode ClientManagementController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const int serverIndex, const QSharedPointer<ServerController> &serverController)
|
||||
{
|
||||
QList<ClientInfo> clientsList;
|
||||
ErrorCode errorCode = updateClientsData(container, credentials, serverController, clientsList);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
return revokeOpenVpn(row, container, credentials, serverIndex, serverController, clientsList);
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController)
|
||||
{
|
||||
QList<ClientInfo> clientsList;
|
||||
ErrorCode errorCode = updateClientsData(container, credentials, serverController, clientsList);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
return revokeWireGuard(row, container, credentials, serverController, clientsList);
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController)
|
||||
{
|
||||
QList<ClientInfo> clientsList;
|
||||
ErrorCode errorCode = updateClientsData(container, credentials, serverController, clientsList);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
return revokeXray(row, container, credentials, serverController, clientsList);
|
||||
}
|
||||
|
||||
|
||||
ErrorCode ClientManagementController::revokeClient(const int row, const DockerContainer container,
|
||||
const ServerCredentials &credentials, const int serverIndex)
|
||||
{
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
QList<ClientInfo> clientsList;
|
||||
ErrorCode errorCode = updateClientsData(container, credentials, serverController, clientsList);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
return revokeClient(row, container, credentials, serverIndex, serverController, clientsList);
|
||||
}
|
||||
|
||||
|
||||
ErrorCode ClientManagementController::revokeClient(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const int serverIndex, const QSharedPointer<ServerController> &serverController,
|
||||
QList<ClientInfo> &clientsList)
|
||||
{
|
||||
if (row < 0 || row >= clientsList.size()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
QString clientId = clientsList[row].clientId;
|
||||
|
||||
ErrorCode errorCode = ErrorCode::NoError;
|
||||
|
||||
switch(container) {
|
||||
case DockerContainer::OpenVpn:
|
||||
case DockerContainer::ShadowSocks:
|
||||
case DockerContainer::Cloak: {
|
||||
errorCode = revokeOpenVpn(row, container, credentials, serverIndex, serverController, clientsList);
|
||||
break;
|
||||
}
|
||||
case DockerContainer::WireGuard:
|
||||
case DockerContainer::Awg: {
|
||||
errorCode = revokeWireGuard(row, container, credentials, serverController, clientsList);
|
||||
break;
|
||||
}
|
||||
case DockerContainer::Xray: {
|
||||
errorCode = revokeXray(row, container, credentials, serverController, clientsList);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
logger.error() << "Internal error: received unexpected container type";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
emit clientRevoked(row);
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::revokeClient(const ContainerConfig &containerConfig, const DockerContainer container,
|
||||
const ServerCredentials &credentials, const int serverIndex,
|
||||
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList)
|
||||
{
|
||||
|
||||
Proto protocol;
|
||||
switch(container) {
|
||||
case DockerContainer::ShadowSocks:
|
||||
case DockerContainer::Cloak: {
|
||||
protocol = Proto::OpenVpn;
|
||||
break;
|
||||
}
|
||||
case DockerContainer::OpenVpn:
|
||||
case DockerContainer::WireGuard:
|
||||
case DockerContainer::Awg:
|
||||
case DockerContainer::Xray: {
|
||||
protocol = ContainerProps::defaultProtocol(container);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
logger.error() << "Internal error: received unexpected container type";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
}
|
||||
|
||||
auto protocolConfig = ContainerProps::getProtocolConfigFromContainer(protocol, containerConfig);
|
||||
|
||||
|
||||
QString clientId;
|
||||
if (container == DockerContainer::Xray) {
|
||||
if (!protocolConfig) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonObject protocolConfigJson = protocolConfig->toJson();
|
||||
if (!protocolConfigJson.contains("outbounds")) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonArray outbounds = protocolConfigJson.value("outbounds").toArray();
|
||||
if (outbounds.isEmpty()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonObject outbound = outbounds[0].toObject();
|
||||
if (!outbound.contains("settings")) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonObject settings = outbound["settings"].toObject();
|
||||
if (!settings.contains("vnext")) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonArray vnext = settings["vnext"].toArray();
|
||||
if (vnext.isEmpty()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonObject vnextObj = vnext[0].toObject();
|
||||
if (!vnextObj.contains("users")) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonArray users = vnextObj["users"].toArray();
|
||||
if (users.isEmpty()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QJsonObject user = users[0].toObject();
|
||||
if (!user.contains("id")) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
clientId = user["id"].toString();
|
||||
} else {
|
||||
if (!protocolConfig) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
|
||||
std::visit([&clientId](const auto &config) -> void {
|
||||
clientId = config->clientProtocolConfig.clientId;
|
||||
}, variant);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < clientsList.size(); i++) {
|
||||
if (clientsList[i].clientId == clientId) {
|
||||
return revokeClient(i, container, credentials, serverIndex, serverController, clientsList);
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const int serverIndex, const QSharedPointer<ServerController> &serverController,
|
||||
QList<ClientInfo> &clientsList)
|
||||
{
|
||||
if (row < 0 || row >= clientsList.size()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QString clientId = clientsList[row].clientId;
|
||||
|
||||
ErrorCode error = ErrorCode::NoError;
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
};
|
||||
|
||||
const QString revokeClientScript = "sudo docker exec -i $CONTAINER_NAME bash -c './easyrsa --batch revoke %1'";
|
||||
QString script = serverController->replaceVars(revokeClientScript.arg(clientId),
|
||||
serverController->generateVarsForContainer(credentials, container));
|
||||
error = serverController->runScript(credentials, script, cbReadStdOut);
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
clientsList.removeAt(row);
|
||||
const QByteArray clientsTableString = QJsonDocument(clientsToJsonArray(clientsList)).toJson();
|
||||
QString clientsTableFile = getClientsTableFilePath(container);
|
||||
|
||||
error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||
return error;
|
||||
}
|
||||
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList)
|
||||
{
|
||||
if (row < 0 || row >= clientsList.size()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QString clientId = clientsList[row].clientId;
|
||||
|
||||
ErrorCode error = ErrorCode::NoError;
|
||||
|
||||
const QString wireGuardConfigFile = QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg");
|
||||
const QString wireguardConfigString = serverController->getTextFileFromContainer(container, credentials, wireGuardConfigFile, error);
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
QStringList configLines = wireguardConfigString.split("\n");
|
||||
QStringList newConfigLines;
|
||||
bool skipPeer = false;
|
||||
|
||||
for (const QString &line : configLines) {
|
||||
if (line.startsWith("[Peer]")) {
|
||||
skipPeer = false;
|
||||
}
|
||||
if (line.contains("PublicKey") && line.contains(clientId)) {
|
||||
skipPeer = true;
|
||||
continue;
|
||||
}
|
||||
if (!skipPeer) {
|
||||
newConfigLines.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
QString newConfig = newConfigLines.join("\n");
|
||||
error = serverController->uploadTextFileToContainer(container, credentials, newConfig.toUtf8(), wireGuardConfigFile);
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
clientsList.removeAt(row);
|
||||
const QByteArray clientsTableString = QJsonDocument(clientsToJsonArray(clientsList)).toJson();
|
||||
QString clientsTableFile = getClientsTableFilePath(container);
|
||||
|
||||
error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||
return error;
|
||||
}
|
||||
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementController::revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList)
|
||||
{
|
||||
if (row < 0 || row >= clientsList.size()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
QString clientId = clientsList[row].clientId;
|
||||
|
||||
ErrorCode error = ErrorCode::NoError;
|
||||
|
||||
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
|
||||
const QString configString = serverController->getTextFileFromContainer(container, credentials, serverConfigPath, error);
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
QJsonDocument serverConfig = QJsonDocument::fromJson(configString.toUtf8());
|
||||
if (serverConfig.isNull()) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
QJsonObject configObj = serverConfig.object();
|
||||
QJsonArray inbounds = configObj["inbounds"].toArray();
|
||||
|
||||
for (int i = 0; i < inbounds.size(); i++) {
|
||||
QJsonObject inbound = inbounds[i].toObject();
|
||||
QJsonObject settings = inbound["settings"].toObject();
|
||||
QJsonArray clients = settings["clients"].toArray();
|
||||
|
||||
for (int j = 0; j < clients.size(); j++) {
|
||||
QJsonObject clientObj = clients[j].toObject();
|
||||
if (clientObj["id"].toString() == clientId) {
|
||||
clients.removeAt(j);
|
||||
settings["clients"] = clients;
|
||||
inbound["settings"] = settings;
|
||||
inbounds[i] = inbound;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configObj["inbounds"] = inbounds;
|
||||
QJsonDocument newServerConfig(configObj);
|
||||
|
||||
error = serverController->uploadTextFileToContainer(container, credentials, newServerConfig.toJson(), serverConfigPath);
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
clientsList.removeAt(row);
|
||||
const QByteArray clientsTableString = QJsonDocument(clientsToJsonArray(clientsList)).toJson();
|
||||
QString clientsTableFile = getClientsTableFilePath(container);
|
||||
|
||||
error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||
return error;
|
||||
}
|
||||
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
QList<ClientInfo> ClientManagementController::clientsFromJsonArray(const QJsonArray &jsonArray)
|
||||
{
|
||||
QList<ClientInfo> clientsList;
|
||||
for (const auto &value : jsonArray) {
|
||||
clientsList.append(ClientInfo(value.toObject()));
|
||||
}
|
||||
return clientsList;
|
||||
}
|
||||
|
||||
QJsonArray ClientManagementController::clientsToJsonArray(const QList<ClientInfo> &clientsList)
|
||||
{
|
||||
QJsonArray jsonArray;
|
||||
for (const auto &client : clientsList) {
|
||||
jsonArray.append(client.toJson());
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
bool ClientManagementController::isClientExists(const QString &clientId, const QList<ClientInfo> &clientsList)
|
||||
{
|
||||
for (const auto &client : clientsList) {
|
||||
if (client.clientId == clientId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
133
client/core/controllers/selfhosted/clientManagementController.h
Normal file
133
client/core/controllers/selfhosted/clientManagementController.h
Normal file
@@ -0,0 +1,133 @@
|
||||
#ifndef CLIENTMANAGEMENTCONTROLLER_H
|
||||
#define CLIENTMANAGEMENTCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QList>
|
||||
#include <vector>
|
||||
|
||||
#include "core/defs.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/models/containers/containerConfig.h"
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
#include "core/models/clientInfo.h"
|
||||
|
||||
class ServerController;
|
||||
class Settings;
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
struct WgShowData
|
||||
{
|
||||
QString clientId;
|
||||
QString latestHandshake;
|
||||
QString dataReceived;
|
||||
QString dataSent;
|
||||
QString allowedIps;
|
||||
};
|
||||
|
||||
class ClientManagementController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ClientManagementController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
|
||||
std::shared_ptr<Settings> getSettings() const { return m_settings; }
|
||||
|
||||
// UI-facing methods (no ServerController parameter - created internally)
|
||||
ErrorCode updateClientsData(const DockerContainer container, const ServerCredentials &credentials);
|
||||
|
||||
ErrorCode renameClient(const int row, const QString &clientName, const DockerContainer container,
|
||||
const ServerCredentials &credentials, bool addTimeStamp = false);
|
||||
|
||||
ErrorCode revokeClient(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const int serverIndex);
|
||||
|
||||
// Core methods using ClientInfo model
|
||||
ErrorCode updateClientsData(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList);
|
||||
|
||||
ErrorCode appendClient(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig, const QString &clientName,
|
||||
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList);
|
||||
|
||||
ErrorCode appendClient(QSharedPointer<ProtocolConfig> &protocolConfig, const QString &clientName, const DockerContainer container,
|
||||
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
|
||||
QList<ClientInfo> &clientsList);
|
||||
|
||||
ErrorCode appendClient(const QString &clientId, const QString &clientName, const DockerContainer container,
|
||||
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
|
||||
QList<ClientInfo> &clientsList);
|
||||
|
||||
ErrorCode renameClient(const int row, const QString &clientName, const DockerContainer container,
|
||||
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
|
||||
QList<ClientInfo> &clientsList, bool addTimeStamp = false);
|
||||
|
||||
ErrorCode revokeClient(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const int serverIndex, const QSharedPointer<ServerController> &serverController,
|
||||
QList<ClientInfo> &clientsList);
|
||||
|
||||
ErrorCode revokeClient(const ContainerConfig &containerConfig, const DockerContainer container,
|
||||
const ServerCredentials &credentials, const int serverIndex,
|
||||
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList);
|
||||
|
||||
// WireGuard specific operations
|
||||
ErrorCode wgShow(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, std::vector<WgShowData> &data);
|
||||
|
||||
signals:
|
||||
void adminConfigRevoked(const DockerContainer container);
|
||||
void clientsDataUpdated(const QList<ClientInfo> &clientsList);
|
||||
void clientAdded(const QString &clientId, const QString &clientName);
|
||||
void clientRenamed(const int row, const QString &newName);
|
||||
void clientRevoked(const int row);
|
||||
|
||||
void clientAppendCompleted(ErrorCode errorCode);
|
||||
void nativeConfigClientAppendCompleted(ErrorCode errorCode);
|
||||
|
||||
private:
|
||||
// Protocol-specific client management
|
||||
ErrorCode getOpenVpnClients(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList);
|
||||
|
||||
ErrorCode getWireGuardClients(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList);
|
||||
|
||||
ErrorCode getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
|
||||
const QSharedPointer<ServerController> &serverController, int &count, QList<ClientInfo> &clientsList);
|
||||
|
||||
// Protocol-specific client revocation
|
||||
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const int serverIndex, const QSharedPointer<ServerController> &serverController,
|
||||
QList<ClientInfo> &clientsList);
|
||||
|
||||
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList);
|
||||
|
||||
ErrorCode revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, QList<ClientInfo> &clientsList);
|
||||
|
||||
// Internal wrappers to fetch clients list then delegate to detailed versions
|
||||
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const int serverIndex, const QSharedPointer<ServerController> &serverController);
|
||||
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController);
|
||||
ErrorCode revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController);
|
||||
|
||||
// Helper methods
|
||||
bool isClientExists(const QString &clientId, const QList<ClientInfo> &clientsList);
|
||||
void migration(const QByteArray &clientsTableString, QList<ClientInfo> &clientsList);
|
||||
QString getClientsTableFilePath(const DockerContainer container);
|
||||
|
||||
// JSON serialization for persistence only
|
||||
static QList<ClientInfo> clientsFromJsonArray(const QJsonArray &jsonArray);
|
||||
static QJsonArray clientsToJsonArray(const QList<ClientInfo> &clientsList);
|
||||
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
};
|
||||
|
||||
#endif // CLIENTMANAGEMENTCONTROLLER_H
|
||||
353
client/core/controllers/selfhosted/exportController.cpp
Normal file
353
client/core/controllers/selfhosted/exportController.cpp
Normal file
@@ -0,0 +1,353 @@
|
||||
#include "exportController.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QDataStream>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include "core/controllers/vpnConfigurationController.h"
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
#include "core/controllers/selfhosted/clientManagementController.h"
|
||||
#include "core/qrCodeUtils.h"
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
|
||||
ExportController::ExportController(std::shared_ptr<Settings> settings,
|
||||
QObject *parent)
|
||||
: QObject(parent),
|
||||
m_settings(settings),
|
||||
m_lastClientAppendResult(ErrorCode::NoError),
|
||||
m_lastNativeConfigAppendResult(ErrorCode::NoError),
|
||||
m_waitingForClientAppend(false),
|
||||
m_waitingForNativeConfigAppend(false)
|
||||
{
|
||||
}
|
||||
|
||||
ExportConfigResult ExportController::generateFullAccessConfig(const QSharedPointer<ServerConfig> &serverConfig)
|
||||
{
|
||||
ExportConfigResult result;
|
||||
result.errorCode = ErrorCode::NoError;
|
||||
|
||||
// Create a copy of the ServerConfig and clean last_config from protocol configs
|
||||
auto modifiedServerConfig = QSharedPointer<ServerConfig>::create(*serverConfig);
|
||||
|
||||
for (auto &containerConfig : modifiedServerConfig->containerConfigs) {
|
||||
for (auto &protocolConfig : containerConfig.protocolConfigs) {
|
||||
// Protocol configs will automatically exclude last_config when serialized to JSON for export
|
||||
// No need to manually remove it here as the toJson() method handles this
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray compressedConfig = QJsonDocument(modifiedServerConfig->toJson()).toJson();
|
||||
compressedConfig = qCompress(compressedConfig, 8);
|
||||
result.config = QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
|
||||
result.qrCodes = generateQrCodeSeries(compressedConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportConfigResult ExportController::generateConnectionConfig(const QString &clientName,
|
||||
const ServerCredentials &credentials,
|
||||
const DockerContainer container,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QSharedPointer<ServerConfig> &serverConfig,
|
||||
const QPair<QString, QString> &dnsSettings)
|
||||
{
|
||||
ExportConfigResult result;
|
||||
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
||||
|
||||
// Use the provided ContainerConfig directly
|
||||
ContainerConfig modifiedContainerConfig = containerConfig;
|
||||
|
||||
result.errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container, modifiedContainerConfig);
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
m_waitingForClientAppend = true;
|
||||
emit clientAppendRequested(container, credentials, modifiedContainerConfig, clientName, serverController);
|
||||
|
||||
QEventLoop loop;
|
||||
QTimer timer;
|
||||
timer.setSingleShot(true);
|
||||
timer.setInterval(30000);
|
||||
|
||||
connect(this, &ExportController::onClientAppendCompleted, &loop, &QEventLoop::quit);
|
||||
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||
|
||||
timer.start();
|
||||
loop.exec();
|
||||
|
||||
m_waitingForClientAppend = false;
|
||||
result.errorCode = m_lastClientAppendResult;
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create a modified ServerConfig for export with only the specific container
|
||||
auto exportServerConfig = QSharedPointer<ServerConfig>::create(*serverConfig);
|
||||
|
||||
// Remove credentials (they are not needed in export)
|
||||
exportServerConfig->containerConfigs.clear();
|
||||
|
||||
// Add only the specific container being exported
|
||||
QString containerName = ContainerProps::containerToString(container);
|
||||
exportServerConfig->containerConfigs.insert(containerName, modifiedContainerConfig);
|
||||
exportServerConfig->defaultContainer = containerName;
|
||||
exportServerConfig->dns1 = dnsSettings.first;
|
||||
exportServerConfig->dns2 = dnsSettings.second;
|
||||
|
||||
QByteArray compressedConfig = QJsonDocument(exportServerConfig->toJson()).toJson();
|
||||
compressedConfig = qCompress(compressedConfig, 8);
|
||||
result.config = QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
|
||||
result.qrCodes = generateQrCodeSeries(compressedConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportConfigResult ExportController::generateOpenVpnConfig(const QString &clientName,
|
||||
const ServerCredentials &credentials,
|
||||
const DockerContainer container,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig)
|
||||
{
|
||||
ExportConfigResult result;
|
||||
QJsonObject nativeConfig;
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
|
||||
Proto protocol = Proto::OpenVpn;
|
||||
if (container == DockerContainer::Cloak || container == DockerContainer::ShadowSocks) {
|
||||
protocol = Proto::OpenVpn;
|
||||
} else {
|
||||
protocol = ContainerProps::defaultProtocol(container);
|
||||
}
|
||||
|
||||
result.errorCode = generateNativeConfig(container, clientName, protocol, credentials,
|
||||
containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
result.config.append(line + "\n");
|
||||
}
|
||||
|
||||
result.qrCodes = generateQrCodeSeries(result.config.toUtf8());
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportConfigResult ExportController::generateWireGuardConfig(const QString &clientName,
|
||||
const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig)
|
||||
{
|
||||
ExportConfigResult result;
|
||||
QJsonObject nativeConfig;
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
|
||||
result.errorCode = generateNativeConfig(DockerContainer::WireGuard, clientName, Proto::WireGuard,
|
||||
credentials, containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
result.config.append(line + "\n");
|
||||
}
|
||||
|
||||
result.qrCodes << generateQrCode(result.config.toUtf8());
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportConfigResult ExportController::generateAwgConfig(const QString &clientName,
|
||||
const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig)
|
||||
{
|
||||
ExportConfigResult result;
|
||||
QJsonObject nativeConfig;
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
|
||||
result.errorCode = generateNativeConfig(DockerContainer::Awg, clientName, Proto::Awg,
|
||||
credentials, containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
result.config.append(line + "\n");
|
||||
}
|
||||
|
||||
result.qrCodes << generateQrCode(result.config.toUtf8());
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportConfigResult ExportController::generateShadowSocksConfig(const ServerCredentials &credentials,
|
||||
const DockerContainer container,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig)
|
||||
{
|
||||
ExportConfigResult result;
|
||||
QJsonObject nativeConfig;
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
|
||||
Proto protocol = Proto::ShadowSocks;
|
||||
if (container == DockerContainer::Cloak) {
|
||||
protocol = Proto::ShadowSocks;
|
||||
} else {
|
||||
protocol = ContainerProps::defaultProtocol(container);
|
||||
}
|
||||
|
||||
result.errorCode = generateNativeConfig(container, "", protocol, credentials,
|
||||
containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
result.config.append(line + "\n");
|
||||
}
|
||||
|
||||
result.nativeConfigString = QString("%1:%2@%3:%4")
|
||||
.arg(nativeConfig.value("method").toString(),
|
||||
nativeConfig.value("password").toString(),
|
||||
nativeConfig.value("server").toString(),
|
||||
nativeConfig.value("server_port").toString());
|
||||
|
||||
result.nativeConfigString = "ss://" + result.nativeConfigString.toUtf8().toBase64();
|
||||
result.qrCodes << generateQrCode(result.nativeConfigString.toUtf8());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportConfigResult ExportController::generateCloakConfig(const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig)
|
||||
{
|
||||
ExportConfigResult result;
|
||||
QJsonObject nativeConfig;
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
|
||||
result.errorCode = generateNativeConfig(DockerContainer::Cloak, "", Proto::Cloak,
|
||||
credentials, containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
nativeConfig.remove(config_key::transport_proto);
|
||||
nativeConfig.insert("ProxyMethod", "shadowsocks");
|
||||
|
||||
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
result.config.append(line + "\n");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportConfigResult ExportController::generateXrayConfig(const QString &clientName,
|
||||
const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig)
|
||||
{
|
||||
ExportConfigResult result;
|
||||
QJsonObject nativeConfig;
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
|
||||
result.errorCode = generateNativeConfig(DockerContainer::Xray, clientName, Proto::Xray,
|
||||
credentials, containerConfig, dnsSettings, isApiConfig, nativeConfig, serverController);
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
result.config.append(line + "\n");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ErrorCode ExportController::generateNativeConfig(const DockerContainer container, const QString &clientName,
|
||||
const Proto &protocol, const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig, const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig, QJsonObject &jsonNativeConfig,
|
||||
const QSharedPointer<ServerController> &serverController)
|
||||
{
|
||||
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
||||
|
||||
// Use the provided ContainerConfig directly
|
||||
ContainerConfig modifiedContainerConfig = containerConfig;
|
||||
|
||||
QString protocolConfigString;
|
||||
ErrorCode errorCode = vpnConfigurationController.createProtocolConfigString(isApiConfig, dnsSettings, credentials,
|
||||
container, modifiedContainerConfig,
|
||||
protocol, protocolConfigString);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
jsonNativeConfig = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||
|
||||
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
|
||||
QString protocolName = ProtocolProps::protoToString(protocol);
|
||||
auto protocolConfig = modifiedContainerConfig.protocolConfigs.value(protocolName);
|
||||
|
||||
m_waitingForNativeConfigAppend = true;
|
||||
emit nativeConfigClientAppendRequested(protocolConfig, clientName, container, credentials, serverController);
|
||||
|
||||
QEventLoop loop;
|
||||
QTimer timer;
|
||||
timer.setSingleShot(true);
|
||||
timer.setInterval(30000);
|
||||
|
||||
connect(this, &ExportController::onNativeConfigClientAppendCompleted, &loop, &QEventLoop::quit);
|
||||
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||
|
||||
timer.start();
|
||||
loop.exec();
|
||||
|
||||
m_waitingForNativeConfigAppend = false;
|
||||
errorCode = m_lastNativeConfigAppendResult;
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
void ExportController::onClientAppendCompleted(ErrorCode errorCode)
|
||||
{
|
||||
if (m_waitingForClientAppend) {
|
||||
m_lastClientAppendResult = errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
void ExportController::onNativeConfigClientAppendCompleted(ErrorCode errorCode)
|
||||
{
|
||||
if (m_waitingForNativeConfigAppend) {
|
||||
m_lastNativeConfigAppendResult = errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QString> ExportController::generateQrCodeSeries(const QByteArray &data)
|
||||
{
|
||||
return qrCodeUtils::generateQrCodeImageSeries(data);
|
||||
}
|
||||
|
||||
QString ExportController::generateQrCode(const QByteArray &data)
|
||||
{
|
||||
auto qr = qrCodeUtils::generateQrCode(data);
|
||||
return qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
|
||||
}
|
||||
117
client/core/controllers/selfhosted/exportController.h
Normal file
117
client/core/controllers/selfhosted/exportController.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef EXPORT_CORE_CONTROLLER_H
|
||||
#define EXPORT_CORE_CONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
#include "core/defs.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/models/containers/containerConfig.h"
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
#include "core/models/servers/serverConfig.h"
|
||||
|
||||
class ServerController;
|
||||
class Settings;
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
struct ExportConfigResult
|
||||
{
|
||||
QString config;
|
||||
QString nativeConfigString;
|
||||
QList<QString> qrCodes;
|
||||
ErrorCode errorCode;
|
||||
};
|
||||
|
||||
class ExportController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ExportController(std::shared_ptr<Settings> settings,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void clientAppendRequested(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig, const QString &clientName,
|
||||
const QSharedPointer<ServerController> &serverController);
|
||||
|
||||
void nativeConfigClientAppendRequested(const QSharedPointer<ProtocolConfig> &protocolConfig, const QString &clientName,
|
||||
const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController);
|
||||
|
||||
void configGenerated(const ExportConfigResult &result);
|
||||
void clientManagementError(ErrorCode errorCode);
|
||||
|
||||
public slots:
|
||||
void onClientAppendCompleted(ErrorCode errorCode);
|
||||
void onNativeConfigClientAppendCompleted(ErrorCode errorCode);
|
||||
|
||||
ExportConfigResult generateFullAccessConfig(const QSharedPointer<ServerConfig> &serverConfig);
|
||||
|
||||
ExportConfigResult generateConnectionConfig(const QString &clientName,
|
||||
const ServerCredentials &credentials,
|
||||
const DockerContainer container,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QSharedPointer<ServerConfig> &serverConfig,
|
||||
const QPair<QString, QString> &dnsSettings);
|
||||
|
||||
ExportConfigResult generateOpenVpnConfig(const QString &clientName,
|
||||
const ServerCredentials &credentials,
|
||||
const DockerContainer container,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig = false);
|
||||
|
||||
ExportConfigResult generateWireGuardConfig(const QString &clientName,
|
||||
const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig = false);
|
||||
|
||||
ExportConfigResult generateAwgConfig(const QString &clientName,
|
||||
const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig = false);
|
||||
|
||||
ExportConfigResult generateShadowSocksConfig(const ServerCredentials &credentials,
|
||||
const DockerContainer container,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig = false);
|
||||
|
||||
ExportConfigResult generateCloakConfig(const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig = false);
|
||||
|
||||
ExportConfigResult generateXrayConfig(const QString &clientName,
|
||||
const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig = false);
|
||||
|
||||
private:
|
||||
ErrorCode generateNativeConfig(const DockerContainer container, const QString &clientName,
|
||||
const Proto &protocol, const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig, const QPair<QString, QString> &dnsSettings,
|
||||
bool isApiConfig, QJsonObject &jsonNativeConfig,
|
||||
const QSharedPointer<ServerController> &serverController);
|
||||
|
||||
QList<QString> generateQrCodeSeries(const QByteArray &data);
|
||||
QString generateQrCode(const QByteArray &data);
|
||||
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
|
||||
ErrorCode m_lastClientAppendResult;
|
||||
ErrorCode m_lastNativeConfigAppendResult;
|
||||
bool m_waitingForClientAppend;
|
||||
bool m_waitingForNativeConfigAppend;
|
||||
};
|
||||
|
||||
#endif // EXPORT_CORE_CONTROLLER_H
|
||||
397
client/core/controllers/selfhosted/installController.cpp
Normal file
397
client/core/controllers/selfhosted/installController.cpp
Normal file
@@ -0,0 +1,397 @@
|
||||
#include "installController.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QRandomGenerator>
|
||||
#include <QStandardPaths>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "core/api/apiUtils.h"
|
||||
#include "core/controllers/selfhosted/serverController.h"
|
||||
|
||||
#include "core/controllers/vpnConfigurationController.h"
|
||||
#include "core/models/servers/selfHostedServerConfig.h"
|
||||
#include "core/models/protocols/awgProtocolConfig.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "logger.h"
|
||||
#include "utilities.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
Logger logger("CoreInstallController");
|
||||
}
|
||||
|
||||
InstallController::InstallController(std::shared_ptr<Settings> settings,
|
||||
QObject *parent)
|
||||
: QObject(parent),
|
||||
m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
InstallResult InstallController::installContainer(DockerContainer container, int port, TransportProto transportProto,
|
||||
const ServerCredentials &serverCredentials, bool shouldCreateServer,
|
||||
const QString &privateKeyPassphrase)
|
||||
{
|
||||
InstallResult result;
|
||||
result.errorCode = ErrorCode::NoError;
|
||||
result.isServiceInstall = (ContainerProps::containerService(container) == ServiceType::Other);
|
||||
result.isInstalledContainerFound = false;
|
||||
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
|
||||
ContainerConfig containerConfig = generateContainerConfig(container, port, transportProto);
|
||||
|
||||
if (shouldCreateServer && isServerAlreadyExists(serverCredentials)) {
|
||||
result.errorCode = ErrorCode::InternalError;
|
||||
return result;
|
||||
}
|
||||
|
||||
QMap<DockerContainer, ContainerConfig> installedContainers;
|
||||
ErrorCode errorCode = getAlreadyInstalledContainers(serverCredentials, serverController, installedContainers);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
result.errorCode = errorCode;
|
||||
return result;
|
||||
}
|
||||
|
||||
QSharedPointer<ServerConfig> serverConfig;
|
||||
if (shouldCreateServer) {
|
||||
errorCode = installServer(container, installedContainers, serverCredentials,
|
||||
serverController, result.message, serverConfig);
|
||||
} else {
|
||||
if (installedContainers.contains(container)) {
|
||||
result.errorCode = ErrorCode::InternalError;
|
||||
return result;
|
||||
}
|
||||
|
||||
errorCode = installContainer(container, installedContainers, serverCredentials,
|
||||
serverController, result.message);
|
||||
}
|
||||
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
result.errorCode = errorCode;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.errorCode = ErrorCode::NoError;
|
||||
return result;
|
||||
}
|
||||
|
||||
InstallResult InstallController::scanServerForInstalledContainers(const ServerCredentials &serverCredentials)
|
||||
{
|
||||
InstallResult result;
|
||||
result.errorCode = ErrorCode::NoError;
|
||||
result.isInstalledContainerFound = false;
|
||||
result.isServiceInstall = false;
|
||||
|
||||
QMap<DockerContainer, ContainerConfig> installedContainers;
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
result.errorCode = getAlreadyInstalledContainers(serverCredentials, serverController, installedContainers);
|
||||
|
||||
if (result.errorCode == ErrorCode::NoError) {
|
||||
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
||||
|
||||
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
|
||||
auto container = iterator.key();
|
||||
ContainerConfig containerConfig = iterator.value();
|
||||
|
||||
if (ContainerProps::isSupportedByCurrentPlatform(container)) {
|
||||
result.errorCode = vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, container, containerConfig);
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
emit clientAppendRequested(container, serverCredentials, containerConfig,
|
||||
QString("Admin [%1]").arg(QSysInfo::prettyProductName()),
|
||||
serverController);
|
||||
}
|
||||
|
||||
result.isInstalledContainerFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController,
|
||||
QMap<DockerContainer, ContainerConfig> &installedContainers)
|
||||
{
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
QString script = QString("sudo docker ps --format '{{.Names}} {{.Ports}}'");
|
||||
|
||||
ErrorCode errorCode = serverController->runScript(credentials, script, cbReadStdOut, cbReadStdErr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
auto containersInfo = stdOut.split("\n");
|
||||
for (auto &containerInfo : containersInfo) {
|
||||
if (containerInfo.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
const static QRegularExpression containerAndPortRegExp("(amnezia[-a-z0-9]*).*?:([0-9]*)->[0-9]*/(udp|tcp).*");
|
||||
QRegularExpressionMatch containerAndPortMatch = containerAndPortRegExp.match(containerInfo);
|
||||
if (containerAndPortMatch.hasMatch()) {
|
||||
QString containerName = containerAndPortMatch.captured(1);
|
||||
QString port = containerAndPortMatch.captured(2);
|
||||
QString transportProto = containerAndPortMatch.captured(3);
|
||||
|
||||
DockerContainer container = ContainerProps::containerFromString(containerName);
|
||||
if (container != DockerContainer::None) {
|
||||
ContainerConfig containerConfig;
|
||||
containerConfig.containerName = ContainerProps::containerToString(container);
|
||||
|
||||
auto containerProto = ContainerProps::defaultProtocol(container);
|
||||
auto containerProtoString = ProtocolProps::protoToString(containerProto);
|
||||
|
||||
// Create appropriate protocol config based on container type
|
||||
QSharedPointer<ProtocolConfig> protocolConfig;
|
||||
|
||||
// Create a temporary QJsonObject to construct the protocol config
|
||||
QJsonObject protoConfigObject;
|
||||
protoConfigObject.insert(config_key::port, port);
|
||||
protoConfigObject.insert(config_key::transport_proto, transportProto);
|
||||
|
||||
// Create the appropriate protocol config based on type
|
||||
switch (containerProto) {
|
||||
case Proto::OpenVpn:
|
||||
protocolConfig = QSharedPointer<OpenVpnProtocolConfig>::create(protoConfigObject, containerProtoString);
|
||||
break;
|
||||
case Proto::WireGuard:
|
||||
protocolConfig = QSharedPointer<WireGuardProtocolConfig>::create(protoConfigObject, containerProtoString);
|
||||
break;
|
||||
case Proto::Awg:
|
||||
protocolConfig = QSharedPointer<AwgProtocolConfig>::create(protoConfigObject, containerProtoString);
|
||||
break;
|
||||
case Proto::Cloak:
|
||||
protocolConfig = QSharedPointer<CloakProtocolConfig>::create(protoConfigObject, containerProtoString);
|
||||
break;
|
||||
case Proto::ShadowSocks:
|
||||
protocolConfig = QSharedPointer<ShadowsocksProtocolConfig>::create(protoConfigObject, containerProtoString);
|
||||
break;
|
||||
case Proto::Xray:
|
||||
case Proto::SSXray:
|
||||
protocolConfig = QSharedPointer<XrayProtocolConfig>::create(protoConfigObject, containerProtoString);
|
||||
break;
|
||||
case Proto::Ikev2:
|
||||
protocolConfig = QSharedPointer<Ikev2ProtocolConfig>::create(protoConfigObject, containerProtoString);
|
||||
break;
|
||||
case Proto::Sftp:
|
||||
protocolConfig = QSharedPointer<SftpProtocolConfig>::create(protoConfigObject, containerProtoString);
|
||||
break;
|
||||
case Proto::Socks5Proxy:
|
||||
protocolConfig = QSharedPointer<Socks5ProtocolConfig>::create(protoConfigObject, containerProtoString);
|
||||
break;
|
||||
default:
|
||||
continue; // Skip unknown protocols
|
||||
}
|
||||
|
||||
if (protocolConfig) {
|
||||
containerConfig.protocolConfigs.insert(containerProtoString, protocolConfig);
|
||||
installedContainers.insert(container, containerConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode InstallController::installServer(const DockerContainer container,
|
||||
const QMap<DockerContainer, ContainerConfig> &installedContainers,
|
||||
const ServerCredentials &serverCredentials,
|
||||
const QSharedPointer<ServerController> &serverController,
|
||||
QString &finishMessage, QSharedPointer<ServerConfig> &serverConfig)
|
||||
{
|
||||
if (installedContainers.size() > 1) {
|
||||
finishMessage += tr("\nAdded containers that were already installed on the server");
|
||||
}
|
||||
|
||||
// Create a SelfHostedServerConfig and populate it properly
|
||||
serverConfig = QSharedPointer<SelfHostedServerConfig>::create(QJsonObject());
|
||||
auto selfHostedConfig = qSharedPointerCast<SelfHostedServerConfig>(serverConfig);
|
||||
|
||||
selfHostedConfig->hostName = serverCredentials.hostName;
|
||||
selfHostedConfig->serverCredentials = serverCredentials;
|
||||
selfHostedConfig->name = m_settings->nextAvailableServerName();
|
||||
selfHostedConfig->defaultContainer = ContainerProps::containerToString(container);
|
||||
selfHostedConfig->type = amnezia::ServerConfigType::SelfHosted;
|
||||
|
||||
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
||||
|
||||
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
|
||||
auto containerConfig = iterator.value();
|
||||
QString containerName = ContainerProps::containerToString(iterator.key());
|
||||
|
||||
if (ContainerProps::isSupportedByCurrentPlatform(iterator.key())) {
|
||||
auto errorCode = vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, iterator.key(),
|
||||
containerConfig);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
selfHostedConfig->containerConfigs.insert(containerName, containerConfig);
|
||||
}
|
||||
|
||||
int serverIndex = m_settings->nextAvailableServerIndex();
|
||||
m_settings->setServerConfig(serverIndex, serverConfig->toJson());
|
||||
m_settings->setDefaultServerIndex(serverIndex);
|
||||
|
||||
finishMessage = tr("Server '%1' was successfully added").arg(serverCredentials.hostName);
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode InstallController::installContainer(const DockerContainer container,
|
||||
const QMap<DockerContainer, ContainerConfig> &installedContainers,
|
||||
const ServerCredentials &serverCredentials,
|
||||
const QSharedPointer<ServerController> &serverController,
|
||||
QString &finishMessage)
|
||||
{
|
||||
bool isInstalledContainerAddedToGui = false;
|
||||
|
||||
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
||||
QList<QJsonObject> allContainerConfigs;
|
||||
|
||||
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
|
||||
QJsonObject containerConfig = iterator.value();
|
||||
|
||||
if (ContainerProps::isSupportedByCurrentPlatform(container)) {
|
||||
auto errorCode = vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, iterator.key(), containerConfig);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
allContainerConfigs.append(containerConfig);
|
||||
} else {
|
||||
allContainerConfigs.append(containerConfig);
|
||||
}
|
||||
|
||||
if (container != iterator.key()) {
|
||||
isInstalledContainerAddedToGui = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInstalledContainerAddedToGui) {
|
||||
finishMessage += tr("\nAlready installed containers were found on the server. "
|
||||
"All installed containers have been added to the application");
|
||||
}
|
||||
|
||||
finishMessage = tr("Container '%1' was successfully added").arg(ContainerProps::containerHumanNames().value(container));
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
bool InstallController::isServerAlreadyExists(const ServerCredentials &serverCredentials) const
|
||||
{
|
||||
for (int i = 0; i < m_settings->serversCount(); i++) {
|
||||
QJsonObject serverConfig = m_settings->serverConfig(i);
|
||||
ServerCredentials existingCredentials = ServerCredentials::fromServerConfig(serverConfig);
|
||||
if (serverCredentials.hostName == existingCredentials.hostName &&
|
||||
serverCredentials.port == existingCredentials.port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ContainerConfig InstallController::generateContainerConfig(const DockerContainer container, int port,
|
||||
const TransportProto transportProto)
|
||||
{
|
||||
ContainerConfig containerConfig;
|
||||
containerConfig.containerName = ContainerProps::containerToString(container);
|
||||
|
||||
auto mainProto = ContainerProps::defaultProtocol(container);
|
||||
for (auto protocol : ContainerProps::protocolsForContainer(container)) {
|
||||
QSharedPointer<ProtocolConfig> protocolConfig;
|
||||
|
||||
if (protocol == mainProto) {
|
||||
// Create the appropriate protocol config based on the protocol type
|
||||
if (protocol == Proto::Awg && container == DockerContainer::Awg) {
|
||||
// Use the VpnConfigurationsController to create proper AwgProtocolConfig
|
||||
// For now, create a basic protocol config and set values
|
||||
protocolConfig = QSharedPointer<AwgProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
|
||||
auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig);
|
||||
|
||||
// Set server protocol config
|
||||
awgConfig->serverProtocolConfig.port = QString::number(port);
|
||||
awgConfig->serverProtocolConfig.transportProto = ProtocolProps::transportProtoToString(transportProto, protocol);
|
||||
|
||||
// Generate AWG-specific parameters
|
||||
awgConfig->serverProtocolConfig.junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5));
|
||||
awgConfig->serverProtocolConfig.junkPacketMinSize = QString::number(10);
|
||||
awgConfig->serverProtocolConfig.junkPacketMaxSize = QString::number(50);
|
||||
|
||||
int s1 = QRandomGenerator::global()->bounded(15, 150);
|
||||
int s2 = QRandomGenerator::global()->bounded(15, 150);
|
||||
QSet<int> usedValues;
|
||||
usedValues.insert(s1);
|
||||
while (usedValues.contains(s2) || s1 + AwgConstant::messageInitiationSize == s2 + AwgConstant::messageResponseSize) {
|
||||
s2 = QRandomGenerator::global()->bounded(15, 150);
|
||||
}
|
||||
usedValues.insert(s2);
|
||||
|
||||
awgConfig->serverProtocolConfig.initPacketJunkSize = QString::number(s1);
|
||||
awgConfig->serverProtocolConfig.responsePacketJunkSize = QString::number(s2);
|
||||
|
||||
QSet<QString> headersValue;
|
||||
while (headersValue.size() != 4) {
|
||||
auto max = (std::numeric_limits<qint32>::max)();
|
||||
headersValue.insert(QString::number(QRandomGenerator::global()->bounded(5, max)));
|
||||
}
|
||||
auto headersValueList = headersValue.values();
|
||||
awgConfig->serverProtocolConfig.initPacketMagicHeader = headersValueList.at(0);
|
||||
awgConfig->serverProtocolConfig.responsePacketMagicHeader = headersValueList.at(1);
|
||||
awgConfig->serverProtocolConfig.underloadPacketMagicHeader = headersValueList.at(2);
|
||||
awgConfig->serverProtocolConfig.transportPacketMagicHeader = headersValueList.at(3);
|
||||
|
||||
} else {
|
||||
// For other protocols, create basic protocol configs
|
||||
// This will need to be expanded for each protocol type
|
||||
protocolConfig = QSharedPointer<ProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
|
||||
|
||||
// For now, store basic port and transport info as JSON until all protocol configs are properly structured
|
||||
QJsonObject tempConfig;
|
||||
tempConfig.insert(config_key::port, QString::number(port));
|
||||
tempConfig.insert(config_key::transport_proto, ProtocolProps::transportProtoToString(transportProto, protocol));
|
||||
|
||||
if (container == DockerContainer::Sftp) {
|
||||
tempConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
|
||||
tempConfig.insert(config_key::password, Utils::getRandomString(16));
|
||||
} else if (container == DockerContainer::Socks5Proxy) {
|
||||
tempConfig.insert(config_key::userName, protocols::socks5Proxy::defaultUserName);
|
||||
tempConfig.insert(config_key::password, Utils::getRandomString(16));
|
||||
}
|
||||
|
||||
// Store this in the protocol config's internal JSON for now
|
||||
// TODO: Replace with proper protocol config structure
|
||||
protocolConfig = QSharedPointer<ProtocolConfig>::create(tempConfig, ProtocolProps::protoToString(protocol));
|
||||
}
|
||||
} else {
|
||||
// Non-main protocols get basic configs
|
||||
protocolConfig = QSharedPointer<ProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
|
||||
}
|
||||
|
||||
containerConfig.protocolConfigs.insert(ProtocolProps::protoToString(protocol), protocolConfig);
|
||||
}
|
||||
|
||||
return containerConfig;
|
||||
}
|
||||
|
||||
ErrorCode InstallController::checkSshConnection(const ServerCredentials &serverCredentials, const QString &privateKeyPassphrase)
|
||||
{
|
||||
Q_UNUSED(privateKeyPassphrase);
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
ErrorCode errorCode = ErrorCode::NoError;
|
||||
serverController->checkSshConnection(serverCredentials, errorCode);
|
||||
return errorCode;
|
||||
}
|
||||
111
client/core/controllers/selfhosted/installController.h
Normal file
111
client/core/controllers/selfhosted/installController.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifndef INSTALL_CORE_CONTROLLER_H
|
||||
#define INSTALL_CORE_CONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QMap>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "core/defs.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/models/containers/containerConfig.h"
|
||||
#include "core/models/servers/serverConfig.h"
|
||||
|
||||
class ServerController;
|
||||
class Settings;
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
struct InstallResult
|
||||
{
|
||||
ErrorCode errorCode;
|
||||
QString message;
|
||||
bool isServiceInstall;
|
||||
bool isInstalledContainerFound;
|
||||
};
|
||||
|
||||
class InstallController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InstallController(std::shared_ptr<Settings> settings,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
// Main installation operations
|
||||
InstallResult installContainer(DockerContainer container, int port, TransportProto transportProto,
|
||||
const ServerCredentials &serverCredentials, bool shouldCreateServer,
|
||||
const QString &privateKeyPassphrase = QString());
|
||||
|
||||
InstallResult scanServerForInstalledContainers(const ServerCredentials &serverCredentials);
|
||||
|
||||
InstallResult updateContainer(const DockerContainer container, const ServerCredentials &serverCredentials,
|
||||
const ContainerConfig &containerConfig);
|
||||
|
||||
// Server management operations
|
||||
ErrorCode removeProcessedServer(const ServerCredentials &serverCredentials);
|
||||
ErrorCode rebootProcessedServer(const ServerCredentials &serverCredentials);
|
||||
ErrorCode removeAllContainers(const ServerCredentials &serverCredentials);
|
||||
ErrorCode removeProcessedContainer(const DockerContainer container, const ServerCredentials &serverCredentials);
|
||||
|
||||
ErrorCode removeApiConfig(const QSharedPointer<ServerConfig> &serverConfig);
|
||||
ErrorCode clearCachedProfile(const ServerCredentials &serverCredentials);
|
||||
|
||||
// Utility methods
|
||||
ErrorCode mountSftpDrive(const ServerCredentials &serverCredentials, const QString &port,
|
||||
const QString &password, const QString &username);
|
||||
|
||||
QString getNextAvailableServerName() const;
|
||||
ErrorCode addEmptyServer(const ServerCredentials &serverCredentials);
|
||||
bool isConfigValid(const ServerCredentials &serverCredentials) const;
|
||||
|
||||
ErrorCode checkSshConnection(const ServerCredentials &serverCredentials, const QString &privateKeyPassphrase);
|
||||
|
||||
signals:
|
||||
void clientAppendRequested(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const ContainerConfig &containerConfig, const QString &clientName,
|
||||
const QSharedPointer<ServerController> &serverController);
|
||||
|
||||
void containerInstalled(const InstallResult &result);
|
||||
void serverScanned(const InstallResult &result);
|
||||
void containerUpdated(const InstallResult &result);
|
||||
void serverRebooted(const QString &message);
|
||||
void serverRemoved(const QString &message);
|
||||
void allContainersRemoved(const QString &message);
|
||||
void containerRemoved(const QString &message);
|
||||
void installationError(ErrorCode errorCode);
|
||||
void wrongInstallationUser(const QString &message);
|
||||
void serverIsBusy(bool isBusy);
|
||||
void cachedProfileCleared(const QString &message);
|
||||
void apiConfigRemoved(const QString &message);
|
||||
void noInstalledContainers();
|
||||
|
||||
private:
|
||||
// Installation helpers
|
||||
ErrorCode installServer(const DockerContainer container,
|
||||
const QMap<DockerContainer, ContainerConfig> &installedContainers,
|
||||
const ServerCredentials &serverCredentials,
|
||||
const QSharedPointer<ServerController> &serverController,
|
||||
QString &finishMessage, QSharedPointer<ServerConfig> &serverConfig);
|
||||
|
||||
ErrorCode installContainer(const DockerContainer container,
|
||||
const QMap<DockerContainer, ContainerConfig> &installedContainers,
|
||||
const ServerCredentials &serverCredentials,
|
||||
const QSharedPointer<ServerController> &serverController,
|
||||
QString &finishMessage);
|
||||
|
||||
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController,
|
||||
QMap<DockerContainer, ContainerConfig> &installedContainers);
|
||||
|
||||
ContainerConfig generateContainerConfig(const DockerContainer container, int port,
|
||||
const TransportProto transportProto);
|
||||
|
||||
bool isServerAlreadyExists(const ServerCredentials &serverCredentials) const;
|
||||
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
};
|
||||
|
||||
#endif // INSTALL_CORE_CONTROLLER_H
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "selfhostedConfigController.h"
|
||||
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/models/protocols/awgProtocolConfig.h"
|
||||
#include "core/models/protocols/cloakProtocolConfig.h"
|
||||
#include "core/models/protocols/openvpnProtocolConfig.h"
|
||||
#include "core/models/protocols/shadowsocksProtocolConfig.h"
|
||||
#include "core/models/protocols/wireguardProtocolConfig.h"
|
||||
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "settings.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
Logger logger("SelfhostedConfigController");
|
||||
}
|
||||
|
||||
SelfhostedConfigController::SelfhostedConfigController(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: ConfigController(settings, parent)
|
||||
{
|
||||
m_isAmneziaDnsEnabled = m_settings->useAmneziaDns();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SelfhostedConfigController::toggleAmneziaDns(bool enabled)
|
||||
{
|
||||
m_isAmneziaDnsEnabled = enabled;
|
||||
m_settings->setUseAmneziaDns(enabled);
|
||||
emit amneziaDnsToggled(enabled);
|
||||
}
|
||||
|
||||
QPair<QString, QString> SelfhostedConfigController::getDnsPair(int serverIndex) const
|
||||
{
|
||||
auto servers = m_settings->serversArray();
|
||||
if (serverIndex >= servers.size()) return {};
|
||||
|
||||
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
|
||||
QPair<QString, QString> dns;
|
||||
|
||||
bool isDnsContainerInstalled = isAmneziaDnsContainerInstalled(serverIndex);
|
||||
|
||||
dns.first = serverConfig->dns1;
|
||||
dns.second = serverConfig->dns2;
|
||||
|
||||
if (isDnsContainerInstalled && m_isAmneziaDnsEnabled) {
|
||||
dns.first = serverConfig->hostName;
|
||||
dns.second = "";
|
||||
}
|
||||
|
||||
return dns;
|
||||
}
|
||||
|
||||
bool SelfhostedConfigController::isAmneziaDnsContainerInstalled(int serverIndex) const
|
||||
{
|
||||
auto servers = m_settings->serversArray();
|
||||
if (serverIndex >= servers.size()) return false;
|
||||
|
||||
auto serverConfig = ServerConfig::createServerConfig(servers.at(serverIndex).toObject());
|
||||
|
||||
for (const auto &container : serverConfig->containerConfigs) {
|
||||
auto containerType = ContainerProps::containerFromString(container.containerName);
|
||||
if (containerType == DockerContainer::Dns) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QMap<QString, QSharedPointer<ProtocolConfig>> SelfhostedConfigController::getProtocolConfigs(const QVector<QSharedPointer<ProtocolConfig>> &protocols)
|
||||
{
|
||||
QMap<QString, QSharedPointer<ProtocolConfig>> protocolConfigs;
|
||||
|
||||
for (const auto &config : protocols) {
|
||||
Proto protocol = ProtocolProps::protoFromString(config->protocolName);
|
||||
|
||||
if (isProtocolSupported(protocol)) {
|
||||
protocolConfigs.insert(config->protocolName, config);
|
||||
}
|
||||
}
|
||||
|
||||
return protocolConfigs;
|
||||
}
|
||||
|
||||
void SelfhostedConfigController::updateProtocolConfiguration(Proto protocol, const QSharedPointer<ProtocolConfig> &protocolConfig)
|
||||
{
|
||||
if (!isProtocolSupported(protocol)) {
|
||||
logger.warning() << "Protocol not supported:" << ProtocolProps::protoToString(protocol);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info() << "Updating protocol configuration for:" << ProtocolProps::protoToString(protocol);
|
||||
|
||||
emit protocolConfigUpdated(protocol, protocolConfig);
|
||||
}
|
||||
|
||||
QSharedPointer<ProtocolConfig> SelfhostedConfigController::createProtocolConfig(Proto protocol)
|
||||
{
|
||||
if (!isProtocolSupported(protocol)) {
|
||||
logger.warning() << "Attempting to create unsupported protocol config:" << ProtocolProps::protoToString(protocol);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
case Proto::Awg:
|
||||
return QSharedPointer<AwgProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::Awg));
|
||||
case Proto::Cloak:
|
||||
return QSharedPointer<CloakProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::Cloak));
|
||||
case Proto::OpenVpn:
|
||||
return QSharedPointer<OpenVpnProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::OpenVpn));
|
||||
case Proto::ShadowSocks:
|
||||
return QSharedPointer<ShadowsocksProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::ShadowSocks));
|
||||
case Proto::WireGuard:
|
||||
return QSharedPointer<WireGuardProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::WireGuard));
|
||||
case Proto::Xray:
|
||||
return QSharedPointer<XrayProtocolConfig>::create(QJsonObject{}, ProtocolProps::protoToString(Proto::Xray));
|
||||
default:
|
||||
logger.warning() << "Unknown protocol in createProtocolConfig:" << ProtocolProps::protoToString(protocol);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool SelfhostedConfigController::isProtocolSupported(Proto protocol) const
|
||||
{
|
||||
switch (protocol) {
|
||||
case Proto::Awg:
|
||||
case Proto::Cloak:
|
||||
case Proto::OpenVpn:
|
||||
case Proto::ShadowSocks:
|
||||
case Proto::WireGuard:
|
||||
case Proto::Xray:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef SELFHOSTEDCONFIGCONTROLLER_H
|
||||
#define SELFHOSTEDCONFIGCONTROLLER_H
|
||||
|
||||
#include "../configController.h"
|
||||
#include "core/models/containers/containerConfig.h"
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
|
||||
#include <QMap>
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
class SelfhostedConfigController : public ConfigController
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SelfhostedConfigController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
|
||||
// Self-hosted specific functionality
|
||||
|
||||
// Amnezia DNS management
|
||||
void toggleAmneziaDns(bool enabled);
|
||||
QPair<QString, QString> getDnsPair(int serverIndex) const;
|
||||
bool isAmneziaDnsContainerInstalled(int serverIndex) const;
|
||||
|
||||
// Protocol management (from ProtocolConfigController)
|
||||
QMap<QString, QSharedPointer<ProtocolConfig>> getProtocolConfigs(const QVector<QSharedPointer<ProtocolConfig>> &protocols);
|
||||
void updateProtocolConfiguration(Proto protocol, const QSharedPointer<ProtocolConfig> &protocolConfig);
|
||||
QSharedPointer<ProtocolConfig> createProtocolConfig(Proto protocol);
|
||||
bool isProtocolSupported(Proto protocol) const;
|
||||
|
||||
private:
|
||||
bool m_isAmneziaDnsEnabled;
|
||||
|
||||
// Helper methods
|
||||
bool checkSplitTunnelingInContainer(const ContainerConfig &containerConfig,
|
||||
const QString &defaultContainer) const;
|
||||
|
||||
signals:
|
||||
// Self-hosted specific signals
|
||||
void amneziaDnsToggled(bool enabled);
|
||||
void protocolConfigUpdated(Proto protocol, const QSharedPointer<ProtocolConfig> &config);
|
||||
};
|
||||
|
||||
#endif // SELFHOSTEDCONFIGCONTROLLER_H
|
||||
@@ -8,6 +8,15 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "core/models/protocols/openvpnProtocolConfig.h"
|
||||
#include "core/models/protocols/wireguardProtocolConfig.h"
|
||||
#include "core/models/protocols/awgProtocolConfig.h"
|
||||
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||
#include "core/models/protocols/shadowsocksProtocolConfig.h"
|
||||
#include "core/models/protocols/cloakProtocolConfig.h"
|
||||
#include "core/models/protocols/sftpProtocolConfig.h"
|
||||
#include "core/models/protocols/socks5ProtocolConfig.h"
|
||||
#include <QPointer>
|
||||
#include <QTemporaryFile>
|
||||
#include <QThread>
|
||||
@@ -22,14 +31,22 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "logger.h"
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
#include "vpnConfigurationController.h"
|
||||
#include "configurators/awg_configurator.h"
|
||||
#include "configurators/cloak_configurator.h"
|
||||
#include "configurators/ikev2_configurator.h"
|
||||
#include "configurators/openvpn_configurator.h"
|
||||
#include "configurators/shadowsocks_configurator.h"
|
||||
#include "configurators/wireguard_configurator.h"
|
||||
#include "configurators/xray_configurator.h"
|
||||
#include "core/models/protocols/sftpProtocolConfig.h"
|
||||
#include "core/models/protocols/socks5ProtocolConfig.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -106,10 +123,10 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
|
||||
|
||||
QString runner =
|
||||
QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, (container == DockerContainer::Socks5Proxy ? "sh" : "bash"));
|
||||
e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
e = runScript(credentials, replaceVars(runner, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
|
||||
runScript(credentials, replaceVars(remover, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
runScript(credentials, replaceVars(remover, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -132,30 +149,30 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
// mkdir
|
||||
QString mkdir = QString("sudo docker exec -i $CONTAINER_NAME mkdir -p \"$(dirname %1)\"").arg(path);
|
||||
|
||||
e = runScript(credentials, replaceVars(mkdir, genVarsForScript(credentials, container)));
|
||||
e = runScript(credentials, replaceVars(mkdir, generateVarsForContainer(credentials, container)));
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
|
||||
genVarsForScript(credentials, container)),
|
||||
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path),
|
||||
generateVarsForContainer(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
|
||||
if (e)
|
||||
return e;
|
||||
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
|
||||
genVarsForScript(credentials, container)),
|
||||
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName),
|
||||
generateVarsForContainer(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
||||
genVarsForScript(credentials, container)),
|
||||
replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path),
|
||||
generateVarsForContainer(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
|
||||
if (e)
|
||||
@@ -167,7 +184,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
return ErrorCode::ServerContainerMissingError;
|
||||
}
|
||||
|
||||
runScript(credentials, replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container)));
|
||||
runScript(credentials, replaceVars(QString("sudo shred -u %1").arg(tmpFileName), generateVarsForContainer(credentials, container)));
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -177,7 +194,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
||||
|
||||
errorCode = ErrorCode::NoError;
|
||||
|
||||
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").arg(ContainerProps::containerToString(container)).arg(path);
|
||||
QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path);
|
||||
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
@@ -236,10 +253,10 @@ ErrorCode ServerController::removeAllContainers(const ServerCredentials &credent
|
||||
ErrorCode ServerController::removeContainer(const ServerCredentials &credentials, DockerContainer container)
|
||||
{
|
||||
return runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::remove_container), genVarsForScript(credentials, container)));
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::remove_container), generateVarsForContainer(credentials, container)));
|
||||
}
|
||||
|
||||
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config, bool isUpdate)
|
||||
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate)
|
||||
{
|
||||
qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
|
||||
ErrorCode e = ErrorCode::NoError;
|
||||
@@ -299,8 +316,8 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
|
||||
return startupContainerWorker(credentials, container, config);
|
||||
}
|
||||
|
||||
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig,
|
||||
QJsonObject &newConfig)
|
||||
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &oldConfig,
|
||||
ContainerConfig &newConfig)
|
||||
{
|
||||
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
|
||||
qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is" << reinstallRequired;
|
||||
@@ -316,70 +333,20 @@ ErrorCode ServerController::updateContainer(const ServerCredentials &credentials
|
||||
}
|
||||
}
|
||||
|
||||
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig)
|
||||
bool ServerController::isReinstallContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig)
|
||||
{
|
||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||
const auto &mainProto = ContainerProps::defaultProtocol(container);
|
||||
const QString protocolName = ProtocolProps::protoToString(mainProto);
|
||||
|
||||
const QJsonObject &oldProtoConfig = oldConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
|
||||
const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
|
||||
|
||||
if (container == DockerContainer::OpenVpn) {
|
||||
if (oldProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto)
|
||||
!= newProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto))
|
||||
return true;
|
||||
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort))
|
||||
return true;
|
||||
const auto oldProtocolConfig = oldConfig.protocolConfigs.value(protocolName);
|
||||
const auto newProtocolConfig = newConfig.protocolConfigs.value(protocolName);
|
||||
|
||||
if (!oldProtocolConfig || !newProtocolConfig) {
|
||||
return true; // If either config is missing, reinstall is required
|
||||
}
|
||||
|
||||
if (container == DockerContainer::Cloak) {
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (container == DockerContainer::ShadowSocks) {
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (container == DockerContainer::Awg) {
|
||||
if ((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)
|
||||
!= newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount))
|
||||
|| (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)
|
||||
!= newProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize))
|
||||
|| (oldProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize)
|
||||
!= newProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize))
|
||||
|| (oldProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize)
|
||||
!= newProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize))
|
||||
|| (oldProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize)
|
||||
!= newProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize))
|
||||
|| (oldProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader)
|
||||
!= newProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader))
|
||||
|| (oldProtoConfig.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)
|
||||
!= newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader))
|
||||
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)
|
||||
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (container == DockerContainer::WireGuard) {
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (container == DockerContainer::Socks5Proxy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Use the existing isServerSettingsEqual method from ProtocolConfig
|
||||
return !oldProtocolConfig->isServerSettingsEqual(newProtocolConfig);
|
||||
}
|
||||
|
||||
ErrorCode ServerController::installDockerWorker(const ServerCredentials &credentials, DockerContainer container)
|
||||
@@ -399,7 +366,7 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
|
||||
};
|
||||
|
||||
ErrorCode error =
|
||||
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)),
|
||||
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::install_docker), generateVarsForContainer(credentials, DockerContainer::None)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
|
||||
@@ -411,17 +378,17 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
|
||||
return error;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
|
||||
{
|
||||
// create folder on host
|
||||
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), genVarsForScript(credentials, container)));
|
||||
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), generateVarsForContainer(credentials, container)));
|
||||
}
|
||||
|
||||
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
|
||||
{
|
||||
QString dockerFilePath = amnezia::server::getDockerfileFolder(container) + "/Dockerfile";
|
||||
QString scriptString = QString("sudo rm %1").arg(dockerFilePath);
|
||||
ErrorCode errorCode = runScript(credentials, replaceVars(scriptString, genVarsForScript(credentials, container)));
|
||||
ErrorCode errorCode = runScript(credentials, replaceVars(scriptString, generateVarsForContainer(credentials, container)));
|
||||
if (errorCode)
|
||||
return errorCode;
|
||||
|
||||
@@ -435,18 +402,27 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
errorCode =
|
||||
ErrorCode error =
|
||||
runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
|
||||
cbReadStdOut);
|
||||
if (errorCode)
|
||||
return errorCode;
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container), generateVarsForContainer(credentials, container, config)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
return errorCode;
|
||||
if (stdOut.contains("doesn't work on cgroups v2"))
|
||||
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, ContainerConfig &config)
|
||||
{
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
@@ -456,7 +432,7 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
|
||||
|
||||
ErrorCode e = runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
|
||||
genVarsForScript(credentials, container, config)),
|
||||
generateVarsForContainer(credentials, container, config)),
|
||||
cbReadStdOut);
|
||||
|
||||
if (stdOut.contains("address already in use"))
|
||||
@@ -469,7 +445,7 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
|
||||
return e;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
||||
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config)
|
||||
{
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
@@ -483,15 +459,16 @@ ErrorCode ServerController::configureContainerWorker(const ServerCredentials &cr
|
||||
|
||||
ErrorCode e = runContainerScript(credentials, container,
|
||||
replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container),
|
||||
genVarsForScript(credentials, container, config)),
|
||||
generateVarsForContainer(credentials, container, config)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
// ensure header is included where needed; call into controller utility if accessible
|
||||
VpnConfigurationsController::updateContainerConfigAfterInstallation(container, config, stdOut);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
|
||||
{
|
||||
QString script = amnezia::scriptData(ProtocolScriptType::container_startup, container);
|
||||
|
||||
@@ -499,7 +476,7 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode e = uploadTextFileToContainer(container, credentials, replaceVars(script, genVarsForScript(credentials, container, config)),
|
||||
ErrorCode e = uploadTextFileToContainer(container, credentials, replaceVars(script, generateVarsForContainer(credentials, container, config)),
|
||||
"/opt/amnezia/start.sh");
|
||||
if (e)
|
||||
return e;
|
||||
@@ -507,133 +484,105 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
|
||||
return runScript(credentials,
|
||||
replaceVars("sudo docker exec -d $CONTAINER_NAME sh -c \"chmod a+x /opt/amnezia/start.sh && "
|
||||
"/opt/amnezia/start.sh\"",
|
||||
genVarsForScript(credentials, container, config)));
|
||||
generateVarsForContainer(credentials, container, config)));
|
||||
}
|
||||
|
||||
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config)
|
||||
|
||||
|
||||
ServerController::Vars ServerController::generateVarsForContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||
const ContainerConfig &config)
|
||||
{
|
||||
const QJsonObject &openvpnConfig = config.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject();
|
||||
const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject();
|
||||
const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject();
|
||||
const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
|
||||
const QJsonObject &amneziaWireguarConfig = config.value(ProtocolProps::protoToString(Proto::Awg)).toObject();
|
||||
const QJsonObject &xrayConfig = config.value(ProtocolProps::protoToString(Proto::Xray)).toObject();
|
||||
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
|
||||
const QJsonObject &socks5ProxyConfig = config.value(ProtocolProps::protoToString(Proto::Socks5Proxy)).toObject();
|
||||
|
||||
Vars vars;
|
||||
|
||||
vars.append({ { "$REMOTE_HOST", credentials.hostName } });
|
||||
|
||||
// OpenVPN vars
|
||||
vars.append({ { "$OPENVPN_SUBNET_IP",
|
||||
openvpnConfig.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress) } });
|
||||
vars.append({ { "$OPENVPN_SUBNET_CIDR", openvpnConfig.value(config_key::subnet_cidr).toString(protocols::openvpn::defaultSubnetCidr) } });
|
||||
vars.append({ { "$OPENVPN_SUBNET_MASK", openvpnConfig.value(config_key::subnet_mask).toString(protocols::openvpn::defaultSubnetMask) } });
|
||||
|
||||
vars.append({ { "$OPENVPN_PORT", openvpnConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) } });
|
||||
vars.append({ { "$OPENVPN_TRANSPORT_PROTO",
|
||||
openvpnConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) } });
|
||||
|
||||
bool isNcpDisabled = openvpnConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
|
||||
vars.append({ { "$OPENVPN_NCP_DISABLE", isNcpDisabled ? protocols::openvpn::ncpDisableString : "" } });
|
||||
|
||||
vars.append({ { "$OPENVPN_CIPHER", openvpnConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher) } });
|
||||
vars.append({ { "$OPENVPN_HASH", openvpnConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash) } });
|
||||
|
||||
bool isTlsAuth = openvpnConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
|
||||
vars.append({ { "$OPENVPN_TLS_AUTH", isTlsAuth ? protocols::openvpn::tlsAuthString : "" } });
|
||||
if (!isTlsAuth) {
|
||||
// erase $OPENVPN_TA_KEY, so it will not set in OpenVpnConfigurator::genOpenVpnConfig
|
||||
vars.append({ { "$OPENVPN_TA_KEY", "" } });
|
||||
// For VPN containers, use configurator pattern
|
||||
if (ContainerProps::containerService(container) != ServiceType::Other) {
|
||||
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
|
||||
QScopedPointer<ConfiguratorBase> configurator;
|
||||
|
||||
// Create the appropriate configurator for this protocol
|
||||
switch (protocol) {
|
||||
case Proto::OpenVpn:
|
||||
configurator.reset(new OpenVpnConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
|
||||
break;
|
||||
case Proto::ShadowSocks:
|
||||
configurator.reset(new ShadowSocksConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
|
||||
break;
|
||||
case Proto::Cloak:
|
||||
configurator.reset(new CloakConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
|
||||
break;
|
||||
case Proto::WireGuard:
|
||||
configurator.reset(new WireguardConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){}), false));
|
||||
break;
|
||||
case Proto::Awg:
|
||||
configurator.reset(new AwgConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
|
||||
break;
|
||||
case Proto::Ikev2:
|
||||
configurator.reset(new Ikev2Configurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
|
||||
break;
|
||||
case Proto::Xray:
|
||||
case Proto::SSXray:
|
||||
configurator.reset(new XrayConfigurator(m_settings, QSharedPointer<ServerController>(this, [](ServerController*){})));
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (configurator) {
|
||||
QString protocolName = ProtocolProps::protoToString(protocol);
|
||||
auto protocolConfig = config.protocolConfigs.value(protocolName);
|
||||
return configurator->generateProtocolVars(credentials, container, protocolConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vars.append({ { "$OPENVPN_ADDITIONAL_CLIENT_CONFIG",
|
||||
openvpnConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig) } });
|
||||
vars.append({ { "$OPENVPN_ADDITIONAL_SERVER_CONFIG",
|
||||
openvpnConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig) } });
|
||||
|
||||
// ShadowSocks vars
|
||||
vars.append({ { "$SHADOWSOCKS_SERVER_PORT", ssConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) } });
|
||||
vars.append({ { "$SHADOWSOCKS_LOCAL_PORT",
|
||||
ssConfig.value(config_key::local_port).toString(protocols::shadowsocks::defaultLocalProxyPort) } });
|
||||
vars.append({ { "$SHADOWSOCKS_CIPHER", ssConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher) } });
|
||||
|
||||
vars.append({ { "$CONTAINER_NAME", ContainerProps::containerToString(container) } });
|
||||
vars.append({ { "$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container) } });
|
||||
|
||||
// Cloak vars
|
||||
vars.append({ { "$CLOAK_SERVER_PORT", cloakConfig.value(config_key::port).toString(protocols::cloak::defaultPort) } });
|
||||
vars.append({ { "$FAKE_WEB_SITE_ADDRESS", cloakConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite) } });
|
||||
|
||||
// Xray vars
|
||||
vars.append({ { "$XRAY_SITE_NAME", xrayConfig.value(config_key::site).toString(protocols::xray::defaultSite) } });
|
||||
vars.append({ { "$XRAY_SERVER_PORT", xrayConfig.value(config_key::port).toString(protocols::xray::defaultPort) } });
|
||||
|
||||
// Wireguard vars
|
||||
vars.append({ { "$WIREGUARD_SUBNET_IP",
|
||||
wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
|
||||
vars.append({ { "$WIREGUARD_SUBNET_CIDR",
|
||||
wireguarConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) } });
|
||||
vars.append({ { "$WIREGUARD_SUBNET_MASK",
|
||||
wireguarConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) } });
|
||||
|
||||
vars.append({ { "$WIREGUARD_SERVER_PORT", wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) } });
|
||||
|
||||
// IPsec vars
|
||||
vars.append({ { "$IPSEC_VPN_L2TP_NET", "192.168.42.0/24" } });
|
||||
vars.append({ { "$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250" } });
|
||||
vars.append({ { "$IPSEC_VPN_L2TP_LOCAL", "192.168.42.1" } });
|
||||
|
||||
vars.append({ { "$IPSEC_VPN_XAUTH_NET", "192.168.43.0/24" } });
|
||||
vars.append({ { "$IPSEC_VPN_XAUTH_POOL", "192.168.43.10-192.168.43.250" } });
|
||||
|
||||
vars.append({ { "$IPSEC_VPN_SHA2_TRUNCBUG", "yes" } });
|
||||
|
||||
vars.append({ { "$IPSEC_VPN_VPN_ANDROID_MTU_FIX", "yes" } });
|
||||
vars.append({ { "$IPSEC_VPN_DISABLE_IKEV2", "no" } });
|
||||
vars.append({ { "$IPSEC_VPN_DISABLE_L2TP", "no" } });
|
||||
vars.append({ { "$IPSEC_VPN_DISABLE_XAUTH", "no" } });
|
||||
|
||||
vars.append({ { "$IPSEC_VPN_C2C_TRAFFIC", "no" } });
|
||||
|
||||
vars.append({ { "$PRIMARY_SERVER_DNS", m_settings->primaryDns() } });
|
||||
vars.append({ { "$SECONDARY_SERVER_DNS", m_settings->secondaryDns() } });
|
||||
|
||||
// Sftp vars
|
||||
vars.append({ { "$SFTP_PORT", sftpConfig.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Proto::Sftp))) } });
|
||||
vars.append({ { "$SFTP_USER", sftpConfig.value(config_key::userName).toString() } });
|
||||
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
|
||||
|
||||
// Amnezia wireguard vars
|
||||
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_MIN_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMinSize).toString() } });
|
||||
vars.append({ { "$JUNK_PACKET_MAX_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMaxSize).toString() } });
|
||||
vars.append({ { "$INIT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::initPacketJunkSize).toString() } });
|
||||
vars.append({ { "$RESPONSE_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::responsePacketJunkSize).toString() } });
|
||||
vars.append({ { "$INIT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::initPacketMagicHeader).toString() } });
|
||||
vars.append({ { "$RESPONSE_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::responsePacketMagicHeader).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() } });
|
||||
|
||||
// Socks5 proxy vars
|
||||
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
|
||||
auto username = socks5ProxyConfig.value(config_key::userName).toString();
|
||||
auto password = socks5ProxyConfig.value(config_key::password).toString();
|
||||
QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : "";
|
||||
vars.append({ { "$SOCKS5_USER", socks5user } });
|
||||
vars.append({ { "$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong" } });
|
||||
// Handle non-VPN services (SFTP, Socks5) with direct variable generation
|
||||
Vars vars;
|
||||
|
||||
// Common variables that apply to all containers
|
||||
vars.append({{"$REMOTE_HOST", credentials.hostName}});
|
||||
vars.append({{"$CONTAINER_NAME", ContainerProps::containerToString(container)}});
|
||||
vars.append({{"$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container)}});
|
||||
vars.append({{"$PRIMARY_SERVER_DNS", m_settings->primaryDns()}});
|
||||
vars.append({{"$SECONDARY_SERVER_DNS", m_settings->secondaryDns()}});
|
||||
|
||||
QString serverIp = (container != DockerContainer::Awg && container != DockerContainer::WireGuard && container != DockerContainer::Xray)
|
||||
? NetworkUtilities::getIPAddress(credentials.hostName)
|
||||
: credentials.hostName;
|
||||
if (!serverIp.isEmpty()) {
|
||||
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
|
||||
} else {
|
||||
qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName";
|
||||
vars.append({{"$SERVER_IP_ADDRESS", serverIp}});
|
||||
}
|
||||
|
||||
// Handle container-specific variables for non-VPN services
|
||||
if (container == DockerContainer::Sftp) {
|
||||
QString protocolName = ProtocolProps::protoToString(Proto::Sftp);
|
||||
auto sftpConfig = qSharedPointerCast<SftpProtocolConfig>(config.protocolConfigs.value(protocolName));
|
||||
|
||||
if (sftpConfig) {
|
||||
QString port = sftpConfig->serverProtocolConfig.port;
|
||||
if (port.isEmpty()) {
|
||||
port = QString::number(ProtocolProps::defaultPort(Proto::Sftp));
|
||||
}
|
||||
|
||||
vars.append({{"$SFTP_PORT", port}});
|
||||
vars.append({{"$SFTP_USER", sftpConfig->serverProtocolConfig.userName}});
|
||||
vars.append({{"$SFTP_PASSWORD", sftpConfig->serverProtocolConfig.password}});
|
||||
}
|
||||
} else if (container == DockerContainer::Socks5Proxy) {
|
||||
QString protocolName = ProtocolProps::protoToString(Proto::Socks5Proxy);
|
||||
auto socks5Config = qSharedPointerCast<Socks5ProtocolConfig>(config.protocolConfigs.value(protocolName));
|
||||
|
||||
if (socks5Config) {
|
||||
QString port = socks5Config->serverProtocolConfig.port;
|
||||
if (port.isEmpty()) {
|
||||
port = protocols::socks5Proxy::defaultPort;
|
||||
}
|
||||
|
||||
vars.append({{"$SOCKS5_PROXY_PORT", port}});
|
||||
|
||||
const QString &username = socks5Config->serverProtocolConfig.userName;
|
||||
const QString &password = socks5Config->serverProtocolConfig.password;
|
||||
QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : "";
|
||||
vars.append({{"$SOCKS5_USER", socks5user}});
|
||||
vars.append({{"$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong"}});
|
||||
}
|
||||
}
|
||||
|
||||
return vars;
|
||||
@@ -663,7 +612,7 @@ void ServerController::cancelInstallation()
|
||||
|
||||
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
|
||||
{
|
||||
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), genVarsForScript(credentials)));
|
||||
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), generateVarsForContainer(credentials, DockerContainer::None)));
|
||||
}
|
||||
|
||||
QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
||||
@@ -675,7 +624,7 @@ QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
||||
return s;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
|
||||
{
|
||||
if (container == DockerContainer::Dns) {
|
||||
return ErrorCode::NoError;
|
||||
@@ -692,18 +641,40 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
};
|
||||
|
||||
const Proto protocol = ContainerProps::defaultProtocol(container);
|
||||
const QString containerString = ProtocolProps::protoToString(protocol);
|
||||
const QJsonObject containerConfig = config.value(containerString).toObject();
|
||||
const QString protocolName = ProtocolProps::protoToString(protocol);
|
||||
auto protocolConfig = config.protocolConfigs.value(protocolName);
|
||||
|
||||
QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container);
|
||||
|
||||
QString defaultPort("%1");
|
||||
QString port = containerConfig.value(config_key::port).toString(defaultPort.arg(ProtocolProps::defaultPort(protocol)));
|
||||
QString defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol);
|
||||
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
||||
QString port = QString::number(ProtocolProps::defaultPort(protocol)); // default
|
||||
QString transportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol); // default
|
||||
|
||||
if (protocolConfig) {
|
||||
if (auto openVpnConfig = qSharedPointerCast<OpenVpnProtocolConfig>(protocolConfig)) {
|
||||
port = openVpnConfig->serverProtocolConfig.port;
|
||||
transportProto = openVpnConfig->serverProtocolConfig.transportProto;
|
||||
} else if (auto wgConfig = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig)) {
|
||||
port = wgConfig->serverProtocolConfig.port;
|
||||
transportProto = wgConfig->serverProtocolConfig.transportProto;
|
||||
} else if (auto awgConfig = qSharedPointerCast<AwgProtocolConfig>(protocolConfig)) {
|
||||
port = awgConfig->serverProtocolConfig.port;
|
||||
transportProto = awgConfig->serverProtocolConfig.transportProto;
|
||||
} else if (auto xrayConfig = qSharedPointerCast<XrayProtocolConfig>(protocolConfig)) {
|
||||
port = xrayConfig->serverProtocolConfig.port;
|
||||
transportProto = xrayConfig->serverProtocolConfig.transportProto;
|
||||
} else if (auto shadowsocksConfig = qSharedPointerCast<ShadowsocksProtocolConfig>(protocolConfig)) {
|
||||
port = shadowsocksConfig->serverProtocolConfig.port;
|
||||
} else if (auto cloakConfig = qSharedPointerCast<CloakProtocolConfig>(protocolConfig)) {
|
||||
port = cloakConfig->serverProtocolConfig.port;
|
||||
} else if (auto sftpConfig = qSharedPointerCast<SftpProtocolConfig>(protocolConfig)) {
|
||||
port = sftpConfig->serverProtocolConfig.port;
|
||||
} else if (auto socks5Config = qSharedPointerCast<Socks5ProtocolConfig>(protocolConfig)) {
|
||||
port = socks5Config->serverProtocolConfig.port;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO reimplement with netstat
|
||||
QString script = QString("which lsof &>/dev/null || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||
QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||
for (auto &port : fixedPorts) {
|
||||
script = script.append("|:%1").arg(port);
|
||||
}
|
||||
@@ -716,12 +687,12 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
tcpProtoScript.append(" | grep LISTEN");
|
||||
|
||||
ErrorCode errorCode =
|
||||
runScript(credentials, replaceVars(tcpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
runScript(credentials, replaceVars(tcpProtoScript, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
errorCode = runScript(credentials, replaceVars(udpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
errorCode = runScript(credentials, replaceVars(udpProtoScript, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
@@ -738,7 +709,7 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
script = script.append(" | grep LISTEN");
|
||||
}
|
||||
|
||||
ErrorCode errorCode = runScript(credentials, replaceVars(script, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
ErrorCode errorCode = runScript(credentials, replaceVars(script, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
@@ -751,10 +722,6 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
|
||||
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
|
||||
{
|
||||
if (credentials.userName == "root") {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
@@ -766,10 +733,18 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
||||
};
|
||||
|
||||
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, generateVarsForContainer(credentials, DockerContainer::None)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
if (!stdOut.contains("sudo"))
|
||||
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
|
||||
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
|
||||
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
|
||||
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;
|
||||
}
|
||||
@@ -796,12 +771,12 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
||||
return ErrorCode::ServerCancelInstallation;
|
||||
}
|
||||
stdOut.clear();
|
||||
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy), genVarsForScript(credentials)),
|
||||
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy), generateVarsForContainer(credentials, DockerContainer::None)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
if (stdOut.contains("Packet manager not found"))
|
||||
return ErrorCode::ServerPacketManagerError;
|
||||
if (stdOut.contains("fuser not installed"))
|
||||
if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed"))
|
||||
return ErrorCode::NoError;
|
||||
|
||||
if (stdOut.isEmpty()) {
|
||||
@@ -4,7 +4,8 @@
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/models/containers/containerConfig.h"
|
||||
#include "core/defs.h"
|
||||
#include "core/sshclient.h"
|
||||
|
||||
@@ -25,12 +26,12 @@ public:
|
||||
ErrorCode rebootServer(const ServerCredentials &credentials);
|
||||
ErrorCode removeAllContainers(const ServerCredentials &credentials);
|
||||
ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container);
|
||||
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config, bool isUpdate = false);
|
||||
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig,
|
||||
QJsonObject &newConfig);
|
||||
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false);
|
||||
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &oldConfig,
|
||||
ContainerConfig &newConfig);
|
||||
|
||||
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config = QJsonObject());
|
||||
const ContainerConfig &config = ContainerConfig());
|
||||
|
||||
ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file,
|
||||
const QString &path,
|
||||
@@ -39,8 +40,8 @@ public:
|
||||
ErrorCode &errorCode);
|
||||
|
||||
QString replaceVars(const QString &script, const Vars &vars);
|
||||
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None,
|
||||
const QJsonObject &config = QJsonObject());
|
||||
Vars generateVarsForContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||
const ContainerConfig &config = ContainerConfig());
|
||||
|
||||
ErrorCode runScript(const ServerCredentials &credentials, QString script,
|
||||
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
||||
@@ -59,14 +60,14 @@ public:
|
||||
|
||||
private:
|
||||
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container);
|
||||
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
||||
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config = ContainerConfig());
|
||||
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config = QJsonObject());
|
||||
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
||||
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
||||
const ContainerConfig &config = ContainerConfig());
|
||||
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config);
|
||||
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config);
|
||||
|
||||
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config);
|
||||
bool isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
|
||||
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config);
|
||||
bool isReinstallContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
|
||||
ErrorCode isUserInSudo(const ServerCredentials &credentials, DockerContainer container);
|
||||
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);
|
||||
|
||||
238
client/core/controllers/settingsController.cpp
Normal file
238
client/core/controllers/settingsController.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
#include "settingsController.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
#include "settings.h"
|
||||
#include "logger.h"
|
||||
#include "ui/qautostart.h"
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
Logger logger("SettingsController");
|
||||
}
|
||||
|
||||
SettingsController::SettingsController(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QObject(parent), m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
void SettingsController::resetAllSettings()
|
||||
{
|
||||
logger.info() << "Resetting all settings to defaults";
|
||||
m_settings->clearSettings();
|
||||
emit settingsReset();
|
||||
}
|
||||
|
||||
void SettingsController::configureDns(const QString &primaryDns, const QString &secondaryDns)
|
||||
{
|
||||
m_settings->setPrimaryDns(primaryDns);
|
||||
m_settings->setSecondaryDns(secondaryDns);
|
||||
emit dnsConfigChanged();
|
||||
}
|
||||
|
||||
void SettingsController::toggleAmneziaDns(bool enable)
|
||||
{
|
||||
m_settings->setUseAmneziaDns(enable);
|
||||
emit dnsConfigChanged();
|
||||
}
|
||||
|
||||
void SettingsController::configureLogging(bool enabled)
|
||||
{
|
||||
m_settings->setSaveLogs(enabled);
|
||||
}
|
||||
|
||||
void SettingsController::checkLoggingExpiration()
|
||||
{
|
||||
if (m_settings->isSaveLogs()) {
|
||||
QDateTime loggingDisableDate = m_settings->getLogEnableDate().addDays(14);
|
||||
if (loggingDisableDate <= QDateTime::currentDateTime()) {
|
||||
configureLogging(false);
|
||||
clearLogs();
|
||||
emit loggingExpired();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsController::clearLogs()
|
||||
{
|
||||
logger.info() << "Clearing application logs";
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
AndroidController::instance()->clearLogs();
|
||||
#else
|
||||
Logger::clearLogs(false);
|
||||
Logger::clearServiceLogs();
|
||||
#endif
|
||||
|
||||
logger.info() << "Logs cleared successfully";
|
||||
}
|
||||
|
||||
void SettingsController::configureKillSwitch(bool enable, bool strict)
|
||||
{
|
||||
m_settings->setKillSwitchEnabled(enable);
|
||||
if (enable) {
|
||||
m_settings->setStrictKillSwitchEnabled(strict);
|
||||
} else {
|
||||
m_settings->setStrictKillSwitchEnabled(false);
|
||||
}
|
||||
emit killSwitchConfigChanged();
|
||||
}
|
||||
|
||||
void SettingsController::configureAutoStart(bool enable)
|
||||
{
|
||||
Autostart::setAutostart(enable);
|
||||
emit autoStartConfigChanged();
|
||||
}
|
||||
|
||||
void SettingsController::configureAutoConnect(bool enable)
|
||||
{
|
||||
m_settings->setAutoConnect(enable);
|
||||
}
|
||||
|
||||
void SettingsController::configureStartMinimized(bool enable)
|
||||
{
|
||||
m_settings->setStartMinimized(enable);
|
||||
}
|
||||
|
||||
void SettingsController::configureScreenshots(bool enable)
|
||||
{
|
||||
m_settings->setScreenshotsEnabled(enable);
|
||||
}
|
||||
|
||||
QString SettingsController::getPrimaryDns() const
|
||||
{
|
||||
return m_settings->primaryDns();
|
||||
}
|
||||
|
||||
QString SettingsController::getSecondaryDns() const
|
||||
{
|
||||
return m_settings->secondaryDns();
|
||||
}
|
||||
|
||||
bool SettingsController::isAmneziaDnsEnabled() const
|
||||
{
|
||||
return m_settings->useAmneziaDns();
|
||||
}
|
||||
|
||||
bool SettingsController::isLoggingEnabled() const
|
||||
{
|
||||
return m_settings->isSaveLogs();
|
||||
}
|
||||
|
||||
bool SettingsController::isKillSwitchEnabled() const
|
||||
{
|
||||
return m_settings->isKillSwitchEnabled();
|
||||
}
|
||||
|
||||
bool SettingsController::isStrictKillSwitchEnabled() const
|
||||
{
|
||||
return m_settings->isStrictKillSwitchEnabled();
|
||||
}
|
||||
|
||||
bool SettingsController::isAutoStartEnabled() const
|
||||
{
|
||||
return Autostart::isAutostart();
|
||||
}
|
||||
|
||||
bool SettingsController::isAutoConnectEnabled() const
|
||||
{
|
||||
return m_settings->isAutoConnect();
|
||||
}
|
||||
|
||||
bool SettingsController::isStartMinimizedEnabled() const
|
||||
{
|
||||
return m_settings->isStartMinimized();
|
||||
}
|
||||
|
||||
bool SettingsController::isScreenshotsEnabled() const
|
||||
{
|
||||
return m_settings->isScreenshotsEnabled();
|
||||
}
|
||||
|
||||
QByteArray SettingsController::backupAppConfig() const
|
||||
{
|
||||
return m_settings->backupAppConfig();
|
||||
}
|
||||
|
||||
bool SettingsController::restoreAppConfig(const QByteArray &data)
|
||||
{
|
||||
return m_settings->restoreAppConfig(data);
|
||||
}
|
||||
|
||||
QString SettingsController::getInstallationUuid() const
|
||||
{
|
||||
return m_settings->getInstallationUuid(false);
|
||||
}
|
||||
|
||||
QString SettingsController::nextAvailableServerName() const
|
||||
{
|
||||
return m_settings->nextAvailableServerName();
|
||||
}
|
||||
|
||||
void SettingsController::resetGatewayEndpoint()
|
||||
{
|
||||
m_settings->resetGatewayEndpoint();
|
||||
}
|
||||
|
||||
void SettingsController::setGatewayEndpoint(const QString &endpoint)
|
||||
{
|
||||
m_settings->setGatewayEndpoint(endpoint);
|
||||
}
|
||||
|
||||
QString SettingsController::getGatewayEndpoint() const
|
||||
{
|
||||
if (m_settings->isDevGatewayEnv()) {
|
||||
return "Dev endpoint";
|
||||
}
|
||||
return m_settings->getGatewayEndpoint();
|
||||
}
|
||||
|
||||
bool SettingsController::isDevGatewayEnv() const
|
||||
{
|
||||
return m_settings->isDevGatewayEnv();
|
||||
}
|
||||
|
||||
void SettingsController::toggleDevGatewayEnv(bool enabled)
|
||||
{
|
||||
m_settings->toggleDevGatewayEnv(enabled);
|
||||
if (enabled) {
|
||||
m_settings->setDevGatewayEndpoint();
|
||||
} else {
|
||||
m_settings->resetGatewayEndpoint();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsController::setDevGatewayEndpoint()
|
||||
{
|
||||
m_settings->setDevGatewayEndpoint();
|
||||
}
|
||||
|
||||
bool SettingsController::isHomeAdLabelVisible() const
|
||||
{
|
||||
return m_settings->isHomeAdLabelVisible();
|
||||
}
|
||||
|
||||
void SettingsController::disableHomeAdLabel()
|
||||
{
|
||||
m_settings->disableHomeAdLabel();
|
||||
}
|
||||
|
||||
QDateTime SettingsController::getLogEnableDate() const
|
||||
{
|
||||
return m_settings->getLogEnableDate();
|
||||
}
|
||||
|
||||
QLocale SettingsController::getAppLanguage() const
|
||||
{
|
||||
return m_settings->getAppLanguage();
|
||||
}
|
||||
|
||||
void SettingsController::setAppLanguage(const QLocale &locale)
|
||||
{
|
||||
m_settings->setAppLanguage(locale);
|
||||
}
|
||||
|
||||
|
||||
88
client/core/controllers/settingsController.h
Normal file
88
client/core/controllers/settingsController.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef SETTINGSCONTROLLER_H
|
||||
#define SETTINGSCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QFuture>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "core/defs.h"
|
||||
|
||||
class Settings;
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
class SettingsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SettingsController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
|
||||
void resetAllSettings();
|
||||
|
||||
void configureDns(const QString &primaryDns, const QString &secondaryDns);
|
||||
void toggleAmneziaDns(bool enable);
|
||||
|
||||
void configureLogging(bool enable);
|
||||
void checkLoggingExpiration();
|
||||
void clearLogs();
|
||||
|
||||
void configureKillSwitch(bool enable, bool strict = false);
|
||||
|
||||
void configureAutoStart(bool enable);
|
||||
void configureAutoConnect(bool enable);
|
||||
void configureStartMinimized(bool enable);
|
||||
void configureScreenshots(bool enable);
|
||||
|
||||
QString getPrimaryDns() const;
|
||||
QString getSecondaryDns() const;
|
||||
bool isAmneziaDnsEnabled() const;
|
||||
bool isLoggingEnabled() const;
|
||||
bool isKillSwitchEnabled() const;
|
||||
bool isStrictKillSwitchEnabled() const;
|
||||
bool isAutoStartEnabled() const;
|
||||
bool isAutoConnectEnabled() const;
|
||||
bool isStartMinimizedEnabled() const;
|
||||
bool isScreenshotsEnabled() const;
|
||||
|
||||
// Backup/restore functionality
|
||||
QByteArray backupAppConfig() const;
|
||||
bool restoreAppConfig(const QByteArray &data);
|
||||
|
||||
// Installation UUID
|
||||
QString getInstallationUuid() const;
|
||||
|
||||
// Server naming
|
||||
QString nextAvailableServerName() const;
|
||||
|
||||
// Gateway endpoint functionality
|
||||
void resetGatewayEndpoint();
|
||||
void setGatewayEndpoint(const QString &endpoint);
|
||||
QString getGatewayEndpoint() const;
|
||||
bool isDevGatewayEnv() const;
|
||||
void toggleDevGatewayEnv(bool enabled);
|
||||
void setDevGatewayEndpoint();
|
||||
|
||||
// Home ad label
|
||||
bool isHomeAdLabelVisible() const;
|
||||
void disableHomeAdLabel();
|
||||
|
||||
// Log date
|
||||
QDateTime getLogEnableDate() const;
|
||||
|
||||
QLocale getAppLanguage() const;
|
||||
void setAppLanguage(const QLocale &locale);
|
||||
|
||||
signals:
|
||||
void settingsReset();
|
||||
void dnsConfigChanged();
|
||||
void loggingConfigChanged();
|
||||
void killSwitchConfigChanged();
|
||||
void autoStartConfigChanged();
|
||||
void loggingExpired();
|
||||
|
||||
private:
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
};
|
||||
|
||||
#endif // SETTINGSCONTROLLER_H
|
||||
228
client/core/controllers/splitTunnelingController.cpp
Normal file
228
client/core/controllers/splitTunnelingController.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
#include "splitTunnelingController.h"
|
||||
#include "settings.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include <QFileInfo>
|
||||
|
||||
SplitTunnelingController::SplitTunnelingController(std::shared_ptr<Settings> settings,
|
||||
QSharedPointer<VpnConnection> vpnConnection,
|
||||
QObject *parent)
|
||||
: QObject(parent), m_settings(settings), m_vpnConnection(vpnConnection)
|
||||
{
|
||||
}
|
||||
|
||||
// Apps split tunneling implementation
|
||||
bool SplitTunnelingController::addApp(const InstalledAppInfo &appInfo)
|
||||
{
|
||||
InstalledAppInfo processedAppInfo = appInfo;
|
||||
|
||||
// Migrated from AppSplitTunnelingController::addApp - app name extraction
|
||||
if (processedAppInfo.appName.isEmpty() && !processedAppInfo.appPath.isEmpty()) {
|
||||
QFileInfo fileInfo(processedAppInfo.appPath);
|
||||
processedAppInfo.appName = fileInfo.fileName();
|
||||
}
|
||||
|
||||
auto currentApps = m_settings->getVpnApps(getAppsRouteMode());
|
||||
|
||||
if (currentApps.contains(processedAppInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentApps.append(processedAppInfo);
|
||||
m_settings->setVpnApps(getAppsRouteMode(), currentApps);
|
||||
|
||||
emit appAdded(processedAppInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SplitTunnelingController::removeApp(const InstalledAppInfo &appInfo)
|
||||
{
|
||||
auto currentApps = m_settings->getVpnApps(getAppsRouteMode());
|
||||
|
||||
if (!currentApps.contains(appInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentApps.removeAll(appInfo);
|
||||
m_settings->setVpnApps(getAppsRouteMode(), currentApps);
|
||||
|
||||
emit appRemoved(appInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
QVector<InstalledAppInfo> SplitTunnelingController::getApps(Settings::AppsRouteMode routeMode) const
|
||||
{
|
||||
return m_settings->getVpnApps(routeMode);
|
||||
}
|
||||
|
||||
Settings::AppsRouteMode SplitTunnelingController::getAppsRouteMode() const
|
||||
{
|
||||
return m_settings->getAppsRouteMode();
|
||||
}
|
||||
|
||||
void SplitTunnelingController::setAppsRouteMode(Settings::AppsRouteMode routeMode)
|
||||
{
|
||||
m_settings->setAppsRouteMode(routeMode);
|
||||
emit appsRouteModelChanged();
|
||||
}
|
||||
|
||||
bool SplitTunnelingController::isAppsSplitTunnelingEnabled() const
|
||||
{
|
||||
return m_settings->isAppsSplitTunnelingEnabled();
|
||||
}
|
||||
|
||||
void SplitTunnelingController::setAppsSplitTunnelingEnabled(bool enabled)
|
||||
{
|
||||
m_settings->setAppsSplitTunnelingEnabled(enabled);
|
||||
emit appsSplitTunnelingToggled();
|
||||
}
|
||||
|
||||
// Sites split tunneling implementation
|
||||
bool SplitTunnelingController::addSite(const QString &hostname, const QString &ip)
|
||||
{
|
||||
QString processedHostname = hostname;
|
||||
|
||||
// Migrated from SitesController::addSite - hostname processing
|
||||
if (!NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
processedHostname.replace("https://", "");
|
||||
processedHostname.replace("http://", "");
|
||||
processedHostname.replace("ftp://", "");
|
||||
processedHostname = processedHostname.split("/", Qt::SkipEmptyParts).first();
|
||||
}
|
||||
|
||||
if (!m_settings->addVpnSite(getSitesRouteMode(), processedHostname, ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Migrated from SitesController::addSite - VPN route management
|
||||
if (m_vpnConnection) {
|
||||
if (!ip.isEmpty()) {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << ip));
|
||||
} else if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(processedHostname)) {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << processedHostname));
|
||||
} else {
|
||||
// Migrated from SitesController::addSite - DNS resolution
|
||||
int lookupId = QHostInfo::lookupHost(processedHostname, this,
|
||||
SLOT(handleHostnameResolved(QHostInfo)));
|
||||
m_pendingResolutions[lookupId] = processedHostname;
|
||||
}
|
||||
}
|
||||
|
||||
emit siteAdded(processedHostname, ip);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SplitTunnelingController::addSites(const QMap<QString, QString> &sites, bool replaceExisting)
|
||||
{
|
||||
if (replaceExisting) {
|
||||
m_settings->removeAllVpnSites(getSitesRouteMode());
|
||||
}
|
||||
|
||||
m_settings->addVpnSites(getSitesRouteMode(), sites);
|
||||
|
||||
// Migrated from SitesController - VPN route management for batch adds
|
||||
if (m_vpnConnection) {
|
||||
QStringList ips;
|
||||
auto i = sites.constBegin();
|
||||
while (i != sites.constEnd()) {
|
||||
const QString &hostname = i.key();
|
||||
const QString &ip = i.value();
|
||||
|
||||
if (ip.isEmpty()) {
|
||||
ips.append(hostname);
|
||||
} else {
|
||||
ips.append(ip);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, ips));
|
||||
}
|
||||
|
||||
auto i = sites.constBegin();
|
||||
while (i != sites.constEnd()) {
|
||||
emit siteAdded(i.key(), i.value());
|
||||
++i;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SplitTunnelingController::removeSite(const QString &hostname)
|
||||
{
|
||||
if (!m_settings->removeVpnSite(getSitesRouteMode(), hostname)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Migrated from SitesController::removeSite - VPN route management
|
||||
if (m_vpnConnection) {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << hostname));
|
||||
}
|
||||
|
||||
emit siteRemoved(hostname);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Migrated from SitesController - DNS resolution handler
|
||||
void SplitTunnelingController::handleHostnameResolved(const QHostInfo &hostInfo)
|
||||
{
|
||||
if (hostInfo.error() != QHostInfo::NoError) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString hostname = m_pendingResolutions.take(hostInfo.lookupId());
|
||||
if (hostname.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const QHostAddress &addr : hostInfo.addresses()) {
|
||||
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
|
||||
if (m_vpnConnection) {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << addr.toString()));
|
||||
}
|
||||
|
||||
m_settings->addVpnSite(getSitesRouteMode(), hostname, addr.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVector<QPair<QString, QString>> SplitTunnelingController::getSites(Settings::RouteMode routeMode) const
|
||||
{
|
||||
QVector<QPair<QString, QString>> sites;
|
||||
const QVariantMap &sitesMap = m_settings->vpnSites(routeMode);
|
||||
|
||||
auto i = sitesMap.constBegin();
|
||||
while (i != sitesMap.constEnd()) {
|
||||
sites.append(qMakePair(i.key(), i.value().toString()));
|
||||
++i;
|
||||
}
|
||||
|
||||
return sites;
|
||||
}
|
||||
|
||||
Settings::RouteMode SplitTunnelingController::getSitesRouteMode() const
|
||||
{
|
||||
return m_settings->routeMode();
|
||||
}
|
||||
|
||||
void SplitTunnelingController::setSitesRouteMode(Settings::RouteMode routeMode)
|
||||
{
|
||||
m_settings->setRouteMode(routeMode);
|
||||
emit sitesRouteModelChanged();
|
||||
}
|
||||
|
||||
bool SplitTunnelingController::isSitesSplitTunnelingEnabled() const
|
||||
{
|
||||
return m_settings->isSitesSplitTunnelingEnabled();
|
||||
}
|
||||
|
||||
void SplitTunnelingController::setSitesSplitTunnelingEnabled(bool enabled)
|
||||
{
|
||||
m_settings->setSitesSplitTunnelingEnabled(enabled);
|
||||
emit sitesSplitTunnelingToggled();
|
||||
}
|
||||
71
client/core/controllers/splitTunnelingController.h
Normal file
71
client/core/controllers/splitTunnelingController.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#ifndef SPLITTUNNELINGCONTROLLER_H
|
||||
#define SPLITTUNNELINGCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
#include <QStringList>
|
||||
#include <QSharedPointer>
|
||||
#include <QHostInfo>
|
||||
|
||||
#include "settings.h"
|
||||
#include "core/defs.h"
|
||||
#include "vpnconnection.h"
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
class SplitTunnelingController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SplitTunnelingController(std::shared_ptr<Settings> settings,
|
||||
QSharedPointer<VpnConnection> vpnConnection = nullptr,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
// Apps split tunneling
|
||||
bool addApp(const InstalledAppInfo &appInfo);
|
||||
bool removeApp(const InstalledAppInfo &appInfo);
|
||||
QVector<InstalledAppInfo> getApps(Settings::AppsRouteMode routeMode) const;
|
||||
|
||||
Settings::AppsRouteMode getAppsRouteMode() const;
|
||||
void setAppsRouteMode(Settings::AppsRouteMode routeMode);
|
||||
|
||||
bool isAppsSplitTunnelingEnabled() const;
|
||||
void setAppsSplitTunnelingEnabled(bool enabled);
|
||||
|
||||
// Sites split tunneling
|
||||
bool addSite(const QString &hostname, const QString &ip = QString());
|
||||
bool addSites(const QMap<QString, QString> &sites, bool replaceExisting = false);
|
||||
bool removeSite(const QString &hostname);
|
||||
QVector<QPair<QString, QString>> getSites(Settings::RouteMode routeMode) const;
|
||||
|
||||
Settings::RouteMode getSitesRouteMode() const;
|
||||
void setSitesRouteMode(Settings::RouteMode routeMode);
|
||||
|
||||
bool isSitesSplitTunnelingEnabled() const;
|
||||
void setSitesSplitTunnelingEnabled(bool enabled);
|
||||
|
||||
signals:
|
||||
// Apps signals
|
||||
void appsRouteModelChanged();
|
||||
void appsSplitTunnelingToggled();
|
||||
void appAdded(const InstalledAppInfo &appInfo);
|
||||
void appRemoved(const InstalledAppInfo &appInfo);
|
||||
|
||||
// Sites signals
|
||||
void sitesRouteModelChanged();
|
||||
void sitesSplitTunnelingToggled();
|
||||
void siteAdded(const QString &hostname, const QString &ip);
|
||||
void siteRemoved(const QString &hostname);
|
||||
|
||||
private slots:
|
||||
void handleHostnameResolved(const QHostInfo &hostInfo);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||
QMap<int, QString> m_pendingResolutions;
|
||||
};
|
||||
|
||||
#endif // SPLITTUNNELINGCONTROLLER_H
|
||||
@@ -7,6 +7,18 @@
|
||||
#include "configurators/shadowsocks_configurator.h"
|
||||
#include "configurators/wireguard_configurator.h"
|
||||
#include "configurators/xray_configurator.h"
|
||||
#include "core/models/protocols/awgProtocolConfig.h"
|
||||
#include "core/models/protocols/cloakProtocolConfig.h"
|
||||
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
||||
#include "core/models/protocols/openvpnProtocolConfig.h"
|
||||
#include "core/models/protocols/shadowsocksProtocolConfig.h"
|
||||
#include "core/models/protocols/torWebsiteProtocolConfig.h"
|
||||
#include "core/models/protocols/wireguardProtocolConfig.h"
|
||||
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||
#include "core/models/protocols/sftpProtocolConfig.h"
|
||||
#include "core/models/protocols/socks5ProtocolConfig.h"
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
#include <variant>
|
||||
|
||||
VpnConfigurationsController::VpnConfigurationsController(const std::shared_ptr<Settings> &settings,
|
||||
QSharedPointer<ServerController> serverController, QObject *parent)
|
||||
@@ -29,8 +41,33 @@ QScopedPointer<ConfiguratorBase> VpnConfigurationsController::createConfigurator
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<ProtocolConfig> VpnConfigurationsController::createProtocolConfig(const Proto protocol)
|
||||
{
|
||||
switch (protocol) {
|
||||
case Proto::OpenVpn:
|
||||
return QSharedPointer<OpenVpnProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
|
||||
case Proto::ShadowSocks:
|
||||
return QSharedPointer<ShadowsocksProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
|
||||
case Proto::Cloak:
|
||||
return QSharedPointer<CloakProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
|
||||
case Proto::WireGuard:
|
||||
return QSharedPointer<WireGuardProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
|
||||
case Proto::Awg:
|
||||
return QSharedPointer<AwgProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
|
||||
case Proto::Xray:
|
||||
case Proto::SSXray:
|
||||
return QSharedPointer<XrayProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
|
||||
case Proto::Ikev2:
|
||||
return QSharedPointer<Ikev2ProtocolConfig>::create(ProtocolProps::protoToString(protocol));
|
||||
case Proto::TorWebSite:
|
||||
return QSharedPointer<TorWebsiteProtocolConfig>::create(QJsonObject(), ProtocolProps::protoToString(protocol));
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCode VpnConfigurationsController::createProtocolConfigForContainer(const ServerCredentials &credentials,
|
||||
const DockerContainer container, QJsonObject &containerConfig)
|
||||
const DockerContainer container, ContainerConfig &containerConfig)
|
||||
{
|
||||
ErrorCode errorCode = ErrorCode::NoError;
|
||||
|
||||
@@ -39,25 +76,34 @@ ErrorCode VpnConfigurationsController::createProtocolConfigForContainer(const Se
|
||||
}
|
||||
|
||||
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
|
||||
QJsonObject protocolConfig = containerConfig.value(ProtocolProps::protoToString(protocol)).toObject();
|
||||
QString protocolName = ProtocolProps::protoToString(protocol);
|
||||
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
|
||||
|
||||
if (!protocolConfig) {
|
||||
protocolConfig = createProtocolConfig(protocol);
|
||||
if (!protocolConfig) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return errorCode;
|
||||
}
|
||||
containerConfig.protocolConfigs.insert(protocolName, protocolConfig);
|
||||
}
|
||||
|
||||
auto configurator = createConfigurator(protocol);
|
||||
QString protocolConfigString = configurator->createConfig(credentials, container, containerConfig, errorCode);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
auto result = configurator->createConfig(credentials, container, protocolConfig, errorCode);
|
||||
if (errorCode != ErrorCode::NoError || !result) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
protocolConfig.insert(config_key::last_config, protocolConfigString);
|
||||
containerConfig.insert(ProtocolProps::protoToString(protocol), protocolConfig);
|
||||
containerConfig.protocolConfigs.insert(protocolName, result);
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns,
|
||||
const ServerCredentials &credentials, const DockerContainer container,
|
||||
const QJsonObject &containerConfig, const Proto protocol,
|
||||
QString &protocolConfigString)
|
||||
const ServerCredentials &credentials, const DockerContainer container,
|
||||
const ContainerConfig &containerConfig, const Proto protocol,
|
||||
QString &protocolConfigString)
|
||||
{
|
||||
ErrorCode errorCode = ErrorCode::NoError;
|
||||
|
||||
@@ -65,20 +111,30 @@ ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isA
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
auto configurator = createConfigurator(protocol);
|
||||
|
||||
protocolConfigString = configurator->createConfig(credentials, container, containerConfig, errorCode);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
QString protocolName = ProtocolProps::protoToString(protocol);
|
||||
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
|
||||
if (!protocolConfig) {
|
||||
errorCode = ErrorCode::InternalError;
|
||||
return errorCode;
|
||||
}
|
||||
protocolConfigString = configurator->processConfigWithExportSettings(dns, isApiConfig, protocolConfigString);
|
||||
|
||||
auto configurator = createConfigurator(protocol);
|
||||
auto result = configurator->createConfig(credentials, container, protocolConfig, errorCode);
|
||||
if (errorCode != ErrorCode::NoError || !result) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
configurator->processConfigWithExportSettings(dns, isApiConfig, result);
|
||||
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(result);
|
||||
std::visit([&protocolConfigString](const auto &config) -> void {
|
||||
protocolConfigString = config->clientProtocolConfig.nativeConfig;
|
||||
}, variant);
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
|
||||
const QJsonObject &containerConfig, const DockerContainer container,
|
||||
ErrorCode &errorCode)
|
||||
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QSharedPointer<ServerConfig> &serverConfig,
|
||||
const ContainerConfig &containerConfig, const DockerContainer container)
|
||||
{
|
||||
QJsonObject vpnConfiguration {};
|
||||
|
||||
@@ -86,24 +142,47 @@ QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QStr
|
||||
return vpnConfiguration;
|
||||
}
|
||||
|
||||
bool isApiConfig = serverConfig.value(config_key::configVersion).toInt();
|
||||
bool isApiConfig = static_cast<int>(serverConfig->type);
|
||||
|
||||
for (ProtocolEnumNS::Proto proto : ContainerProps::protocolsForContainer(container)) {
|
||||
if (isApiConfig && container == DockerContainer::Cloak && proto == ProtocolEnumNS::Proto::ShadowSocks) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString protocolConfigString =
|
||||
containerConfig.value(ProtocolProps::protoToString(proto)).toObject().value(config_key::last_config).toString();
|
||||
QString protocolName = ProtocolProps::protoToString(proto);
|
||||
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
|
||||
QString protocolConfigString;
|
||||
|
||||
if (protocolConfig) {
|
||||
auto configurator = createConfigurator(proto);
|
||||
configurator->processConfigWithLocalSettings(dns, isApiConfig, protocolConfig);
|
||||
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(protocolConfig);
|
||||
std::visit([&protocolConfigString](const auto &config) -> void {
|
||||
protocolConfigString = config->clientProtocolConfig.nativeConfig;
|
||||
}, variant);
|
||||
} else {
|
||||
protocolConfigString = "";
|
||||
}
|
||||
|
||||
auto configurator = createConfigurator(proto);
|
||||
protocolConfigString = configurator->processConfigWithLocalSettings(dns, isApiConfig, protocolConfigString);
|
||||
|
||||
QJsonObject vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||
QJsonObject vpnConfigData;
|
||||
if (proto == Proto::Xray || proto == Proto::SSXray) {
|
||||
vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||
} else {
|
||||
vpnConfigData[config_key::config] = protocolConfigString;
|
||||
if (protocolConfig) {
|
||||
QJsonObject protocolJson = protocolConfig->toJson();
|
||||
for (auto it = protocolJson.begin(); it != protocolJson.end(); ++it) {
|
||||
if (it.key() != config_key::config && it.key() != config_key::last_config) {
|
||||
vpnConfigData[it.key()] = it.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (container == DockerContainer::Awg || container == DockerContainer::WireGuard) {
|
||||
// add mtu for old configs
|
||||
if (vpnConfigData[config_key::mtu].toString().isEmpty()) {
|
||||
vpnConfigData[config_key::mtu] = container == DockerContainer::Awg ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
|
||||
vpnConfigData[config_key::mtu] =
|
||||
container == DockerContainer::Awg ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,30 +195,32 @@ QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QStr
|
||||
vpnConfiguration[config_key::dns1] = dns.first;
|
||||
vpnConfiguration[config_key::dns2] = dns.second;
|
||||
|
||||
vpnConfiguration[config_key::hostName] = serverConfig.value(config_key::hostName).toString();
|
||||
vpnConfiguration[config_key::description] = serverConfig.value(config_key::description).toString();
|
||||
vpnConfiguration[config_key::hostName] = serverConfig->hostName;
|
||||
vpnConfiguration[config_key::description] = serverConfig->toJson().value(config_key::description).toString();
|
||||
|
||||
vpnConfiguration[config_key::configVersion] = static_cast<int>(serverConfig->type);
|
||||
|
||||
vpnConfiguration[config_key::configVersion] = serverConfig.value(config_key::configVersion).toInt();
|
||||
// TODO: try to get hostName, port, description for 3rd party configs
|
||||
// vpnConfiguration[config_key::port] = ...;
|
||||
|
||||
return vpnConfiguration;
|
||||
}
|
||||
|
||||
void VpnConfigurationsController::updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig,
|
||||
const QString &stdOut)
|
||||
void VpnConfigurationsController::updateContainerConfigAfterInstallation(const DockerContainer container, ContainerConfig &containerConfig,
|
||||
const QString &stdOut)
|
||||
{
|
||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||
|
||||
if (container == DockerContainer::TorWebSite) {
|
||||
QJsonObject protocol = containerConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
|
||||
QString protocolName = ProtocolProps::protoToString(mainProto);
|
||||
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
|
||||
|
||||
qDebug() << "amnezia-tor onions" << stdOut;
|
||||
|
||||
QString onion = stdOut;
|
||||
onion.replace("\n", "");
|
||||
protocol.insert(config_key::site, onion);
|
||||
|
||||
containerConfig.insert(ProtocolProps::protoToString(mainProto), protocol);
|
||||
|
||||
if (auto torConfig = qSharedPointerCast<TorWebsiteProtocolConfig>(protocolConfig)) {
|
||||
torConfig->serverProtocolConfig.site = onion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,28 +4,35 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "configurators/configurator_base.h"
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
#include "core/models/containers/containerConfig.h"
|
||||
#include "core/defs.h"
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
#include "core/models/servers/serverConfig.h"
|
||||
#include "settings.h"
|
||||
|
||||
class VpnConfigurationsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VpnConfigurationsController(const std::shared_ptr<Settings> &settings, QSharedPointer<ServerController> serverController, QObject *parent = nullptr);
|
||||
explicit VpnConfigurationsController(const std::shared_ptr<Settings> &settings, QSharedPointer<ServerController> serverController,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container,
|
||||
QJsonObject &containerConfig);
|
||||
ContainerConfig &containerConfig);
|
||||
ErrorCode createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns, const ServerCredentials &credentials,
|
||||
const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol,
|
||||
const DockerContainer container, const ContainerConfig &containerConfig, const Proto protocol,
|
||||
QString &protocolConfigString);
|
||||
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
|
||||
const QJsonObject &containerConfig, const DockerContainer container, ErrorCode &errorCode);
|
||||
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QSharedPointer<ServerConfig> &serverConfig,
|
||||
const ContainerConfig &containerConfig, const DockerContainer container);
|
||||
|
||||
static void updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig, const QString &stdOut);
|
||||
static void updateContainerConfigAfterInstallation(const DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut);
|
||||
signals:
|
||||
|
||||
public:
|
||||
QSharedPointer<ProtocolConfig> createProtocolConfig(const Proto protocol);
|
||||
|
||||
private:
|
||||
QScopedPointer<ConfiguratorBase> createConfigurator(const Proto protocol);
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
|
||||
namespace amnezia
|
||||
{
|
||||
|
||||
constexpr const qint16 qrMagicCode = 1984;
|
||||
|
||||
struct ServerCredentials
|
||||
{
|
||||
QString hostName;
|
||||
@@ -22,6 +19,13 @@ namespace amnezia
|
||||
}
|
||||
};
|
||||
|
||||
enum ServerConfigType
|
||||
{
|
||||
SelfHosted,
|
||||
ApiV1,
|
||||
ApiV2
|
||||
};
|
||||
|
||||
struct InstalledAppInfo {
|
||||
QString appName;
|
||||
QString packageName;
|
||||
@@ -47,6 +51,7 @@ namespace amnezia
|
||||
InternalError = 101,
|
||||
NotImplementedError = 102,
|
||||
AmneziaServiceNotRunning = 103,
|
||||
NotSupportedOnThisPlatform = 104,
|
||||
|
||||
// Server errors
|
||||
ServerCheckFailed = 200,
|
||||
@@ -56,6 +61,13 @@ namespace amnezia
|
||||
ServerCancelInstallation = 204,
|
||||
ServerUserNotInSudo = 205,
|
||||
ServerPacketManagerError = 206,
|
||||
ServerSudoPackageIsNotPreinstalled = 207,
|
||||
ServerUserDirectoryNotAccessible = 208,
|
||||
ServerUserNotAllowedInSudoers = 209,
|
||||
ServerUserPasswordRequired = 210,
|
||||
ServerDockerOnCgroupsV2 = 211,
|
||||
ServerCgroupMountpoint = 212,
|
||||
DockerPullRateLimit = 213,
|
||||
|
||||
// Ssh connection errors
|
||||
SshRequestDeniedError = 300,
|
||||
@@ -97,6 +109,7 @@ namespace amnezia
|
||||
// import and install errors
|
||||
ImportInvalidConfigError = 900,
|
||||
ImportOpenConfigError = 901,
|
||||
NoInstalledContainersError = 902,
|
||||
|
||||
// Android errors
|
||||
AndroidError = 1000,
|
||||
@@ -109,6 +122,11 @@ namespace amnezia
|
||||
ApiConfigSslError = 1104,
|
||||
ApiMissingAgwPublicKey = 1105,
|
||||
ApiConfigDecryptionError = 1106,
|
||||
ApiServicesMissingError = 1107,
|
||||
ApiConfigLimitError = 1108,
|
||||
ApiNotFoundError = 1109,
|
||||
ApiMigrationError = 1110,
|
||||
ApiUpdateRequestError = 1111,
|
||||
|
||||
// QFile errors
|
||||
OpenError = 1200,
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef APIENUMS_H
|
||||
#define APIENUMS_H
|
||||
|
||||
enum ApiConfigSources {
|
||||
Telegram = 1,
|
||||
AmneziaGateway
|
||||
};
|
||||
|
||||
#endif // APIENUMS_H
|
||||
@@ -12,6 +12,7 @@ QString errorString(ErrorCode code) {
|
||||
case(ErrorCode::UnknownError): errorMessage = QObject::tr("Unknown error"); 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::NotSupportedOnThisPlatform): errorMessage = QObject::tr("The selected protocol is not supported on the current platform"); break;
|
||||
|
||||
// Server errors
|
||||
case(ErrorCode::ServerCheckFailed): errorMessage = QObject::tr("Server check failed"); break;
|
||||
@@ -19,8 +20,15 @@ QString errorString(ErrorCode code) {
|
||||
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::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); 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: Packet manager error"); break;
|
||||
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user is not a member of the sudo group"); break;
|
||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Package 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
|
||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
||||
@@ -51,6 +59,7 @@ QString errorString(ErrorCode code) {
|
||||
|
||||
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
|
||||
case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break;
|
||||
@@ -63,7 +72,12 @@ QString errorString(ErrorCode code) {
|
||||
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::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
|
||||
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;
|
||||
|
||||
@@ -5,12 +5,12 @@ IpcClient *IpcClient::m_instance = nullptr;
|
||||
|
||||
IpcClient::IpcClient(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
IpcClient::~IpcClient()
|
||||
{
|
||||
if (m_localSocket) m_localSocket->close();
|
||||
if (m_localSocket)
|
||||
m_localSocket->close();
|
||||
}
|
||||
|
||||
bool IpcClient::isSocketConnected() const
|
||||
@@ -25,13 +25,15 @@ IpcClient *IpcClient::Instance()
|
||||
|
||||
QSharedPointer<IpcInterfaceReplica> IpcClient::Interface()
|
||||
{
|
||||
if (!Instance()) return nullptr;
|
||||
if (!Instance())
|
||||
return nullptr;
|
||||
return Instance()->m_ipcClient;
|
||||
}
|
||||
|
||||
QSharedPointer<IpcProcessTun2SocksReplica> IpcClient::InterfaceTun2Socks()
|
||||
{
|
||||
if (!Instance()) return nullptr;
|
||||
if (!Instance())
|
||||
return nullptr;
|
||||
return Instance()->m_Tun2SocksClient;
|
||||
}
|
||||
|
||||
@@ -42,15 +44,28 @@ bool IpcClient::init(IpcClient *instance)
|
||||
Instance()->m_localSocket = new QLocalSocket(Instance());
|
||||
connect(Instance()->m_localSocket.data(), &QLocalSocket::connected, &Instance()->m_ClientNode, []() {
|
||||
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);
|
||||
|
||||
if (!Instance()->m_ipcClient->isReplicaValid()) {
|
||||
qWarning() << "IpcClient replica is not connected!";
|
||||
}
|
||||
|
||||
Instance()->m_Tun2SocksClient.reset(Instance()->m_ClientNode.acquire<IpcProcessTun2SocksReplica>());
|
||||
auto t2sNode = 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);
|
||||
|
||||
if (!Instance()->m_Tun2SocksClient->isReplicaValid()) {
|
||||
@@ -58,9 +73,8 @@ bool IpcClient::init(IpcClient *instance)
|
||||
}
|
||||
});
|
||||
|
||||
connect(Instance()->m_localSocket, &QLocalSocket::disconnected, [instance](){
|
||||
instance->m_isSocketConnected = false;
|
||||
});
|
||||
connect(Instance()->m_localSocket, &QLocalSocket::disconnected,
|
||||
[instance]() { instance->m_isSocketConnected = false; });
|
||||
|
||||
Instance()->m_localSocket->connectToServer(amnezia::getIpcServiceUrl());
|
||||
Instance()->m_localSocket->waitForConnected();
|
||||
@@ -77,7 +91,7 @@ bool IpcClient::init(IpcClient *instance)
|
||||
|
||||
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";
|
||||
return nullptr;
|
||||
}
|
||||
@@ -100,18 +114,15 @@ QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
|
||||
pd->ipcProcess.reset(priv);
|
||||
if (!pd->ipcProcess) {
|
||||
qWarning() << "Acquire PrivilegedProcess failed";
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pd->ipcProcess->waitForSource(1000);
|
||||
if (!pd->ipcProcess->isReplicaValid()) {
|
||||
qWarning() << "PrivilegedProcess replica is not connected!";
|
||||
}
|
||||
|
||||
QObject::connect(pd->ipcProcess.data(), &PrivilegedProcess::destroyed, pd->ipcProcess.data(), [pd](){
|
||||
pd->replicaNode->deleteLater();
|
||||
});
|
||||
QObject::connect(pd->ipcProcess.data(), &PrivilegedProcess::destroyed, pd->ipcProcess.data(),
|
||||
[pd]() { pd->replicaNode->deleteLater(); });
|
||||
}
|
||||
|
||||
});
|
||||
pd->localSocket->connectToServer(amnezia::getIpcProcessUrl(pid));
|
||||
pd->localSocket->waitForConnected();
|
||||
@@ -119,5 +130,3 @@ QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
|
||||
auto processReplica = QSharedPointer<PrivilegedProcess>(pd->ipcProcess);
|
||||
return processReplica;
|
||||
}
|
||||
|
||||
|
||||
|
||||
76
client/core/models/clientInfo.cpp
Normal file
76
client/core/models/clientInfo.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "clientInfo.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace configKey
|
||||
{
|
||||
constexpr char clientId[] = "clientId";
|
||||
constexpr char clientName[] = "clientName";
|
||||
constexpr char container[] = "container";
|
||||
constexpr char userData[] = "userData";
|
||||
constexpr char creationDate[] = "creationDate";
|
||||
constexpr char latestHandshake[] = "latestHandshake";
|
||||
constexpr char dataReceived[] = "dataReceived";
|
||||
constexpr char dataSent[] = "dataSent";
|
||||
constexpr char allowedIps[] = "allowedIps";
|
||||
}
|
||||
}
|
||||
|
||||
ClientInfo::ClientInfo()
|
||||
: container(DockerContainer::None)
|
||||
{
|
||||
}
|
||||
|
||||
ClientInfo::ClientInfo(const QString &clientId, const QString &clientName)
|
||||
: clientId(clientId), clientName(clientName), creationDate(QDateTime::currentDateTime()), container(DockerContainer::None)
|
||||
{
|
||||
}
|
||||
|
||||
ClientInfo::ClientInfo(const QJsonObject &jsonObject)
|
||||
{
|
||||
clientId = jsonObject.value(configKey::clientId).toString();
|
||||
container = ContainerProps::containerFromString(jsonObject.value(configKey::container).toString());
|
||||
|
||||
QJsonObject userData = jsonObject.value(configKey::userData).toObject();
|
||||
clientName = userData.value(configKey::clientName).toString();
|
||||
creationDate = QDateTime::fromString(userData.value(configKey::creationDate).toString());
|
||||
|
||||
latestHandshake = jsonObject.value(configKey::latestHandshake).toString();
|
||||
dataReceived = jsonObject.value(configKey::dataReceived).toString();
|
||||
dataSent = jsonObject.value(configKey::dataSent).toString();
|
||||
allowedIps = jsonObject.value(configKey::allowedIps).toString();
|
||||
}
|
||||
|
||||
QJsonObject ClientInfo::toJson() const
|
||||
{
|
||||
QJsonObject jsonObject;
|
||||
jsonObject[configKey::clientId] = clientId;
|
||||
jsonObject[configKey::container] = ContainerProps::containerToString(container);
|
||||
|
||||
QJsonObject userData;
|
||||
userData[configKey::clientName] = clientName;
|
||||
userData[configKey::creationDate] = creationDate.toString();
|
||||
jsonObject[configKey::userData] = userData;
|
||||
|
||||
if (!latestHandshake.isEmpty()) {
|
||||
jsonObject[configKey::latestHandshake] = latestHandshake;
|
||||
}
|
||||
if (!dataReceived.isEmpty()) {
|
||||
jsonObject[configKey::dataReceived] = dataReceived;
|
||||
}
|
||||
if (!dataSent.isEmpty()) {
|
||||
jsonObject[configKey::dataSent] = dataSent;
|
||||
}
|
||||
if (!allowedIps.isEmpty()) {
|
||||
jsonObject[configKey::allowedIps] = allowedIps;
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
ClientInfo ClientInfo::fromJson(const QJsonObject &jsonObject)
|
||||
{
|
||||
return ClientInfo(jsonObject);
|
||||
}
|
||||
34
client/core/models/clientInfo.h
Normal file
34
client/core/models/clientInfo.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef CLIENTINFO_H
|
||||
#define CLIENTINFO_H
|
||||
|
||||
#include <QString>
|
||||
#include <QDateTime>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "core/models/containers/containers_defs.h"
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
class ClientInfo
|
||||
{
|
||||
public:
|
||||
ClientInfo();
|
||||
ClientInfo(const QString &clientId, const QString &clientName);
|
||||
ClientInfo(const QJsonObject &jsonObject);
|
||||
|
||||
QJsonObject toJson() const;
|
||||
|
||||
static ClientInfo fromJson(const QJsonObject &jsonObject);
|
||||
|
||||
QString clientId;
|
||||
QString clientName;
|
||||
QDateTime creationDate;
|
||||
DockerContainer container;
|
||||
|
||||
QString latestHandshake;
|
||||
QString dataReceived;
|
||||
QString dataSent;
|
||||
QString allowedIps;
|
||||
};
|
||||
|
||||
#endif // CLIENTINFO_H
|
||||
5
client/core/models/containers/containerConfig.cpp
Normal file
5
client/core/models/containers/containerConfig.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "containerConfig.h"
|
||||
|
||||
ContainerConfig::ContainerConfig()
|
||||
{
|
||||
}
|
||||
21
client/core/models/containers/containerConfig.h
Normal file
21
client/core/models/containers/containerConfig.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef CONTAINERCONFIG_H
|
||||
#define CONTAINERCONFIG_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
|
||||
#include "core/models/protocols/protocolConfig.h"
|
||||
#include "containers_defs.h"
|
||||
|
||||
class ContainerConfig
|
||||
{
|
||||
public:
|
||||
ContainerConfig();
|
||||
|
||||
QString containerName;
|
||||
amnezia::DockerContainer containerType;
|
||||
QMap<QString, QSharedPointer<ProtocolConfig>> protocolConfigs;
|
||||
};
|
||||
|
||||
#endif // CONTAINERCONFIG_H
|
||||
@@ -110,22 +110,19 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
|
||||
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.") },
|
||||
{ DockerContainer::ShadowSocks,
|
||||
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.") },
|
||||
QObject::tr("Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems.") },
|
||||
{ DockerContainer::Cloak,
|
||||
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
|
||||
"active-probing detection. Ideal for bypassing blocking in regions with the highest levels "
|
||||
"of censorship.") },
|
||||
"active-probing detection. It is very resistant to detection, but offers low speed.") },
|
||||
{ DockerContainer::WireGuard,
|
||||
QObject::tr("WireGuard - New popular VPN protocol with high performance, high speed and low power "
|
||||
"consumption. Recommended for regions with low levels of censorship.") },
|
||||
QObject::tr("WireGuard - popular VPN protocol with high performance, high speed and low power "
|
||||
"consumption.") },
|
||||
{ DockerContainer::Awg,
|
||||
QObject::tr("AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, "
|
||||
"but very resistant to blockages. "
|
||||
"Recommended for regions with high levels of censorship.") },
|
||||
QObject::tr("AmneziaWG is a special protocol from Amnezia based on WireGuard. "
|
||||
"It provides high connection speed and ensures stable operation even in the most challenging network conditions.") },
|
||||
{ DockerContainer::Xray,
|
||||
QObject::tr("XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
|
||||
"Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
|
||||
QObject::tr("XRay with REALITY masks VPN traffic as web traffic and protects against active probing. "
|
||||
"It is highly resistant to detection and offers high speed.") },
|
||||
{ DockerContainer::Ipsec,
|
||||
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.") },
|
||||
@@ -143,100 +140,83 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||
{
|
||||
return {
|
||||
{ DockerContainer::OpenVpn,
|
||||
QObject::tr(
|
||||
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
|
||||
"It employs its unique security protocol, "
|
||||
"leveraging the strength of SSL/TLS for encryption and key exchange. "
|
||||
"Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, "
|
||||
"catering to a wide range of devices and operating systems. "
|
||||
"Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, "
|
||||
"which continually reinforces its security. "
|
||||
"With a strong balance of performance, security, and compatibility, "
|
||||
"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.") },
|
||||
QObject::tr("OpenVPN is one of the most popular and reliable VPN protocols. "
|
||||
"It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, "
|
||||
"and is continuously improved by the community due to its open-source nature. "
|
||||
"It provides a good balance between speed and security but is easily recognized by DPI systems, "
|
||||
"making it susceptible to blocking.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available on all AmneziaVPN platforms\n"
|
||||
"* Normal battery consumption on mobile devices\n"
|
||||
"* Flexible customization for various devices and OS\n"
|
||||
"* Operates over both TCP and UDP protocols") },
|
||||
{ DockerContainer::ShadowSocks,
|
||||
QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. "
|
||||
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection."
|
||||
"However, certain traffic analysis systems might still detect a Shadowsocks connection. "
|
||||
"Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n"
|
||||
"* Available in the AmneziaVPN only on desktop platforms\n"
|
||||
"* Configurable encryption protocol\n"
|
||||
QObject::tr("Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. "
|
||||
"Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. "
|
||||
"Due to limited support in Amnezia, we recommend using the AmneziaWG protocol.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available in AmneziaVPN only on desktop platforms\n"
|
||||
"* Customizable encryption protocol\n"
|
||||
"* Detectable by some DPI systems\n"
|
||||
"* Works over TCP network protocol.") },
|
||||
"* Operates over TCP protocol\n") },
|
||||
{ DockerContainer::Cloak,
|
||||
QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for "
|
||||
"protecting against blocking.\n\n"
|
||||
"OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client "
|
||||
"and the server.\n\n"
|
||||
"Cloak protects OpenVPN from detection and blocking. \n\n"
|
||||
"Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, "
|
||||
"and also protects the VPN from detection by Active Probing. This makes it very resistant to "
|
||||
"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"
|
||||
QObject::tr("This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.\n"
|
||||
"\nOpenVPN securely encrypts all internet traffic between your device and the server.\n"
|
||||
"\nThe Cloak plugin further protects the connection from DPI detection. "
|
||||
"It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. "
|
||||
"If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems.\n"
|
||||
"\nIn regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available on all AmneziaVPN platforms\n"
|
||||
"* High power consumption on mobile devices\n"
|
||||
"* Flexible settings\n"
|
||||
"* Not recognised by DPI analysis systems\n"
|
||||
"* Works over TCP network protocol, 443 port.\n") },
|
||||
"* Flexible configuration options\n"
|
||||
"* Undetectable by DPI systems\n"
|
||||
"* Operates over TCP protocol on port 443") },
|
||||
{ DockerContainer::WireGuard,
|
||||
QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n"
|
||||
"WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption "
|
||||
"settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n"
|
||||
"WireGuard is very susceptible to blocking due to its distinct packet signatures. "
|
||||
"Unlike some other VPN protocols that employ obfuscation techniques, "
|
||||
"the consistent signature patterns of WireGuard packets can be more easily identified and "
|
||||
"thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n"
|
||||
"* Available in the AmneziaVPN across all platforms\n"
|
||||
"* Low power consumption\n"
|
||||
"* Minimum number of settings\n"
|
||||
"* Easily recognised by DPI analysis systems, susceptible to blocking\n"
|
||||
"* Works over UDP network protocol.") },
|
||||
QObject::tr("WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. "
|
||||
"It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. "
|
||||
"However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available on all AmneziaVPN platforms\n"
|
||||
"* Low power consumption on mobile devices\n"
|
||||
"* Minimal configuration required\n"
|
||||
"* Easily detected by DPI systems (susceptible to blocking)\n"
|
||||
"* Operates over UDP protocol") },
|
||||
{ DockerContainer::Awg,
|
||||
QObject::tr("A modern iteration of the popular VPN protocol, "
|
||||
"AmneziaWG builds upon the foundation set by WireGuard, "
|
||||
"retaining its simplified architecture and high-performance capabilities across devices.\n"
|
||||
"While WireGuard is known for its efficiency, "
|
||||
"it had issues with being easily detected due to its distinct packet signatures. "
|
||||
"AmneziaWG solves this problem by using better obfuscation methods, "
|
||||
"making its traffic blend in with regular internet traffic.\n"
|
||||
"This means that AmneziaWG keeps the fast performance of the original "
|
||||
"while adding an extra layer of stealth, "
|
||||
"making it a great choice for those wanting a fast and discreet VPN connection.\n\n"
|
||||
"* 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.") },
|
||||
QObject::tr("AmneziaWG is a modern VPN protocol based on WireGuard, "
|
||||
"combining simplified architecture with high performance across all devices. "
|
||||
"It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, "
|
||||
"making VPN traffic indistinguishable from regular internet traffic.\n"
|
||||
"\nAmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available on all AmneziaVPN platforms\n"
|
||||
"* Low battery consumption on mobile devices\n"
|
||||
"* Minimal settings required\n"
|
||||
"* Undetectable by traffic analysis systems (DPI)\n"
|
||||
"* Operates over UDP protocol") },
|
||||
{ DockerContainer::Xray,
|
||||
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
|
||||
"is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion.\n"
|
||||
"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, "
|
||||
"thus presenting an authentic TLS certificate and data. \n"
|
||||
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
|
||||
"legitimate sites without the need for specific configurations. \n"
|
||||
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
|
||||
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. "
|
||||
"This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.")
|
||||
},
|
||||
QObject::tr("REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. "
|
||||
"REALITY identifies censorship systems during the TLS handshake, "
|
||||
"redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. "
|
||||
"This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration."
|
||||
"\nUnlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in \"friend-or-foe\" detection mechanism, "
|
||||
"effectively protecting against DPI and other traffic analysis methods.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Resistant to active probing and DPI detection\n"
|
||||
"* No special configuration required to disguise traffic\n"
|
||||
"* Highly effective in heavily censored regions\n"
|
||||
"* Minimal battery consumption on devices\n"
|
||||
"* Operates over TCP protocol") },
|
||||
{ DockerContainer::Ipsec,
|
||||
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
|
||||
"One of its distinguishing features is its ability to swiftly switch between networks and devices, "
|
||||
"making it particularly adaptive in dynamic network environments. \n"
|
||||
"While it offers a blend of security, stability, and speed, "
|
||||
"it's essential to note that IKEv2 can be easily detected and is susceptible to blocking.\n\n"
|
||||
"* Available in the AmneziaVPN only on Windows\n"
|
||||
"* Low power consumption, on mobile devices\n"
|
||||
"* Minimal configuration\n"
|
||||
"* Recognised by DPI analysis systems\n"
|
||||
"* Works over UDP network protocol, ports 500 and 4500.") },
|
||||
QObject::tr("IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. "
|
||||
"It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. "
|
||||
"While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking.\n"
|
||||
"\nFeatures:\n"
|
||||
"* Available in AmneziaVPN only on Windows\n"
|
||||
"* Low battery consumption on mobile devices\n"
|
||||
"* Minimal configuration required\n"
|
||||
"* Detectable by DPI analysis systems(easily blocked)\n"
|
||||
"* Operates over UDP protocol(ports 500 and 4500)") },
|
||||
|
||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
||||
@@ -332,9 +312,7 @@ QStringList ContainerProps::fixedPortsForContainer(DockerContainer c)
|
||||
bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Awg: return true;
|
||||
// case DockerContainer::Cloak: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -342,9 +320,7 @@ bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
||||
QString ContainerProps::easySetupHeader(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return tr("Low");
|
||||
case DockerContainer::Awg: return tr("High");
|
||||
// case DockerContainer::Cloak: return tr("Extreme");
|
||||
case DockerContainer::Awg: return tr("Automatic");
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
@@ -352,10 +328,8 @@ QString ContainerProps::easySetupHeader(DockerContainer container)
|
||||
QString ContainerProps::easySetupDescription(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return tr("I just want to increase the level of my privacy.");
|
||||
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.");
|
||||
case DockerContainer::Awg: return tr("AmneziaWG protocol will be installed. "
|
||||
"It provides high connection speed and ensures stable operation even in the most challenging network conditions.");
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
@@ -363,9 +337,7 @@ QString ContainerProps::easySetupDescription(DockerContainer container)
|
||||
int ContainerProps::easySetupOrder(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return 3;
|
||||
case DockerContainer::Awg: return 2;
|
||||
// case DockerContainer::Cloak: return 1;
|
||||
case DockerContainer::Awg: return 1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
@@ -384,9 +356,9 @@ bool ContainerProps::isShareable(DockerContainer container)
|
||||
QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
|
||||
{
|
||||
QString protocolConfigString = containerConfig.value(ProtocolProps::protoToString(protocol))
|
||||
.toObject()
|
||||
.value(config_key::last_config)
|
||||
.toString();
|
||||
.toObject()
|
||||
.value(config_key::last_config)
|
||||
.toString();
|
||||
|
||||
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
||||
}
|
||||
256
client/core/models/protocols/awgProtocolConfig.cpp
Normal file
256
client/core/models/protocols/awgProtocolConfig.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
#include "awgProtocolConfig.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
AwgProtocolConfig::AwgProtocolConfig(const QString &protocolName) : ProtocolConfig(protocolName)
|
||||
{
|
||||
}
|
||||
|
||||
AwgProtocolConfig::AwgProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
|
||||
{
|
||||
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::awg::defaultPort);
|
||||
serverProtocolConfig.transportProto = protocolConfigObject.value(config_key::transport_proto).toString("udp");
|
||||
serverProtocolConfig.subnetAddress = protocolConfigObject.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
serverProtocolConfig.mtu = protocolConfigObject.value(config_key::mtu).toString(protocols::awg::defaultMtu);
|
||||
|
||||
serverProtocolConfig.awgData.junkPacketCount = protocolConfigObject.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
|
||||
serverProtocolConfig.awgData.junkPacketMinSize = protocolConfigObject.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize);
|
||||
serverProtocolConfig.awgData.junkPacketMaxSize = protocolConfigObject.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize);
|
||||
serverProtocolConfig.awgData.initPacketJunkSize = protocolConfigObject.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
|
||||
serverProtocolConfig.awgData.responsePacketJunkSize = protocolConfigObject.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
|
||||
serverProtocolConfig.awgData.cookieReplyPacketJunkSize = protocolConfigObject.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize);
|
||||
serverProtocolConfig.awgData.transportPacketJunkSize = protocolConfigObject.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize);
|
||||
serverProtocolConfig.awgData.initPacketMagicHeader = protocolConfigObject.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
|
||||
serverProtocolConfig.awgData.responsePacketMagicHeader = protocolConfigObject.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader);
|
||||
serverProtocolConfig.awgData.underloadPacketMagicHeader = protocolConfigObject.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader);
|
||||
serverProtocolConfig.awgData.transportPacketMagicHeader = protocolConfigObject.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader);
|
||||
|
||||
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
|
||||
if (!clientProtocolString.isEmpty()) {
|
||||
clientProtocolConfig.isEmpty = false;
|
||||
|
||||
QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object();
|
||||
|
||||
clientProtocolConfig.awgData.junkPacketCount = clientProtocolConfigObject.value(config_key::junkPacketCount).toString();
|
||||
clientProtocolConfig.awgData.junkPacketMinSize = clientProtocolConfigObject.value(config_key::junkPacketMinSize).toString();
|
||||
clientProtocolConfig.awgData.junkPacketMaxSize = clientProtocolConfigObject.value(config_key::junkPacketMaxSize).toString();
|
||||
clientProtocolConfig.awgData.initPacketJunkSize = clientProtocolConfigObject.value(config_key::initPacketJunkSize).toString();
|
||||
clientProtocolConfig.awgData.responsePacketJunkSize = clientProtocolConfigObject.value(config_key::responsePacketJunkSize).toString();
|
||||
clientProtocolConfig.awgData.cookieReplyPacketJunkSize = clientProtocolConfigObject.value(config_key::cookieReplyPacketJunkSize).toString();
|
||||
clientProtocolConfig.awgData.transportPacketJunkSize = clientProtocolConfigObject.value(config_key::transportPacketJunkSize).toString();
|
||||
clientProtocolConfig.awgData.initPacketMagicHeader = clientProtocolConfigObject.value(config_key::initPacketMagicHeader).toString();
|
||||
clientProtocolConfig.awgData.responsePacketMagicHeader =
|
||||
clientProtocolConfigObject.value(config_key::responsePacketMagicHeader).toString();
|
||||
clientProtocolConfig.awgData.underloadPacketMagicHeader =
|
||||
clientProtocolConfigObject.value(config_key::underloadPacketMagicHeader).toString();
|
||||
clientProtocolConfig.awgData.transportPacketMagicHeader =
|
||||
clientProtocolConfigObject.value(config_key::transportPacketMagicHeader).toString();
|
||||
|
||||
clientProtocolConfig.clientId = clientProtocolConfigObject.value(config_key::clientId).toString();
|
||||
|
||||
clientProtocolConfig.wireGuardData.clientIp = clientProtocolConfigObject.value(config_key::client_ip).toString();
|
||||
clientProtocolConfig.wireGuardData.clientPrivateKey = clientProtocolConfigObject.value(config_key::client_priv_key).toString();
|
||||
clientProtocolConfig.wireGuardData.clientPublicKey = clientProtocolConfigObject.value(config_key::client_pub_key).toString();
|
||||
clientProtocolConfig.wireGuardData.persistentKeepAlive =
|
||||
clientProtocolConfigObject.value(config_key::persistent_keep_alive).toString();
|
||||
clientProtocolConfig.wireGuardData.pskKey = clientProtocolConfigObject.value(config_key::psk_key).toString();
|
||||
clientProtocolConfig.wireGuardData.serverPubKey = clientProtocolConfigObject.value(config_key::server_pub_key).toString();
|
||||
clientProtocolConfig.wireGuardData.mtu = clientProtocolConfigObject.value(config_key::mtu).toString();
|
||||
|
||||
clientProtocolConfig.hostname = clientProtocolConfigObject.value(config_key::hostName).toString();
|
||||
clientProtocolConfig.port = clientProtocolConfigObject.value(config_key::port).toInt(0);
|
||||
|
||||
clientProtocolConfig.nativeConfig = clientProtocolConfigObject.value(config_key::config).toString();
|
||||
|
||||
if (clientProtocolConfigObject.contains(config_key::allowed_ips)
|
||||
&& clientProtocolConfigObject.value(config_key::allowed_ips).isArray()) {
|
||||
auto allowedIpsArray = clientProtocolConfigObject.value(config_key::allowed_ips).toArray();
|
||||
for (const auto &ip : allowedIpsArray) {
|
||||
clientProtocolConfig.wireGuardData.allowedIps.append(ip.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgProtocolConfig::AwgProtocolConfig(const AwgProtocolConfig &other) : ProtocolConfig(other.protocolName)
|
||||
{
|
||||
serverProtocolConfig = other.serverProtocolConfig;
|
||||
clientProtocolConfig = other.clientProtocolConfig;
|
||||
}
|
||||
|
||||
QJsonObject AwgProtocolConfig::toJson() const
|
||||
{
|
||||
QJsonObject json;
|
||||
|
||||
if (!serverProtocolConfig.port.isEmpty()) {
|
||||
json[config_key::port] = serverProtocolConfig.port;
|
||||
}
|
||||
if (!serverProtocolConfig.transportProto.isEmpty()) {
|
||||
json[config_key::transport_proto] = serverProtocolConfig.transportProto;
|
||||
}
|
||||
if (!serverProtocolConfig.subnetAddress.isEmpty()) {
|
||||
json[config_key::subnet_address] = serverProtocolConfig.subnetAddress;
|
||||
}
|
||||
if (!serverProtocolConfig.mtu.isEmpty()) {
|
||||
json[config_key::mtu] = serverProtocolConfig.mtu;
|
||||
}
|
||||
|
||||
if (!serverProtocolConfig.awgData.junkPacketCount.isEmpty()) {
|
||||
json[config_key::junkPacketCount] = serverProtocolConfig.awgData.junkPacketCount;
|
||||
}
|
||||
if (!serverProtocolConfig.awgData.junkPacketMinSize.isEmpty()) {
|
||||
json[config_key::junkPacketMinSize] = serverProtocolConfig.awgData.junkPacketMinSize;
|
||||
}
|
||||
if (!serverProtocolConfig.awgData.junkPacketMaxSize.isEmpty()) {
|
||||
json[config_key::junkPacketMaxSize] = serverProtocolConfig.awgData.junkPacketMaxSize;
|
||||
}
|
||||
if (!serverProtocolConfig.awgData.initPacketJunkSize.isEmpty()) {
|
||||
json[config_key::initPacketJunkSize] = serverProtocolConfig.awgData.initPacketJunkSize;
|
||||
}
|
||||
if (!serverProtocolConfig.awgData.responsePacketJunkSize.isEmpty()) {
|
||||
json[config_key::responsePacketJunkSize] = serverProtocolConfig.awgData.responsePacketJunkSize;
|
||||
}
|
||||
if (!serverProtocolConfig.awgData.cookieReplyPacketJunkSize.isEmpty()) {
|
||||
json[config_key::cookieReplyPacketJunkSize] = serverProtocolConfig.awgData.cookieReplyPacketJunkSize;
|
||||
}
|
||||
if (!serverProtocolConfig.awgData.transportPacketJunkSize.isEmpty()) {
|
||||
json[config_key::transportPacketJunkSize] = serverProtocolConfig.awgData.transportPacketJunkSize;
|
||||
}
|
||||
if (!serverProtocolConfig.awgData.initPacketMagicHeader.isEmpty()) {
|
||||
json[config_key::initPacketMagicHeader] = serverProtocolConfig.awgData.initPacketMagicHeader;
|
||||
}
|
||||
if (!serverProtocolConfig.awgData.responsePacketMagicHeader.isEmpty()) {
|
||||
json[config_key::responsePacketMagicHeader] = serverProtocolConfig.awgData.responsePacketMagicHeader;
|
||||
}
|
||||
if (!serverProtocolConfig.awgData.underloadPacketMagicHeader.isEmpty()) {
|
||||
json[config_key::underloadPacketMagicHeader] = serverProtocolConfig.awgData.underloadPacketMagicHeader;
|
||||
}
|
||||
if (!serverProtocolConfig.awgData.transportPacketMagicHeader.isEmpty()) {
|
||||
json[config_key::transportPacketMagicHeader] = serverProtocolConfig.awgData.transportPacketMagicHeader;
|
||||
}
|
||||
|
||||
if (!clientProtocolConfig.isEmpty) {
|
||||
QJsonObject clientConfigJson;
|
||||
|
||||
if (!clientProtocolConfig.clientId.isEmpty()) {
|
||||
clientConfigJson[config_key::clientId] = clientProtocolConfig.clientId;
|
||||
}
|
||||
|
||||
if (!clientProtocolConfig.awgData.junkPacketCount.isEmpty()) {
|
||||
clientConfigJson[config_key::junkPacketCount] = clientProtocolConfig.awgData.junkPacketCount;
|
||||
}
|
||||
if (!clientProtocolConfig.awgData.junkPacketMinSize.isEmpty()) {
|
||||
clientConfigJson[config_key::junkPacketMinSize] = clientProtocolConfig.awgData.junkPacketMinSize;
|
||||
}
|
||||
if (!clientProtocolConfig.awgData.junkPacketMaxSize.isEmpty()) {
|
||||
clientConfigJson[config_key::junkPacketMaxSize] = clientProtocolConfig.awgData.junkPacketMaxSize;
|
||||
}
|
||||
if (!clientProtocolConfig.awgData.initPacketJunkSize.isEmpty()) {
|
||||
clientConfigJson[config_key::initPacketJunkSize] = clientProtocolConfig.awgData.initPacketJunkSize;
|
||||
}
|
||||
if (!clientProtocolConfig.awgData.responsePacketJunkSize.isEmpty()) {
|
||||
clientConfigJson[config_key::responsePacketJunkSize] = clientProtocolConfig.awgData.responsePacketJunkSize;
|
||||
}
|
||||
if (!clientProtocolConfig.awgData.initPacketMagicHeader.isEmpty()) {
|
||||
clientConfigJson[config_key::initPacketMagicHeader] = clientProtocolConfig.awgData.initPacketMagicHeader;
|
||||
}
|
||||
if (!clientProtocolConfig.awgData.responsePacketMagicHeader.isEmpty()) {
|
||||
clientConfigJson[config_key::responsePacketMagicHeader] = clientProtocolConfig.awgData.responsePacketMagicHeader;
|
||||
}
|
||||
if (!clientProtocolConfig.awgData.underloadPacketMagicHeader.isEmpty()) {
|
||||
clientConfigJson[config_key::underloadPacketMagicHeader] = clientProtocolConfig.awgData.underloadPacketMagicHeader;
|
||||
}
|
||||
if (!clientProtocolConfig.awgData.transportPacketMagicHeader.isEmpty()) {
|
||||
clientConfigJson[config_key::transportPacketMagicHeader] = clientProtocolConfig.awgData.transportPacketMagicHeader;
|
||||
}
|
||||
|
||||
if (!clientProtocolConfig.wireGuardData.clientIp.isEmpty()) {
|
||||
clientConfigJson[config_key::client_ip] = clientProtocolConfig.wireGuardData.clientIp;
|
||||
}
|
||||
if (!clientProtocolConfig.wireGuardData.clientPrivateKey.isEmpty()) {
|
||||
clientConfigJson[config_key::client_priv_key] = clientProtocolConfig.wireGuardData.clientPrivateKey;
|
||||
}
|
||||
if (!clientProtocolConfig.wireGuardData.clientPublicKey.isEmpty()) {
|
||||
clientConfigJson[config_key::client_pub_key] = clientProtocolConfig.wireGuardData.clientPublicKey;
|
||||
}
|
||||
if (!clientProtocolConfig.wireGuardData.persistentKeepAlive.isEmpty()) {
|
||||
clientConfigJson[config_key::persistent_keep_alive] = clientProtocolConfig.wireGuardData.persistentKeepAlive;
|
||||
}
|
||||
if (!clientProtocolConfig.wireGuardData.pskKey.isEmpty()) {
|
||||
clientConfigJson[config_key::psk_key] = clientProtocolConfig.wireGuardData.pskKey;
|
||||
}
|
||||
if (!clientProtocolConfig.wireGuardData.serverPubKey.isEmpty()) {
|
||||
clientConfigJson[config_key::server_pub_key] = clientProtocolConfig.wireGuardData.serverPubKey;
|
||||
}
|
||||
if (!clientProtocolConfig.wireGuardData.mtu.isEmpty()) {
|
||||
clientConfigJson[config_key::mtu] = clientProtocolConfig.wireGuardData.mtu;
|
||||
}
|
||||
|
||||
if (!clientProtocolConfig.wireGuardData.allowedIps.isEmpty()) {
|
||||
QJsonArray allowedIpsArray;
|
||||
for (const auto &ip : clientProtocolConfig.wireGuardData.allowedIps) {
|
||||
if (!ip.isEmpty()) {
|
||||
allowedIpsArray.append(ip);
|
||||
}
|
||||
}
|
||||
if (!allowedIpsArray.isEmpty()) {
|
||||
clientConfigJson[config_key::allowed_ips] = allowedIpsArray;
|
||||
}
|
||||
}
|
||||
|
||||
if (!clientProtocolConfig.hostname.isEmpty()) {
|
||||
clientConfigJson[config_key::hostName] = clientProtocolConfig.hostname;
|
||||
}
|
||||
if (clientProtocolConfig.port) {
|
||||
clientConfigJson[config_key::port] = clientProtocolConfig.port;
|
||||
}
|
||||
if (!clientProtocolConfig.nativeConfig.isEmpty()) {
|
||||
clientConfigJson[config_key::config] = clientProtocolConfig.nativeConfig;
|
||||
}
|
||||
|
||||
if (!clientConfigJson.isEmpty()) {
|
||||
json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
bool AwgProtocolConfig::hasEqualServerSettings(const AwgProtocolConfig &other) const
|
||||
{
|
||||
if (serverProtocolConfig.subnetAddress != other.serverProtocolConfig.subnetAddress
|
||||
|| serverProtocolConfig.port != other.serverProtocolConfig.port
|
||||
|| serverProtocolConfig.awgData.junkPacketCount != other.serverProtocolConfig.awgData.junkPacketCount
|
||||
|| serverProtocolConfig.awgData.junkPacketMinSize != other.serverProtocolConfig.awgData.junkPacketMinSize
|
||||
|| serverProtocolConfig.awgData.junkPacketMaxSize != other.serverProtocolConfig.awgData.junkPacketMaxSize
|
||||
|| serverProtocolConfig.awgData.initPacketJunkSize != other.serverProtocolConfig.awgData.initPacketJunkSize
|
||||
|| serverProtocolConfig.awgData.responsePacketJunkSize != other.serverProtocolConfig.awgData.responsePacketJunkSize
|
||||
|| serverProtocolConfig.awgData.initPacketMagicHeader != other.serverProtocolConfig.awgData.initPacketMagicHeader
|
||||
|| serverProtocolConfig.awgData.responsePacketMagicHeader != other.serverProtocolConfig.awgData.responsePacketMagicHeader
|
||||
|| serverProtocolConfig.awgData.underloadPacketMagicHeader != other.serverProtocolConfig.awgData.underloadPacketMagicHeader
|
||||
|| serverProtocolConfig.awgData.transportPacketMagicHeader != other.serverProtocolConfig.awgData.transportPacketMagicHeader) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AwgProtocolConfig::hasEqualClientSettings(const AwgProtocolConfig &other) const
|
||||
{
|
||||
if (clientProtocolConfig.wireGuardData.mtu != other.clientProtocolConfig.wireGuardData.mtu
|
||||
|| clientProtocolConfig.awgData.junkPacketCount != other.clientProtocolConfig.awgData.junkPacketCount
|
||||
|| clientProtocolConfig.awgData.junkPacketMinSize != other.clientProtocolConfig.awgData.junkPacketMinSize
|
||||
|| clientProtocolConfig.awgData.junkPacketMaxSize != other.clientProtocolConfig.awgData.junkPacketMaxSize) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AwgProtocolConfig::clearClientSettings()
|
||||
{
|
||||
clientProtocolConfig = awg::ClientProtocolConfig();
|
||||
}
|
||||
76
client/core/models/protocols/awgProtocolConfig.h
Normal file
76
client/core/models/protocols/awgProtocolConfig.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef AWGPROTOCOLCONFIG_H
|
||||
#define AWGPROTOCOLCONFIG_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QStringList>
|
||||
|
||||
#include "protocolConfig.h"
|
||||
#include "wireguardProtocolConfig.h"
|
||||
|
||||
namespace awg
|
||||
{
|
||||
struct AwgData
|
||||
{
|
||||
QString junkPacketCount;
|
||||
QString junkPacketMinSize;
|
||||
QString junkPacketMaxSize;
|
||||
|
||||
QString initPacketJunkSize;
|
||||
QString responsePacketJunkSize;
|
||||
QString cookieReplyPacketJunkSize;
|
||||
QString transportPacketJunkSize;
|
||||
|
||||
QString initPacketMagicHeader;
|
||||
QString responsePacketMagicHeader;
|
||||
QString underloadPacketMagicHeader;
|
||||
QString transportPacketMagicHeader;
|
||||
};
|
||||
|
||||
struct ServerProtocolConfig
|
||||
{
|
||||
QString port;
|
||||
QString transportProto;
|
||||
|
||||
QString subnetAddress;
|
||||
|
||||
AwgData awgData;
|
||||
};
|
||||
|
||||
struct ClientProtocolConfig
|
||||
{
|
||||
bool isEmpty = true;
|
||||
|
||||
QString clientId;
|
||||
|
||||
wireguard::WireGuardData wireGuardData;
|
||||
|
||||
AwgData awgData;
|
||||
|
||||
QString hostname;
|
||||
int port;
|
||||
|
||||
QString nativeConfig;
|
||||
};
|
||||
|
||||
const int messageInitiationSize = 148;
|
||||
const int messageResponseSize = 92;
|
||||
}
|
||||
|
||||
class AwgProtocolConfig : public ProtocolConfig
|
||||
{
|
||||
public:
|
||||
AwgProtocolConfig(const QString &protocolName);
|
||||
AwgProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
|
||||
AwgProtocolConfig(const AwgProtocolConfig &other);
|
||||
|
||||
QJsonObject toJson() const override;
|
||||
|
||||
bool hasEqualServerSettings(const AwgProtocolConfig &other) const;
|
||||
bool hasEqualClientSettings(const AwgProtocolConfig &other) const;
|
||||
void clearClientSettings();
|
||||
|
||||
awg::ServerProtocolConfig serverProtocolConfig;
|
||||
awg::ClientProtocolConfig clientProtocolConfig;
|
||||
};
|
||||
|
||||
#endif // AWGPROTOCOLCONFIG_H
|
||||
58
client/core/models/protocols/cloakProtocolConfig.cpp
Normal file
58
client/core/models/protocols/cloakProtocolConfig.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "cloakProtocolConfig.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
CloakProtocolConfig::CloakProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
|
||||
{
|
||||
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::cloak::defaultPort);
|
||||
serverProtocolConfig.cipher = protocolConfigObject.value(config_key::cipher).toString(protocols::cloak::defaultCipher);
|
||||
serverProtocolConfig.site = protocolConfigObject.value(config_key::site).toString(protocols::cloak::defaultRedirSite);
|
||||
|
||||
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
|
||||
if (!clientProtocolString.isEmpty()) {
|
||||
clientProtocolConfig.isEmpty = false;
|
||||
|
||||
QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object();
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject CloakProtocolConfig::toJson() const
|
||||
{
|
||||
QJsonObject json;
|
||||
|
||||
if (!serverProtocolConfig.port.isEmpty()) {
|
||||
json[config_key::port] = serverProtocolConfig.port;
|
||||
}
|
||||
if (!serverProtocolConfig.cipher.isEmpty()) {
|
||||
json[config_key::cipher] = serverProtocolConfig.cipher;
|
||||
}
|
||||
if (!serverProtocolConfig.site.isEmpty()) {
|
||||
json[config_key::site] = serverProtocolConfig.site;
|
||||
}
|
||||
|
||||
if (!clientProtocolConfig.isEmpty) {
|
||||
QJsonObject clientConfigJson;
|
||||
json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson());
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
bool CloakProtocolConfig::hasEqualServerSettings(const CloakProtocolConfig &other) const
|
||||
{
|
||||
if (serverProtocolConfig.port != other.serverProtocolConfig.port ||
|
||||
serverProtocolConfig.cipher != other.serverProtocolConfig.cipher ||
|
||||
serverProtocolConfig.site != other.serverProtocolConfig.site) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CloakProtocolConfig::clearClientSettings()
|
||||
{
|
||||
clientProtocolConfig = cloak::ClientProtocolConfig();
|
||||
}
|
||||
52
client/core/models/protocols/cloakProtocolConfig.h
Normal file
52
client/core/models/protocols/cloakProtocolConfig.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef CLOAKPROTOCOLCONFIG_H
|
||||
#define CLOAKPROTOCOLCONFIG_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
#include "protocolConfig.h"
|
||||
|
||||
namespace cloak
|
||||
{
|
||||
struct ServerProtocolConfig
|
||||
{
|
||||
QString port;
|
||||
QString cipher;
|
||||
QString site;
|
||||
};
|
||||
|
||||
struct ClientProtocolConfig
|
||||
{
|
||||
bool isEmpty = true;
|
||||
|
||||
QString transport = "direct";
|
||||
QString proxyMethod = "openvpn";
|
||||
QString encryptionMethod = "aes-gcm";
|
||||
QString uid;
|
||||
QString publicKey;
|
||||
QString serverName;
|
||||
int numConn = 1;
|
||||
QString browserSig = "chrome";
|
||||
int streamTimeout = 300;
|
||||
QString remoteHost;
|
||||
QString remotePort;
|
||||
|
||||
QString nativeConfig;
|
||||
};
|
||||
}
|
||||
|
||||
class CloakProtocolConfig : public ProtocolConfig
|
||||
{
|
||||
public:
|
||||
CloakProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
|
||||
|
||||
QJsonObject toJson() const override;
|
||||
|
||||
bool hasEqualServerSettings(const CloakProtocolConfig &other) const;
|
||||
void clearClientSettings();
|
||||
|
||||
cloak::ServerProtocolConfig serverProtocolConfig;
|
||||
cloak::ClientProtocolConfig clientProtocolConfig;
|
||||
};
|
||||
|
||||
#endif // CLOAKPROTOCOLCONFIG_H
|
||||
32
client/core/models/protocols/ikev2ProtocolConfig.cpp
Normal file
32
client/core/models/protocols/ikev2ProtocolConfig.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "ikev2ProtocolConfig.h"
|
||||
|
||||
#include "config_key.h"
|
||||
|
||||
Ikev2ProtocolConfig::Ikev2ProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName)
|
||||
: ProtocolConfig(protocolName)
|
||||
{
|
||||
Q_UNUSED(protocolConfigObject)
|
||||
}
|
||||
|
||||
Ikev2ProtocolConfig::Ikev2ProtocolConfig(const QString &protocolName)
|
||||
: ProtocolConfig(protocolName)
|
||||
{
|
||||
}
|
||||
|
||||
QJsonObject Ikev2ProtocolConfig::toJson() const
|
||||
{
|
||||
QJsonObject protocolConfigObject;
|
||||
|
||||
return protocolConfigObject;
|
||||
}
|
||||
|
||||
bool Ikev2ProtocolConfig::hasEqualServerSettings(const Ikev2ProtocolConfig &other) const
|
||||
{
|
||||
Q_UNUSED(other)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Ikev2ProtocolConfig::clearClientSettings()
|
||||
{
|
||||
clientProtocolConfig = ikev2::ClientProtocolConfig();
|
||||
}
|
||||
45
client/core/models/protocols/ikev2ProtocolConfig.h
Normal file
45
client/core/models/protocols/ikev2ProtocolConfig.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef IKEV2PROTOCOLCONFIG_H
|
||||
#define IKEV2PROTOCOLCONFIG_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "protocolConfig.h"
|
||||
|
||||
namespace ikev2
|
||||
{
|
||||
struct ServerProtocolConfig
|
||||
{
|
||||
bool isEmpty = true;
|
||||
};
|
||||
|
||||
struct ClientProtocolConfig
|
||||
{
|
||||
bool isEmpty = true;
|
||||
|
||||
QString hostName;
|
||||
QString userName;
|
||||
QString cert;
|
||||
QString password;
|
||||
|
||||
QString nativeConfig;
|
||||
};
|
||||
}
|
||||
|
||||
class Ikev2ProtocolConfig : public ProtocolConfig
|
||||
{
|
||||
public:
|
||||
Ikev2ProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
|
||||
explicit Ikev2ProtocolConfig(const QString &protocolName);
|
||||
|
||||
QJsonObject toJson() const override;
|
||||
|
||||
bool hasEqualServerSettings(const Ikev2ProtocolConfig &other) const;
|
||||
void clearClientSettings();
|
||||
|
||||
ikev2::ServerProtocolConfig serverProtocolConfig;
|
||||
ikev2::ClientProtocolConfig clientProtocolConfig;
|
||||
};
|
||||
|
||||
#endif // IKEV2PROTOCOLCONFIG_H
|
||||
99
client/core/models/protocols/openvpnProtocolConfig.cpp
Normal file
99
client/core/models/protocols/openvpnProtocolConfig.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "openvpnProtocolConfig.h"
|
||||
|
||||
#include "protocols/protocols_defs.h"
|
||||
#include <QJsonDocument>
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
OpenVpnProtocolConfig::OpenVpnProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName)
|
||||
: ProtocolConfig(protocolName)
|
||||
{
|
||||
serverProtocolConfig.subnetAddress = protocolConfigObject.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress);
|
||||
serverProtocolConfig.transportProto = protocolConfigObject.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto);
|
||||
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::openvpn::defaultPort);
|
||||
serverProtocolConfig.ncpDisable = protocolConfigObject.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
|
||||
serverProtocolConfig.hash = protocolConfigObject.value(config_key::hash).toString(protocols::openvpn::defaultHash);
|
||||
serverProtocolConfig.cipher = protocolConfigObject.value(config_key::cipher).toString(protocols::openvpn::defaultCipher);
|
||||
serverProtocolConfig.tlsAuth = protocolConfigObject.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
|
||||
serverProtocolConfig.blockOutsideDns = protocolConfigObject.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns);
|
||||
serverProtocolConfig.additionalClientConfig = protocolConfigObject.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig);
|
||||
serverProtocolConfig.additionalServerConfig = protocolConfigObject.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig);
|
||||
|
||||
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
|
||||
if (!clientProtocolString.isEmpty()) {
|
||||
clientProtocolConfig.isEmpty = false;
|
||||
|
||||
QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object();
|
||||
|
||||
clientProtocolConfig.clientId = clientProtocolConfigObject.value(config_key::clientId).toString();
|
||||
clientProtocolConfig.nativeConfig = clientProtocolConfigObject.value(config_key::config).toString();
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject OpenVpnProtocolConfig::toJson() const
|
||||
{
|
||||
QJsonObject json;
|
||||
|
||||
if (!serverProtocolConfig.subnetAddress.isEmpty()) {
|
||||
json[config_key::subnet_address] = serverProtocolConfig.subnetAddress;
|
||||
}
|
||||
if (!serverProtocolConfig.transportProto.isEmpty()) {
|
||||
json[config_key::transport_proto] = serverProtocolConfig.transportProto;
|
||||
}
|
||||
if (!serverProtocolConfig.port.isEmpty()) {
|
||||
json[config_key::port] = serverProtocolConfig.port;
|
||||
}
|
||||
json[config_key::ncp_disable] = serverProtocolConfig.ncpDisable;
|
||||
if (!serverProtocolConfig.hash.isEmpty()) {
|
||||
json[config_key::hash] = serverProtocolConfig.hash;
|
||||
}
|
||||
if (!serverProtocolConfig.cipher.isEmpty()) {
|
||||
json[config_key::cipher] = serverProtocolConfig.cipher;
|
||||
}
|
||||
json[config_key::tls_auth] = serverProtocolConfig.tlsAuth;
|
||||
json[config_key::block_outside_dns] = serverProtocolConfig.blockOutsideDns;
|
||||
if (!serverProtocolConfig.additionalClientConfig.isEmpty()) {
|
||||
json[config_key::additional_client_config] = serverProtocolConfig.additionalClientConfig;
|
||||
}
|
||||
if (!serverProtocolConfig.additionalServerConfig.isEmpty()) {
|
||||
json[config_key::additional_server_config] = serverProtocolConfig.additionalServerConfig;
|
||||
}
|
||||
|
||||
if (!clientProtocolConfig.isEmpty) {
|
||||
QJsonObject clientConfigJson;
|
||||
|
||||
if (!clientProtocolConfig.clientId.isEmpty()) {
|
||||
clientConfigJson[config_key::clientId] = clientProtocolConfig.clientId;
|
||||
}
|
||||
if (!clientProtocolConfig.nativeConfig.isEmpty()) {
|
||||
clientConfigJson[config_key::config] = clientProtocolConfig.nativeConfig;
|
||||
}
|
||||
|
||||
if (!clientConfigJson.isEmpty()) {
|
||||
json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson());
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
bool OpenVpnProtocolConfig::hasEqualServerSettings(const OpenVpnProtocolConfig &other) const
|
||||
{
|
||||
if (serverProtocolConfig.subnetAddress != other.serverProtocolConfig.subnetAddress
|
||||
|| serverProtocolConfig.transportProto != other.serverProtocolConfig.transportProto
|
||||
|| serverProtocolConfig.port != other.serverProtocolConfig.port
|
||||
|| serverProtocolConfig.ncpDisable != other.serverProtocolConfig.ncpDisable
|
||||
|| serverProtocolConfig.hash != other.serverProtocolConfig.hash || serverProtocolConfig.cipher != other.serverProtocolConfig.cipher
|
||||
|| serverProtocolConfig.tlsAuth != other.serverProtocolConfig.tlsAuth
|
||||
|| serverProtocolConfig.blockOutsideDns != other.serverProtocolConfig.blockOutsideDns
|
||||
|| serverProtocolConfig.additionalClientConfig != other.serverProtocolConfig.additionalClientConfig
|
||||
|| serverProtocolConfig.additionalServerConfig != other.serverProtocolConfig.additionalServerConfig) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenVpnProtocolConfig::clearClientSettings()
|
||||
{
|
||||
clientProtocolConfig = openvpn::ClientProtocolConfig();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user